Friday, May 29, 2020

Something like a Summer Devlog, Part 3: Some things I learned using UE4 in May 2020

I spent the lion's share of May working on a prototype for a local multiplayer turn-based RPG in Unreal Engine 4.25. I feel like I've learned a lot from it, and this post is my attempt to gather some of the more important lessons in one place. At this point, it's not clear if I will continue this project, whether using UE4 or not, but I'll come to that later.

BindWidget

A lot of my project involves UI-based interactions, and the first couple of times through it, I had awkward splits of logic between C++ and Blueprint. I came across this post by Ben Humphreys in my Web searches, and it shone light on something I am sure I had not seen before: BindWidget. This gives you an easy way to access elements added in blueprint widgets. For example, consider a widget like this:

UCLASS()
class FOO_API UCustomWidget : public UUserWidget {
  GENERATED_BODY()

protected:
  UPROPERTY(BlueprintReadWrite, meta=(BindWidget))
  class UTextBlock* TheTextBlock;
}

Given a widget subclass—call it WBP_Custom—you can set its parent class to UCustomWidget, give it a widget called “TheTextBlock”, and then that widget will be accessible to the C++ implementation.

FClassFinder

A crucial counterpart to this for dynamically-created interfaces is ConstructorHelpers::FClassFinder. Let's say I have another widget, UCustomContainer, that needs to make one UCustomWidget for each connected player. That's the kind of thing I've had to do a lot in my project, for example. In order to instantiate the UCustomWidget, I need to know the actual runtime type, which is WBP_Custom, not just the C++ type UCustomWidget. I have done things like this using EditAnywhere UPROPERTY TSubclassOf<UCustomWidget&rt; fields before, but I found them fragile: the links often break when parts of the system are recompiled, leading to tedious plugging of types in through the editor.

Reading through discussion boards and blog posts, I think a more conventional and effective approach for folks who are doing heavy C++ is to use FClassFinder, as in the following example:

UCLASS()
class FOO_API UCustomContainer : public UUserWidget {
  GENERATED_BODY()

  UCustomContainer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
  {
    ConstructorHelpers::FClassFinder<UUserWidget> CustomWidgetClassFinder(TEXT("/Game/Foo/UI/WBP_Custom"));
    CustomWidgetClass = CustomWidgetClassFinder.Class;
  }

public:
  void Setup(TArray<APlayerData*> Players) 
  {
    for (auto Player : Players)
    {
      UCustomWidget* Widget = CreateWidget<UCustomWidget>(GetOwningPlayer());
      Widget->SetupFor(Player);
      PlayerWidgetBox->AddChildToHorizontalBox(Widget);
    }
  }

protected:
  UPROPERTY(BlueprintReadWrite, meta=(BindWidget))
  UHorizontalBox* PlayerWidgetBox;

private:
  TSubclassOf<UUserWidget> CustomWidgetClass;
}

Take note of the custom method (SetupFor) I introduced to handle initialization. You cannot ExposeOnSpawn variables since there's no way to create custom widgets in a deferred way as you can with actors, you need a different way to set up the widget. This method can contain UI construction logic or other initialization logic too, as long as you call the method before adding the custom widget to a container.

I am sure I came across FClassFinder a long time ago in my UE4 programming, but I never really liked it because of the hardcoding of string paths. Indeed, the hardcoding of strings is something that turned me off from Unity many years ago: it's just not robust in the face of refactoring, and refactoring is crucial to keeping the code healthy. That said, this feature did help me build up my UIs much faster than my old approach, so I think it's a matter of idiolectics: to get things done in UE4 C++, you have to approach it on its own terms.

Gameplay Abilities and Gameplay Tags

Anyone want to guess how many times I tried adding UE4's Gameplay Abilities System to this project? I wrote some thoughts about it a few weeks ago, and I spent days exploring, reading, programming, and learning. In the end, every time, I came to the conclusion that for some reason, it was not right for me. Some of those reasons were wrong-headed, which led to my exploring it again, only to find another reason that it wasn't right for me.

