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.