CS 184: Computer Graphics and Imaging, Spring 2018

Final Project : Virtual Reality Bullet Hell

Caleb Wyllie, 26733519

Jonathan Tan, 3031980108

Ray Pan, 3032030795


Index

Abstract

We built a VR Bullet Hell. We built this game in Unity and integrated VR through Google Cardboard. As with all games in the Bullet Hell genre, our game involves shooting and dodging oncoming enemies. In order to do so, the player must aim by facing towards enemies with their cardboard headset, and dodge by tilting their headset/body. We also implemented various shaders, a skybox, and various special effects in order to best depict our synthwave landscape.

Technical Approach

Unity

We decided to use Unity for our game engine. As the mantra goes, there’s no point in reinventing the wheel. We chose Unity for no particular reason, but in retrospect, it was probably a better choice than the other popular multipurpose game engine, Unreal Engine, because Unity facilitates mobile development. There is a stigma against Unreal because it is extremely computationally intensive, which is not well suited for deploying to mobile.

Google Cardboard

For some reason, Google Cardboard’s iOS Hello World app required Android SDKs to build, which made no sense to us and should make no sense at all. Installing said SDK was also a pain, as we couldn’t just install the base SDK: it needed to be built through Android Studio for it to work, which needed twenty times the disk space of the base SDK (200mb vs >2gb). Building for Android had it's own suite of problems, such as obscure configurations causing it to try to build for an SDK version that wasn't downloaded (and not versions that were downloaded).

Mockup

The final project we have here is built off of the mockup. The mockup that Jonathan made with basic left/right movement and using outlined diffuse shaders set the stage for the rest of the project to come. It helped familiarize everyone with how Unity worked, how to program game logic in C#, and how to work with shaders written in Cg (C for Graphics, HLSL variant).

Our mockup

Accelerometer/Gyroscope

Our first implementation of movement was with the accelerometer. By adding the acceleration from the accelerometer to our player object, we were able to have movement “function”. However this functionality was not anywhere near good enough. As we were making jerky movements, the accelerometer quickly became unstable leading to quite a bit of headache. It’s basically a symptom of explicit Euler integration, how our systems and controls quickly compounded error and destabilized.

In order to solve this issue, we moved to a much more stable scheme of tracking position, the rotation about the z-axis (imagine a ray going through the center of the camera of the phone, that’s the z-axis). In order to do so we took the attitude of the object which was given in quaternions, converted it into the space of our app, and finally took only the z-component and used it to determine our position. The game position was updated at every tick of the game, making the system feel extremely stable. This approach worked remarkably well as we didn’t feel any type of sickness even after prolonged periods of play.

Enemy Generation/Game Mechanics

In our game we have two types of enemies, red and green space invaders. The difference between the two is that the red one is indestructible (must be dodged) and the green one can be shot down for points.

When we had only green enemies, the game was too easy. A player could stand straight and only shoot down one lane of spawns. To rectify this, we could either have enemies fly towards the player at varying angles or make it so that some enemies were unavoidable. We went with the latter, thinking it’d be better for playing and cause less confusion on the screen. After this we added another layer of difficulty by making our enemy speeds increase logarithmically with time, making the game more interesting for skilled players as it goes on.

Originally we had enemy generation such that it was completely random whether or not the invader which appeared was green or red, however this leads to unfortunate situations where a player can get screwed over by having a screen full of red monsters. In order to solve this we added probabilistically scaled generation of enemies. We have a set probability that a red would spawn and this probability would increment each time a red invader spawns until the red invader spawned, at which point the probability would reset.

Gameplay

Models

We didn't cover much about how to make cool 3D models in CS 184. However, we did cover a lot of topics that are important for modeling and understanding materials. One of our team members had learned how to use Autodesk 3DS Max for 3D modeling in high school, but revisiting the program, he finally understood what so many of the options meant, from bezier curves to diffuse materials.

The low-poly hill was trivial to model as synthwave hills are generally wireframes with quad meshes of varying height peaks. We modeled this by taking a plane, subdividing it into 100 quads, and raising the control points to random heights to give the appearance of a hill. Although using the same hard-coded model for all hills is not ideal, we solved this by writing a custom shader.

The low-poly palm tree took several hours to model to have a retro tropical aesthetic. Because it captured the essence of our game, it became the de facto mascot and the centerpiece of our logo.

Low-poly Tree
Low-poly Hill

Shader