My experimentation with GAS did get me also working with Gameplay Tags. The API around them is great for quick and expressive set operations. In Dan's excellent GASDocumentation project, he points out how gameplay tags—especially in combination with GAS—can replace most uses of enumerated types and booleans. As I tried to get my head around these technologies, I found this to be true. Indeed, for my core system of skills and runes, I represented all the data as gameplay tags and got a quick, efficient implementation.

The problem comes from the lack of expressiveness. It seems to me that gameplay tags are like set-theoretic versions of the String datatype: you can put anything in there, which means it can be used for anything, which means it's not tuned for any one thing. For example, in my code, I had some gameplay tags that represented skills ("Skill.X") and some that represented runes ("Rune.X"). However, this meant that when I passed these around, they would both be simply type FGameplayTag, leading to awkward signatures where the meaning of a variable could only be determined by its name and not its type. That is, you get code like the following:

int32 CountSuccesses(FGameplayTag SkillTag, FGameplayTag RuneTag)

This reminds me of my point above about encoding asset paths as string literals in C++: yes, it can work, but it's not very robust or maintainable. I can see why homogenous types are necessary for a complex system like GAS, but I struggle to understand why some of the conventions in the game engines I have used defy the general rules for good software development. By contrast, although I've been away from Java-based game development for years, I never had that feeling when I was programming in PlayN, where I had wonderful libraries to draw from and Java's rock-solid implementation of enumerated types.

Rider for Unreal Engine

I think Rider for Unreal Engine deserves a quick shout out. I joined the Early Preview and have no regrets. One of my biggest frustrations with writing C++ in UE4 was the unpredictable interactions between Visual Studio and Visual Assist X. I know everybody seems to praise these two tools, but I never got a good flow working where I could consistently get the predictive text (intellisense) to work consistently. Sometimes, I would get Visual Studio filling things in, and sometimes Visual Assist. There was a lot of mousing around to try to get the pop-ups to work, and I hate mousing around. Rider had consistently-available keyboard shortcuts, including a handy message when you gave a command that it wasn't ready for yet, such as requesting a build while it was still initializing. The first release I used had a terrible bug that would require terminating a zombie process, but that was fixed in the next release, and I've been very happy with it.

What's next?

I'm still interested in the gameplay idea that I've been exploring. Briefly, the core system involves cooperative bag-building, but where the items in the bag can have data attached to them: for example, if a player uses a rune from the bag, mark the rune, so that the next time they draw it, they get a bonus. Part of the inspiration was to explore what could be done with cooperative bag-building in a digital space that would be arduous or impossible in a tabletop one. It was fun to tinker with, but at some point it would become a content-driven game. Given how long it's taken be to build a minimal engine (that is still incomplete as I waffle on some of the core mechanisms), I am not convinced that I should not just pull the plug on it. Another option is to see if I can whip up a playable prototype in Godot, whose support for rapid iteration on scenes is a great strength over the relatively plodding process of UE4 level development, but of course, this won't solve the content problem.

Speaking of data, I want to mention another Ben Humphreys blog post, this one from last January about data-driven design in UE4. This is a topic near and dear to my software developer's heart, and in some ways, it was both relieving and frustrating to read his conclusions. Yes, UE4 seems not to have a good, all-purpose tool for doing data-driven development, so it's not just me who feels a bit unsettled with my options; but also, why not? Maybe Epic will give him a MegaGrant to build and integrate his ideal tool.

As I wrap up the month, I turn my eye toward the announcement of the Epic Spring Jam starting on June 4. I make no excuses about the state of my project, but I have been distracted by the hubbub around what universities are going to do in the Fall. I know I will have to spend much more of my own time than usual planning my Fall courses, and I still don't have a timeline of when I will hear whether the university will be altering my times, sections, or modes. Something like the Spring Jam would give me five focused days and a theme to continue my summer professional development. I would land in the middle of June afterward, which seems a convenient time to dive into planing—hopefully with a little less ambiguity around me.

