Ultra Air Arcade

Responsive image
Created by
Drew Troxell (Project Manager)
Billy Aujla
Aaron Koeppel
Riley McGovern
Lejon McGowan
Zachary Weisman
Class
CPE 476 Spring 2015
Professor
Dr. Zoe Wood
Inspired by
Mario Kart
War Thunder
Air Brawl

Ultra Air Arcade is a flight racing game inspired by the racing of Mario Kart and the flight models of War Thunder and Air Brawl. The game has a fast-paced race around a challenging terrain in the shape of a circular valley, with many hills and cliffs to stand in a racer's path. Try to earn first place and beat your best times in this high velocity plane racer!

Installation & Play Guide

Installing from links above

To run the game from the precompiled links above, download the game for your system, decompress the tar.gz file, and run the "Ultra Air Arcade" file in the bin directory. The tarball comes with all assets and shaders required at runtime.

Installing from source

To run the game from source, clone the repository from the github link above, enter the src directory, and run "make osx" to build for Mac OSX or "make fedora" to build for Linux. Then run the a.out file that is created from the make.

How to play

Goals

The goal of Ultra Air Arcade is to win the race is the fastest time possible. Feedback is provided at the end of each race for the player's finishing place as well as individual lap times and overall race time.

Controls

  • W, S: Throttle up/down
  • A, D: Roll left/right
  • Mouse: Turning & Pitching (inverted pitch)
  • X: Fire projectile

Technologies

Collision Detection Riley McGovern and Zachary Weisman

The collision detection in Ultra Air Arcade was done using hierarchical bounding spheres for the planes. The spheres are arranged into two levels: One large sphere surrounding the entire volume of the aircraft, and five smaller spheres placed on the left and right wings, tail and nose, and one in the center. To reduce computational complexity, the game would not check any of the five smaller spheres until after a collision was flagged with the large outer sphere. For the collision with the terrain, the plane's Y coordinate was checked to make sure it was not below the height of the terrain object's height based on the plane's position.

Collision Detection Response Riley McGovern

The collision detection response in Ultra Air Arcade contained collisions between a plane and another plane, a plane and the terrain, and a plane and a missile. The collisions between two planes were based on the positional vectors of the two planes that were colliding and small math with quaternions would softly "bounce" them off of each other. The collisions between a plane and the terrain would send back the plane two times the normal vector of the triangle of the terrain that the plane hit. After resetting, the plane would continue using it's thrust that it had before the collision so the plane would not get too far behind the opponents. The collisions between a missile and a plane would cause the targeted plane to lose half of it's thrust, so that the player could catch up.

Sound Riley McGovern

The sound in Ultra Air Arcade was run through a third party library called SFML. The library was wrapped in a custom class for use within the game. The sound of the plane was done by increasing and decreasing the pitch based on whether the player was speeding up or slowing down. Both the main theme song and race song were played on a loop. Collisions with the terrain and other planes were played in short snippets when those collisions occurred. The main countdown was done by counting down frames and playing the sound after a certain number of frames had passed. All sound was used from royalty-free sound libraries that are listed below for reference.

Skybox Zachary Weisman

A simple cube map was implemented, and an array of textures were loaded onto it. A special GLSL datatype - samplerCube - handled the fetching of the correct texel depending on what the camera was facing, thus making implementation relatively simple. The position of the skybox was locked to the players x - z coordinate, but was given a y - position of zero in order to keep an accurate sense of height as the player travels up and down.

Shadows Zachary Weisman

Shadows in our game were implemented using the shadow mapping technique. Using shadow maps, the scene is rendered twice using two separate sets of shaders. The first pass simply writes the depth value per pixel of the scene from the light-sources perspective to a framebuffer object; this is called a depth map. Then, on the CPU, that depth map is transformed to fit over the scene from the cameras perspective. Finally, a separate shader is run to render to the screen using the depth at each pixel to calculate a light factor, or simply a number to multiply the output color by in order to reduce its brightness, and make it appear shadowed.

Particles Zachary Weisman

A particle class was developed for Ultra Air Arcade that was based off of a simple billboard system. Upon initialization of a particle object, sixty variable sized billboards would be created, and upon rendering, the particles would be instanced one per frame for the first sixty frames of their lives, which makes them appear from the same position at different times. This, along with a randomness factor calculated at instantiation helps to make them move a little more naturally.

3D Camera View & Flight Dynamics Drew Troxell

In order to provide a fulfilling flight control system and camera view, the 3D camera is controlled by two areas: Entity.hpp and Camera.hpp. The Entity class contains the flight dynamics necessary to propel one of our jet models through the air. The flight utilizes a mostly physical model, relying on a forward thrust force and a counteracting drag force to stabilize acceleration. Each entity maintains attributes such as mass, surface area, and drag coefficient to support this. Flight occurs on a time step that is set by the current fps of the game. All rotations of a plane are contained in two quaternions: the rotation and the target rotation. The rotation quaternion contains the current rotation of the model, while the target rotation contains the rotation to which the plane model is attempting to rotate. The rotation quaternion is proportionally controlled toward the target rotation, allowing for a very clean feedback system that makes fast rotations when the mouse is moved swiftly and slow rotations when the mouse is moved lightly.

The 3D camera view for the is dictated based on the player's entity. The camera view is set to be behind the player and pulls further back as the player accelerates, giving an increased sense of speed. The camera attempts to match the player's rotations, but includes some lag in order to provide the visual change of the player model rotating relative to the camera and then the camera catching up.

