Entries in this blog

Here we are at another milestone in the 100 days of VR challenge! Day 40! Who would have known that I would have made it to this point?

We’ve come a long way, we learned a bit about Unity, made a simple game, and now here we are working in VR!

Yesterday we finished fixing most of the technical problems involved with porting our game to VR.

Well, turns out, I lied, there are a couple more things I’d like to fix today along with working a bit on the UI.

For some reason, our camera is lower than we set it

Enemies are literally running into us when trying to hit us

Get our UI to show up again

Step 1: Changing Our Camera Starting Position

When we play the game on Unity, we have an interesting problem with our camera position being changed.

We set our camera position to be 1.5 at Y:

However, what’s interesting is that when we play the game on the Android platform, our camera position gets set to 0:

After debugging around, I found the cause. Our GvrEditorEmulator prefab forces our Main Camera to be set to the position: 0, 0, 0.

While technically, we don’t need the prefab, it is convenient, so we’ll keep it in. Instead, I’ve found a different solution.

While our camera is forced to be a set position, we can child our camera to another game object and then change the position of the parent game object.

Coincidentally, we’re already doing that with our Player game object.

All we have to do is raise our Player Y position up by the amount of the camera.

Select Player, change the Y position from 1 before to 1.5

(Optional) Go to the Main Camera and change the Y position to 0

Now when we play the game, we’ll have a much better height when playing:

Step 2: Stopping Enemies from Going Inside the Player

Next problem, the enemies are literally running into us.

See:

There could be many things that are causing the problem, but I decided to look at the Nav Mesh Agent attach to each of the enemy prefabs, because there are options that control how close the enemy would get to their target:

After playing around with the settings I got the enemies to stop right in front of us:

StoppingDistance: 1.25

AutoBraking: Disable

Radius: 1.25

Here are our new settings:

With these new settings in, here’s a bit of our gameplay now:

Isn’t it nice that they’re beating us from the outside and not the inside? No? Oh well…

Step 3: Getting the UI to Show Up Again

Now that we have all the technical problems resolved (promise this time!), it’s time for us to go back and look at how we can get the UI to show up for our game.

This is going to be an annoying problem to solve because the problem only occurs on our Android device and not anywhere else. Which means there will be A… lot… of… building…

Luckily for you, I’ve gone through the torture of re-building multiple of time so the rest of us don’t have to!

It turns out that getting the UI to show up isn’t too bad!

It turns out that if we have VR mode enabled in Unity, any UI we have on our Android device will NOT be available unless the canvas the UI is set with is set to World Space.

Here’s what it looks like on my phone when I disabled the VR options in Player Settings and just have normal Android app:

The biggest key takeaway I got from these 2 articles is that you should NEVER have the UI on your player’s screen, like how we’ve been doing it. This could cause motion sickness.

Specifically, we want to use Diegetic UI, where the UI is attached to a game object in the game world as opposed to an overlay on top of our screen.

Instead of having it float around or statically placed would be the better way to go.

Step 3.1: Getting the UI to show up

Anyways, to fix our problem and have our UI show up when VR is enabled, we must set our Canvas to be displayed in World Space.

Select HUD in our hierarchy.

In the Canvas Component, change Render Mode from Screen Space – Overlay to World Space

There are 3 options available to use, here’s the canvas documentation to explain what they are, but for a quick summary of the available render modes:

Screen Space – Overlay: The UI is rendered on top of the scene

Screen Space – Camera: The UI has put a certain distance from the Camera. It’s very similar to the Overlay, except certain changes to the camera could also cause changes to the UI. An example would be Perspective Camera would render the UI differently from an Orthogonal Camera

World Space: The UI Canvas will act like a game object that just hangs somewhere in the game world for us to see. Also, this is the only option we can use for our UI

Here’s what our game looks like now with the World Space:

Now we need to make some adjustments with our UI.

Step 3.2: Figuring out where to put the UI

The biggest question now at this point is, where should we put our UI elements?

While there isn’t a clear answer, I think our best option might be to attach the UI to be our weapon.

On the Google Cardboard, this would be the equivalent of having it right in our face, but if we were to switch to use the Daydream Viewer, we would be able to move it independently of our gaze.

With the decision being made, let’s go and see how we can attach our health and time UI to our gun!

Step 3.3: Putting Our UI Into World Space

We already have an existing HUD canvas game object in our game.

We’re going to repurpose that for our game, however, because we have more than just our UI on the canvas (the Victory and Game Over panels), I’m going to duplicate our HUD

On HUD in our game hierarchy, hit Ctrl + D to duplicate it.

Rename the duplicated HUD (1) to be called GunUICanvas

Delete the Victory, Game Over, and Ammo UI child objects

Make the GunUICanvas a child of MachineGun_01

When we’re done, here’s what your hierarchy should look like.

Next up, we’re going to change the settings of our GunUICanvas so that it would be right in front of our gun on the right side:

Select GunUICanvas

In the Canvas component, the Render Mode should already be World Space, if not, change it

In the Rect Transform component, I’ve played around with the settings and changed our position to be (-0.15, 0.22, -0.14), our Width to be 480, and our Height to be 80.

Set Scale to be (0.001, 0.001, 0.001), we want our UI to be small enough to fit in our screen

(Optional) Remove the Screen Manager Script

Here’s what we should have:

Next, I’m going to change the Anchor Presets for our Score UI game object to be in the bottom middle of our screen.

Yesterday we started to look at fixing problems that involved the limitation of Mobile VR (and a lot of raycasting), today we’re going to make some more changes. Specifically, the goal today is:

Change our Event Trigger logic to deal with what happens if we’re holding down on the screen

Fix a problem with our player being pushed around

Fix why our Knight turns black

Change our enemy to be slower to make the game easier

Let’s get to it!

Step 1: Changing Our Event Trigger Logic for Continuous Fire

Right now, we’re trying to solve the problem where we only damage the enemies when we tap on them. If we were to hold on, then we would continue to shoot, but the enemies won’t take damage

Yesterday we discovered that there was 2 ways we could have implemented our game.

Use our existing code where we shoot a raycast and depending on what we hit, run some function.

Use the Event Trigger system along with Google’s changes.

I’ve played around quite a bit with the Event Trigger system and made a solution, but it’s not the best, in fact, I might have preferred just keeping what we have, but that’s okay, we’re just learning!

There are 2 problems that we must solve:

What happens when we’re holding down the screen on an enemy

What happens when we’re holding down and then we move to point at another enemy.

After playing around for a while, the PointerClick solution we have will no longer work.

Instead, I’ve started playing with the PointerEnter and PointerExit events.

Walking Through the Code

These functions are going to be called from the PointerEnter and PointerExit from our Event Trigger that we set up.

In these functions, we set new variable we introduced called _isEnter so we know when they’re being selected.

Inside Update() we check to see if we’re currently hovering over the enemies and if we’re pressing down on the screen. If we are, we would call our already existing Shoot function.

I’m not a fan of this method because it requires us to constantly call Update() in all of our enemy health scripts as opposed to just inside our PlayerShootingController script, but for just playing around, this is okay.

I also changed the hit sound effect to be able to play every 0.1 seconds just like our shooting delay.

Step 1.1: Updating our Event Trigger

Now that we have our shooting script in, the next and final thing we need to do is to create the Event Triggers to use them.

Get rid of the PointerClick event that we’ve previously set up. Instead, we’re going to create 2 new types of Event Triggers: PointerExit and PointerEnter.

Here’s what we’re going to do:

Attach HealthEnter() to PointerEnter

Attach HealthExit() to PointerExit

Now we can play our game like we intended to do from the very beginning. Make sure to make these changes to the Bandit and Zombie too!

Step 2: Preventing Our Player from Falling

Currently, if we were to play the game, when an enemy gets closed to our player character, we would fall.

We should have addressed this in the past when we set constraints inside our RigidBody component, but it appears that we have not.

Let’s go back and fix this

In the hierarchy, select Player

Under the RigidBody component, we’re going to set our constraints. Specifically, we want to freeze our position and rotation so that the enemies won’t push our character around. Having the game move us could cause nausea for our players.

Step 3: Fixing our Knight’s Color

If we were to play our game on our mobile device, we’ll notice one big problem. Our knights are all black.

If we pay attention to our log, we’ll see this:

It seems that we have some problems with the shaders that the asset is using.

Unfortunately, I don’t know enough about this problem to resolve this. We have 2 choices:

Ignore the problem and just have all black knights

Change the materials that use these to use the standard shader.

In our case, we’re going to explore the 2nd option.

In Assets/Knight/models/Materials we have 3 materials that we’re using: clothColor, knight1Color, weaponsColor, and all of them uses one of those 2 shaders above. Let’s select them and changed them to Standard.

Now if we were to play the game on Unity, here’s what the knights would look like:

It lost the coloring we originally had for it, but at least we keep the details of the models.

Step 4: Making the Game Easier

Currently, in our game, we would be getting swarmed with enemies. That would have been fine if we can move around, unfortunately, we can’t do that anymore, thus as a result, we need to make some adjustments

We’re going to do 2 things:

Change the rate of how long it takes for an enemy to spawn

Slow down the rate our enemies move

Step 4.1: Changing Spawn Rate on the Spawn Manager

Currently, we spawn the next enemy every 2 seconds. Let’s change that to 5.

Select the SpawnManager game object (child of GameManager)

Set Time Between Enemies to be from 2 to 5

Step 4.2: Changing Enemy Walking Speed

As we might recall, to control the enemy speed, we must look at the Nav Mesh Agent component in our enemy prefabs.

In order of speed, our speed order is Bandit, Knight, Zombie, with the Bandit being the fastest and Zombie being the slowest.

I’m going to change the speed a bit.

Bandit to 2

Knight to 1.5

Zombie to 1

Here’s an example:

Conclusion

Now we’re done! We have taken care of a lot of the technical problems that we encountered.

Tomorrow, we’re going to continue to finish the rest of the game by figuring out how we would add UI into a virtual reality environment.

Looking through the Changes

We basically removed any mentions of our ammo and reloading, however, I kept the changes involved with the shooting animation, shooting sound effects, and shooting rate.

There were only 2 changes that were made:

I changed the input we use to shoot from GetMouseButton to GetButton(“Fire1”), I believe this is the same thing, but I’m making the change anyways. Either option returns true when we’re touching the screen on our mobile device.

I also changed our Ray from our raycasting system. Before casted a ray from where our mouse was located at, which before we fixed at the center. However, after we got rid of the code that fixed cursor to the middle, we needed a new way to target the middle. Instead of firing the raycast from our mouse, we now fire the raycast from the middle of our camera, which will fix our problem with our mobile device.