Thanks for reading!

Thursday, May 14, 2020

Declaring a TArray parameter in a BlueprintImplementableEvent

After a long day of programming in UE4, I ran into a problem that caused me quite a headache. I have a custom PlayerController subclass in a project I'm working on, and I wanted to add a method to trigger the need for the player to choose from among a list of options. The method should look something like this:
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
void ChooseFrom(TArray<UScenarioAction*> Actions);

I spent too long hitting my head against the resulting error message, which looks like this:
'void AMyPlayerController::ChooseFrom(const TArray<UScenarioAction *,FDefaultAllocator> &)': overloaded member function not found in 'AMyPlayerController'

This should work: I just want a function stub that the blueprint subclass implements. I tried every conceivable combination of annotations to the UPROPERTY and tried adding empty implementations in the cpp file, knowing that there really should be nothing wrong with the code.
It wasn't until I sat on the couch in the living room that I thought that the problem might be the parameter. Sure enough, if I took out the TArray parameter, everything worked as expected. I little poking around the web inspired me to try passing the array by constant reference:
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
    void ChooseFrom(const TArray<UScenarioAction*>& Actions);

Sure enough, that did the trick. This seems like a place where the compiler could give a much friendlier message; there's nothing about the message it provides that makes me think that the parameter needs modification.
Hopefully writing this quick note on my blog will help save someone else an hour's aggravation—even if that someone is just future-me.

Monday, May 11, 2020

Post-semester thoughts on new CS222 achievements: incentivize good behavior

Since the semester ended and I wrote up my thoughts on Spring's CS222 course, I have had a few passing ideas about how I could use the achievement system to reward virtuous behavior. These can all be categorized as things that I think students should be doing, but evidence (and my memory of being a student) tells me that they are not doing. The main idea here is to give students credit for deploying the techniques that are taught in class but that are not otherwise directly assessed. The names and descriptions are unedited drafts, but they are enough for me to leave a mark for future self next time I teach the course.

  • First Responder: Consider the instructor's feedback given to you on the first iteration of the final project. Pick a part of it that you do not understand after having discussed it with your team and respond to it, by either emailing or meeting with the instructor. Once you have resolved the feedback, write a paragraph or two to share the experience with your classmates.*
  • Code Reviewer: Conduct a formal code review of your project following the format introduced in class. Submit a summary of who took which roles, when and where the review was conducted, the checklist you used, and the resulting artifacts such as a list of potential defects.
  • Yes, I Am a Model: Do a CRC analysis of your final project at the beginning of the second or third iteration. Compare and contrast this against the initial one developed for the first iteration.

I would be remiss not to acknowledge a perennial difference of opinion that my respected colleague David Largent and I have had about a particular CS222 achievement: his Re-reader achievement, which a student can earn by re-reading a portion of the textbook after it was already assigned. I've pushed back against this, arguing that of course the students have to be going back to the textbook in order to succeed. Dave's point, I think, has been that the system can incentivize the behaviors we want of them. So, ... thanks, David—I'll probably add that one next time, too.

I'm not assigned to teach CS222 in the Fall, so now I just need to remember to read my blog next time I am assigned it.

* Note that this achievement makes it clear that not all the feedback given on an assignment is of the same grain. Some of it will be small and actionable; some of it will push students to having to learn new things that are specific to their project or their implementation.

Sunday, May 10, 2020

Something like a Summer DevLog, Part 2: Gameplay Ability Systems and Me

After tinkering with simple burndown charts for the Web, I found myself thinking about whether and what kind of game I could make over the summer. I was inspired by A Short Hike recently. It was a freebie from Epic Games Store, and I played it to see if it would be appropriate for my kids. I was surprised at how it pulled me in. After finishing it, a GDC video was posted about it, and the original release of the game was made in only three months. There's an inspiration! Of course, as with Kaiju Kaboom, I don't think I can afford a success, so my exploration will likely be more academic, in the good sense of the word.

