First Person Controls
- • WASD + Mouse for camera movement
- • Shift - Sprint
- • E - Interact / Pick Up Items
- • Space - Jump
- • Left Click - Shoot Laser Shotgun
- • 1,2,3,4,5,6 - Craft Upgrade
Space Ship Controls
- • W - Pitch Ship Down
- • S - Pitch Ship Up
- • A - Yaw Ship Left
- • D - Yaw Ship Right
- • Q - Roll Camera Left
- • E - Roll Camera Right
- • Shift - Activate Boost
- • Left Click - Shoot Laser
Our game has two distinct sections with separate controls and unique cameras. The first section involves a normal first person camera, with camera pitch limited to 180 degrees. We used a sin and cos function to add view bob, as well as the camera matrix transforms to position the laser shotgun.
The second part of the game required a more complex camera that had full 360 degrees of movement on all axes. To accomplish this whilst avoiding issues like gimbal lock, we implemented a quaternion based rotation scheme. Additionally, the camera moves on a slight spring when boost is activated, to give more of a sensation of speed.
Procedural World Generation
In order to increase replayability and difficulty of our game, we implemented procedural world generation. A starting room is created, and then rooms are placed adjacent
to existing rooms in such a way to guarantee a spralling spaceship, until a specified number of rooms are placed. We designate five special rooms - the starting room, the exit room, and three rooms with penguins that must be
freed before moving to the second game stage. Once rooms are placed, each is populated with decor including servers, power generators, and pipes, in addition to having
collectibles placed for the player to find. Finally, turrets are placed in some rooms to provide additional difficulty for the player.
The game engine automatically will group together entities using the same vertex buffers and pass them to the gpu only once when performing draw calls.
This way, the minimum amount of data transfers necessary occur in order to speed up performance.
The main places in the game this technology is utilized is for drawing multiple rooms and laser shots.
Collision Detection & Physics
For collision detection in our game, we decided to implement an octree data structure. This data structure represents the world as a cube, which is then subdivided into octants
recursively in order to classify where objects are in the game world. Every frame a new octree must be created, as objects may move between frames. The purpose of this data structure
is two-fold - it is used both for collisions and for view frustum culling.
In the process of detecting colissions, the octree allows us to only check for detailed colissions between objects that are in close enough octants that they even have a
chance to be colliding. Once two objects are close enough to check for a colission, we check if they are colliding in three different ways depending on if the collision would
be sphere to sphere, bounding box to bounding box, or sphere to bounding box.
After collisions are determined, the physics system will then update the positions and velocities of the objects involved in collisions, with different ways of determing the
new position and velocity for each colission type. Furthermore, if an object is involved in multiple collisions, the resultant velocity for the object is determined by a combination
of all the collisions it is in by keeping track of a delta velocity due to each collision.
Our game uses a single point-light which casts omnidirectional shadows. For performance reasons, we decided to only have one point-light and have it "follow" the player around by jumping to the nearest room to the player.
To make the shadows, we first render the scene to a cubemap from the light's perspective, writing the depth values instead of color values.
To do this, we use a geometry shader which is able to output primitives to all 6 faces of the cubemap in one render pass.
We then use this depth cubemap when rendering the scene to determine if each fragment should be shadowed. We compare the depth of the current
fragment to the stored depth in the cubemap. We also implemented Percentage-Closer Filtering to make our shadows have smoother edges.
View Frustum Culling
This game implements view frustum culling. We use the Octree that is generated for collision detection for performing the test to determine if an entity should be culled or not.
A cell shading aesthetic is achived in two ways. Firstly, after lishting and shadow computation, the reultant color is binned in each of its RGB components. After color is binned,
some specular light is added back in to prevent the world from looking too flat. Secondly, outlining of objects is done by writing to a depth map and then applying a Sobel edge
detector in order to determine if a fragment is on an edge and should be colored black.
In order to update the player with vital information and improve the overall look and feel of the game, we implemented a fully dynamic hud. The first section's hud features a sprint and health bar that map the players actual values to screen coordinates to accurately display the player's current status. The player also has an inventory hot bar, which is used to display icons of each item that the player has collected. We also integrated FreeType to handle text-based needs such as the game timer, inventory item counts, and status messages. The second section's camera includes two semi-circular health bars that are incremented by an angle that changes over time. All of our hud elements were custom made in photoshop.
Via the use of HDR buffers, the game engine naturally implements bloom for any extremely bright objects in the scene.
This can be seen specifically in the lasers, however, the engine supports it for any bright objects.
The bloom happens through a simple gaussian blur applied to a multiple-render-target-produced FBO containing objects over a certain threshold of brightness.