Go ahead and play the game now. We should be able to have a playable game now.

There are 2 things that will happen when we shoot:

We’ll shoot a raycast and if it hits the enemy, they’ll be pushed back

The enemies trigger event will detect that we clicked down on the enemy, so they’ll take some damage

At this point, we have a problem: if we were to hold down the screen, we’ll push the enemy back, but they’ll only be hit once! That’s because we only have that deals with an OnClick event, but not if the user is currently selecting them.

We’re going to fix this problem tomorrow, but I’ve done a lot of investigation work with raycasts now and want to take a break!

Step 2.4: Changing the ScreenManager script

One more thing we need to do before we leave.

The Unity compiler would complain about a missing reference with our ScreenManager, specifically with the MaxAmmo variable that we got rid of.

And we’re good to go! Technically speaking, we won’t be using this script anymore either.

Conclusion

And another day’s worth of work has ended! There’s a lot of things I learned about VR, such as: we don’t need ANYTHING that the Google VR SDK provides!

Unity as a game engine already provides us with everything we need to make a VR experience. Google’s SDK kit is more of a utility kit that help make implementation easier.

The TLDR I learned today is that we don’t have to be fixed on using Unity’s Raycasting script, we don’t need it. We can continue to use what we already have. However, for the sake of learning, I’m going to continue down re-implementing our simple FPS with the Google Cardboard assets!

The instructions are straightforward, so I’ll just leave the link to the instruction.

In our current state, because we have no way of restarting the game, we must rebuild and run every single time we want to see the console wirelessly. The reason being we lost the ability to restart the game inside the game.

However, when we re-add our ability to restart the game, wireless debugging will be more useful.

Step 2: Removing Old Scripts that Needs VR Replacements

It’s finally time to start working in Unity!

Before we do anything, let’s think about the things that the Google VR SDK gave us and what must we get rid of in our current system that conflicts with the VR SDK.

The main thing that the Google VR SDK provides is:

The ability to move the camera with our head

Its own Raycasting system

What we need to remove from our game is:

The ability to move our character

The ability to move our camera

The ability to shoot

The crosshair UI

Luckily for us, this process is going to be fast and easy.

First, let’s remove our ability to move:

In our game hierarchy, select the Player game object.

Select the little cog on the top right-hand corner of the Player Controller (Script) and select Remove Component

Next, let’s get rid of the game following our mouse.

Select Player > Main Camera

Remove our Mouse Camera Controller (Script) Component

After that, let’s get rid of the shooting script. We’re going to come back later and re-purpose this script, but that’s going to be for a different day:

Select Player > Main Camera > MachineGun_00

Disable the Player Shooting Controller (Script) We’re still going to need this.

Finally, let’s get rid of the crosshair. As you recall, when we add the VR SDK, we get a gaze prefab that already adds a cursor in for us.

Select HUD > Crosshair and delete it from our hierarchy.

When we’re done, we’ll have a completely unplayable game! Yay….

Step 3: Adding the Google VR SDK in

Recalling from the Google Cardboard demo, for our game, we’ll need to add:

GvrEditorEmulator – to simulate head movement

GvrEventSystem – to use Google’s Event System for dealing with raycasting

GvrReticlePointer – for our gaze cursor

GvrPointerPhysicsRaycaster – The Raycaster that GoogleVR uses to hit other objects

Drag GvrReticlePointer in Assets > GoogleVR > Prefabs > Cardboard to be the child of Main Camera

Selectcs from Assets > GooglveVR > Scripts > GvrPointerPhysicsRaycaster.cs and attach it to our Main Camera.

When we’re done, we’ll have something like this:

Now with these prefabs and scripts in, we can rotate and look around our game by holding Alt.

We can also shoot our raycasts with our VR Raycaster, however right now we don’t have an Event Trigger set up in our enemies that will detect them getting hit.

Let’s do that!

Step 4: Setting Up an Event Trigger

Before we end today, I want to make a simple event trigger that allows us to be able to defeat an enemy.

Luckily for us, we already have the function available to us! Specifically, inside our Enemy Health script, we have a code that we call to damage an enemy.

Let’s set this up. We want to get something like this:

For now, we’re only going to change our Knight enemy. Here’s what we’re going to do:

Select our Knight prefab in Assets > Prefab > Knight

Add an Event Trigger Component to our prefab.

Click Add New Event Type to select what type of event we want to listen for

Select PointerClick

Now click + to add the object we want to access the scripts of.

Drag our Knight Prefab into the empty Object slot

Then we need to select the function to call: EnemyHealth > TakeDamage(float)

Set the float value we pass in as 1

When we play our game now, when our gazer focuses on an enemy and we click, we’ll shoot him!

There are a lot of things that we’re missing like the push back, but we can start focusing on the rest of that tomorrow!

Now let’s do that to the rest of our prefabs: Bandit and Zombie!

Conclusion

There we have it! Our first dive into doing some work with VR. It turns out right now, there’s a lot less code that needs to be written, instead, a lot of it is just putting prefabs and scripts to the correct location so our game would work.

Either way, now we have a game that is playable. Tomorrow, we’re going to discuss what changes that we should do to make a better VR experience. Or at the very least, as good as it was before we try to VR-ify it!

Welcome back to Day 36! Yesterday we set up our mobile device to be able to play VR device and there’s nothing quite like that experience, especially if you’re the one that “made it”.

If you’ve made it this far in the journey, but you haven’t tried using the Cardboard or other VR devices, I highly recommend trying it once!

Now… with my pitch out of the way, let’s talk about what we’re going to do today!

We finally started working in VR, today, we’re going to try and convert our simple FPS into a VR experience.

This will be a multi-day progress. Today, I want to:

Do some Asset clean ups for our app so we don’t have to spend forever building

Set up our Google VR tools

Play our game

Getting the Project GitHub Repository

Before we get started. I realize that not everyone (or anyone) followed me step by step to get to Day 36.

In our Simple FPS game, we have reached a complete prototype so I’ve decided to make a Github Repository of our game before we start adding the VR components in. Specifically, after doing some manual cleanup work in Step 1.

Step 1: (FAILED/SKIP) Clean Up Un-Used Assets

If you haven’t tried switching to the Android platform in our simple FPS game, you might notice, that it… takes… FOREVER.

I’ve tried building the app (and I waited 10+ minutes). Thank goodness for the Unity previewer, I would not know what to do if I had to build my app on my phone every single time!

Luckily, I found that Google/Unity does have a solution for this. Google has Instant Preview. Unfortunately, which loads the game into our phone while also playing on the editor. The bad news is that this only appears to be for the Daydream Viewer so I’m going to hold off for now.

However, let’s see what we can do to optimize this!

When I say optimize, I really mean get rid of our un-used assets! Looking at what we have, we have 1 GB worth of files! That’s not good!

IMPORTANT NOTE

Unfortunately, this didn’t exactly work. I tried to export all our dependencies and then import it into a new project and there were some problems.

It turns out, things like Layers and Tags do not get preserved so if we wanted everything to work, we had to manually add everything back in.

Instead, I used the files I exported into a new project as a reference and then manually removed assets from a copy of our project (that’s what I get for not having source control!)

Also from testing with a before and after build time, I believe that un-used assets DO NOT improve our build and runtime, so the only useful thing that came out of Step 1 was that I:

Cleared some clutter so we can find files more easily now

Made the project smaller so people can download it from Github faster, so not a complete loss!

Just do the exact same thing in downloading the SDK and setting up our Unity configuration.

Note: In the repo, I already included Google VR SDK 1.100.1 and the necessary changes for Player Settings. I assume the PlayerSettings are project-based and not computer-based, but if it’s not, follow the instructions in Day 35.

Step 3: Playing Our Game

At this point, we should be almost ready to play our game!

At this point, we have:

imported the Google VR SDK

Switched to the Android platform

configured our PlayerSettings to the appropriate settings to run a Google Cardboard app

The last thing we need to do that’s specific to our Game Project is that we try to build in Build Settings… we run into a problem to resolve incompatibilities between Color Space and the current settings.

To fix this, we just need to change our Color Space from Linear to Gamma. To do that:

To do that, I recommend running the development build to build our app.

Contrary to what the name sounds, development buildDOES NOT make our build faster, instead it allows us to have access to useful debugging settings gives us access to the Profiler and Script Debugging.

You might have to restart your computer first for your computer to recognize the new Java SDK that you installed.

When we’re done downloading and installing Android Studio (which will take a LONG time), we want to open the SDK Manager.

In our new project, we can find our SDK Manager under Configure.

Now we’ll get this:

Under SDK Platform, select the platform we want to support, in this, case it’s Android 4.4 for Cardboard and Android 7.0 for DayDream, however, I believe if you install the newest version that’ll work for both.

Under SDK Tools, install:

Android SDK Platform-Tools

Android SDK Tools

Google USB Driver if you have a Nexus device

With all of this, we should now have everything we need to be able to build our game into our Android device.

Step 2: Install a USB Driver to Detect our Phone

The next part (and probably the part I hate the most) is installing a USB driver that allows our computer to detect our phone.

Failed to compile resources with the following parameters: major version 52 is newer than 51, the highest major version supported by this compiler.

The 1st and 2nd problem can be resolved easily.

The first problem is because we need to make sure that we create a minimum version of Android devices that have the software we need to run our VR application.

In Player Settings under Other Settings… in Minimum API Level select API Level 19 for Google Cardboard support and API Level 24 for Google Daydream. If you choose API Level 24, just make sure that your phone can run Daydream!

For the second problem, every Android app has a unique identifier that Google uses to identify the app. The error that we’re getting is that Unity is telling us that we’re using the default one and we should change it.

In Player Settings under Other Settings… in Package Name change the string to be something else. Just make sure you follow the convention of <companyname>.<appname>. In our case, it doesn’t matter what it is, we can put anything we want.

Now for the third and final problem. This one more interesting. Most likely your error is something like this:

warning: C:\Users\JoshDesktop\AppData\Local\Android\android-sdk\platforms\android-24\android.jar(java/lang/Object.class): major version 52 is newer than 51, the highest major version supported by this compiler.

It is recommended that the compiler be upgraded.

warning: C:\Users\JoshDesktop\AppData\Local\Android\android-sdk\platforms\android-24\android.jar(java/lang/AutoCloseable.class): major version 52 is newer than 51, the highest major version supported by this compiler.

What all of this is saying is that our Java is out of date and we need to have at least Java SDK 8.52.

In my case, I previously had 8.51 installed and when I installed version 8.52, Unity didn’t pick up on the changes.

To fix this:

