While searching for internships, I came across a job posting from a very successful game studio here in Vancouver. One of the big requirements for this internship is “Strong Python skills.” At first, I was a bit discouraged since I had no experience in python at that point (until I made this commit). But I realized I’ve been programming for exactly 2 years now, and I have a bunch of Java and some C/C#/C++ experience. I also understand object-oriented programming pretty well and I’m familiar with some high level software design principles (thanks to UBC), so I shouldn’t have any trouble picking up python.
So I started tinkering with pygame and I’m working on a little 2D platformer. So far, I only have a couple of images being rendered on the screen and I can move the player horizontally using arrow keys. As I started thinking about more movement related things to add to my player sprite, I realized I desperately needed to organize my code. Then I realized the bigger mistake I had made. I didn’t plan. I don’t have a UML diagram, and no idea how various modules of this game will work together, or what these modules even are.
I’ve done some game programming in Unity, but never coded a game “from scratch” like this before. My software engineering classes have taught me a very “top down” approach to programming, especially if you’re starting from scratch (which you rarely do in a full-time job setting). This approach involves coming up with a list of “features” or eliciting them from a customer. Since this is a simple 2D game, my feature list will be simple:
- I should be able to move horizontally, and jump on platforms. Gravity will bring me down
- I should be able to kill enemy sprites
- I need to be able to progress through levels, even if I only make a couple of levels to start with
In order to implement these features, I need a few modules (aka classes).
- A main file will start the game up, contain the game loop and call other functions as necessary
- A constants module, so I can tweak all static/global constants from a single file
- An enemy module allows me to spawn them easily, and add different types of enemies in the future (using inheritance!)
- A player class will keep track of player stats. There will only be one instance of this class at any time
- A level class also makes sense. Perhaps this class will take a text file as a parameter and turn that into a level of tiles
That seems to be a pretty good list so far. The only thing I’m confused about is whether I need an “animation” module. In Unity, there’s this notion of an “animation set” which is essentially a collection of animations. For instance, player_jump could have two image frames played for 15 frames each for a 30 fps game. Add in player_shoot, player_die etc. and you have an animation set. You can create a new animation instance based on some conditions and pass any of these animations as a parameter in order to play that sequence of images. So an animation module makes sense. This is what my class UML looks like so far:
The classes we came up in the above diagram seem to capture all the things necessary for the “logic” aspect of the game. I’m not going to define separate modules for things like “audio” and “renderer” etc. because pygame provides me with those classes. However, the above diagram tells us nothing about how these modules are related to each other. So let’s fix that. If you’re not familiar with class diagrams and class relationships, I’ll let you read this page.
- The main module has a strong "has-a" relationship with the rest of the modules since the main represents the core instance of the game. Think of the game as being composed of constants, enemies, a player, animation sets and a few levels
- As mentioned above, the enemy class could be the "abstract" class for different types of enemies but for now, I'll keep it simple
- Constants and level are likely related in that constants will keep track of how many levels have been successfully completed. So a general dependency makes sense here
Here’s the refined UML
This makes sense because all the modules together form the main instance of the game. None of the other modules can exist without main although the main instance of the game can exist without constants or levels (maybe we’re in the menu), or player (maybe our player died and hasn’t respawned yet), or enemy (no enemies currently alive).
I’m happy with this so far, although I may tweak it later if I decide to add extra modules or refactor code into its own class. I guess I should start implementing this now.
Note: High level design can be very tricky. There are likely better ways to organize the code base for a game like this. If there are, please suggest them in the comments below. I’m always open to constructive feedback!