Hack a Thon Thon Thon
This last year I wanted to challenge myself and was lucky enough to be invited to an hackathon team. We were tasked with making an entire virtual reality game in 36ish hours. We were allowed to come up with an idea and develop assets (which we ended up just developing during the weekend) but couldn’t code anything until the time began.
I was tasked with coding the game play. The idea was a Beat Saber or Guitar Hero like game where snowflakes and ice would fly at the player and they would have to hit the snowflakes with their hands and feet while avoiding the icicles and snowballs. Successful hits of the snowflakes would net the player points and a bit of extra health. Missing the snowflakes would result in a hit to the player’s health. Hitting a snowball or icicle would give an even bigger health hit to the player. The game would end either at the end of the song or when the player’s health reaches 0. The game would be played using a combination of touch controllers and a Kinect with full body tracking so the player’s entire body could be used.
Since my task was game play programming I decided to break up the functionality into a few tasks:
1.) Create spawn points for the objects 2.) Randomize which object of the 3 (snowflake, snowball, and icicle) would spawn 3.) Move objects toward player 4.) Hit detection with the touch controllers 5.) Scoring and health system and UI
Breaking up each helped me further make a task list and helped me focus on each one. I’ll go into more detail of each one (Note: The game was built in Unity so it uses C# with the UnityEngine library).
Out of Thin Air
The easiest part of the project (initially) was spawning the objects. I began by setting up prefabs for the 3 objects and then setting them up as public GameObject variables in our spawn script. I used Unity’s standard Instantiate method to spawn in the objects. Something like this:
Instantiate(snowflakes, transform.position, snowflakes.transform.rotation);
Side note: We were having a bug where the snowflake object wasn’t coming in at the correct rotation. I originally had it set to just transform.rotation thinking it would maintain the position of the prefab but that wasn’t the case, I learned you actually have to specify which prefab you want to set the rotation to. In hindsight it should have been obvious but wasn’t after 20+ hours of coding at the time.
Since I was already setting up the spawning, I could easily set up the randomization. This was done by setting up a random number generator in the code and every time it would use a number in a specific range, it would spawn that object. I set the range of the snowflakes a bit wider so they would spawn more often.
Now that the code and the prefabs were set up, I had to decide where to spawn them in the scene. Because of the time constraint, I set up 4 empty game objects that acted as spawners in the scene. One spawner for each limb. Given more time, I probably would have coded in a range on the spawners so it would be a bit more random and chaotic but this worked perfectly for what we were doing.
The spawners were working perfectly and randomly instantiating each object but since they were in the Update() method, they were spawning every frame. Two of the other members of the group were working on a choreography script to time the object spawning to the music but in the meantime, I set up a delay in each spawn by using a float time variable.
As I mentioned, the others were able to build a brilliant choreography script that allowed them to tap the spacebar to the beat and register those keystrokes to a database file that we then converted to spawn times so most of this ended up getting modified heavily but it was like magic when we got it to work with the music beat.
Fly You Fools
The next task was to move the objects toward the player. I initially tried to add velocity to each object but wasn’t getting anywhere or it would cause the objects to fly away from the player or bounce off in all sorts of directions. What eventually ended up working was creating an empty game object behind the player and using Unity’s MoveTo function.
transform.position = Vector3.MoveTowards(transform.position, playerSpawner.position, step);
This script went on the prefabs and told each one’s position that after it has spawned, move toward the playerSpawner game object (set up here as a public GameObject variable). This worked but it was causing all the instantiated objects to fly toward the center of the player since the playerSpawner object was generally center of the player and behind them. I fixed this by setting up a second playerSpawner object and placed it below the first and moved them up a bit. In the script I adjusted the change so that if an object spawned on one of the lower spawners (the foot spawners), it would fly to the lower playerSpawner and if they spawned on one of the top spawners, they would fly to the top playerSpawners. I know, this is a lot of spawners to keep up with and I should have changed the playerSpawner objects to something like flyTo objects but it was left over from something I was initially trying to do. Also again, 20+ hours of coding and limited time. Finally, I placed a large box collider behind the player. This served 2 purposes. The first, it would destroy the game objects so that they wouldn’t continue to pile up behind the player and drag the frame rate down with it, and second, it would register whether a snowflake hit it and would count it as a miss for the player.
This script ended up working perfectly although if I had time to do it over, I would have created an object for each spawner and had them randomly oscillate to get a bit more random movement in where the object flew to.
Hit Me Baby One More Time
My next task was to get the touch controllers working so that the player could hit the objects. Because of my relative inexperience with VR at the time (I had only really worked on one project and dabbled a bit with the API), this took the longest to implement correctly. I’ll save the details for another blog post because it was mostly bug fixing and trial and error but I was able to get it, and then it would break again when we implemented or changed something else, then it would work in a continuous cycle all weekend. It worked in the final version but this is where I spent most of my time.
The last feature that I worked on (that I remember, sorry, this is almost 3 months later) was the scoring/health system and the general game UI. Since this was VR, I wanted to keep all UI elements within the world. For debugging I used an “on screen” HUD but it was quickly taken out when I got the scoring and health systems completely built out and working. The simple solution was creating a large ice block in front and off to the side of the player. The scoring text UI resided within the ice block and was a cool effect. We initially talked about a health bar that functioned in much the same way Beat Saber does. A small, thin bar is off to the player’s side and it increases or decreases depending whether the player is doing well or not. This is more or less how ours functions in the back end. Since this was a fun thing, I wanted to experiment a bit with UI in virtual reality. My idea was to increase the amount of ice in the player’s view the lower their health was. As they did better and their health increased, the ice would melt away.
In the time we had left, I got the functionality halfway. What I did was attach a cube to the center anchor of the player’s headset. This would ensure that the cube would always be following the player’s movement and would always be in view. I then attached a cool ice shader that caused the cube to look like a sheet of ice. I then set the cube inactive and whenever the player would miss a snowflake or hit an icicle or snowball, the cube would be activated (using SetActive) and it would appear in the player’s view. It was a cool feature and if I had more time, I would have like to set the cube active and then if it was active, adjust the shader amount up or down depending on the context. If the player lost too much health, their entire vision would be iced over and they couldn’t see anything which would end the game. It’s a fun concept I’ll probably try in another project. I think view based player feedback is a great mechanic in VR if done correctly.
That was my favorite part of building the UI and scoring/health systems. I won’t go into detail on the back end systems. Essentially, each object was given a point value and health value. Snowflakes would increase score and health while the other two would decrease them in varying degrees. If the player was on a streak, they would get a multiplier that added to their score. Once the player was hit or missed a snowflake, the multiplier would reset. The score and multiplier were then sent to text objects which, like I said, was embedded in an ice block.
I tried to keep this one a little lighter on code but let me know if you have any questions on how something worked and I’ll try to remember. I’m hoping to update this in the future with a video of actual game play but I’m on a different version of Unity right now and everything is broken so it’ll have to wait. It was a great project with one of the best groups I have ever worked with. It was exciting, stressful, and fun to have to create a game with new technology in a weekend. I am proud of what we produced and I hope to do it again someday.