Go to Edit > Preferences > External Tools under Android, select JDK and choose the path to your newest JDK file. For me, on my window machine, it was located at C:\Program Files\Java\jdk1.8.0_152

With all of this done, hopefully, you should be able to successfully build and run the GvrDemo on your phone + Google Cardboard if you have one.

Conclusion

Hopefully, this was a useful guide to getting your Android device set up to play the scene. Leave a comment if you run into problems and I’ll try to help and update this article with any new information.

On a different note, it’s truly amazing playing with VR on our own mobile device. Just playing the VR game from Unity was interesting, but words can’t describe how much more realistic and interesting it becomes until you strap your phone onto your face!

I think at this point, we have a good understanding of the basics and what is and isn’t possible with the Google Cardboard now.

Tomorrow we’re going to look and see how we can incorporate the VR SDK into our simple FPS game to see how our game fairs in VR!

Now that we have made a conscious decision to work in VR, today I finally had the chance to play around with VR in Unity.

Today we’re going to explore setting up and using Google’s VR SDK. You might think that setting up VR would be an extremely complex process, but after going through the process, I can say that starting out is simpler than you would think!

Here’s our objective for today:

Setting up support for Google Cardboard on Unity

Going through Unity’s Tutorial

Let’s get started!

Step 1: Setting up Google Cardboard on Unity

For today, I’ll be going through Google’s documentation for setting up VR on Unity.

The nice thing about the Google VR SDK is that we can re-use most of the prefabs and scripts that are used with Google Cardboard and use them with Google Daydream. That’s 2 different platforms for the price of one.

Step 1.1: Install Unity

At this point, I’m going to assume that we all have an older version of Unity (5.6+).

To support being able to run our VR App, we’re going to need to Android Build Support, if you don’t have that installed already, re-download Unity and during the installation process, choose to include Android Build Support.

Step 1.2: Adding the Google VR SDK

After we have Unity set up correctly with Android Build Support, we need to get Google’s VR assets.

After we have the package downloaded, it’s time to add it to a Unity project.

For our case, we’re going to create a new project to play around with.

In Unity create a New Project (File > New Project…)

Once inside our new project, import everything from the package that we downloaded. In Unity we can import by going to Assets > Import Package > Custom Package.

Step 1.3: Configuring Unity to Run VR

Now we have imported everything we need, the last thing to do is to change some of our settings in Unity so we can run our game.

Change Our Build Setting to Build and Run Android Applications

The first thing we need to do is get Unity to run our game project on an Android platform.

Open the Build Settings by going to File > Build Settings.

Select Android and hit Switch Platform

Wait for the game to finish re-packaging our assets for our new platform

Change Our Player Settings to Support VR

The next thing we need to do is to change our Player Settings so that we can support the specific VR SDK that we want. In our case, it’s going to be Google Cardboard.

In Build Settings, next to Switch Platform, we have Player Settings, select it.

In Player Settings, enable Virtual Reality Supported and then add Cardboard to our Virtual Reality SDKs

Finally, in Minimum API Level, select API level 19 for the minimum Android version the device the players must have. Google Cardboard requires a minimum of level 19 and the Google Daydream Viewer requires a minimum of level 24.

Once we have everything installed, we can finally get started on taking a first look at working with VR!

Step 2: Looking Through the Unity Tutorial

Now that everything is configured, we can officially start looking through Google’s SDK Basics.

I went through the SDK basics while also going through the GVRDemo scene.

In our new project go to Assets > GoogleVR > Demos > Scenes and open GVRDemo

Google provides prefabs and scripts that will take care of the VR features for you. These are all located in Assets > GooglveVR >Prefab and Scripts. Here’s a breakdown of what they and the script attached to them do:

GvrEditorEmulator prefab– Allows us to control our camera like how we might control it with our headset. Hold on to the alt button to rotate your view around the camera.

GvrControllerMain prefab – Gives us access to the Daydream controller which we can implement actions with Google’s controller API to interact with the game

GvrPointerGraphicRacyater script – This script is like a normal Graphic Raycaster that we would attach on to a UI canvas so that we can interact with our UI using our input devices (gaze or controller)

GvrPointerPhysicsRaycaster script – This script shoots out a raycast directly in the middle of our screen to select something when we decide to click. We should attach this to our main camera. We must also attach Unity’s event system on each object we want to interact with when we select them.

GvrControllerPointer prefab – This is the Daydream’s controller. It gives us an arm asset to imitate our controller. This prefab must the sibling of our Main Camera object where we attached our GvrPointerPhysicsRaycaster

GvrReticlePointer prefab – This is the Google Cardboard’s gaze controller. It creates a dot in the middle of our screen which we use to select objects that are in the game. For this prefab to work we must make it a child of the Main Camera game object.

There are quite a couple of other prefabs and scripts, but on the high level, these are the basics we’ll need to make a VR game.

Let’s see this in action with the GvrDemo scene!

Step 2.1: Looking at the demo scene

When we open up GvrDemo, here’s what we see:

I suggest that you explore around the scene and see the objects in our hierarchy, but on the high-level summary, here’s what we have in our hierarchy that’s relevant to just the Google Cardboard (because it has Daydream assets too)

GvrEditorEmulator for us to emulate head movement in VR

GvrEventSystem for Unity to detect our VR inputs when we select an object

Inside Player >Main Camera, we have our GvrPointerPhysicsRaycaster script which allows us to use Google’s raycasting system for 3D objects

Inside the Floor Canvas game object, we have the GvrPointerGraphicRacyate for us to interact with the UI.

Finally, inside Player > Main Camera > GvrReticlePointer, we have our gaze cursor for Google Cardboard that we use to interact with the game world.

The main point of this game is to click on the cube that appears in the game. When we click on the cube, it’ll be randomly moved somewhere else in the game.

The interesting part of all of this is how we can trigger the code with our Gaze.

The Event Trigger System is a way for Unity to recognize any action taken on the game object that the Event Trigger is registered onto.

An action is something like:

OnPointerClick

OnPointerEnter

OnPointerExit

In our example, OnPointerClick will be triggered whenever we click on an object that has the Event Trigger attached to it.

Here’s the teleport script:

// Copyright 2014 Google Inc. All rights reserved.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.usingUnityEngine;usingSystem.Collections;[RequireComponent(typeof(Collider))]publicclassTeleport:MonoBehaviour{privateVector3 startingPosition;publicMaterial inactiveMaterial;publicMaterial gazedAtMaterial;voidStart(){
startingPosition = transform.localPosition;SetGazedAt(false);}publicvoidSetGazedAt(bool gazedAt){if(inactiveMaterial != null && gazedAtMaterial != null){GetComponent<Renderer>().material = gazedAt ? gazedAtMaterial : inactiveMaterial;return;}GetComponent<Renderer>().material.color = gazedAt ?Color.green :Color.red;}publicvoidReset(){
transform.localPosition = startingPosition;}publicvoidRecenter(){#if !UNITY_EDITORGvrCardboardHelpers.Recenter();#elseGvrEditorEmulator emulator =FindObjectOfType<GvrEditorEmulator>();if(emulator == null){return;}
emulator.Recenter();#endif// !UNITY_EDITOR}publicvoidTeleportRandomly(){Vector3 direction =Random.onUnitSphere;
direction.y =Mathf.Clamp(direction.y,0.5f,1f);float distance =2*Random.value +1.5f;
transform.localPosition = direction * distance;}}

We can ignore what the code does, but the important thing that I want to bring attention to are the public functions that are available:

SetGazedAt()

Reset()

Recenter()

TeleportRandomly()

Where are these called?

Well, if you look back at our Event Trigger that’s created in Cube, we set 3 event types:

Pointer Enter

Pointer Exit

Pointer Click

Then whenever any of these events occur, we call our public function.

In this example, when we look at our cube, we’ll trigger the Pointer Enter event and call the SetGazedAt() function with the variable gazedAt to be true.

When we look away, we trigger the Pointer Exit event and call the SetGazedAt() function with gazedAt to be false.

Finally, if we were to click on the cube, we would trigger the Pointer Click event and call TeleportRandomly() to move our cube to a new location.

Conclusion

It’s surprising how un-complex this whole process is so far! I’m sure there are a lot more things to consider once we dive deeper into Unity, however for today, I think the progress we have made is sufficient.

Tomorrow, we’re going to look at how we can get the demo app to run on a phone that supports a Google Cardboard (which I assume at this point is 99% of you guys here)

Side Note: I've been feeling sick recently and progress have been slow, but I'm feeling better and ready to get back to it!

Welcome back to day 33!

Yesterday, we looked at 3 ways we can save and load data in Unity: with PlayerPrefs, Data Serialization, and saving our data to a server.

Today we’re going to use what we learned the previous day to save our score in our simple FPS.

Here’s the goal for today:

Implement our SaveManager to help us save score

Update our UI to show our high score when the game is over

So, let’s get started!

Step 1: Saving our Data

Of the methods we’ve talked about, I’m going to use the PlayerPrefs to help us save our data.

While we can technically use our PlayerPrefs anywhere we want between our scripts, it’s better for us to create a manager where we will centralize everything all our Saving/Loading work so that when we need to make changes, we don’t have to comb through all our Script to fix things.

Step 1.1: Creating our SaveManager

The first step to saving our score is to create our ScoreManager script and attach it to our GameManager game object.

Select our GameManager game object in our hierarchy.

In the Inspector, click Add Component and create a new ScoreManager

In our SaveManager, we want to be able to save and load our high score. Here’s what we’ll have:

Variables Used

For our SaveManager, we only create a string _highScoreKey that we use to store the text that we want to use for our score.

We never want to manually type our key in as that might lead to us mistyping and many hours spent debugging over a single spelling mistake.

Walking Through the Code

Our SaveManager script is only used to help us access our PlayerPrefs in one centralized location.

The beauty of this system is that if one day we decide that we don’t want to use PlayerPrefs and use DataSerialization instead, we can just change SaveManager, but everything else that’s using it can stay the same.

Here’s the code flow:

In SaveHighScore() we save the score that we’re given to our high score.

In LoadHighScore() we return the high score that we saved. It’s important to note that if we don’t have a value in our Prefab, we would return 0, however, in our case, a lower score is better, instead we return a very high score.

Step 1.2: Modifying our ScoreManager to Expose Our Score

Previously, we had our ScoreManager change the text of the score in our page, however, if we want to be able to show our high score at the end of the game.

To do that, we need to use the Victory and GameOver panels that we made in the past. Luckily for us, in our GameManager, we already have some code that uses them.

Now, for our GameManager to access our time (and save it with our SaveManager), we need to expose the score for other scripts to access them.

New Variables Used