I have been intrigued by Unreal Engine's Gameplay Abilities System (GAS) since watching a livestream about it over a year ago. It sounded fascinating, and at the same time, the dearth of scaffolded instruction gave it a sort of mystic air, like some arcane wisdom that's just out of reach. Several times, I glanced through some docs or watched a video and thought, "That sounds really interesting," but it never fit what I was trying to accomplish. Enter the summer, and I'm thinking about some of the digital RPG bugs that have been crawling around my head for years. Could the Gameplay Ability System be the kind of technology that, if I invested in learning it, would reap the benefits of faster iteration times, or would it be a sunk cost?

Let me take a step back for a moment and explain briefly why GAS is interesting to me from a scholarly point of view. I've been studying games, design, and patterns for around fifteen years now. It's certainly the case that traditional OO design patterns can be applied, as explained in Bob Nystrom's amazing book that I wish I had written. There are also patterns specific to games, including such fundamentals as the main game loop. Game engines provide solutions to stock problems like loading assets, rendering, networking, and accessing the operating system; there is a sense in which these building blocks are also patterns. Beyond that, though, there always seemed to me to be something of a grey area in game development. That is, there seemed to be a point where there likely were patterns, but in practice, it seemed like each thing had to be built anew each time, in contrast with what the engines generally provided. GAS, then, is intriguing because it is an engine's attempt to grasp at this ephemeral thing of game design. I think this, fundamentally, is why GAS is so hard to learn, as every presentation and document I've seen about it echoes. It's a much bigger thing than, say, an Observer or a Builder. Perhaps it's a reification of an architectural pattern, but one that has not been widely grasped, from what I can tell. (Caveat: There are limits to my professional network here, and if there's a community of practitioners already addressing this, I'm outside of it. Perhaps, at some point, when I get my head more around GAS, I will reach out to the engineers at Epic and see if we can have a more serious conversation about any of this.)

Bringing it back to my summer project, I spent around a week oscillating between a few different states. First, I would excited explore some aspect of GAS, get it barely working, and then get excited about using it. Then, I would think that GAS is overkill and that I should just write the minimal system that gives me what I want so I can determine if the project itself is worth pursuing. Then, I would realize that my hand-written approach was so inspired by what I had just read about GAS that I should buckle down and use it. Wash, rinse, repeat.

As I repeated this process, I was able to find the absolute best resource for learning GAS: Dan Kestranek's GASDocumentation project. After finding that, I only rarely had to go look for supplemental information from other sources. Matt Edmond's talk from Unreal Fest Europe 2019 is also good for approaching the concepts and business case, but it's Kestranek's work that really gives you the details you need to build something from scratch.

My experience last week was complicated by the fact that I didn't really know what I was building: I was doing feasibility analysis but only for ideas that were in my imagination. At first, I was thinking of my project as a tech demo for a single-player computer RPG with deckbuilding mechanisms. There are some great inspirations in this area, including of course Slay the Spire and Griftlands, which is technically still in alpha but which I bought and thoroughly enjoyed. Both of these take a tabletop mechanism and wrap it in some ideas that are only possible in digital, especially Griftlands' use of cards that gain experience and level up. 

As I kept tinkering and oscillating, I found myself reflecting on the positive experience my boys and I have been having with For the King and comparing that to some of the cooperative board games we have played. This caught my fancy, so by the end of last week, I had a tech demo built of a four-player cooperative game that runs on my Steam Link. It's really raw, but it shows how each player is connected to its own controller, which each separately control a UMG-driven action menu, and the actions themselves have cooldowns that are shown in progress bars. You can't even really "play" it, but it all the pieces are in place to be able to explore this direction.

