Tuesday, March 19, 2024

Porting a Game from Java

I decided to learn about games, so I followed along with a tutorial by Kaarin Gaming. His tutorial was written in Java, but of course I used Common Lisp. I made no attempt to faithfully replicate his design, but I followed it closely in some places, less so in others. The resulting program was more or less a port of the Java program to Common Lisp, so it not very remarkable in and of itself. Certainly I don’t expect many people to be interested in reading beyond this point.

It’s known that Java is wordy language and it shows in the end result. The tutorial had 3712 lines of Java code in 39 files and the equivalent Common Lisp was 2255 lines in 21 files. A typical Common Lisp file would contain more code than a typical Java file. It was often the case that a Common Lisp file would contain multiple classes.

Both versions used separate render and game mechanics threads. The render thread ran at about 60 frames per second where the game mechanics ran at 200 steps per second. The threads were mostly independent, but the game mechanics would occasionally have to query the render thread to find out whether an animation had completed or what frame the animation was on in order to synchronize attacks with reactions.

There were a couple of notable differences in the two implementations. The Java implementation would advance animation frames imperatively by incrementing the animation frame counter every few rendering cycles. The Common Lisp implementation would instead compute the animation frame functionally by subtracting the current time from the animation start time and dividing by the ticks per animation frame. In Common Lisp, different animation effects could be achieved by changing how the animation frame was computed. If you computed the frame number modulo the number of frames in the animation, you’d get an animation loop. If you clamped the frame number, you get a one-shot animation.

CLOS made some things easier. An :after method on the (setf get-state) of an entity would set the animation. The get-y method on some objects would (call-next-method) to get the actual y position and then add a time varying offset to make the object “float” in mid air. The get-x method on projectiles would would (call-next-method) to get the starting x position and then add a factor of the current ticks. This causes projectiles to travel uniformly horizontally across the screen. I often used the ability of CLOS to specialize on some argument other than the first one just to make the methods more readable.

The Common Lisp code is at http://github.com/jrm-code-project/Platformer, while the Java code is at https://github.com/KaarinGaming/PlatformerTutorial. Being a simple port of the Java code, the Common Lisp code is not exemplary, and since I didn’t know what I was doing, it is kludgy in places. The Common Lisp code would be improved by a rewrite, but I’m not going to. I was unable to find a sound library for Common Lisp that could play .wav files without a noticable delay, so adding sound effects to the game is sort of a non-starter. I think I’ve gotten what I can out of this exercise, so I’ll likely abandon it now.

7 comments:

Charlie McMackin said...

Judging by package.lisp, it looks like you had plans to use your linear fractional transformations... did that not work out? Or am I missing their use? Github search isn't showing me matches at least.

Joe Marshall said...

I had an idea of using linear fractional transforms for mapping world co-ordinates to screen co-ordinates, but they didn't come in handy.

Anonymous said...

The project looks interesting and I'd like to investigate further, but sadly there are a few problems with the code. First it does not quickload out of the box. While quickloading I had trouble with linear-fractional-transformations (removed refernces entirely from the code), package utilities (found it on your github and hand loaded it), named-list (fpund it on your github, but quickloading it fails), pathnames are wrong (I adjusted function project-directory), Font path (I changed main-window to reference a font found on my system). Eventually the Code compiled and could be started, but it crashes(terminates?) when pressing "config" or pressing "x". Since I'm a noob I'm clueless why the game terminates there, without debugger or error message.

Would you mind to clean up the code a bit, so that noobs like me have a sane starting point? that would be great, because it seems to be a promising starting point to learn CL.

Anonymous said...

... continued: it is a promising starting point, because it outputs some nice graphics and much of the stuff is already working. And there are still some construction sites to improve on the code or to expand the functionality. I would not try to start this project myself, but fiddling with this existing code could be a joy. But the crashes without some hint whats wrong and the difficulties while loading are a big obstacle for a noob like me, though

Joe Marshall said...

Thanks a lot for your interest. This is very much an abandoned work in progress, so there are a lot of rough edges, as you noticed. I debated posting it because I didn't think anyone would be that interested.

The pathnames are not relativized, it isn't configured for quickload, and several points in the code just silently exit the game, so I imagine it is frustrating for a beginner.

I'll spend some time going through the code and trying to make it more friendly and post an update later on.

Anonymous said...

That is great! It now easily quickloads as a local project and now I realize that the game quits on "config" and the key "x". I'll start fiddling with it, thank you!

One thing for other readers: the function game-loop, runs out of stack space, if compiled with debug settings, due to tail call optimization not in effect, when compiled with debug settings.

Joe Marshall said...

No one who reads my blog will be surprised that I expect tail recursion to not push stack.