We create a new float _score that we’ll use to keep track of the time that passed for the player.

Walking Through the Code

Most of the code is the same. We just wrote some new functions, here’s what we did:

In Start() we set _score to have a starting value

In UpdateTime() we update _score to be the current time in the game. I also moved the code that gave us the time in a nice: minutes:seconds:milliseconds format to a static function called GetScoreFormatting() where we used to set our _time. Notice how I use GetScoreFormatting()? GetScoreFormatting() can be used just like this anywhere else in our game now, even if we don’t have an instance to ScoreManager.

GetScoreFormatting() is just a copy and paste of what we originally had in UpdateTime().

Finally, we create a public function GetScore() to get the score the player earned in GameManager when they win

That’s it! Now let’s combine everything we’ve worked on to create our high score system.

Step 1.3: Use Everything in Our GameManager

Now that we have everything we want, let’s use it in our GameManager script!

Now we’re going to write some code that allows us to save our when the player wins.

New Variables Used

The first thing we did was we got ourselves an instance of SaveManager: _saveManager.

Now with our SaveManager in GameManager, we can save and load scores.

Walking Through the Changes

The code that we’re adding uses our SaveManager to save our score.

In Start() we instantiate our SaveManager which is also attached to the GameManager game object.

When Victory() is called, we check their current score and then compare it with our high score from our SaveManager if our time is lower than the high score, than we set our score as the new high score

With our changes to the GameManager, we now have a working high score system.

Now the problem? We have no way of seeing if any of this works!

Worry not, that’s going to be the next step of our work!

Step 2: Update the Game Over Panels to Show Our Score

Now that we have the code to change our high score, we’re going to work on displaying our high score in our UI.

To do this, we’re going to make a couple of changes to our script, we’re going to:

Change our GameOverUIManager to change the Text that we show.

Change our GameManager to get our GameOverUIManager from our game over panels and then set the high score to show when the game is over.

Step 2.1: Making Changes to GameOverUIManager

If you recall, our GameOverUIManager was created to help us detect when the player clicks on the start over a button in our panel.

We’re going to make some changes to change the text in our Panel to also say what our high score is.

Step 1.1: Using PlayerPrefs

PlayerPrefs is a class that we call that allows us to save and load simple data (think numbers and string) across multiple game sessions.

We can think of PlayerPrefs as a dictionary/table object that takes in a key/value pairing.

For those who haven’t worked with something like this before, what this means is that PlayerPrefs is an object that we can store specific values to a string and when we want to get that value back, we can give it the string.

We’ll see how this works:

In our GameManager game object, click Add Component and create a new TestSaveManager

In the code, we’re just going to store a value in and then print it out to our console.

Here it is:

usingUnityEngine;publicclassFakeSaveManager:MonoBehaviour{voidStart(){if(PlayerPrefs.HasKey("FakeScore")){int savedScore =PlayerPrefs.GetInt("FakeScore");
print("We have a score from a different session "+ savedScore);}else{PlayerPrefs.SetInt("FakeScore",1337);
print("We don't have a score yet, so we're adding one in");}}}

New Variables Used

None

Walking Through the Code

PlayerPrefs is a static object that we can just access. Here’s what we did:

In Start() the first thing we did was check if we stored a value to our key string: FakeScore.

From here 2 things will happen.

First Run Through

The first time we run the code, we haven’t stored anything yet, so we’ll go to our else statement and set an Int with the value 1337 to our key FakeScore.

We also print in our console notifying us that it’s the first time we did that.

Now if we run the game for the first time, here’s what we’ll see:

Notice that it says we don’t have a score yet?

Second Run Through

Now after we ran the code for the first time and we added our key/value pair to our PlayerPrefs. If we were to run the code again, we’ll go to the other code path.

In the if statement, we’ll get the value we stored inside our key “FakeScore”, which is 1337.

We’ll print out 1337.

Play our game again and here’s what we get:

See how we have a score now? That means our score persisted from our previous game. Neat!

Step 1.2: Why use PlayerPrefs

Looking at what we’ve done, when do we want to use PlayerPrefs?

The benefit of PlayerPrefs is:

Fast and easy to use with no setup required to store primitive variables

The con of PlayerPrefs:

If the data we’re storing is important, the user can easily access the data and change it

Why is it insecure you might ask?

Well, it turns out that any data that we’re saving is saved in open space on your computer. In a Windows machine, you can find these values un-encrypted in our registry. Here it is:

Do you see the 2nd item from the top of the list FakeScore and the value is 1337.

Any computer savvy hacker who has knowledge of how Unity works can easily go in and make changes to these values to increase our score.

To summarize: Only use PlayerPrefs to store information that doesn’t really affect the game, such as player settings: brightness, audio sound, special effects, etc.

Do not use it to store things like high scores or sensitive information like your credit card information.

Step 2: Saving with Data Serialization

After looking at PlayerPrefs we have another method of storing data and that is using data serialization with the help of BinaryFormatter and FileStream class.

How this method works, is that we’re:

Taking data (either an object we made serializable, a primitive value, or a JSON)

New Variables Used

Walking Through the Code

The first thing you might notice is that I commented out the PlayerPrefab code. We don’t need that anymore, we’re just going to focus on the serialization part.

Before we walk through the code, let’s talk about these individual classes that we’re using.

FileStream

We can think of FileStream is a class that allows us to interact with files. In this case, use File.Create() and File.Open() to give us access to these files that we want to access.

Notice how we use something called Application.persistentDataPath when we create and open a file? persistentDataPath is a file location in our computer/phone that we can use to store our game files. Each path is different depending on the OS that the game is running on.

On a windows machine, here’s where the file would be saved to:

Notice how we called our file “save.dat”, that’s just what we call it, the file can be any format we want and it’ll still function the same way.

After we set up our FileStream, we will give it to our BinarySerializer so that it can use it to store our data.

It’s also important to remember that when we’re done using a FileStream, we should always call Close() to prevent a memory leak.

BinaryFormatter

We can think of the BinaryFormatter class as a useful utility that takes in a FileStream and reads/write data into the file.

In our specific example, I put the float 1337 inside, but we can put in classes that we create ourselves.

However, if we were to create our own object, we must make sure we tell Unity to serialize it (by putting [Serialize] on top of the class name).

The two important function we have to know is Serialize() and Deserialize().

Serialize is how we change our data into binary and then store them in our File

Deserialize is how we read the data back from binary.

If we were to open the file that we saved data into, here’s what we have:

Now that we understand everything used, let’s walk through the code:

In Start(), we call SaveWithSerializer() which uses a BinaryFormatter and a FileStream to help us store our data, which in this case is just a float value.

We create a new FileStream with the path to where we want to save our data to. We use Application.persistentDataPath to give us a location to store our file that changes depending on what OS is running our game. This is Unity making sure we put our save files in the right location so we can access it back later no matter what platform we’re using.

After we have our FileStream we would use the BinaryFormatter to Serialize our data to the path we specified. After that, make sure to close our FileStream to prevent memory leaks!

After we save, we call LoadWithSerializer() we do a similar operation where we create a BinaryFormatter and a FileStream to the location of the file we previously saved.

With these two objects, we Deserialize, which means we unencrypt our data and put it back into a usable form for us. At this point, we have our data back.

Step 2.2: Why Use Data Serialization?

At this point, you might guess why we might want to use Data Serialization:

Our data is encrypted so it’s harder for the players to find it and modify the file.

In our example, we only store one variable, but we can actually save a lot more than just that

However, just like the PlayerPrefab, the con is:

We know the location of the file, and just because it’s in binary, it doesn’t mean it can’t be reverse engineered!

When should we use Data Serialization?

The answer: almost everything! Data serialization can be used as a database for our equipment, magic spells, and more! We can also use it to store information about our player character.

My only caveat is that if the game you’re creating is multiplayer where you don’t want players to have an unfair advantage, then you DO NOT want to save data about the player locally.

We can store information that the game references where we don’t modify, such as stats of an equipment locally, however information like player inventory is best stored somewhere else.

Where is that somewhere else? Good question, that’s the topic of our next method.

Step 3: Saving Data to A Server

The final and last way for us to store data for our game are to save the data to an external web server which we control and access.

From the external server, we would keep track of the state of the player, for example:

What level they are

How much money they are

What items they have in their inventory

All information that is changeable by the player should be saved externally in a server, where we can do verifications to make sure the player is what they say they are.

Step 3.1: Using a Server

I’m not going to create a server, but rather discuss how we would do it if we wanted to.

The first thing we must do is host a web server online somewhere.

Once the server is up, from our game, ie the client, we would make HTTP network request to our server passing around whatever data our server requires.

A general flow of what we would expect is that:

Whenever we’re ready to save our data, we would send the data to our server

Whenever we want to load our data, we would make a request to our server for our data.

When sending data over the network, we can’t just send our whole class over, our web server have no idea what anything is.

To solve this problem, we’re going to have to convert our objects into a JSON object and then send it across the network.

I won’t go too deep into the details, but a JSON object is basically a string representation of our object that we can easily convert back and forth.

We would send this string to our server where we would parse it, validate the data, and then save it in the database.

When we’re loading information, the server would send a JSON object back to the game where we would also parse and change the JSON back into something we can use.

Step 3.2 Why use a server?

The benefit of using this approach to store our data is:

Cheating is impossible. Assuming there are no vulnerabilities and we have good data checks, it’ll be very hard for the player to be able to modify their own data and have it saved on our server.

Similar to Data Serialization, we can save more complex data than just primitive variables.

The cons are:

We need a server, which costs money.

There’s more overhead work needed. We need to create our own database, know how to set up a server, and deal with storing multiple players instead of one.

The big question here is: when should we use a server?

The answer: In any game that has multiplayer that we don’t want players to get an unfair advantage against each other. If not, there should be no problem to storing everything locally.

Conclusion

Today, we explored the options that are available to us to save data in a game.

The 3 ways we explored are:

PlayerPrefs

Data Serialization

Sever

Most likely, in a real game, we would use all 3, but the best way for me to summarize when to use each is:

Use PlayerPrefs to store player setting information that doesn’t affect the game itself.

Use Data Serialization to store database information that we will only reference, but not change

Use a Server when we have a multiplayer game where we want to ensure no one can cheat by sending all player data to a server where we can do validation on the authenticity of the data.

For a single player game, however, we can save the data in a serialized file or if it’s simple enough, even in our PlayerPrefs.

Now that we spent Day 32 looking at saving data, it’s finally time for us to start using it on Day 33!

I’ll see you all next time to start putting everything we learned into action!

New Variable Used

Like what we said earlier, we introduced our ScoreManager object, _scoreManager so we can call GameOver().