I did encounter something strange that I will share here, in part so that I can remember it later. When my son and I made Disarmed for Global Game Jam 2020, we had local multiplayer working fine on a Windows PC, but when we tried running it on the Steam Link at home, it did not work at all. I am not entirely sure why it doesn't work, but even more strangely, with a slightly different approach, I was able to get last week's tech demo working. When the game starts up, in the default game mode, my tech demo creates four players using the Create Player node. Then, it finds the Player Start on the map with corresponding Player Start Tag that corresponds to the player number, spawns a pawn, grabs the Player Controller with that index, and possesses the pawn. The only major difference I can see between this and Disarmed is that in the tech demo, the game mode has no default pawn class specified but creates them manually, whereas in Disarmed, we rely on the default pawn class specified in the game mode. I have not (yet?) gone back into Disarmed to see if this change would make it work, but I was delighted to get my tech demo working in my living room, even though testing it took several runs up and down the stairs.

My next step is to more seriously sketch the design of the game project itself. Over the weekend, I've been reading rulebooks and online threads and talking to a limited number of people about what I'm trying to do. I started writing some notes in an old notebook, though they are pretty haphazard. This coming week, I want to try to organize them to more carefully articulate some design goals, then see if I can turn my tech demo into a minimally playable game. I was thinking also about throwing together a one-man project for the seasonal Epic Jam, in which I have never participated, but it looks like the Spring one has been postponed.

Saturday, May 9, 2020

Something like a Summer Devlog, Part 1: Exploring simple burndown charts for the Web

The past two weeks, I have had the freedom to explore some new territory, and it seemed like a good idea to try to leave some notes about it. Two weeks ago was finals week, during which time I could work on my own explorations between grading and report-writing; this past week had me wrapping up the last loose ends from the Spring semester while taking some deep dives. What I'll do in this post is write about what I was working on two weeks ago, since I made a significant pivot in my attention around last weekend.

I wrote about how my CS490 students did not use burndown charts as effectively as I would have liked, but how there was also no easy tools I could put into their hands to do them the way that I prefer. I started looking into building something myself. My first thought was to go back to my staple for Web development, lit-html and the PWA Starter Kit that uses it. I was surprised to see that the PWA Starter Kit has been discontinued, but the authors suggested trying open-wc, which I did. It's an impressive project, and exploring this got me looking into several other interesting pieces, especially Storybook for testing web components. I suspect that, once we get to summer's Fall Course Planning phase, I'll turn back to open-wc as a new generator for my course sites.

The major complication of a web-component-based burndown chart generator was the unexpected complexity of handling dates and times. The Javascript Date object works as expected, but there is no standard widget for reading a date from the user. Vaadin has a nice one, but I was surprised that the Material design component developer documentation is still only "planned". The documentation for the "date" type for the standard HTML input element says, basically, don't use it. None of this was a showstopper of course, but also, none of it was fun. Using Vaadin's input component, I built a minimum working prototype that draws a steady line using the web component version of Google's chart API.

Around this time, something inspired me to check again whether Flutter had incorporated their HTML5 support. It's been on my radar for a while, but last I checked, Web support was being developed while Android and iOS were the clear "real" targets. As of last week, you could do HTML5 output with Flutter through the beta channel, so I decided to check this out as well. I was impressed by how tight all the tooling worked together, but unfortunately I ran into a problems with hot reload working on Linux. Again, on the flip side, the tooling made it painless to get everything set up again on Windows, although I much prefer to develop in Linux. I enjoyed tinkering with Flutter, and there are some nice aspects to the Dart language. I was also able to make a minimum working demo for the Web in Flutter of a burndown chart with a steady line and sample data using the charts_flutter library.

In both cases, I was able to get demos up and running very quickly. Both were just technology demos though: I did not put any real thought into the UI design, just assembling pieces to understand how they would work together. I feel like I got out of this project everything I needed for now, since there's no imminent need for something that makes simple burndown charts. I may return to this project later in the summer, or even in the semester as a teaching and service project, but I set them aside at the end of last week so I could turn toward some game development ideas. That's a topic for another time.

