TL;DR
I made a thing! Download links to Linux, Windows, and HTML5 builds are on the game's Ludum Dare 38 page, and you can play in the browser right now (keyboard required).
A two-player space combat game |
Background
Ludum Dare 38 was the 15th anniversary celebration of one of the world's largest game development events, and this alone was enough to encourage me to participate. I did some digging and was a little surprised to see that the last one I participated in was with Summoned, way back in LD29 (blogged about it here). I know I've followed the themes and done a few sketches since then, but it wasn't until LD38 that I made a full submission. Ludum Dare comes every four months, which means it aligns with the end of the Spring semester, the start of the Fall semester, and the end of the Fall semester—an epic trilogy of bad timing for students and faculty alike. Still, I love the event: it encourages anybody with some spare time and interest to make a thing and make it public. It's the spirit of this blog and of my immersive learning projects to make in public.LD38 provided another important opportunity for me. A few weeks into the Fall 2016 semester, I decided to invest a significant amount of time learning Unreal Engine, a decision I wrote about in December. Well, it's practically final exam season here at Ball State, so why not give myself a final exam as well: could I make a game in 48 hours for LD38 using what I've learned in Unreal Engine? In some ways, it's like the final exam I gave to my Game Programming students back in 2011, to make a game about Beneficence to demonstrate what they learned, except I had 48 hours, and the theme would come from the Ludum Dare community.
Before the event, I participated in two rounds of community theme voting. At 9PM on Friday, I popped over to ldjam.com and saw the announcement that the theme was "A Small World." Of course, that terrible Disney song was the first thing to come to mind, particularly the rendition from the old 45 I had as a kid. I thought about games taking place on small worlds, and I thought about the board game Small World, but nothing really jumped out at me. That late at night is awfully late for me to start being creative, anyway. After stepping away, I got to thinking about games I've played that have small worlds in them, and my thoughts came to Star Control 2 and it's classic Super Melee space combat.
A friend gave me a copy of Star Control 2 when I was in high school or college, and it quickly became one of my favorites. For those who don't know, the game is now available for free as Ur-Quan Masters. It's an epic space opera taking place over a whole galaxy, complete with planet-roving and resource-gathering long before Mass Effect—and they did it better, too! My goodness, don't get me started on the ridiculous tedium and carpal-tunnel-inducing stress of Mass Effect 2's planet scanning.
But I digress. I decided to make a simple two-player space combat game along the lines of Star Control 2 Super Melee. I also decided to do it primarily in 2D, since I'm gearing up for a 2D reimplementation of Collaboration Station starting this summer—but more on that in another post.
Making Subpar Melee
Saturday morning, I drew a quick red spaceship in Gimp and implemented the fundamental inputs for one player to fly around the screen. I made two planning decisions that morning: to use the Unreal Engine naming conventions and to follow its framework of separating pawn, controller, game state, and game mode. As I worked, I found the former to be a bit troublesome: not every kind of asset I was dealing with had a naming convention, and not every gameplay element had a clear place in the folder-structure taxonomy. It's still not entirely clear to me how conventional these conventions really are, since I have not read enough high-quality code to build an intuition for it. From the tutorials I've seen, I think my next project will take a more emergent approach—when there's a structural problem, refactor, but keep it lean until then. I'll come back to the gameplay framework decision in a little while.The first major hurdle I encountered was that I wanted to set walls around the play area, such that a player would bounce off of them and take some damage. I watched a Pong tutorial last semester where the developer simply placed boxes around the play area off camera, and that seemed elegant to me. However, I kept passing right through the boxes, despite the physics settings. I don't know how long I was stuck on this, but at one point I was adding spheres into the board with gravity enabled, and watching them fall and pass right through the walls, scratching my head, when I was inspired to fiddle with the box size. See, I had decided to make these boxes look like walls, so I had taken their width to zero... and it turns out that even though the wall will render, a zero-width box will not generate collisions. A silly mistake, but one I won't make again! Setting the walls' width to 0.1 was enough for it all to work brilliantly.
Next I added the planet in the center of the screen and gave it gravity. A quick search confirmed that you cannot set a point source gravity using the canned physics settings, but it was no trouble to give the planet the behavior of pulling every ship instance toward itself, and then to modify the algorithm to make this value inversely proportional to the square of the distance.
With the edges and the center collisions working, I added a damage system, and then I began to add support for exploding when health reaches zero. This is where everything took a turn for the worse, early Saturday afternoon: Unreal Engine suddenly became unreasonably unstable. I couldn't work for a minute without it crashing and restarting. I narrowed the problem down to involving the details panel, especially when dealing with a flipbook animation. I sent about a million crash reports to Epic, so hopefully they find something in there, but I was stymied. I had been working in Windows, since several weeks ago I began learning C++, and the Visual Studio bindings are clearly more robust than the Linux ones I tinkered with. For LD38, I wanted to test my ability to get something up and running quickly, and so I was only using Blueprints—UE4's visual scripting language, which is much faster to iterate with than C++. Since I was not using C++ for this project, though, I thought perhaps I could switch over to Linux... except that the version of UE4 I had installed on my Linux partition was 4.14.x, and this project was already started in 4.15.1. With the wind out of my sails, I rebooted into Linux and started the lengthy clone, dependency download, and build process. I was able to finish painting some miniatures and go out and run some errands, and when the build was complete, I was rewarded with a perfectly stable UE4 running in my preferred operating system. Success!
The next major hurdle was adding the second ship for local multiplayer. At this point, I had a PlayerShip blueprint to represent the ship, and I had written a custom PlayerController to handle transforming the input events into ship events. I had also separated the ship's health and damage into a separate GameState object. It seemed like a good idea at the time, but I had no luck getting local multiplayer working. I thought gamepads would be fun, but UE4 didn't recognize my gamepads. I spent a lot of time just trying to get my old Logitech pads recognized as XBox360 controllers in Linux so that they would be accessible in UE4. Incidentally, this is done through xboxdrv, but it took a lot of reading and fiddling to make any progress. Even when I had the controllers recognized, I had no luck getting the second player to properly be assigned a PlayerController and possess the second ship. Again, I didn't keep track of exactly how much time I whittled away here, but clearly I was not making headway. I'd still like to know what I was doing wrong, although many of the signs point to the fact that custom C++ integrations are needed to do what I wanted to do. I cut my losses and moved forward with a different input scheme: I could get away with using the keyboard for both players if I eliminated the idea of using UE4's built-in multiplayer systems and instead just faked it. I wiped out my custom PlayerController and GameState and put the keyboard events for both players directly into my map, which had references to the red and blue ships. This was straightforward to set up and had me up and testing multiple ships in short order. Remember, kids: the player cannot see your engineering.
Moving into Sunday midafternoon, I finally added the ability to shoot. I spent too long on this, since I started with the Projectile Movement component, which was fine except that I had drawn my sprites aligned to the Y-axis instead of the X-axis. This is a newbie mistake and made me have to manually rotate a few vectors to get projectiles moving the right direction. Then, I decided to tinker with having the projectiles also affected by the planet's gravity, but this required then undoing all the work with Projectile Movement, since I needed a physics object whose velocity I could manually "pull" on. I got that working, but it wasn't really noticeable or fun, and I ended up going back to the original approach.
By Sunday late afternoon I had put in the end screen and title screen, and I kicked back to look at it. My ship drawings were meant to be placeholders, but the comic badness of them had become endearing, so I decided it wasn't worth the time to futz with them. Drawing isn't my strong hand, but it also isn't my interest in this kind of work. The lack of music, on the other hand, was really noticeable. This, combined with the fact that I had never done any audio in Unreal Engine, made me think that this would be the best use of my last push. Of course, the spirit of Ludum Dare compo is that you create a game from scratch, including the music, but at this point I didn't feel like re-learning enough composition software to make a bad custom soundtrack. Instead, I turned to the reliable and amazing Kevin MacLeod (Incompetech.com), who has a browsable collection of CC-BY-licensed tunes appropriate for games. It was a little choppy figuring out how to bring the audio into the game, but no more so than I expected, never having done it before. I grabbed an explosion sound effect from the Unreal Engine Starter Kit and a laser blast from a public domain sounds site.
The last step was to create the shippable executables, which I had also never done before. The build process is slow, and at first it didn't have the window size that I wanted. I discovered that the best way to work with this was by manually editing some .ini configuration files, and once I had that right, it worked fine. I uploaded the Linux binaries, rebooted to Windows, pulled from the project's repository, and built and uploaded the Windows executables. At 8:15PM, with 45 minutes to spare, I posted to Facebook that I was done and went downstairs for a celebratory drink.
A friend with a Mac was interested in trying the game, but of course you cannot build Mac executables without a Mac. (Of course. Because reasons. And thinking different!) On Monday, I built an HTML5 version and uploaded that one too.
There is one "bug" in the game that my son and I noticed when playing together: if both players explode, then red wins. I know exactly where that is in the code, and what to do to fix it, but I decided that I'd just leave the game as-is, in the spirit of the jam. The workaround is to make sure that you get to be red.
What it all means
As I mentioned above, this was a sort of final exam to myself, and I definitely passed. I navigated through the pieces of UE4 that I had been studying and practicing, I troubleshooted a few problems by working with other engine features, and I was able to quickly learn and integrate new features. I'm still not sure how I will teach others to use this engine, but course planning is for another time.
This very modest game took me approximately 16 hours of work. If this were a three credit-hour course like my CS315 Game Programming course, that would be roughly two weeks of effort—except, of course, it's actually much more, because you'd also have to account for the extremely high cost of context switching. I heard a heuristic for designing exams that says that you should give students three times as much time as it takes you to take the exam. Let's assume that this is a good rough estimate, and we can say that my three-week project would then equate to a nine-week project by an inexperienced undergraduate. Now the amount of time I spent learning Unreal Engine the past eight months or so definitely exceeds a single course, and I have a wealth of background knowledge to draw upon. I'm afraid all of this leads me to the unfortunate conclusion that maybe something this simple is still beyond the scope of what I can expect from an average CS315 student in the Fall.
I know I've heard professors say that one of the advantages of using games as a motivating context is that students will put in more hours on them than normally required, but this feels unethical to me. Sure, some students may put in more than the expected nine effort-hours per week for a three-credit course, but the course design should not really encourage nor reward that: that's just encouraging crunch, in a field where we know it's destructive to individuals and families. I would not stop a student from learning, but I also want to be very careful to design a course that permits glorious success within the expected amount of time and effort, given the prerequisite knowledge.
I already received one pre-course complaint from a student whinging about how he doesn't like working in teams, but I think this experience shows me that teams will be necessary. My past experience with pair programming tells me that if I was learning UE4 with a friend or a cohort, I would be much further along now than I would be otherwise. Also, my time investment should help me bootstrap the students, so I can be the mentor for them that I didn't really have on my random walk through YouTube tutorials, documentation, forums, and samples.
Parting thoughts
This has become quite the epic post, but I think that's everything I wanted to record for future-me, when I'm trying to remember the kinds of mistakes I made and my thoughts about implications. I know it's easy to lose sight of how hard it is to learn something new after you've learned it. At last night's Computer Science spring banquet, an alumnus on the panel put it well, talking about programming problems: "None of us know how to do it until after we've done it."
Two final thoughts about Ludum Dare. First, I was tickled to see a LD38 entry from an alumnus I worked with years ago, a creative and clever guy who enjoys the hobby of making little independent games. I'm so glad to see that he is finding joy in making games. Even better though is the second parting thought: one of my CS222 students was inspired by my course to make their first game this past weekend as well. With no other background besides sophomore-level computer science, this student learned Stencyl and made a fun little game from scratch, and shared it with the world as part of Ludum Dare. It doesn't get better than that!
Thanks for reading!
You're welcome, and in case you needed it: DIRECT ENCOURAGEMENT! :)
ReplyDeleteBe sure to let me know if you do an LD or a Global Game Jam so I can follow your project!