• Home  / 
  • Unity3D
  •  /  7 VR / VIVE Games – Day 3 – Zombie Shooter

7 VR / VIVE Games – Day 3 – Zombie Shooter

Zombie - FullScreen

HTC VIVE Game Challenge

This week, I’ve decided to start a new VIVE challenge.  Each day, I’ll be creating a new Unity game developed specifically for the HTC VIVE.

I’ll spend a single day on each game (really about 4-6h of a day), creating the base gameplay and polishing up the experience a little.htc-vive-steam-vr-logo

After each game is complete, I’ll post the results here with a gameplay video and some thoughts on how it worked out.  I’ll also provide a download link for any current Vive owners to try it out and see how the experiences turn out.

 

Game 3 is ready now!

VIVE – Zombie Shooter

Zombie Shooter was my 7yr old sons first choice.  When I got the Vive, his favorite game quickly became the Space Pirates.  He’s always been a big FPS fan (mainly playing TF2), and wanted me to make one too.  Where he got the Zombie idea, I’m not sure, but it seemed like a good fit and a reasonable one to put together.

In the Zombie Shooter, you have one goal…. stay alive..
The zombies keep coming, until you’re dead.  They spawn in waves every 10 seconds with a 10 second delay between each wave.  Their spawn rates are slightly randomized as are their positions, but they come from all sides.  In addition to that, some of them like to run!

 

 

 

Result:

I really liked the result here.  After a bit of iteration, my wife seemed to really get into it as well.  If you watch the video, she went for almost 6 minutes in that session alone.  The mixing of slow zombies with fast ones adds quite a bit of intensity to the game, even though only 12% of them actually run.  My kids haven’t yet had a chance to play it though, so I won’t know the full result until after this post goes live.  I’d love to hear from others though on how you like it.

Zombie - Spawner

Implementation:

This game has a few interesting implementation details that aren’t present in the previous ones.  It takes advantage of the mecanim animation system and has some basic wave spawning.

 

Guns

Zombie - GunZombie - Gun

Zombie – Gun

The guns are were a little difficult to calibrate.  For them, I’ve added a mesh from a gun pack and a “Gun” script.  The script checks for the trigger pull of a controller, and when it’s pulled does a raycast using Physics.Raycast.  The Raycast actually comes from an empty transform placed at the tip named “muzzlePoint”.  I also use this to determine where the muzzle flash should appear.
If any Zombie is in front of the gun (detected by the raycast), I call a TakeHit method on the zombie.  Other than that, they have a reload timer which is adjustable in the editor and currently set to 1 second.  If the timer isn’t refreshed, I just ignore trigger pulls.Zombie - CameraRig

They also have a laser scope, which is actually a copy of the one from the Dungeon Defense game.  This is toggled by the grip buttons and helped my wife get used to aiming.  Once she had it down though, she was able to play fine without them.

 

Zombie

The zombies took a little longer to setup due to an issue I had with animations.  For some reason, my walk animation wouldn’t properly move the zombie using RootMotion.  In the end, it turned out that just re-importing the walk animation (deleting and re-adding it) fixed the problem.  I got the Zombie and his animations from Mixamo.com.  I believe they’re free, but it’s possible I paid for them and didn’t notice.  There are also a variety of other cool zombie animations on there, but I didn’t want to go overboard on prettying this game up right now.

When they spawn, they have a 12% chance to run.  If they fall into that 12%, I just use a trigger to switch animation states from Walk to Run.  Because I’m using Unitys RootMotion, the zombie starts moving faster without any other changes.

Zombie - Animation Controller

Zombie – Animation Controller

Aside from the animations, there’s a basic call in the Update() method that just finds the player by a tag and does a transform.LookAt().  While this wouldn’t be the best way to go in a real game, because you’d want to use proper navigation, it was fine for this quick project.  If the zombie is in “attack range” of the player, he’ll make a noise from his audio source, play an attack animation, then kill the player (restart the level) if he doesn’t die within 3 seconds.

I also have the TakeHit method mentioned above in the Zombie.  This method reduces his health by 1 (initially I was going to require 2 shots, but ended up doing away with that).  When the health reaches 0 (on the first hit), I switch his animation with an animation trigger and make him schedule a GameObject.Destroy after a few seconds via a coroutine.

Spawners

This is actually the longest script of the game.  Because of that, and because there’s been some interest in code samples, here it is:

using UnityEngine;

public class Spawner : MonoBehaviour
{
    // Note I don't use the NavMeshAgent.  I was planning to but decided to ditch it for the simpler method of .LookAt
	// I do still use this _prefab though, so the zombies have a NavMeshAgent on them.  To cleanup, I should remove this
	// but I wanted to show what I initially planned vs what actually happened.
	[SerializeField]
	private NavMeshAgent _prefab;
	[SerializeField]
	private float _respawnTimerMax = 5f;
	[SerializeField]
	private float _respawnTimerMin = 2f;

	[SerializeField]
	private float _waveDuration = 20f;
	[SerializeField]
	private float _waveDelay = 10f;

	private float _countDownTimer;
	private float _waveTimer;
	private float _waveDelayTimer;
	private bool _spawning = true;

	private void Update()
	{
		_waveTimer += Time.deltaTime;
		if (_waveTimer >= _waveDuration)
		{
			PauseSpawning();
		}
		else
		{
			_countDownTimer -= Time.deltaTime;
			if (_countDownTimer <= 0)
				Spawn();
		}
	}

	
	private void PauseSpawning()
	{
		_spawning = false;
		_waveDelayTimer += Time.deltaTime;
		if (_waveDelayTimer >= _waveDelay)
		{
			_waveDelayTimer = 0f;
			_waveTimer = 0f;
			_spawning = true;
		}
	}

	private void Spawn()
	{
		var randomOffset = new Vector3(UnityEngine.Random.Range(-1, 1), 0, UnityEngine.Random.Range(-1, 1));
		var agent = Instantiate(_prefab, transform.position + randomOffset, transform.rotation) as NavMeshAgent;
		_countDownTimer = UnityEngine.Random.Range(_respawnTimerMin, _respawnTimerMax);
	}
}

I want to note that this is not to be taken as an example of a good / clean script.  It is in-fact pretty terrible and messy.  In a real game, we’d have a state system that would handle different game & spawn states.  But as a throwaway, it gets the job done fine.  Also, if you’ve used Unity much before, please take note of and start using [SerializeField].  If you don’t know what it’s for, or why you should be using it, please read my article on the subject here.

 

Up Tomorrow: Catch Something