Lisp Game Jam Post-mortem

I recently participated in the 2016 Lisp Game Jam, an event where participants had 7 days (later extended to 10 days) to create a computer game using a dialect of Lisp, a family of programming languages. This is a "post-mortem" of that experience, in which I discuss what went well, what could have gone better, what I learned, and so on.

About My Game

Screenshot of the game. There are three colorful alien characters standing on grassy and ice blocks, with various coins and gems scattered around.For the jam, I attempted to create a local-multiplayer action platformer game, entitled "Treasure Jumpers", where you run and jump around trying to collect more coins and gems than the other players. (It's not a very inspired title, I'll admit. But it's better than "Platformer", the working title it had for most of the jam.)

I programmed Treasure Jumpers in CHICKEN Scheme (an implementation of Scheme, a member of the Lisp family of programming languages). I used Simple DirectMedia Layer (SDL) 2 for graphics, user input, etc., via chicken-sdl2, a set of library bindings I have created to make it easy to use SDL2 from CHICKEN Scheme.

The characters, tiles, and other image assets used in the game are from Kenney Game Assets, a set of public domain game assets created by Kenney game studio.

I decided to create an action platformer game because it would allow me to test the performance limits of chicken-sdl2 more than, say, a puzzle game would. I initially considered making a space shooter game instead, but I decided it would be better to make that after I release chicken-sdl2 0.2.0, which will have support for SDL2's accelerated 2D rendering API. An action platformer doesn't need anything that wasn't already in chicken-sdl2 0.1.0 (except music and sound effects, which will have to wait until I make chicken-sdl2-mixer).

You can read about the development of Treasure Jumpers in my blog posts from during the jam. You can view my game jam submission and download the source code if you like. The latest code is in the chicken-sdl2-examples repository. I recorded and uploaded two videos so you can get a sense of what the game is like:

What Went Well

Coding in CHICKEN Scheme was a lot of fun, and very productive. Adding each new feature to the game was quite painless, as was refactoring and cleaning up the code. Scheme's functional style and macro system make it very easy to compose small, simple parts into a larger whole. CHICKEN also has many useful extra features that helped. Scheme is sometimes derided as being overly simplistic and lacking in features, but this is certainly not true of the major implementations.

It was great to put the chicken-sdl2 library bindings to the test with a complete game. It gave me a number of good ideas of things that I should add or improve in chicken-sdl2, and things I should create as a separate library. I was expecting to uncover at least one major bug in chicken-sdl2 during the jam, but the only bugs I found were relatively minor and easy to work around. I did run into a performance issue (described below), but I don't think it is caused by chicken-sdl2, just my quick and dirty game code. So, I'd say chicken-sdl2's maiden game jam was a success!

I really enjoyed creating my game. It is very satisfying to watch your own creation coming together, one step at a time. At first I had nothing but a few sprites laid out in a static scene. Then I was able to load a level from a file and draw it on the screen. Then I added characters which followed a simple parabolic gravity curve, but didn't collide with any of the tiles. Then I added collision detection, so the characters would collide with the tiles. Then I added user input, so you could control the characters by pressing keyboard keys. Then I added treasure that you could run around and collect. It was wonderful to see each new piece of the game come to life on my screen. Game development gives me a visceral joy that I don't experience with most other kinds of programming, where the results are usually less interactive and your successes feel less tangible.

Also, thanks to the long deadline (many game jams are only 2 days long!), I was able to take my time and clean up and document my code. This was important to me, because I was creating the game primarily as an example for people to learn how to use chicken-sdl2.

What Could Have Gone Better

Overall, things went pretty smoothly. There were a few things that could have gone better, though.

Performance

The main thing is that the game has a performance issue, where CPU usage gradually increases and framerate gradually degrades over time. After several minutes of playing, the framerate can become so choppy that it is difficult to keep playing. Since the issue takes several minutes to manifest, I didn't notice it until late in the jam. For most of the jam, I was testing the game for only a few seconds at a time to verify that the level was rendering correctly, or the players were moving correctly, etc. It wasn't until my game was complete enough that I could actually play the game for several minutes, that I could finally notice the issue.

I suspect that the issue is caused by my quick-and-dirty code allocating more objects than it needs to, which puts a lot of pressure on the garbage collector, so that garbage collection takes longer and longer over time. The main culprits in my code for allocating objects were the physics and collision detection systems, since they generated new temporary bounding boxes and hitboxes every frame, at ~60 frames per second. I spent a few hours trying to optimize that code by reusing existing bounding boxes and hitboxes, and I managed to alleviate the performance issue somewhat, but I wasn't able to fully solve it.

I think if I spent more time optimizing the code, or if I had been more performance-conscious from the start, I could solve the issue. But in a game jam setting, you're always trying to implement things quickly, and the quickest way to implement things often involves a lot of temporary allocation, for example mapping over a list to create a new modified list. This practice isn't specific to Scheme, but functional-leaning languages like Scheme make this especially tempting, because many of the built-in operations make this style of programming more concise and convenient than other styles. But for a game, especially an action game running at 60 frames per second, that way of programming can easily lead to performance issues, even when you are using a mature, high-performance implementation like CHICKEN Scheme.

This experience has given me ideas for features I can add to chicken-sdl2 to help with performance, such as pools of reusable rects and points, so that the programmer can borrow a rect or point for a while, use it for some temporary calculation, then return it to the pool. That would help alleviate some of the allocation and garbage collection of SDL objects, at least.

Since Treasure Jumpers is an example game for chicken-sdl2, I will probably revisit it from time to time to improve it and showcase new features. I will also probably try to improve performance when I do. But for now, I'm putting this game on the shelf.

Spending Too Much Time

There were a few aspects of the game that I spent too much time on, to the detriment of other parts of the game. The biggest time sink was collision detection and resolution. Even for very simple collision detection like this game has, it takes a lot of time to implement, and then a lot more time to tune it so it feels fun. Platformer games are notorious for this, and many experienced game-jammers advise not to attempt them. Certainly if this had been a shorter jam, I wouldn't have tried.

There are CHICKEN bindings for the Chipmunk physics engine currently in development, but they are not released yet, so I didn't want to depend on them for my game. But if those bindings (or bindings to another similar physics engine) had been available, I certainly would have used them instead of rolling my own physics engine. Rolling my own simple engine was certainly fun, but it was not an efficient use of my time.

Besides collision detection, there were many smaller parts of the game that I spent more time on than I should, such as the event handling system. Again, they were fun to write, but I could have started with something simpler, and used the extra time for other features.

Postponed Features

There were quite a few features I wanted to put in the game, but had to cut or postpone in order to get the game done in time for the jam, or because I ran out of energy towards the end. The features that I thought I would probably finish were:

I could probably have finished those features if I had spent less time on collision detection, or if I had postponed more of the cleanup, documentation, and blog posts until after the jam. On the other hand, if I had put those things off until after the jam, I might have never gotten around to actually doing them. So maybe I made the right choice.

There were also some "maybe someday" features that I wish I could have added, but I realized early on that I would need to cut:

Life Disruption

The final thing that didn't go well, is that the jam was quite disruptive to my daily life and sleep habits. This is common with game jams, but with a 2 day game jam you can usually compress and limit the disruption to a single weekend, then get a good night's rest Sunday night and go back to your normal life. For a jam lasting 7 or 10 days like this one, you have a whole week of disruption, and it is much harder to maintain a balance between the jam and your daily obligations and habits.

I work from home and set my own schedule, so I was never "late to work" because I stayed up too late working on my game. But, it was very hard to pry myself away from the jam and do my work, interact with my family, walk the dog, etc. I also found myself staying up much too late and not being able to sleep, because my mind was still thinking about the game and what I would program next. More than a week of late nights and trouble sleeping takes a significant toll on you! The last few days of the jam, I just ran out of steam and had to give up on some features I was planning to implement.

I think my ideal game jam schedule would be opening Friday morning or noon, and ending Sunday night. That way you have some time to think about what you will make during the day on Friday, then you can work Friday evening, Saturday, and Sunday, then you're done.

In any case, I think I will limit my involvement in future jams to no more than 4 days.

What I Learned

I'm pretty experienced with CHICKEN, and SDL, and I wrote chicken-sdl2 myself, so I didn't learn a ton of new technical things during the jam. For me, the jam was more an opportunity to help other people learn, by making an example game. It was also an opportunity to sharpen my skills, and test out chicken-sdl2.

I did learn one new technical skill, though: how to use the CHICKEN Scheme performance profile to locate performance bottlenecks. This is a skill which I will be happy to use in the future to optimize my libraries and applications.

I also gained more experience with simple physics simulations and collision detection. I was already familiar with the basic concepts, but I did pick up a few tricks, such as having multiple hitboxes or points located on different sides of a player, so that you can more easily determine how to respond to the collision.

Final Thoughts

I got everything I wanted out of this jam:

I'm looking forward to the next one — although if it is another 7 day jam, I will have to limit my participation, to minimize the disruption to my daily life.


Comments


Thanks for the great write up, I really enjoyed it. Big fan of game devel and lisp in general!Can you eleborate on your development style with Chicken/Schene. Could you do things interactively, or would you develop, compile and test? BTW, I let you game run for a couple of hours from 11:50 till 15:32, and the memory increased from 54,3MB to 253,9MB, so you have a memory leak I guess and it is pretty choppy as you described it but the players still move around. I will dive into the code later to learn hopefully something.



Hey John, thanks for your comments. I played around a bit more with Chicken+Slime+Emacs and also Chicken+Geiser together with your sea+stars demo as well as the treasure jumper game. What I am interested most in Lisps+game development is the interactivity, being able to change variables or even complete flows while things are running. When running both mentioned projects from within Emacs+Slime or Emacs+Geiser the game/demo starts and performs well/normal, but I am not able to change anything from with Geiser/Slime because the repl is blocked for some reason. I tried to instantiate the main loop in a separate thread but the situation is the same. Do you have any idea why this is happening? Is there even a chance to get this running in the way I wanted it to be ? Sorry for bothering you with this. Best regards



Comments are closed.

Previous post: Lisp Game Jam Log #11 Next post: chicken-sdl2 0.2.0 Released