Wednesday, June 5, 2024

Summer course revisions 2024: Game Programming (CS315)

It's time again for Summer Course Revisions. I spent this week focused on my Game Programming course, which is a junior-level elective for most students and required for the Game Design & Development concentration. I think it is one of my best courses, and I feel good about the general stability of it.

In preparation for revisions, I went back to my notes from last year, including my reflective blog post from last Fall and my notes from reading Grading for Growth. I also referenced my internal notes, which I keep in my course planning spreadsheet. The most important things I came across here were: a reflection on the idea of using "more hurdles" instead of "higher hurdles" for specifications grading; and the need clean up how stars were earned on the final project to remove shortcuts. The latter is something I will have to consider later in Fall since, to address the former, I decided to make a dramatic change in the grading scheme.

This course is where I pioneered checklist-based grading, which I also wrote about in an academic paper. As my post from last Fall makes it clear, though, something shifted in my teaching experience that led to significant frustrations with that approach. I suspect the causes are cultural and not personal, but you have to negotiate with a system. I decided to try an alternative inspired by Joshua Bowman's work that is documented in Grading for Growth. In particular, I am replacing the higher-hurdles specs approach with atomic, clear goals and multi-tiered resubmission. The overall structure of the semester will be the same, and I expect the primary student activity to be exactly the same; the changes are almost entirely in the activity systems around assessment.

I have rewritten the first three weeks' assignments as a proof of concept. For each, I removed the checklists and replaced them by an articulation of essential and auxiliary goals. The essential goals are always required for a satisfactory grade, and when there are auxiliary ones, a subset of them are required. Each goal is graded on a four point scale. Successful has an obvious meaning. Needs minor revision is for the cases where it's mostly right, but something crucial needs to be addressed to show understanding. These minor revisions can be done within three days, accompanied by a short report explaining what went wrong. New attempt required is for cases where something critical is wrong; related to that is Incomplete, for work that is not done. These latter two require a more significant reworking by the student, and I've put in a throttle similar to what I use in CS222: one resubmission per week. 

Concomitant with this change is a revision to course grades. I have written before and done several experiments regarding the assigning of course grades. One of the things I really liked about my old approach to Game Programming in particular was that it was easy to give each week equal contribution to the final grade. However, exercises are now being evaluated as either satisfactory or unsatisfactory, and it's not clear that this categorical data makes sense "averaged" with other data. I have put together a simple specification table, akin to what I did last Fall in CS222.

I am hopeful that this approach will alleviate some of the frustration of students' mismanaging the checklist system. It narrows the number of things students need to think about at the cost that each item is slightly larger now. 

I have not written up policies for the midsemester exam and the final project yet, but my inkling is to pull out specific objectives in which each student must individually show competency. This would represent something much more like a transition from "higher hurdles" to "more hurdles," as long as I can make the hurdles roughly the same size. I am also considering dropping the team requirement from this course. Teamwork is common but not essential to game development. The students in the GDD concentration will have opportunities to work in teams in the production capstone sequence, where the students from other majors won't have teamwork experience anyway. I would rather my CS students' skills be all up to snuff coming into that sequence than that they've already been introduced to interdisciplinary teamwork concepts that will have to be in that sequence anyway.

The other major change for Game Programming is more technical. For years, I've maintained my own websites for my courses, and I've done that for three main reasons: it gives me complete control over the presentation and public nature of the content; it gives me reliability in case of the campus systems' going down; and I can use my software development knowledge to separate model and view. My system for representing, rendering, and downloading checklists was pretty robust, but its assumptions also weaved through the whole course site. When I started reconfiguring my template to handle these changes, I ran into common Web development frustrations: changing dependencies, confusing errors from libraries, and CSS. I decided to pivot and just put all the content onto GitHub. This is what I did with my games capstone and preproduction classes last year as an experiment. It's not ideal, but it meets several of my needs. 

You can find the current draft of my course plan at in case you want to take a look. As usual, the content is licensed under CC BY, so feel free to adapt it for your own teaching and learning purposes. I wanted to experiment with how Canvas might link to the individual exercises and their assessment goals, but Fall's schedule isn't loaded into Canvas yet, which points to yet another reason not to bind one's course planning to that platform.

Monday, June 3, 2024