Walking Through the Code

There aren’t any major or surprising changes that were done:

In Start() we got our _scoreManager that was attached to our GameManager game object

In DisableGame(), where we go and disable everything, we call GameOver() from our _scoreManager to pause the timer.

Step 2: Fixing the Player State After the Game Ends

The next thing we’re going to do is fix a problem that I’ve been talking about for the past 2 days or so. When we win, our player, specifically, the gun, is stuck in a perpetual shooting state and the shooting sound effect will keep playing until the game is over.

We’re going to fix that.

Most of the code is already in place, we just need to:

Create a new state transition to stop shooting in our gun Animator

Create a new game over function for our gun to stop everything when the game is over

Call the game over function from our GameManager

Step 2.1: Create Transition to Stop Shooting

If you might recall from Day 16 where we added our weapon, we created a state machine that would transition from our shooting animation to our idle information (and vice-versa) whenever we activate our Shoot Trigger

We’re going to make a new GameOver state and create a new transition from Any State to GameOver.

Here’s what we’re going to do:

Select Player > Main Camera > MachineGun_00 and open the Animator tab (Windows > Animator)

Create a new parameter, it’ll be a trigger and call it GameOver

Create a new state called GameOver

Create a new transition from Any State to

Select that transition and set the transition condition to be when GameOver is triggered

When you’re done, we should have something like this:

At this point, you might be wondering, if we don’t have any clip for our GameOver state, why not just re-use our default state?

The answer to that is, because this is our finished state where we’re done. We don’t want to accidentally trigger a new transition and move us from the default state to another state.

Step 2.2: Add GameOver in PlayerShootingController

Now that we have our new transition, it’s time to use it!

As we might recall, the script that controls our player’s shooting is PlayerShootingController.

New Variable Used

All we’re currently introducing in our code is a string_timethat keeps track of the time that took place once the game started.

Walking through the code

The code is short right now, but here’s what happens.

InStart()we instantiate_timeto be empty

InUpdate()we call a functionUpdateTime()to get our correctly formatted time string.

InUpdateTime(), we calculate what our minutes, seconds, and miliseconds are, and then we use String.format to build the string we want.

If you aren’t familiar withFormat, we pass in a string for where we want variables to replace, and then we put the variables we want to do the replacing afterwards. In this example:{1:00}refers tosecondsand we want to always show the string in the format with 2 numbers. For example: 01 or 02

If we were to play the game now and look at theconsoleyou can see that we’re printing out the time.

Step 2: Creating the UI to show our Score

Now that we have the code to create the string for our score, the next thing we’re going to do is to show the time on the player’s score.

New Variables Used

We added a new Text variable that we call Score. This is the new UI that we just created to show the score in the top center of our page.

Note that we need to import UnityEngine.UI to be able to use aTextobject.

Walking through the code

The changes have been simple:

InUpdateTime()we set the text of our Score Text UIto be the time.

Note: I’m a fond supporter of writing code that sends push notification for changes, however in this, case, because time is always changing, we have no choice but to useUpdate()every frame to get our new time.

Step 2.3: Attach our Score UI to our ScoreManager script

Finally, we need to add our new Score UI to our ScoreManager so we can update the time.

Just drag the UI into theScoreslot in theScoreManager.

When we’re done, we should have this:

And when we play the game we’ll have our time increasing:

Nice!

Conclusion

That’ll be all for today!

We did some great work today laying the foundation to our score system. Right now we created a time system on the top of our screen so that the player will know how long it’s taking them to beat the enemies.

However, if we were to lose or win, you’ll notice that our time will continue to keep running.

We will address these issues tomorrow by making some changes in our victory and game over game states, but until then, good night, and I’ll see you all later on Day 31!

New Variables Used

None

Walking Through the Code

There’s only one major change that we did and that’s inDeath()where search through all of the enemy’s child game objects looking for any collider component. If a collider is found, we would just disable it.

One interesting thing to note is that we’re looking for anything of typeCollider. We’re more used to finding something of exact type, for example, GetComponent<SpawnManager>();

However, taking advantage of the fact that all of theColliders:Box Collider,Mesh Collider,Capsule Collider, etc all extend from theColliderparent class, they’re all considered of typeCollider.What that means is that we can do what we did inDeath()and look for anything that is of typeCollideror inherits the class.

Now after we added the code, if we play the game, we’ll see that dead enemies no longer have their Colliders on anymore:

There are things that are sometimes better off staying dead.

Conclusion

That’s it for today! Today, we incorporated the rest of our enemies to the game and we even went in to fix “ghost” mesh colliders that prevented us from shooting properly.

I think we’re going to work on one more feature before I start moving on to the actual VR portion of this series, and that’s making a point system!

In our current game, we win and that’s it. There’s no replay value at all!

Tomorrow we’re going to start making this game more interesting by adding a scoring system!

Yesterday, we started our next task on creating multiple new types of enemies and made a new bandit enemy.

Today, we’re going to create our last enemy, a slower enemy with more health that won’t get pushed back as far.

Today is going to be very similar to yesterday, however we’re going to try and create a newanimator controllerfor our enemy. We’ve seen this before in the Survival Shooter, which I once again blissfully ignored, however we’re re-visiting this topic today.

Let’s get started!

Step 1: Acquire new asset

Let’s return to our favorite place in the world! The candy store Unity Asset Store!

Just like yesterday, we’re going to look for an asset that’smecanimready.

If you’re not aware of the glory that is themecanimsystem, you can visitDay 11where we first used it.

Step 1.1: Get Zombie Asset

Browsing around the asset store for another enemy to use and I found a nice barbarian mage to use for our tank enemy!

Download and import the barbarian into our game. It’ll be inAssets>Zombie_0_1.

Step 2: Setup the Zombie

Yesterday, we created ourBanditenemy that we re-use our existingKnight Animator Controller, however for ourZombieI want him (it?) to have different move set and animations.

Today, we’re going to do things differently!

Step 2.0: Set Wave 1 SpawnManager to Spawn -1 Enemies

If you have set our Wave 1 to spawn -1 enemies already then you don’t have to do anything.

However, if not, go to our SpawnManager and make this change:

Step 2.1: Creating an Animator Override Controller

To achieve the goal of creating an enemy with new animations (but the same states), we’re going to use theAnimator Override Controller.

AnAnimator Override Controllerallows us to give it anAnimator Controllerfor us to override and put our own animation clips in. This is very handy for us if we want to create an enemy that has the same states from anotherAnimator Controller, but you want to use different animations for each state.

EnemyHealth

EnemyMovement

For the EnemyMovement scripts, we want to set:

Walking Clips > Size:4

Set the 4 newAudio Clipsto beFootstep01 — Footstep04

Walking Delay:7

Knock Back Force:9

EnemyAttack

We’re going to do some re-work with ourEnemyAttackscript, technically speaking, we should use sound effects that are not punching sounds, however, because we’re only practicing, let’s just use the same sound effects we have been using.

Attack SfxClips > Size:3

Set the 3 newAudio Clipsto bePunch_01 — Punch_03

Leave the Fist Colliders alone for now

Now if we were to play the game, ourZombiewill start chasing after us, but he won’t attack us yet, we haven’t set up our triggers and colliders yet!

We want to attach a box collider. We’re just going to make it as big as our weapon.

We need to make some changes with the collider. Specifically, theZvalue forCenterandSize.

Set theZvalue forCenterto be 0.6

Set theZvalue forSizeto be 0.2

The reason why we’re doing this is because we don’t want our weapon to block our gun raycasting when we try to shoot the enemy.

Next, we’ll attach ourFistColliderscript to our game object.

At this point, we really should call it a weapon collider, but we’ll keep it the same for simplicity.

When we’re done, we should have something like this:

We’ll do the exact same thing for the right weapon as well.

Adding the Mesh Collider

Next up is ourMesh Collidercomponent.

The same as yesterday, the purpose of theMesh Collideris for 2 reasons, it allows us to:

shoot the enemy

get pushed by the enemy

For the zombie, this is easy.

All we need to do is add a Mesh Collider component toZombie > Zombie_0_1.

And for the Mesh, attachZombie_0_1

When you’re done, you’ll have something like this:

Note: it has come to my attention, that using Mesh Colliders wouldn't be the best approach. Instead a better approach that was brought to light to me by Scouting Ninja is to use individual Colliders that covers our enemies.

However, seeing how we already have this, I'll leave that as a future optimization we can do.

We’re almost there, we still need to make our game object shootable so we can hit him.

Step 2.6: Set Zombie Layer to be Shootable

Straightforward, just like ourBandit, we did the day before, we want to go to ourZombieand change hislayerto beShootable.

Step 2.7: Attach the Fist Colliders to EnemyAttack

We attached ourFirst Collider scriptto the Zombie’s weapons. It’s time to attach both of them to ourEnemyAttack Scriptso we can detect when the weapon makes contact with the player inAttack().

InEnemyAttackset:

Left Fist:Zombie_0_1_002

Right Fist:Zombie_0_1_1

With this, we can take damage now.

Except for two problems:

If we don’t move and let the enemy attack us, he just stops attacking us at one point!

We don’t take damage when our enemy attacks!

Step 2.7: Allowing Enemies to Continuously Attack Us

Fixing the enemy not attacking problem turned out to be straightforward.

If we were to look at theAnimatorof our zombie when he attacks us, you’ll notice something interesting.

The zombie is in the right state attack state, but he only attacks us once.

Step 2.8: Creating an Event in our Animation to Call Attack()

The next problem is that our player character doesn’t seem to notice when they get attacked.

If we were to do some print debugging, if we were to add a print statement inside ourEnemyAttackscript atAttack(), we’ll notice that we never callAttack()!

As you might recall, we callAttack()by setting an event in our animation! OurBanditwas using the same knight attack animation so we never had to run into the problem, however, ourZombieuses a new animation!

Step 1.1: Get bandit asset

Download and import the bandit into our game. It’ll be inAssets > BasicBandit.

Step 2: Setup BasicBandit

Now that we have our assets the next thing we need to do is to setup up ourBasicBanditasset to use our existing animation and scripts!

This work will be the same as what we have done with the knight model, except we don’t have to code anything this time!

Step 2.0: Set Wave 1 SpawnManager to Spawn -1 Enemies

Not necessary, but for the ease of testing our new enemy. If we setWave 1of ourSpawnManager,to spawn -1 enemies, we won’t create any new enemies and the wave won’t progress, allowing us to experiment around with our enemies.

Another option is to comment out the instantiation code inSpawnManagercode to not create any new enemies.

Step 2.1: Attach the Knight Animatior Controller