Sunday, May 3, 2020

Family Painting: Arcadia Quest - Riders

In March, I wrote about how much fun my family had painting Arcadia Quest. We had so much fun, in fact, that when I found the Riders expansion on sale, I jumped on it. This set contains several miniatures in conventional scale, but its raison d'etre is really the giant mounts. One of the first challenges I faced, then, was how to prime, paint, and varnish these without handling them? Unlike the other figures, the mounts have no bases that could just be easily glued to a cork or a block.

Looking around the Web, I came across a post that showed how a painter had pinned his figure to some kind of block. This seemed to me to be the best way to go, so I looked around for something to use as a handle. I had a spare square of exercise mat foam that my son and I had prepped to be used as Frostgrave terrain years ago, but we stopped playing that game before making the scenery. Unbelievably, no one had tossed it in the interim, so I cut out a chunk to try. I stuck a paperclip into it, but it didn't hold fast until I superglued it into place. I proceeded to drill holes in the figures and secure both ends of the pin with a dot of superglue. This worked well, being surprisingly steady. I did try to splay out the pins slightly so that the angle would give a little extra strength, but I didn't have an orthogonal test model to verify whether this helped.

Mounting Mounts
Mounted Mounts after zenithal prime
After deliberation and consideration, we decided that my two older boys and I would each paint two mounts, we three would split the heroes and big bad, and each member of the family would paint one of the jacklols. A lot of this set then was done in evening painting sessions with the big boys while my wife sat with us and worked on other crafts. 

We started with the trio of BawkBawk, Luda, and Tianlong. The boys picked theirs first, and I took BawkBawk not because I was particularly excited to paint it. Rather, I knew that yellow was a tricky color to paint, and I figured I was most prepared for the challenge. I took the following WIP image after spending the first night just trying to get the yellows in place. I think there's three coats of just the base color there, and then I used two-brush blending to layer in the shadows and then the highlights. I'm really happy with the result. Maybe it's kind of silly to spend this much time on a knock-off chocobo, but hey, it's a hobby.

BawkBawk WIP after the first painting session
Here are the finished first trio:
BawkBawk, Tianlong, and Luda
These are in descending age order by painter. After the first night, we talked about our progress and I gave them some advice on how to proceed. In both cases, I was really encouraging them to increase the contrast. Luda—who is a little washed out in that picture—was essentially all one tone originally. I showed #2 Son how to thin out some shade and paint it into the recesses, and it looks a lot better. I think #1 Son knocked Tianlong out of the park. Notice the spot of white he put into the eyes to make them look reflective: that's a pro move from a 13-year-old.

Here is the second set of mounts:
Beka, Toshi, and Hornsteady
These are also in descending age order by painter. I had been kind of excited to paint Hornsteady for a similar reason to my interest in BawkBawk: it's basically all armor with very little sculpted detail, and that seemed like an interesting painting challenge. However, #2 Son decided that was the one for him. Fortunately, #1 Son took Toshi, which was the only one I was really uninterested in.

Once again, we painted in two nights, and they checked in with me after the first night. And, once again, I encouraged contrast. Like Luda before him, Hornsteady was basically all one tone: silver paint over the whole thing. We talked again about thinning out shades and painting them into the recesses. After that, Hornsteady was much improved, and accenting the contrast on Toshi helped make him more visually interesting as well.

I am actually really proud of Beka, which is kind of silly, because it's some kind of battle owl in a bra. Why is it wearing a bra? To have a "feminine" mount? Do they know that birds are not mammals? One of my sons pointed out that what I described as a bra could actually be goggles that are hung around the neck. I suppose that's possible, but if so, the goggles wouldn't work at all: they are not shaped to Beka's head, nor is there a cutout for the beak. End rant.