Something like a latte

It's time for that recurring feature, "What is Paul drinking?"

The warm summer weather turned my mind toward coffee concentrate. I use a simple approach gleaned from online advice: fill a pitcher 24-35% with ground coffee, top it off with water, and let it sit on the counter for eight hours or so to brew, shaking occasionally. This gives a strong, dark liquid that is a great base for iced coffee, and I often have both decaf and regular in my fridge in season. It also works for a hot morning cup in cases where I am out of whole beans, which happened to be the case last week. 

The last week has been quite mild, the high temperatures only being in the low 70s. It wasn't the right weather for iced coffee, so I wondered what else I could do with my concentrate. I came across a site describing how to make a latte at home with no fancy equipment, and this inspired me to try something like that myself. Turns out, I can make something at home that is a lot like the cappucino I might order at the Bookmark Cafe.

Here's what I've been tinkering with:

  • About two ounces of coffee concentrate in a coffee cup, heated in the microwave for thirty seconds
  • Just under six ounces of whole milk in a ball jar, heated in the microwave for a minute
  • Froth up the milk using the fancy battery-powered handheld frother that, until recently, I didn't know we had in our kitchen utility drawer
  • Slowly pour the milk into the coffee cup, which will leave the foam on top.
The result is quite nice. It's not that much complicated than my usual French press coffee. It is pretty, and if your pants are really fancy, you can sprinkle cinnamon on top. The flavor and mouthfeel are pleasant, and I don't think I could tell you if it was made with espresso or coffee concentrate. I call this a successful experiment, and there's a good chance I'll be making a decaf one this afternoon. 

I normally don't sweeten my cappucino, but I wanted to try that last time I made one of these homemade lattes. I added some simple syrup and vanilla extract after combining everything, but I think if I did this again, I'd add it before frothing to eliminate stirring later.

I'm also working on a batch of mulberry mead, and I will have more to say about that later. In particular, I will probably say, "Don't put the bag of berries into the carboy because it will float to the top and cause headaches." 

An evening with Microscope

I heard about Microscope from Ben Robbins' interview on Justin Gary's podcast. It is a game about creating a history: the rules guide the players in the collaborative creation of the periods and events that make up a historical arc. I became intrigued, and it seemed like the kind of game one would have to play to follow a conversation about it. I borrowed a copy of the rulebook and convinced my wife and two elder sons to try it out with me.

The rulebook gives specific advice on how to introduce the game, and I appreciated being able to follow the script. Our first decision was the overall theme of the history, but we could not settle on one that we all liked. We agreed to take the first one of three that were recommended for players like us who weren't sure how to start: three nations are united as a single empire.

Our next step was to bookend history. One of my sons recommended that the end of the history should be that three nations, each on the back of a turtle, come together under one emperor. We then came up with the idea that at the beginning of history, there was one nation, on the back of the Great Mother Turtle, but she died, and the nation divided onto the backs of her three children.

As we got into the game, we created the history of three turtle-nations who lived in harmony until a blight caused scarcity of Turtle Orchids—the only food eaten by the giant turtles. The three turtle-nations separated to search for new sources, and their cultures evolved due to the different environments under which they found themselves. We never got into the details of how the turtle-nations came back together after this separation, especially not how they resolved religious differences that emerged, but we know they did, and that it was positive for them.

There were some rocky spots, as to be expected in any first play of an RPG where only one person has read the rules. The last page of the rulebook is a convenient rules summary, but Robbins has not provided this as a downloadable player aid. I feel like it would have helped the players—including me—keep some of the terminology and sequencing right. 

One scene did not go particularly well. Scenes answer particular questions in history, and this one was supposed to answer the question, "What monsters attacked Medium Turtle that caused the society to become more militaristic?" It was only our third played scene, and we jumped into it with gusto. It didn't seem to go anywhere, though, as no one roleplayed an answer that satisfied the question. At one point, I just put the kibosh on the scene, suggesting that the answer seemed to be that we didn't know. This was a little unsatisfying, but so was the scene at that point. 