The great thing about Unity’s mecanim system is that we can re-use existing animator controllers for models that share the same animation.

In this case, because our knight and bandit are both humanoids, we can share ourKnight Animator Controllerwith our bandit.

Now let’s do it!

InAssets > BasicBandit, drag and dropBasic_BanditPrefabinto our game.

Select our bandit in the hierarchy and in theAnimatorcomponent, underController, select ourKnight Animator Controllerto fill in the slot.

You’ll have something like this now:

Now with this, our bandit will have the same animation rules as our knight (running, attacking, death, and idle)

We have the colliders to detect when our bandits get hit, however, if we were to try and shoot our enemy, we wouldn’t “hit” him yet.

Step 2.4: Set Bandit’s Layer to be Shootable

As you might recall when we were shooting enemies with Raycasting inPlayerShootingController, we did an optimization to only hit objects that have a layer mask set toShootable.

Select our bandit in the hierarchy and set ourLayerto beShootable.

Now with these colliders set, we can shoot the bandit and they’ll take damage and be knocked back.

Step 2.4: Attach our Fist Colliders to EnemyAttack

Earlier on, we attached ourFirst Collider scriptto the bandit’s hands. It’s time to attach both of our hands to ourEnemyAttack Scriptso we can detect if either of our the bandit’s fists makes contact with the player inAttack().

InEnemyAttackset:

Left Fist:L

Right Fist:R

Step 2.5: Create a prefab of our Bandit

Now that we have everything setup, we’re going to make a prefab of our finished bandit model.

Step 3.1: Getting the crosshair from the asset store

Download and import the asset to our game. Everything will be in theSMC Pack 1in ourAssetsfolder

Step 3.2: Using the crosshairs pack

I’m not going to be picky on my crosshair, so I just chose the first one.

Setting our Crosshair image

Inside our hierarchy, go toHUD>Crosshairand in theImage (Script)component, changeSource Imageto be one of the crosshairs you like. I chose 1 because it’s the first one.

Changing our crosshair to be red

Right now, it’sWhite, I decided to change it toRed. Also, it’s hard to see it when it’s so small, so I resized our crosshair. I set thewidthandheightof our image to be40.

Re-center our crosshair

Finally, after we adjusted the size of our image, we should reposition the crosshair to be in the middle of the screen again.

To do that we click on theAnchor Presets(the box on the top left corner of ourRect Transformcomponent) and hitctrl + shiftin the middle center box.

Now when we’re done, we should have something like this:

Step 4: Stopping Enemies in Game Over

Now we’re on the final and more complicated part of today.

Currently, in the state of our game, when the player loses, the enemies will continue attacking the player and the spawner would continue spawning.

While leaving that as is could be considered a feature, for practice, we’re going to learn how to disable enemy spawning and movement when the game is over.

The first thing we’re going to do is stop the enemies from moving after we win.

Note:this is only when the player loses. If they win, all the enemies are already dead and there’s already no more enemies to spawn.

Step 4.1: Spawn all enemies inside a parent holder in SpawnManager

If we want to disable our enemy knights after the game is over, a simple change we could do inside ourEnemyHealthorEnemyMovementscript is to check ourGameManagerto see if the game is over inUpdate(),over and over and over again…

As you can imagine doing this for ALL our enemies could become computationally expensive. So instead, I think a better solution is to store all enemies in a parent game object and then when we’re done, cycle through all of them.

The best way for us to have this parent container is to create it in ourSpawnManagerand then push all enemies that we spawn in there.

For our code to work, we need to access theEnemyHealthandEnemyMovmenetscript to:

Check if the enemy is still alive

If alive, set them to an idle state and stop them from moving, all of which is controlled in our EnemyMovement script.

Here’s the code, note that it won’t compile yet until we changeEnemyMovement:

New variable used

The only new variable that we used is to aGameObjectthat we call _enemyContainer.

_enemyContainer, is literally an empty game object that we create that’s sole purpose is to function as a container.

Walking through the code

The complexity of this specific feature isn’t the code itself, it’s changing multiple pieces that intermingle with each other.

Here’s what we need to know about the changes done toSpawnManager:

InStart(), we create a new instance of aGameObject, which will put_enemyContainerin our actual game. It’ll be called“Enemy Container”

We create a new public function calledDisableAllEnemies(), in here, we check all child game objects in our_enemyContainer. We make sure they all have ourEnemyHealthandEnemyMovementIf they all do, we’ll call the currently non-existentPlayVictory().

Once again, currently our code does not compile, we need to addPlayVictory()to ourEnemyMovementscript.

Step 4.2: Creating PlayVictory() in EnemyMovement

InSpawnManager, we’re essentially disabling all enemy movements after the game has ended. To do that we’re putting that logic in a function that we’ll callPlayVictory()

Step 1: Creating the Victory Panel

The first thing we need to do is create another panel that tells the user that they won the game.

Instead of going through the work of creating another UI and animation for our Victory Panel, we’re going to take the easy approach and make a duplicate of our existingGameOvergame object and re-purpose it.

Currently, we can use the same script and animator, because they both do the same thing.

Duplicate GameOver

To do that, selectGameOverin our hierarchy and then hitCtrl + Dto make a duplicate. Rename the duplicate toVictory.They can both just stay in the canvas.

Changing Victory Text

Now that we have ourVictorypanel, we need to change our text so that the players will know that they won.

Now this is a lot to take in, which is why I added comments, however, here’s the run through of the code.

About the Wave Class

Before we talk about our variables, I want to introduce theWaveclass.

Waveis a container for us to hold the data for each wave that we’re going to face.

If you remember from theSpace Shooter tutorial, we did something similar. We created a new class to hold information and we made itSerializableso that Unity knows how to show it in the editor.

Originally, I was going to just pass each of its content to our SpawnManager, but that’s prone to us causing mix-ups of how many enemies to spawn per wave and which enemies.

About the variables

For our public variable we have:

Waves — An array of theWaveclass that we created for an easy way for us to access data for each wave

SpawnPoints — An array of locations that we’re going to instantiate our enemies

TimeBetweenEnemies — The delay we wait before we spawn our next enemy

For our private variables to keep track of the enemies, we have:

_totalEnemiesInCurrentWave — Self explanatory

_enemiesInWaveLeft — The number of enemies that are still alive in the wave

_spawnedEnemies — Self explanatory

We also keep track of what wave we’re in:

_currentWave — Self explanatory

_totalWaves — Self explanatory

The Code Flow

Now that we know the variable we’re using, we can walk through the rest of the code.

InStart()we initialize all our variables to 0. Note that we set_currentWaveto be -1 and our_totalWavesis the length of our array — 1. For those who aren’t familiar, all of this is, because we’re working in a 0-based indexing for arrays (meaning everything starts from 0 instead of 1).

FromStart()we also callStartNextWave()this code handles what happens when we clear our wave. We increment our_currentWaveand assuming we haven’t finished the game, we’ll setup the enemies we need to encounter for the next wave and callSpawnEnemies()

SpawnEnemies()is a coroutine that we use to create our enemies. The function will spawn the enemy for the wave based from one of the random spawn points we set. The code will wait 2 seconds before we spawn the next enemy so we don’t flood the players with all the enemies in a wave at once.

Finally, we have publicEnemyDefeated()which means this function is called from something else. In this case, our enemy will call this when they’re killed. We will then decrement our enemy counter and if we have defeated all the enemies in a wave, we’ll callStartNextWave()

That’s the basic summary of the code. That wasn’t so bad, now was it? RIGHT?!

Setting up our SpawnManager script

Now that we have our script up and running, the last thing we need to do is to setup ourWaveand create ourSpawn Points.

Setting up the Wave

For now, let’s just make 2 waves for ourSpawnManagerthat will create our knight enemy.

There are 2 things we need to provide for ourWaveclass:

The knight game object

How many enemies to spawn for the wave

Let’s first set the knight game object.

We could just drag and drop our knight from our hierarchy into the spot and everything would be fine. However, the correct way to do this, is to first create aprefabof our knight.

We can think of aprefabis a template of a game object that we can drag and share to different scenes and games. In our case, we can also instantiate them like what we’re doing here.

The main benefit is that, if you were to change prefab, anytime we need to change something, we can just change the prefab and anything that instantiates it from code will also get the changes.

The first thing we’re going to do is:

Create a folder in ourAssetsfolder calledPrefabs

Create our prefab, by dragging ourKnightgame object into thePrefabsNote, in this example ourKnightis already a prefab, but that’s okay, we’ve already changed some things about it, so let’s make another prefab from it.

On a side note: we candeletetheKnightgame object from our hierarchy. He’s served us well, but we won’t be needing his services anymore.

Now that we have everything we need, we can get started in Creating our waves.

Back in ourSpawnMangerscript, expandWavesand setSizeto be 2 so we can create new waves.

Here’s what it’ll look like:

Setting up the Spawn Point

Now that we have the wave system setup, we need to create our spawn points.

Exactly like the survival shooter, we need to add some game object that we’ll use as the location that we’re going to spawn the enemies.

Let’s get to it!

Create 3Emptygame objects: I call themSpawnPoint1 — SpawnPoint3.I made them the child of theSpawnManager, however, it doesn’t really matter where we put them.

Right now, we can’t see the position of our SpawnPoints in the game, however we can add aLabelto them to see where the objects are.

We just want to set the Spawn points to be in 3 of the houses.

Here are myPositionvalues, yours might be different:

SpawnPoint1: 98, 0, 118

SpawinPoint2: 72, 0, 89

SpawnPoint3: 106, 0, 80

Finally, go back to ourSpawnManagerand add ourSpawnPointsin.

ExpandSpawn Pointsand then for theSizechange it to 3, and then drag ourSpawnPointsinto the empty game spots.

With all of this in place, if we play the game, we should now have multiple enemies coming at us.

Conclusion

We’re not quite done with our Spawning System, however we laid the ground works for spawning enemies.

We still need to implement the logic for generating new enemies and finally creating our victory condition. We’ll start implementing those tomorrow on Day 24!

We get theGameOverAnimatorfrom ourGame Overobject and we take the Animator from it.

We create a publicGameOver()that someone will call that will set our IsGameOver parameter to be true so that we’ll play the animation to show our game over panel.

When you’re done with the script, make sure to set ourGameOverUI GameObject to ourGameOverAnimatorslot in ourGameManagercomponent.

Quick note on pushing vs pulling for updates

For those of you who saw Unity’s tutorial, I’m not actually a big fan of what Unity does with having all of these checks in theUpdate(), where we’re always checking for something to happen.

Personally, I much prefer to take a more push functionality where we only do something, if something else notifies us of it happening.

