Coincident with the severity of the pandemic becoming more apparent, I was talking with our partners at Minnetrista about whether Canning Heroes could be made more available somehow. It was written in Unreal Engine 4, which does support deployment to multiple platforms, but distributing those applications is non-trivial—especially for Mac. I also remembered that much of the code and the asset design assumed the game would always run at one resolution, further complicating any attempts to port it to other hardware configurations. I imagine that many museums and cultural institutions are looking for ways to reach out to people who cannot visit their facilities in person, but at the time, I reported that it was possible but not easy to build the game for other platforms.
A little over a week ago, this story crossed another one that has been bouncing around my mind: that, as I wrote about in my March Fam Jam post, I have been interested in learning Godot Engine. The Fam Jam game was a good one-day project, but it didn't give me the opportunity to look into some of the real details of Godot. This lead me, on the weekend of April 28–29, to look into what it would take to make an HTML5 version of Canning Heroes using Godot Engine.
I'm glad to tell you that the exploration has been fruitful. I have been able to reuse the original assets and design to make a Web-playable version of Canning Heroes that, for lack of something punnier, I am calling Canning Heroes: Special Edition. Like the original, it's free software licensed under the GNU GPL, and I just flipped the switch to make the repository public this morning. You can play the game at http://doctor-g.github.io/CanningHeroesGE, and you can browse the source at https://github.com/doctor-g/CanningHeroesGE.
One of the beneficial side-effects of the shift to online courses is that I've been able to put my head down and get this port done in just over a week. I have been able to have many meetings with my students, but I haven't had any other distractions to get in my way. This effort clearly fits into research, teaching, and service aspects of my job as a professor, so I feel like this was fruitful scholarship. I worked on it for several hours every day, including a several evenings last week as well as a few this past weekend. I would estimate the whole effort took about 60 hours, but I did not track my hours. I thought about using this as an opportunity to learn HackNPlan as well, but this seemed like busywork since I already had a reference implementation to guide my work. In fact, I recorded a video of myself playing from my desktop machine (awkward without a touch interface) as well as this photo of the credits screen so that I could use them for reference.
For the rest of this blog post, I want to highlight a few interesting things from this week of development. Note that the current build as of this post is Build 14.
Tween Pool
I started in with washing carrots, just like Happy Accident Studio did in the original. I knew that the carrot minigame contained everything that all the others needed, so it seemed like a good place to start. I knew there was an easy cap on the number of carrots on the screen at once, but I could also foresee that as I dealt with more and more moving pieces, I would need to manage more dynamically-defined animations. Since I didn't know where exactly on the screen I would spawn the vegetables or where I wanted them to go, I used Tween nodes, which are easily configurable through scripting in a way that AnimationPlayers seem not to be. However, I wasn't sure exactly how many of them I would need, nor was I really sure how expensive they were to create, so I decided to implement a quick pooling solution.
The code for it is pretty simple. I made a scene that can be instanced into any other one, but was really designed to be used by the Game "shell". It responds to requests for tween instances and returns free ones or, if none are free, makes a new one. When the tween is finished, it goes back
into the queue of free ones.
I embedded that code using a Gist. For many years, I've used this formatter instead, which is fine but always struck me as a bit of a kludge. I'm not so sure about the Gist approach since I cannot actually see it while editing in the rich-text editor, and in the HTML editor, it is of course just a script.
In any case, back to the approach: I admit that it is likely a case of YAGNI, but it gave me a good opportunity to think about the implications of the node structure on a classic design pattern.
In any case, back to the approach: I admit that it is likely a case of YAGNI, but it gave me a good opportunity to think about the implications of the node structure on a classic design pattern.
Thinking in Scenes
I mentioned in my Fam Jam post how I think that "scene" is a bad metaphor for what one is building using Godot Engine. I still think that's the case, but it's also true that the longer you work in the engine, the less that this word has any conventional meaning: it becomes simple a tag for the thing you do in the editor.
There's nothing fundamentally novel about Godot's tree of nodes. In UE4, a level is really a tree, and a good OO approach would have "branches" which are actors that aggregate other actors. I wonder, though, if seeing the world as a single tree can help learners understand dependency injection better than when it is only a mental model. It makes me wonder what it would be like to scaffold a learning experience between the two engines. That is, would learning something like Godot help someone pick up on how to write high quality UE4 code, or even PlayN code—two other elegantly-designed systems that leave a lot of architectural power in the hands of the developer?
Build Scripting and Build Numbers
Once I had my tools in place, I knew I needed to move from manual uploading of builds to automating the process. Godot's command line makes it easy to invoke an export configuration, and so from there, it was mostly just remixing the scripts I have for publishing other applications to GitHub Pages: build the artifact, put it into a new repository that points to the gh-pages branch of the main repository, and push away
As I was debugging the HTML5 build, I had some trouble knowing what version of the application I was running on the Web. When you push to GitHub Pages, it's not the case that you instantaneously can get those files through a Web query. Also, it was not at all clear to me if the HTML5 exporter in Godot had any kind of cache checks as I use in Polymer to ensure that the most recent version of the application is loaded. I decided that a good, conventional solution would be to show the build number right on the UI of the game. You can see the build number in the bottom left of this screenshot.
I'm not sure I've used build numbers like this before, despite it being bog standard practice. I did a little searching on the Web to see if anyone had documented approaches for doing this within Godot Engine, but I didn't find any. I put together a quick test that I could read a build number from a TextFile resource (whose API documentation you may notice is a bit sparse). From there, I created this BuildNumber script that simply loads the text resource's content into an integer and makes it available. The script is used by the MainMenu scene to create the label.
With that done, it was easy to be sure which version of the application I was testing.
Style
A fair part of my learning last week involved reading documentation and poking around the Web for tips and tricks. The Godot Engine documentation is really quite nice, being both technical detailed and approachable. There are some ideas whose explanation is unfortunately spread across multiple, non-interlinked pages, but with enough patience, one can find what they need. The popularity of the engine among novices means that it's easy to find quick answers to common problems as well.
I am glad that I came across the GDQuest Best Practices: Godot GDScript page. It gave me a solid framework on which to build the application skeleton, which is important since it's too hard to refactor once things are in place.
As with the word "scene", working with the idea that whitespace is significant gets easier with time. However, I still think it's a serious language design problem. This is best illustrated by the fact that the API encourages the use of long, untyped parameter lists but the style guide calls for short lines of code. You end up, then, in a position where you're using whitespace both for significance of blocks and also to make single lines readable. While it's true that you can scan around and try to figure out what a broken line might mean, you it also means you cannot use indentation unambiguously—which was, as I understand it, the point of this design choice in the first place. Compare to C-style languages, where if you see someone using a fluent interface and arranging it cleverly with whitespace, there's no doubt what's happening: it's not surrounded in braces, after all. Not to beat a dead horse, but some of the complications of long methods could be fixed by breaking them down into more functions, but without good refactoring support, all such changes are potentially game-breaking. Combine this with the idea that scripts link to each other through names of methods (strings) rather than function pointers, and you get the potential to silently break distal parts of the system.
It's not all pedantic. I'm seriously thinking about the pedagogic implications of learning programming through GDScript. Parts of it are appealing, like the speed with which you can get something up and running. Other parts are disconcerting. Consider this phenomenon: when you use the assignment operator, it may assign a value to a variable, or it may invoke a mutator method. At the calling site, you don't know. As a developer, I get the convenience of that: essentially, I can think of this as always going through a mutator and only sometimes does it matter. However, what if I'm a complete novice and I'm trying to understand the semantics of assignment? Whoah. Suddenly I have to either go with the pedagogy of little white lies or I have to understand function call semantics and object scope in order to debug what look like assignment operations. Let's say I make sense out of that: what practices will I move forward into languages like C++ or C#? Sure, C# does something similar with properties, but then we can kick it up a notch: if all we're doing is adding accessors and mutators to private fields, then we're not actually doing good OO modeling at all! We're repeating the sins of the 1990s all over again and still never teaching learners to do anything besides procedural design.
But I digress. Hopefully you can see that there are some interesting considerations for someone who does both teaching and practice, and there are many opportunities for research on teaching and learning.
Differences from the Original
I tried to keep Special Edition as close as possible to the original in terms of the gameplay. The only feature I added was the ability to go fullscreen, which, combined with screen scaling, was amazingly easy in Godot Engine. I kept the assets, even the ones that I thought were a bit underproduced by the original team, such as the red dots. Yes, they call your attention to the think you can chop, but they're not very visually interesting. I should mention that it was a joy to use most of the assets: dropping in those lovely carrots or that jaunty music let me go much faster than if I was building up a game from scratch or if I had to scour the Web or my hard drive for assets.
I made some changes to the "About" page to provide a bit more context about the game. Again, since the original was made for a specific physical location, it could get away with some shorthand.
There are a few things in the original that I do not have in the Special Edition. The most obvious omissions are the illustrations on the main and end pages and the tutorials. The illustrations are really just a mater of laziness: they don't do anything, and I know I can add them, I just haven't done it yet. Who knows, maybe by the team you read this, I will have gone in and added them. I remember the original team talking about wanting to make them more interactive, like having the little jar people boogie or react to being touched, but that's probably out of scope: I would likely just make them play the "clink" sound, which is what they do in the original.
The tutorials are a more serious omission, since our playtesting showed that they really helped players understand what they want to do. I've been able to learn both the Tween and AnimationPlayer APIs, and so I am confident that I could add animations to demonstrate how each minigame works. This would take a lot more time than, for example, dropping in the illustrations. It would also be time that I'm not sure would be productive for me. For now, then, I'm leaving this on the cutting room floor.
Wrapping Up
I believe those are all the major points I wanted to share about last week's learning adventure. I'm glad I did it, and I hope that the result is something that can bring some joy to other people. I am feeling more confident now with Godot Engine. If I don't get too rusty too quickly, I think I could use it productively in a summer project or a jam. This week, I need to return to making some video tutorials for my YouTube channel. I have considered whether any of the things I've mentioned above might make good tutorials, but I can't shake the feeling that something like pooling or build scripts is really better explored in text: the devil's in the details for such things, and it's hard to get at those in a bite-sized video.
Thanks for reading! If you have any questions, let me know.
No comments:
Post a Comment