I did some work afterward to better understand the rules for played scenes. The introductory advice that we followed had us start with a played scene, and that one had gone well. In re-reading that portion of the rulebook, though, I was reminded that playing the scene is a combination of narrative and dialogue. We had only been engaging in dialogue, and if I were to teach the game again, I would make sure to open a scene by using both. We also were too light with setting the stage, which is an explicit part of playing a scene: while we had established who and where we were, we had not established what we all knew and what happened prior. 

I came across two interesting resources during my post-play research. One is this rules cheat sheet created by Nicole van der Hoeven. It may be a good way to introduce someone to the game, but it's a great summary of the rules. Reading it provided a more convenient reminder about the core rules over re-reading the book itself, since the book necessarily combines rules and exposition. Seeing the topic list on van der Hoeven's site, I think I may spend some more time exploring her notes on other topics as well.

The other interesting resource I found was a recent blog post by Robbins himself. It presents alternate rules for scenes which I am sure would have given us a better experience even in our first play. Among the benefits of the revision is that it eliminates the need for "push" rules. These are the rules that allow players, during a played scene, to push back on something that someone has introduced into the world. They seemed necessary but secondary in the book, containing more details than I could hold in memory when teaching the game. They were to be deployed in reaction to play, which also meant that I did not want to review them while we were actually in the game. I am not just happy with the simplification of the scene rules, but I am also chuffed to see a designer improving a game he published over ten years ago.

In summary, I enjoyed my first play of Microscope, and I would like to play again, now having a better understanding of the rules and a handy revision thereof.  If I were teaching my game design class in Fall, I would likely bring this in as an example of an RPG. In an era where all of my students are at least aware of Dungeons & Dragons, it would be a great example of how "role-playing game" is bigger than that.

Monday, May 13, 2024

Letter to Sphere Province Games on the occasion of the launch of Mission Rovee

I shared a personal reflection about my work with Sphere Province Games at the launch party for Mission Rovee. At my college's request, I rewrote my comments in the form of an open letter. They have just published it as a featured blog from the College of Sciences and Humanities. You can read it here.

Thursday, April 25, 2024

Dante Alighieri on Social Media

In Canto 30 of The Divine Comedy, Dante lingers in the eighth circle of Hell, watching the damned insult and attack each other. Virgil, as the voice of reason and wisdom, calls him out on this foolishness. Dante repents, and Virgil responds,

"Never forget that I am always by you
should it occur again, as we walk on,
that we find ourselves where others of this crew
fall to such petty wrangling and upbraiding.
The wish to hear such baseness is degrading."

(John Ciardi translation)

Thursday, April 11, 2024

Using C# with Rider in Godot Engine on Ubuntu

I was inspired by the announcement of Slay the Spire 2 and Casey Yano's description of his experience with Godot Engine to investigate the C# bindings in Godot Engine. I've been using Godot Engine for years but only scripted it with GDScript. Like Yano, I prefer statically typed languages over dynamic ones, so this seemed worth a shot. I was introduced to Rider when I was doing C++ in Unreal Engine, and I found it to be an amazing IDE. This, combined with the availability of free academic licenses for people like me, made that my first stop for trying Godot's C# side.

Unfortunately, some of official documentation had me going in unproductive directions. That's why I am taking a moment here to share my quick notes about the experience. The most important thing I learned was not to bother with Mono: it is being phased out. If I had to do it all again from scratch, I would do something like the following.

  • Install dotnet SDK using Microsoft's feed. I used version 8.0 and that seemed fine.
  • Download and install JetBrains Rider. I did this with snap, which is how I've installed Android Studio for my Flutter work.
  • The first time you run Rider, go to the settings and install the "Godot Support" plug-in.
  • Of course, make sure you have the .NET version of Godot Engine, and tell Godot Engine to use Rider as its external "dotnet" editor.
That's it. Make a Godot Engine project, make some scene, and set it as the main scene to run. Then open the project in Rider, and everything else just worked. 

This was my trial case. At the time of this writing, it is the entirety of the C# code I have written for Godot Engine.

 using Godot;  
 namespace RiderTest;  
 public partial class World : Node2D  
   public override async void _Ready()  
     await ToSignal(GetTree().CreateTimer(2.0), Timer.SignalName.Timeout);  
     GD.Print("Did it!");  

Monday, April 8, 2024

From Asset Forge to Mixamo to Godot Engine