A perfect example of a push approach is our public GameOver(). Currently we have some other script call the function to indicated game over.

An example of pulling is that we have a script that checks for changes every frame. For example. We could have givenGameManagera reference the the player Game Object and inUpdate()it could constantly check to see if the player is dead.

Both way works, I just prefer the way where we only do something when we’re told to do it and not check every frame.

Using our GameManager

After creating our GameManger, we need to use it.

In this particular case, we’re going to use it in ourPlayerHealthscript. We’re going to:

InStart()we instantiate our button and we set the OnClick Listener to our button to runClickPlayAgain()whenever the user clicks the button

InClickPlayAgain(), we use the SceneManager to restart our scene so we can play again. As we might recall, everything in Unity runs in a scene,SceneManageris what we use to help ustransition from one scene to the next. Note: usingLoadLevelis depreciated forSceneManager

Now with all of this, we’re almost done!

If we were to try and play the game now and let the knight beat us, you’ll realize a problem. We can’t click on the Restart button!

Let’s fix it.

Allow Clicking on the Button UI

The main reason why we can’t touch our button UI is because of ourGroup Canvascomponent attached toHUD. We disabled everything so that our UI will just be something the user sees.

We could enableInteractableandBlock Raycastsso that ourGraphics Raycasterscript will be able to detect the button being clicked, like explainedhere.

However, if we were to do that, the problem is that everything else in the game would become interactable, including our health bar!

We don’t want that, so instead we’re going to make a separateCanvas Groupcomponent just forGameOver.

We need to do 2 things:

Add a newGroup Canvascomponent and check all settings so that we can interact with our game over panel and ignore the rules set by the parents.

Add aGraphic Raycaster, which is used to detect if the user clicks on anything in the canvas.

Once we have these setup, play the game again and you’ll see that we can restart the game!

Conclusion

We did a lot of work today!

We:

Created a new animation state for our GameOver animations

Enabled our animation when our player is defeated

Allowed the user to restart the game when they lose

There are some more things that we can add to our Game Over scene to make things more complete, however, tomorrow, we’ll jump into a new topic, specifically enemy spawning!

Welcome back to day 21. In the past couple of days, we worked on creating a health system for our player and fixing some problems with our enemy that relied on it.

Now we can kill enemies and enemies can kill us, it’s time for us to make this a more complete game!

Today the first thing to do is create a game over state when the player’s health reaches 0.

To do that we’re going to use Unity’s UI system.

Here’s our game plan today. We’re going to:

Create aPanelto hold our content

Add in a retry button and a game over text

Use an animation for our panel to appear when we lose

Use our new Game Over UI into our code

Let’s get to it!

Creating the Game Over UI

The first thing we’re going to do is create the game over UI that we’ll show to the player when their health goes down to 0.

The first thing that we must do is create a newPanelUI element to our existing UI canvasHUD.We’ll call the panel UIGameOver.

Then as a child to ourGameOvergame object, we’re going to create a newTextandButtonUI element.

Let’s make some changes, first we’re going to change theGameOverscreen, the first thing we’re going to do is change theRect Transform.

Set theRect Transformto be the center of the screen withShift + Ctrlto center our panel.

Width:300

Height:150

Next, let’s change ourTextand here are the settings we want to make to our Text:

Font Size:28

Font Style:Bold

Rich Text:Selected

Text:Game Over

Color:Alpha set to 255

For theButtongame object, inside theRect Transform:

Set theRect Transformto be the center bottom of the screen withShift + Ctrlto center our Button

Width:160

Height:30

Pos Y:20

Then inside theTextgame object in theButton, we’re going to change the setting forText.

Font Style:Bold

Font Size:18

Rich Text:checked

When you’re done, we’ll have something like this:

So now that we have our Game Over pane, the next thing we need to do is to create an animation to get this to show up.

Creating the Game Over Animation

Right now, we just have a panel standing in front of the screen, if we were to play the game, it’d just be in front of us.

Just like in the Survival Shooter tutorial, let’s create an animation for our panel to show when the game is over.

To do that, we’re going to use Unity’sAnimationfeature to create a new animation for ourGame Overgame object.

If the Animation tab isn’t available, go toWindows>Animationto get the window to show up.

Select theGameOvergame object from the panel and then in theAnimationtab, clickCreate:

and you’ll be ask to create a new .animfile.

Move it to our Animation folder (or anywhere you want to store it) and name itGame Over.anim.

This will automatically create both the GameOver animation clip and the GameOver controller that we use to attach to our game object. In fact, Unity will already automatically attach the controller to our game object.

Now that we have the animation selected, the next thing we need to decide what we want our animation to look like.

Here’s what I imagine:

Our panel would be invisible and then it slowly appears (from alpha 0 to 1), as that happens, it’ll start to drop from the top of the screen to the center (let’s say from Y position 100 to 0).

Let’s make it happen!

Setting up our Game Object

The first thing we need to do is add aGroup Canvascomponent to our GameOver game object.

The reason why we need this is that we need to be able to make everything transparent in our animation the parent and the child. Just by changing the parent’s alpha will only change its transparency and not its child’s.

Luckily Unity has thought of that and that’s why among many reasonsGroup Canvasoffers an alpha property.

Creating our Animation Clip

Now that we have all the setup that we need, let’s get started creating our animation.

Now that we have all the properties we needed, we’re going to make changes to the value in the animator at different frames and Unity will fill in the values from the start to the end.

By default, for all of our properties, we’ll have akey(the grey diamonds) at the start of our animation at frame 0 and we have another key at frame 60. Each key we can set a value and Unity will automatically lerp the values from one key to the next.

Theimportantpart is that we move the white line to the specific key whose value we want to change.Changes only apply to the location of where our white line is.

For Canvas Group, at the key at frame:

0, set alpha to be 0

60, we can just leave the value at 1

For Rect Transform, at the key at frame:

0, setAnchored Position.yto be 100

60, we can just leave the value at 0

When we’re done, we should have something like this:

Finishing Touches with our new animation

Before we finish for today, there are 2 finishing touches that should be done.

The first is that we shouldn’t show our Game Over game object in the scene.

To fix this, we go back to ourCanvas Groupcomponent insideGame Paneland then we set ouralphato be0.

Next, if you were to play our scene, our animation would constantly play on repeat. We can fix this by selection ourGameOver.animinside ourAnimationfolder and unselectLoop Time.

This will be our short fix, tomorrow, we’re going to implement a state controller for our animation so we can really control how our animations.

Conclusion

That’s it for today! Tomorrow, we’ll go on and attach our animation to when the player’s health drops below 0 and other things like restarting the game is over.

Welcome back to another exciting day of Unity development at day 20! Can you believe it? We’re 1/5 of the way to 100 days! I’m hoping that the momentum will keep me going forward until I ship something!

Now looking back at day 19, we created a health system that we can now use, however we encountered a problem.

For some reason, our collider only hits the enemies once even if we go through the attack animation multiple times.

Today we’ll be solving that problem. So, let’s get to it!

Fixing Our Mesh Collider

The problem lays with the Mesh Collider that we created for our knight on Day 12. Remember this?

That standing mesh collider, is literally what we’re colliding with every time the Knight attacks us.

The problem is that Mesh Colliderscan’t follow animations of the model, as a result when the knight first touches us, the mesh might touch us, but afterwards it we, the player, don’t get pushed back by the collider, the mesh will never touch us again.

First, we create new public variables:LeftArmandRightArmthat are theFistColliderscript that we just created.

We cleaned up a lot of the code, specifically theCollisionpart and anything that had to do with it.

InAttack()we use our newFistColliderto make collisions with the player and then when the attack event from our animation gets fired, we check if the player gets hit. If they do (meaning the knights fist collided with the player), the player will take damage.

With our script done, the final thing that we need to do is to make sure that we attach theFirstColliderobject to our player.

We can directly attach our game object into the spot and the script will be automatically selected.

When you’re done, we should have something like this:

Conclusion

Phew that was a lot of work!

In these past 2 days, we created the health system and we re-visited some of the existing problems with the enemy knight’s mesh collider such as walking over them and not accurately colliding with the player.

We also separated out the code with attack collision work while still making our original collision script work as normal.

Now that we finally have health for our player, tomorrow, the next thing we’ll do is start working on an end game state for when the player’s health reaches 0.

Go toFill Areaand drag the bar until it fits into the background. In my case, my ending values arePos X: 75,Width: 160, and the rest is left to its default value.

Here’s what it looks like now:

However now we have another problem, if we were to set the value of the slider to 1, here’s what we would get:

Notice how our fill bar is on the right side of the actual bar itself.

Unfortunately, there doesn’t seem to be an answer that resolved the problem for me. This might be more of a Unity problem than anything else.

However, that doesn’t mean we’ll just give up.

We can simply fix it the same way you might never have noticed it in the Survival Shooter tutorial: we’ll expand the background of the slider so that the bar would naturally look like it’s correct.

To do that, we go toBackgroundand then changeLeft,Top, Right, and Bottomto -3

After that we should have something like this:

Much better, right? Nothing looks out of place!

Now that we have the health bar looking nicer, it’s time to move it to the bottom left corner. SelectingHealth Bar, go to theRect Transformcomponent, click on the square, to open up the UI alignments and hitShift+Ctrland left click thebottom leftoption to move everything to the bottom left corner.

Right now, everything is exactly at the bottom left corner and doesn’t look nice. Let’s add some adding.

ClickHealth Barand setPox Xto be15andPos Yto be10.

When you’re done, your game should look like this now (don’t forget to set your slider value to be 100)!

Creating our Player Health System

Creating the Player’s health

Now that we have an UI health bar, it’s time to write some code to create the player’s health.

First, click on ourPlayergame object and create a new script calledPlayerHealth.This script will control the player health whenever they get damaged and then show these changes in our health bar.

We create a public Slider that will be ourHealthBar. It’s important to note that we need to importUIotherwise the compiler would complain to us about ourSliderobject.

Next, we create a public Health to represent our max health and _currentHealth to represent how much health our player has.

In Start() we instantiate our_currentHealthto be our max health.

Finally, we create publicTakeDamage(), public meaning that another script can use this component and call the function. Inside this function, we get our damage amount and we update our_currentHealthand we change the value of our slider to reflect health loss.

Before we proceed on, make sure to drag ourHealthBargame object to ourPlayerHealthscript component.

Like so:

Writing the Damage Dealing Code

Now that we have that setup, the next thing we need to do is create the code that callsTakeDamage().

Luckily for us, we already have a script that deals with most of this:EnemyAttack.

All we need to do is grab an instance of our newPlayerHealthscript and then run the take damage code.

