My First Serious Game for Some Definition of Serious: Just Wait
I have been learning about game development for quite some time now. I have started dabbling with PyGame but never really found the motivation and time to make a complete game. Then I have found LÖVE and it seemed like a better platform to learn game development. I have been learning lua and LÖVE, learning game development techniques and working on my first game simultaneously for the last four months. I think it is time for a pre-pre-pre-alpha tech demo release.
When I began working on this project my goals were modest. I wanted to make a simple game with a loader, menu and end game screens. I wanted to figure out how those different states of a game software are best expressed. Soon I found myself building abstractions over abstractions due to equal parts of clumsiness and curiosity. Scene manager was the first.
Just Enough Rope to Hang Myself
You integrate your code with LÖVE by overriding callbacks like love.update, love.draw and love.keypressed. The problem you face is that you want to do different things in different program states. For instance you want to draw a menu before the game starts and afterwards you want to draw a representation of your game world. It seemed most natural to group all the callbacks for a state together, as opposed to branching from within individual callbacks like this:
if state == "this" then do_this() elseif state == "that" then do_that() end
So I went ahead and implemented what a call scene_manager. And scenes were basically modules (in lua they are actually associative arrays) that contained their own versions of callback methods. Then I used the scene_manager to replace LÖVE callbacks with the methods in the scene module given:
function change_scene(new_scene) unload_current_scene() load_scene(new_scene) end function load_scene(scene) _previous_callback = love.callback love.callback = scene.callback end
I am just trying to give you an idea of the difference between the two approaches. Code blocks above are in no way complete or correct. Also my intention is not to get into implementation details in this post. I just want to share my experiences. Moving on...
So I took my scene_manager and wrote the states of my program as scenes. This provided a comfortable blueprint until I realized the individual scenes were too similar to be seperated this way. The menu is essentially a mini game with the mechanics of navigating the choices and it ends once you make your choice. I said to myself; why not absorb these scenes into the game itself, as a different game state?
An Object Oriented Confusion
As I mentioned LÖVE provides a callback based framework. I figured I could use a global dictionary to hold the game state. I built my game objects, such as the player, as classes and stuffed instances of those classes in the global world dictionary. In this first design all behaviour (event handlers and drawing) was on these classes.
It didn’t take long for this design to get out of control. If there is an event, say a keystroke, that affects object X, just call X.event(), right? And if it affects Y, call Y.event(). What if X and Y are interacting? Do I call X.event(Y) or Y.event(X)?
I hear you shouting at your monitor; “Observer pattern, you idiot!” It would certainly be an improvement but when you are prototyping you don’t really have a solid understanding of your domain model and you still want to keep things simple and tidy.
vst told me, rather matter-of-factly “MVC”, when I described my problem. And unsurprisingly, he was right. With an MVC design, not only interactions between entities are handled elegantly, seperation of state, logic and presentation makes it easy to build more complex systems.
Well, until it doesn’t. I might expand on this in a future post. I will just summarize the shortcomings of MVC here:
- If you are writing your game in a dynamic programming language; you either end up implementing a crude type system or leave yourself open to surprises, because you will be dealing with 3x things (a model, view/renderer and approximately 1 controller per entity).
- LÖVE essentially renders each frame from scratch starting with an empty canvas. That would mean, assuming you have implemented MVC in a strict sense, you will be running expensive rendering calculations every frame even when the underlying data doesn’t necessarily change.
Laying Out More Infrastructure
Games are event driven. This fact is independent from how you express your game rules in a high level. Starting a new game from the menu is an event. So is winning and losing. And everything in between, from user input to audio-visual feedback. So you can express your business logic (i.e. your game rules) as a set of logical facts, but you will still need an engine to run them.
An event system is a crucial part of this engine. LÖVE comes with a low-level event system that is more suitable for processing input events. You can still feed your custom game events in it but I decided to roll my own because I wanted to implement publish-subscribe pattern. It ended up being quite a powerful piece of code in comparison to its size. I would say it is one of the very few (a set of one, perhaps?) things I got right in this project.
The most challenging part of this project has been the rendering. I ended up implementing a simple scene graph but I am still not sure if that’s the way to go. There are two issues that still bother me. First is the accidental state related to the presentation of game objects. I know some of you will argue that in a game, that is essential state and belong to models. But I still think the models should be completely decoupled from the views. An example is the coordinates an object’s sprite gets stamped at. These coordinates are upper left corner of the sprite image and depend on the screen size as well as the object’s size and position (usually the center coordinates). So the upper left coordinates in pixels can be calculated every frame but as the situations get more complex keeping state in the view becomes necessary. That brings me to my second issue; how to keep scene graph and game state (models) in sync? I am currently creating a whole new scene graph based on the models every frame.
I think that’s why most game code I’ve read wasn’t following MVC strictly. Models and and views were fused in together in what was called entities. Then again, maybe trying to fit the whole game into MVC was a mistake. Maybe MVC is a good fit for some parts of the program and other approaches are better to for others.
Without Further Ado...
Here is the download link(latest version) for the game. Once you have LÖVE installed on your system just double-click on the file and it should run.
Unfortunately, I have spent too much time on the engine so there is not much there that we can call game. LÖVE gives you only the basic API, so you need to build your own game engine. Perhaps I should have used a more complete game engine. I have no regrets though. I learned a lot. I want to make games, but I want to emphasize learning for now.
If you have any feedback on the game or any suggestions in general, you should shoot me an email. I intend to finish this game, so expect more updates.
|||Here is an interesting discussion on the topic. Especially Kylotan’s, Iain’s and munificent’s answers.|
|||Input events can be managed more conveniently via callbacks in my opinion.|
If you have any questions, suggestions or corrections feel free to drop me a line.