Here are the steps I followed to get a 3D character from Asset Forge (2.4.1 Deluxe) into Mixamo and from there into Godot Engine (4.2.1). These are notes I took to help me remember my particular process; see the update at the bottom for my recommendations.

End Result

Make a thing in Asset Forge. I'd never done this before, so it was a little rocky at first. One of the first things I learned was that I could increase the UI scale in the preferences, which was important since I could not make out the body parts in the character section. The legs and hips don't align with the default snap size, but I discovered that the toolbar widget, right of the two squares, adjusts this amount. Dropping it down to 0.1 allowed me to get the legs both into the hips, although it was tedious to click through the values rather than be able to type them. Once I dropped an arm in place, I had to look up how to mirror the copy for the other side. This is done with the widgets in the top-right of the toolbar, choosing an axis and then mirroring around the selected one (or 'M' as an accelerator).

Export from Asset Forge to FBX. "Merge blocks" needs to be enabled, and the character should be in T-pose, as per the docs.

Import into Mixamo. This was quite easy. For my test model, I changed the Skeleton LOD down to "No fingers."

Export from Mixamo. Select an animation that you want, then choose Download. Make sure you grab this first one "with skin."

Bring that downloaded model into Godot Engine and duplicate it. Name the duplicate after the character (e.g. "bald_guy.fbx"). The original one will be the one from which we'll get the animation, and the copy will be the one from which we'll get the rigged mesh. This is an optional step, but I think it makes things a bit easier to manage. 

For any other animations you want from Mixamo, download them one at a time. You can get these without the skins, since you'll be applying them to the character you already downloaded. Bring this all into Godot Engine.

In Godot Engine, double-click the character fbx ("bald_guy.fbx" in my example above) to get the advanced import options. In the Scene root, you can disable importing animations. In the Actions popup, extract the materials and save these someplace convenient, such as a materials folder. This will make it easy to fix some material settings, which is important since they will all come in as metallic, and you probably don't want that.

Now, we can bring in all the animations as animation libraries. Select all the relevant FBX files from the filesystem view (in my case, all of them except "bald_guy.fbx"), then click on the Import tab. Switch from Scene to Animation Library, then click Reimport. If any of these are looping animations, open them individually, go to the mixamo_com animation entry, and select the appropriate loop mode.

All the pieces are now in place. Create a 3D Scene, and drag your character ("bald_guy") into it to instantiate it. Select the node and enable editable children. Now, you can get to the AnimationPlayer node, and under Animation, choose to Manage Animations. Load each of your animations as its own library. Notice that the first animation, the one embedded with the character, will be listed under the name mixamo_com, and all the other animations will be called AnimationName/mixamo_com. The reason we duplicated that initial fbx above was to make it so that the animation name would be sensible here, since we cannot edit it.

From my initial explorations, this approach is robust in the face of needing to change elements of the model. For example, if you tweak the model in Asset Forge, then push it up to Mixamo, rig it, bring it back down, and reimport it, your animations are still stable. I am surprised that I haven't had to even reload the libraries into the animation player.

A relevant disadvantage, though, is that you cannot add your own animations to the animation player. 

Note that I tried the Character Animation Combiner, but every time I did so, I lost my textures. I also watched a video about combining animations in Blender, but I haven't tried that technique yet. That approach looks like it could make things a little simpler once in the Engine, particularly to rename the animations in a canonical way, but I also like that I can do this without having to round-trip through yet another tool.

Here's a proof of concept created in Godot Engine where tapping a key transitions between a confident strut and some serious dance moves.

Simple transitions for confident boogie

UPDATE: Since taking my initial notes, I tried the approach described in the aforelinked video from FinePointCGI. All things considered, I think that approach is actually simpler than my Blender-avoidance technique. Being able to rename the animations is helpful, as I expected. Having all the animations stored in one .blend file, which can be imported directly into Godot Engine, also saves on cognitive load when looking over the filesystem. I could not have taken his approach without doing the rest of my experimentation, though, coming to understand Asset Forge and Mixamo along the way.

Verdict: Make the model in Asset Forge, upload to Mixamo, let it generate a rig, download all the animations you want (leaving skins on), bring all these into Blender, remove the excess armatures, rename the animations using the action editor of the animation view, save it as a Blender project, import that into Godot Engine, and use the advanced import editor to update looping configurations.