Urge to Merge is a simple arcade game rendered in 3D. The player drives a racecar down a freeway at speeds of questionable legality, obtaining points for successfully dodging cars.
Keyboard | Gamepad | Meaning |
---|---|---|
W | Analog Stick Up | Accelerate |
S | Analog Stick Down | Brake |
A | Analog Stick Left | Turn Left |
D | Analog Stick Right | Turn Right |
Space | A (Button 1) | Start Game |
Escape | Start (Button 8) | Pause |
Shift-Q | Quit |
I created the car models and UV maps using Blender. The cars were modeled using subdivision surfaces, which were baked into polygons and manually edited to reduce the triangle count. While I was creating the textures, I realized that photorealistic images I had in mind didn't fit the arcade feel of the game, so I opted for a simple cartoon appearance.
The tires are modeled separately from the cars so that I can apply hierarchical modeling. This has two benefits: the same tire model can be instanced to reduce GPU memory and the tires can be transformed separately from the car body. This reduced modeling time and allows the player car to turn its tires when rotating.
Each object was exported out as an OBJ file. Because I intend to try to port this program to multiple platforms and languages (possibly Android and iOS), I chose to write a single program that reads in OBJ files and generates source code. This has a negligible effect on executable size (roughly 100KiB for the two car models and the tires) and speeds up model loading because the data is already in a usable VBO format.
All shading is being done on a per-pixel basis in GLSL. The cars use Phong shading with a texture map bound to the diffuse color. The scene uses a single point light. Blender precomputes per-vertex normals; I do not perform normal mapping. The road is a procedurally generated texture that can overlay simple radial shadows that are passed to the shader.
There is only one texture map for the enemy car. However, a tint can be passed to the car shader. Any grayish pixels in the texture are multipled by the tint color. This allows a variety of enemy car colors without altering the color of the tail lights or head lights.
All collisions are calculated using axis-aligned bounding boxes (AABBs) for simplicity. The enemy cars are represented using one box per car. To handle the player car, I split the object into three discrete AABBs that are translated with respect to the car's rotation.
No scene partitioning is being done because the number of objects at a time being considered is so small (<10).
All in-game 3D graphics are rendered to an off-screen buffer before being displayed to the screen. I use this to provide multisample antialiasing using OpenGL's framebuffer objects.
The 2D overlays in the game and in the UI are rendering using the SDL surface API. The surface is copied to a texture map in OpenGL. I use alpha blending on a rectangle that covers the screen to finally render the HUD. Multiple passes can be used to produce a layering effect (demonstrated with the text outline in the splash screen).
I used SDL_mixer to load Ogg Vorbis files for sound effects and music (I composed the music with GarageBand, the sound effects are from a stock sound effects library). The program plays three songs in a loop.
See the README file for the licenses on included components.
The splash screen. Uses two layers of 2D compositing, and performs a turntable animation on the model.
The level start screen. Displays the current level and how many lives the player has left.
In-game. The cars are randomly distributed, but cluster in the middle of lanes. Each car is assigned a random color when it is spawned.
In-game. The player's movement is determined by applying a simple one-dimensional physical model with acceleration (masses are not considered). The wheels can rotate independently from the player's car.
In-game. The HUD updates after the player has crashed into another car. Each car has a simple shadow drawn onto the road.
The pause screen. Notice the darkening effect and the translucent text.
Each car has a set of axis-aligned colliding boxes assigned to it. Each enemy car has one, but the player has three that transform with respect to the racecar.
A raytraced render during development of the car models. No texturing has been applied and minimal material assignment has been done.
A raytraced render during development of the car models after UV mapping, but before the final textures. The final game adds a subtle black stroke around the racing stripes. I also experimented with applying a motion-blur effect for this image.