We had decided that we wanted to make our game have a synthwave aesthetic. We developed a preliminary shader that colored cubes with main and outlined colors, using blues/purples and teals/pinks for highlights. By lining up cubes in a grid, Jonathan achieved a classic neon grid pattern. However, we were disappointed by this workaround because it required manually copy-and-pasting the cubes which wouldn't be available for the entire field of view. Furthermore, we wanted to achieve the same aesthetic on his low-poly models of hills and trees. It would be far too much effort to form these manually out of polyhedrons.

So, we created a wireframe shader that would achieve this aesthetic. We implemented this by having the shader hook into the graphics rendering pipeline. The pipeline breaks the mesh down into triangles and the shader takes advantage of this to calculate the barycentric coordinates of a point. Then the shader finds how far the barycentric coordinate is from the center and calculates an exponential value that is near 1 at the edge and near 0 everywhere else. Then that value is used to lerp between the edge color and the face color and the result is a wireframe mesh shader.

There was a pernicious bug that made the colors or transparency really hard to control, but eventually we worked around that to get the shader to look exactly how we wanted. A bug that we couldn’t solve was that iPhones didn’t have graphics chips that supported the shader hooking into the graphics pipeline, so the wireframe shader would only render on new Android phones, and iPhones just continued to use our neon outline highlighting shader.

Wireframe shader

We applied the shader to the trees and it looked really nice, but when duplicating the hills, they all had exactly the same geometry. We then solved this by implementing a shader that applied noise to the height y-values of each vertex of the low-poly hill, thus making it look like each hill was generated randomly. We implemented this by essentially hashing the position of the vertex to achieve a pseudorandom value and raising the y position by a random amount.

Wireframe shader with noise

We applied a scanline shader effect to the camera which varied intensity of light by strips of height. At first we were worried it would be distracting or disorienting but eventually we decided this enhanced the game’s retro aesthetic so we kept it. We also hoped to create a scanline effect like that shown on the sun in our game’s icon, and pair that with a lerp gradient sky, but that is still a work-in-progress.

Results

Here's a video of our final product!

We believe that we did everything that we set out to do, in the end we had a fully functional game with all of the specifications we listed, minus a few of our stretch goals. In addition, when we were demoing the game we had a decently sized crowd lining up to try it out and those people were enjoying it! After the demo, when we went to the CSUA, most people in the office wanted to play as well.

CSUA Members enjoying our virtual reality

We found that the iPhone ran the game better due to a more precise gyroscope and ran smoother; Caleb’s Android couldn’t run the game at the same framerate as the iPhones, making the game feel a bit slower than it should. However the Android phone has a more updated graphics chips which let it run our custom wireframe shaders.

At the end of CS 184's project 3, we made our own shader and thought that was a cool introduction, but we feel like it was so open-ended that we just made something dumb. However, building a shader for this synthwave game was really rewarding because we wanted to build a shader to achieve a particular aesthetic and we were successful at making it look how we hoped it would.

Despite technical difficulties, we found this game to be very rewarding to build. It was awesome to have an artistic design goal for the game and then use our technical expertise to achieve that goal. Even those of us who don't feel like "graphic design is my passion", we were still able to meaningfully contribute to making the game look and feel the way we envisioned our theme of retro synthwave. Best of all, actually playing the game is really fun for us and for all those who demoed it.

If you’d like to play, the game is currently in closed alpha so feel free to email one of the developers for that spicy close alpha access.

P.S. Our game is more fun than Megan’s was

References

Member Contributions

Jonathan

  • Outlined milestone deliverables and weekly work in project proposals
  • Original game mockup in unity
    • Three lane movement, keyboard left/right to jump from lane to lane
    • Beginning usage of outlined diffuse shaders
  • Final game in unity
    • Player, playing area, enemies, game logic (scoring/hit detection/restarting/moving/spawning)
    • Attitude (tilt) integration + vr movement w/ quaternions, gyroscope and Cardboard SDK
    • Movement now on z-rotational axis v.s. lane based hopping
  • Compiled Ray’s skybox and Caleb’s assets into final product
  • Made final presentation slides and content
  • Made final project video

Ray

  • Prototype accelerometer integration
  • Skybox creation
  • Explosion particle effects
  • Logarithmic difficulty curve
  • Enemy generation algorithm
  • Milestone deliverable final write up
  • Created the websites for the proposal, milestone deliverables, and final deliverables

Caleb

  • Asset generation
    • 3D Modeling in 3DS Max
      • Low-poly tree
      • Low-poly hill
    • Rendered game icon
  • Shaders
    • Wireframe
    • Randomized topology
  • Website
    • Graphic design
    • Final report
  • Developed and built for Android
  • Photographer