In our script, we already have access to the player and detection for when we attack, so all we need to do is grab ourPlayerHealthscript and trigger our TakeDamage function when we callAttack()inEnemyAttack.

As you might recall from previous tutorials, the way that this code works is that:

We have a trigger collider that detects when our knight should start attacking

Then we have a mesh collider on our player that will detect when we’re touching the player

Finally, in our animation, we set a point in time to callAttack()and when that happens, if the knight is still in contact with our player, we’ll take damage.

With this, we have everything we need to have a complete game, or you might think

If you were to play the game right now, you would have encountered a new problem that hasn’t been realized until now:

If we were to play the game while looking at our console. Whenever we bump into the knight, we would hitOnColliderEnter()get hit, and then for some reason runOnColliderExit()even if we don’t move the player.

As a result, if the player never moves, they’ll only get damaged once. How’s that for being boring?

We’ll solve this tomorrow, because it’s getting late today.

Conclusion

Today in day 19, we created a health system that allows our player to receive damage from our already existing enemy attack code.

We’re almost done, but we’ve encountered a problem where the enemy stops damaging us even when it looks like they successfully attacked us.

I have an idea of what the problem is, but we’ll talk more about that tomorrow!

We made 2 new variables MaxAmmo and _currentAmmo to represent how many bullets we have left to shoot.

InStart()we initialize our current ammo amount.

Whenever we shoot, we make sure that our ammo is above 0 otherwise we can’t shoot and then we decrement our _currentAmmo count.

When we finish reloading, we’ll restore our ammo to max.

With the code, we have one problem, while we’re shooting, if we never let go of our mouse, we’ll continue to play the shooting animation and sound effect. We need to change this.

I fixed the problem by adding another check to make sure that we stop shooting when we either let go of our mouse or when we run out of bullets.

Here’s the change we did for Update():

voidUpdate(){
_timer +=Time.deltaTime;// Create a vector at the center of our camera's viewportVector3 lineOrigin = _camera.ViewportToWorldPoint(newVector3(0.5f,0.5f,0.0f));// Draw a line in the Scene View from the point lineOrigin in the direction of fpsCam.transform.forward * weaponRange, using the color greenDebug.DrawRay(lineOrigin, _camera.transform.forward *Range,Color.green);if(Input.GetMouseButton(0)&& _timer >=ShootingDelay&&!_isReloading && _currentAmmo >0){Shoot();if(!_isShooting){TriggerShootingAnimation();}}elseif(!Input.GetMouseButton(0)|| _currentAmmo <=0){StopShooting();if(_isShooting){TriggerShootingAnimation();}}if(Input.GetKeyDown(KeyCode.R)){StartReloading();}}

Now that we have the script for reloading and shooting, we need to add our shooting mechanisms.

The first thing we’re going to do is to go to our originalHUDcomponent that we created inDay 10and create a new script calledScreenManager.

The script will be used for ammo and later score count, and in the future health. Here’s our code:

In Start() we initialize our text ammo by getting ourPlayerShootingControllerthat’s a child of our camera and using the max ammo value we set

InsideUpdateAmmoText()we give it the ammo amount to print out. I made this public, because we want to call this function from elsewhere.

There was a design decision that I was thinking of as I was going through this.

I was looking back at the score UI that was made for the Survival Shooter tutorial and it used static variables to represent the score. Static meaning you can access is anywhere, anytime without needing access to the script itself.

That worked in the context that only the manager needed to know anything about the score, however in our case, we already have our ammo amount in ourPlayerShootingController,so if that’s the case, there’s no need to keep a separate instance to keep track of our ammo amount.

Instead let’s just pass in the values that we want the text to print out.

Another benefit of this is that we don’t have to re-render our text every single time we call Update() and we only change it when we need to.

The only downside is that we must get an instance ofScreenManagerwhenever we want to make any changes as opposed to the static method that was used in the tutorial.

We made 2 new variables MaxAmmo and _currentAmmo to represent how many bullets we have left to shoot.

InStart()we initialize our current ammo amount.

Whenever we shoot, we make sure that our ammo is above 0 otherwise we can’t shoot and then we decrement our _currentAmmo count.

When we finish reloading, we’ll restore our ammo to max.

With the code, we have one problem, while we’re shooting, if we never let go of our mouse, we’ll continue to play the shooting animation and sound effect. We need to change this.

I fixed the problem by adding another check to make sure that we stop shooting when we either let go of our mouse or when we run out of bullets.

Here’s the change we did for Update():

voidUpdate(){
_timer +=Time.deltaTime;// Create a vector at the center of our camera's viewportVector3 lineOrigin = _camera.ViewportToWorldPoint(newVector3(0.5f,0.5f,0.0f));// Draw a line in the Scene View from the point lineOrigin in the direction of fpsCam.transform.forward * weaponRange, using the color greenDebug.DrawRay(lineOrigin, _camera.transform.forward *Range,Color.green);if(Input.GetMouseButton(0)&& _timer >=ShootingDelay&&!_isReloading && _currentAmmo >0){Shoot();if(!_isShooting){TriggerShootingAnimation();}}elseif(!Input.GetMouseButton(0)|| _currentAmmo <=0){StopShooting();if(_isShooting){TriggerShootingAnimation();}}if(Input.GetKeyDown(KeyCode.R)){StartReloading();}}

Now that we have the script for reloading and shooting, we need to add our shooting mechanisms.

The first thing we’re going to do is to go to our originalHUDcomponent that we created inDay 10and create a new script calledScreenManager.

The script will be used for ammo and later score count, and in the future health. Here’s our code:

In Start() we initialize our text ammo by getting ourPlayerShootingControllerthat’s a child of our camera and using the max ammo value we set

InsideUpdateAmmoText()we give it the ammo amount to print out. I made this public, because we want to call this function from elsewhere.

There was a design decision that I was thinking of as I was going through this.

I was looking back at the score UI that was made for the Survival Shooter tutorial and it used static variables to represent the score. Static meaning you can access is anywhere, anytime without needing access to the script itself.

That worked in the context that only the manager needed to know anything about the score, however in our case, we already have our ammo amount in ourPlayerShootingController,so if that’s the case, there’s no need to keep a separate instance to keep track of our ammo amount.

Instead let’s just pass in the values that we want the text to print out.

Another benefit of this is that we don’t have to re-render our text every single time we call Update() and we only change it when we need to.

The only downside is that we must get an instance ofScreenManagerwhenever we want to make any changes as opposed to the static method that was used in the tutorial.

Welcome back to day 17 of our Unity development. Previously, we switched out our cube gun with a real gun model.

Today, we’re going to look at how to make a gun shoot in Unity.

Originally our code sends a raycast directly from our camera, however I’ve learned fromScouting Ninjathat when we shoot a projectile it comes from the gun muzzle. We did something similar in the Survival Shooter back in the Unity tutorial.

Technically since we still don’t have any projectiles coming out, shooting straight from the middle is fine, but let’s say we want to shoot something out, what would we do?

Today, I’m going to make adjustments to fire a raycast from the muzzle of our gun and then go towards where we’re facing.

A lot of the things that were talked about were mentioned in the Survival Shooter tutorial and I’ve also worked on a lot of similar parts my own… wow why didn’t I just follow this tutorial….

Anyways, I’m going to cover the things I learned and added. Let’s get to it!

Debug Raycast

Have you ever wondered what your raycast would look like if you visualized it? Great! Neither have I! Glad I’m not alone on this.

The first addition I want to make is a raycast debugger that visualizes your raycast in the scene tab when you play the game.

Like so:

See the green line on the right?

To attach this, all we need to do is add a line of code:

Debug.DrawRay()

Adding that intoPlayerShootingControllerinside Update(), we’ll have something like this:

voidUpdate(){
_timer +=Time.deltaTime;// Create a vector at the center of our camera's viewportVector3 lineOrigin = _camera.ViewportToWorldPoint(newVector3(0.5f,0.5f,0.0f));// Draw a line in the Scene View from the point lineOrigin in the direction of fpsCam.transform.forward * weaponRange, using the color greenDebug.DrawRay(lineOrigin, _camera.transform.forward *Range,Color.green);// rest of code…}

The important part is that we’re using our camera to create a raycast with Debug.DrawRay()

We pass in very similar parameters as you might expect with a raycast except we don’t detect if we hit anything, instead we just create a green line.

Shooting from our Gun

If we want to shoot from the end of our gun, we need a location to start shooting from. We can’t use the gun itself, because we can’t exactly pinpoint the muzzle of the gun for something to be shot out.

We need to create something that indicates we shot something.

We’ll be using the example provided by the tutorial to make aLine Rendererto create a line from our gun muzzle to the target.

The first thing we need to do is some setup work.

Adding our Game Objects

The first thing we need to do is create the end of our gun:

I created a cube calledgunEndas a childMachingGun_00to represent the end of the gun.

I used a cube so that it’s easier showing you the end of the gun, but I’ll be removing theMesh FilterandMesh Rendererso that it’ll just be an empty object.

The position/scale might differ for you than the ones I set above, but just mess around with it in Unity until you get something satisfactory.

InMachingGun_00we’re going to create aLine Renderercomponent to represent a laser that we will shoot from our gun to our mouse click.

Make sure that theLine Rendererhas a width of0.1

With all of this in place, we’re ready to use these components for our script.

Writing the code to Fire Bullets from our Gun

We’re going to make some new additions to ourPlayerShootingControllerto use our new pieces and shoot a laser from our gun endpoint.

We have two new variablesGunEndPointand_lineRendererthat we instantiate in Start()

The wayLineRendererworks is that you set 2 points to form the line you want to make. The 1st point is at the tip of our gun. The second point depends on our raycast.

If our raycast hits something, we want to set whatever we hit as our 2nd point in our line.

If our raycast doesn’t hit anything, we just make the player’s shot go towards the center of our screen by the length of our bullet

Finally, we create a Coroutine that calls FireLine() that enables our LineRenderer component so we can it, wait about 0.05 seconds and then disable it. Doing this gives us the illusion that we’re shooting something.

After we add in our code, make sure that we drag ourgunEndgame object into the appropriate slot in our PlayerShootingController script and now we can shoot purple lasers!

Conclusion

So that’s it for Day 17.

Today we learned how to use raycast debug so that we can see where our raycast will end up and we used a line renderer to shoot lasers from the end of our gun to where the player is standing at.

It’s very exciting to say that I’ve come a long way since I first started doing the Survival Shooter tutorial, especially since I was just copying code.

Now I’m fluent enough in Unity that I can write some code myself!

With all that being said, it’s getting late so I’m going to sleep. I’ll see you all in day 18 where I’ll be setting up the UI for the game!