The point is, I'm proud of the work I did on the feathers. I think I got a nice slightly-brown grey tone, similar to what one would find on a real bird. Mostly, I am proud of the texture on the wings. Unlike BawkBawk's smooth shading, I used brushtrokes to imply a texture that's not sculpted in, and I think it really sells. The spots I added are much smaller than the ones in the card art, and of course I was afraid to go in and drop spots over something I was so happy with, but these too turned out well.

For reference, and because it is my blog after all, here are the two I did, next to each other.
BawkBawk and Beka: The Mounts I Painted
I was not eager to paint Malkhor, the Big Bad, but #1 Son said he would do it. Let's look at him next.
Makhor, front 
Malkhor, Back

I think he did a great job with this one. He got some nice shade into the muscles to help them stand out. You can see some of the transitions on the cape, but that's OK: a nigh-featureless cape that large is hard to paint, and it shows his progression in the use of blending. Keep in mind that, as before, we're just using the Vallejo Basic Colors, so no fancy glaze mediums or anything like that.

Meanwhile, #2 Son and I worked on the two heroes from this expansion: Gaston and Colette.
Gaston and Colette
I was excited to try my hand at Gaston, following the theme of "Let's try painting some challenging colors." I worked on practically just the black and white during our first painting session, while my son nearly finished Colette. She was looking pretty stark, in solid whites and blacks. In the next session though, he did some much nicer work adding greys to highlight the black and shade the white. I think he did a nice job with this. Also, it's an easy detail to miss, but I think he did a nice job on the scarf.

Here are a few more close-ups of Gaston:
No one fronts like Gaston

Faces back like Gaston
The big chunky tail was tricky to paint around, but I am happy with how it turned out. Normally, I take an inside-out approach—painting the skin, then the lowest layer of clothes, and working outward. With him, this was tricky, because the colors of his fur are the same in the innermost levels like his arms and the outermost level of his tail. I am happy with how the blends came together. As usual, I copied the colors from the card art, and I think the artist did a good job accenting the black, white, and browns with bursts of saturated red, green, and orange.

 As I mentioned above, I find myself generally turning to two-brush blending these days, starting with a mid color, painting in shades, and then adding highlights. I like being able to see the basic colors in place before shading and highlighting, and this has been working for me better than starting with the shade layers like I used to.

Finally, we get to the whole family painting part: Jacklols! In truth, I thought they were "Jackols" and wondered why my son was pronouncing it so strangely, until I read the card name more carefully.

Jacklols, just lolling around
Each member of the family got one, and the photo shows them in increasing order by painter age. My wife and I spent the longest on ours, and by the end, we both felt the palette was a bit dull. Still, it was a good place to practice some fundamental techniques.

This is a good place to mention the strange lighting in the cards. Here's an image from the Kickstarter campaign:
Jacklol Cart Art
Notice how they all have a sort of magenta backlighting whose impact varies a bit depending on which background is used for the figure. Indeed, the other art has this design as well, which I think contributes to different interpretations of figures like Beka: I saw it as a grey owl with reddish light, while others have seen it as brown or red itself. In any case, it was interesting to hear how my boys were surprised at #3 Son's interpretation of the Jacklol fur as being pink. My wife and I agreed that this was a reasonable interpretation of the art. Whether #4 Son really thought the armor should be reddish-purple—or if that's just what the five-year-old ended up with on his palette—is lost to history. In any case, they give us a nice bit of variation on the table. Indeed, it is a variation that has me eyeballing my boxes of unpainted Massive Darkness mobs.

Here are some close-ups of my Jacklol:
Jacklol, out of clever caption ideas

Jacklol turning his backlol on us
It's kind of a combination of smooth blending of light colors as an BawkBawk with the random dark spots of Beka. I thought I had finished all the fur when one of my sons commented about the "eyebrows" on his own miniature, that he had thought they were part of the helmet. I had done the same thing, and fortunately, my fur colors were still wet, so I could go in and add the eyebrows. Truly, the eyebrows-over-helmet design adds a lot of life to what would otherwise be a less interesting face.

That's all for today's painting write-up. Thanks for coming along!