StevieJump is a fast paced jump’n’run’n’gun game. The goal is to climb an endless tower while fighting enemies and collecting upgrades, to get on the top of the scoreboard.
I programmed Stevie Jump during the summer semester of the year 2020. The idea was to use my programming skills to create an application of my own completly from scratch. I started with an empty visual studio project and worked my way up using c++, openGL and many other libraries.
Download the game here
How do you play the game?
For every higher platform you reach, you will get 10 points. Killing enemies will give you more points. The aiming is done with the mouse cursor, your weapon will get stronger if you collect more upgrades. As the game progresses the enemies will move faster and also the level is scrolling downwards faster. Once an enemy touches you, or you fall of a platform, the game is over. The game menu is openend by pressing ESC.
Small enemy: This enemy type moves fast and is killed with one shot. It gives 100 points.
Big enemy: This enemy moves slower and tanks up to 10 shots. It gives 300 points. This enemy only spawns when you progress far enough into the game.
Weapon upgrade: This upgrade will increase the range and power of your weapon, while also decreasing the cooldown between shots. When you collect four upgrades you get an extra shot.
Stevie Jump – behind the scenes
- openGL, graphics
- glfw, window handling
- glm, mathematics
- box2D, physics
- imGUI, menues
- DevIL, textures
- irrKlang, sound/music
- libcurl, http connection
- micropather, A* pathfinding
- GIMP, creating animations and normal maps
- famiTracker, 8 bit music
all assets are taken from itch.io
The level you jump through is generated with an one dimensional cellular automata. A cellular automata is a grid of cells with different states. In the case of a elementary cellular automata there are only two states. The state of the neighbour cells influence if the state of a cell is changed within a timestep.
On this picture you can see a ruleset to describe the change of cells. Every elementary cellular automata is given a unique name, based on the ruleset.
For more information about cellular automata see https://mathworld.wolfram.com/ElementaryCellularAutomaton.html, or chapter 3.2 of my bachelor thesis https://steven.schuerstedt.com/?page_id=17.
The beaty of cellular automata is, that they form interesting and diversified patterns. They are not completly random, so that no pattern is seen at all, but they also do not reproduce the same pattern again and again. They are complex. That why I decided to use different cellular automata to generate the level for my game. To generate the level the cellular automata is turned upside down, so the level advances at the top. Next, the vertical space between is set to a fixed value, to allow for more room for the player to move. At random the current rule of the cellular automata is changed, so that the generated patterns will change. This change can be seen when the texture of the blocks and also the music is changing. A downside of this approach is, that I cannot guarantee the generated level is actually beatable within the limits of the game physics. There are situations (which are very rare) where the player cannot advance further through the level.
For the game physics I used the box2D library. box2D is a very powerful library and I spent most time working on the game physics. The game physics deals with moving the player, the enemies and the bullets and also handles all collisions between those entities and the environment.
In order to move the player, an impulse is applied to the player object when a key is pressed. This information is given to the box2D library that calculates how the player and the environment respond to that event. The new position and rotation of the objects is then given to shader to draw the updated position.
The enemy ai was one of the hardest parts to program. I used micropather, a A* path finding library. Path finding is the process of finding the cheapest path in a tree like graph structure. A* is a very effective algorithm doing so. The hard part is to translate the problem into a graph. In my case I divided the level into different blocks, which are the nodes of the graph. If the block is passable, its cost is the distance from the current block. If it is not passable, the cost is at a max value. This information, together with the position of the enemy and the player in this graph, are given to the micropather algorithm, which then calulates the cheapes (and therefore shortest) path for the enemy to take. Now this path has to be translated back into impulses or forces for box2D, in order to move the enemy into the correct direction.
This process was very error-prone and for an easy fix I disabled the collision detection of the enemy completly. This results in the enemy sometimes walking right throught the corner of a block.
The lighting is done with normal mapping. The phong lighting model uses the normal of a surface to calculate the lighting value. Because 2D surfaces have no normal, normal-mapping “fakes” a normal by encoding it in rgb values, as seen below. This normal is then used to perform the lighting calculations. This way the geometry appears 3D, although it is just a 2D Texture.
The phong lighting model is a very broad estimation of light. Its main component is the so called lambertian term, which calculates the orientation of the surface in relation to the light source.
Because I programmed the game in c++ with opengl and some other low level libraries, there was alot of work todo, and alot of things I had to do where not directly related to the game.
Many people might ask (including myself) why I did it that way, and not used a tool like unity, which would be easier to use and more effective to create a game. I think there are several reasons to use these low level tools for creating an application, one of them it being an educational experience for myself. (see https://steven.schuerstedt.com/?page_id=505 for an indepth, philosopical explanation)
For the next project I would spent a lot of time thinking about what tools I want to use for the problem I want to solve and on what level between hardware and software I need to be in to solve it most efficiently.