The camera also includes some debug tools. The camera is capable of switching which model it is using as it's "player" on the fly, allowing for debugging by using an AI character as the camera's center. Without this view, the AI in its current state would largely not be possible. The camera also has a simple free look debug state that allows for free look movement around the map regardless of entity location.

Game States & Opponent AI Drew Troxell

The underlying game intelligence is split into two areas: Rules.hpp and RacerAI.hpp. Rules.hpp dictates the current state of the game and controls transitions from one game state to another. The game consists of 6 main states: splash screen, character select, race, finish, and leaderboard display. The main game mechanics occur in the race state, while the other states exist to provide context, direction, and goals for the player. Transitions between game states are controlled by input for all states that aren't race. The transition from race state to finish state occurs when the player has finished 3 laps. Rules.hpp also handles keeping track of the player's current place in the race as well as activating the avoidance states of the opponent AIs.

RacerAI.hpp dictates the current state of the opponent AI and controls transitions from one AI state to another. The opponent AI consists of 6 states: splash screen, race setup, race, avoid, bounce, and finish. Splash screen simply sets up the track positions for the player's AI if they are not already set. Setup state reinitializes all opponents and the player to the starting location of the race (and resets race status variables). Race controls the waypoints to which the opponents are heading as well as controlling thrust and boost bonuses for rings. The avoid and bounce states are triggered by external classes and provide AI responses for avoiding nearby opponents and bounceing off of colliding opponents respectively.

AI Pathfinding is controlled by a global list of designated track checkpoints. When instantiated, each AI takes this list and runs a noise function based on a normal distribution around it to generate an identically sized list containing their now modified track checkpoints. The AI then uses this new noisy checkpoint list to navigate the map instead of the global list. In doing so, each AI follows a unique path with a varying level of optimality. This leads to opponents that are spread out through the map in small groupings depending on the optimality of their paths, allowing the player to move through the standings rather than have one large swarm of AI pass by with every mistake.

View Frustum Culling Aaron Koeppel

In order to perform view frustum culling on the objects in Ultra Air Arcade, the view frustum has to be defined. There are six planes that make up the view frustum: near plane, far plane, top plane, bottom plane, left plane, right plane. Once they are calculated, Frustum.cpp has a method to check if a bounding sphere is within the view frustum that is defined for the current frame. Each object in the world has a bounding sphere and is checked to determine if it should or should not be drawn. This operation is an optimization to only draw objects that can be seen through the view of the camera.

Heads Up Display Aaron Koeppel

The HUD in Ultra Air Arcade was partially created using a third party library called FreeType and also using texture reader. The use of both of these technologies were wrapped in custom classes for use within the game. The HUD displays text information about the game on the splash screen. It displays plane color selection on the color selection screen. The main HUD shows how much time has passed in the race, your lap times when you complete a lap, your current place in the race, and your current lap. When you complete a race, it displays your final place, your lap times, and your total race time.

Enviroment Billy Aujla

The terrain is loaded in using a greyscale heightmap. The shade of grey in each pixel determines just how high the terrain will be. This makes it expecially easy for every entity to be able to know where it is reletive to the terrain. Procedural generation of different props was also made. This was done using blue noise. This assigns multiple cells to the enviroment, ensures the cells do not touch each other, and then randomly places object in the cell. The latter feature was taken out in the 100 percent version, as textures were not able to bind to the objects; however the placement of the objects worked perfectly and was shown in the 90 percent demo.

Spline Animation Billy Aujla

Made spline system that can update to hit a target using a Catmull-Rom spline. An object traveling across will move at a constant speed regardless of how far the control points are from each other. It can also change the contol points as an object is traveling across the spline curve to make it much more dynamic.

Shading LeJon McGowan

There are two main techniques of shading used here: toon shading and Cook-Torrance shading.

Toon shading take in the diffuse colors of a terrrain, then clamps down final color to create a distinct number of shades. This technique is use to simulate hand-drawn painting and drawing produced by artists. This is seen on the terrain of the track, where there is a clear transition from one shade of color to the next. Height is also taken into account for the terrain, allowing diffrent diffuse calues to be represented at different height (white snow at high peaks, yeallow-ish sand valley towards the bottom).

Additionally, silhouettes are used to create a distinct, black outline at the edges of the terrain. These are created by taking into account the player's view at an instance and shading points where the angle between it and the terrain's normals fall below a certain threshold. This threshlod can be increased to create a larger silhouette.

Cook-Torrance shading is one method of physically-based rendering, where an object is shaded based on its real-world properties. Cook torrace takes into account the amount of light an object reflects, how the light is disperesed, and the geometry of the object. These allow smooth surface to retain the shimmering properies, and rougher materials to look duller. The planes and envoronmental props both make use of this shading technique

Click on the images for full size versions.

...
...
...
...
...
...

Video

Demo of a race - better gameplay.

Link to video on youtube

The following are resources we either used or found helpful in the course of developing Ultra Air Arcade:

Future Work

Component - Based Shader Handling
Using a component-based system for handling the loading of shaders, and their references to different game elements would have helped speed the process of inserting graphics elements, different shading effects, framebuffers, geometry shaders, etc.
Articulated Plane Animations
Animations on plane control surfaces would have increased the visual appeal of the models.
Bump mapped terrain
Bump mapping the terrain would have provided it with more depth and made the game more visually appealing.