Jump to content
  • entries
    44
  • comments
    19
  • views
    2315

100 Days of VR: Day 4 Going through the Unity Space Shooter Tutorial III

Josh Chang

1070 views

Here we are back on Day 4, today, we’re going to finish the Unity Space Shooter by adding the remaining UI, enemy spawning system, and then creating an end game state.

So without anymore delays, let’s get started!

Audio

To create audio for the game we need to add an Audio Source component to our GameObject.

Inside the Audio Source component, we add our music file to the AudioClip slot.

A quick and easy way to add the Audio Source component is to just drag your music file to your GameObject that you wish to add.

 
0*czY29VB4bKZR8jet.png

There are a lot of controls that we can use. The most important in this case being:

  • Play On Awake — play the sound when the GameObject that this AudioSource is connected to is created
  • Loop — repeats playing the music when it finishes
  • Volume — Self-explanatory

Adding Explosion Sounds to Asteroids

The audio samples are already all provided for us in the tutorial, so all we had to do was follow along and attach the explosion sound effects to the Explosion GameObject.

Afterwards, whenever an asteroid is destroyed the explosion sound effect gets played.

On a side note, I want to mention that I really enjoy using Unity’s Component system.

Normally you have to manually code everything, but in Unity, it’s as easy as drag and drop!

Adding Shooting Sounds to the Bullets

For the bullet’s sound effects, the video had us use an existing script to play the sound of our bullets being fired.

However I believe we could have just as easily attached a AudioSource to our bullet prefab like we did with the explosion and achieve the same thing when we instantiate the bullet.

However, this is something good to know, so let’s see the code:

using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}
public class PlayerController : MonoBehaviour
{
    public float speed;
    public float tilt;
    public Boundary boundary;
    public GameObject shot;
    public Transform shotSpawn;
    public float fireRate;
    private float nextFire;
    void Update ()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
            GetComponent<AudioSource>().Play ();
        }
    }
    void FixedUpdate ()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");
        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rigidbody.velocity = movement * speed;
        rigidbody.position = new Vector3 
        (
            Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax), 
            0.0f, 
            Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
        );
        rigidbody.rotation = Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x * -tilt);
    }
}

The only addition is the highlighted part:

GetComponent<AudioSource>().Play ();

We use GetComponent, to search for our AudioSource component through all components attached to the game object. If it finds it we get an instance of it, which we call Play(), to play the sound attached to it.

Adding Background Music

Adding the background music is straightforward.

We just attach Background music as an AudioSource component to the game controller game object and set the Audio Source component to loop.

Counting Points and Displaying the Score

GuiText vs UI

In the next section, we create a UI to show the score on the game.

The video had us create an empty game object and then attach a GUIText component to it.

As you might have recalled, back in the Roll-A-Ball tutorial, we used UI Gameobjects that created a canvas. So what’s the correct thing to do?

From my own research, GUIText is the old way Unity used to show Text, the Canvas system is the new way to implement UI’s.

One of the many benefits of using the Canvas system is so that we can anchor ourselves to specific corners as we have seen in the roll-a-ball tutorial. If we were to use GUIText, we have to manually move them ourselves.

Creating Our Score and Calling other Scripts… In Scripts!

Now that we have our GUI available, the next thing that needs to be done is to figure out how to get the Component and edit it. Luckily, if you’ve been following along, we should have an idea on how to do it!

We use GetComponent and grab the component that we attached to our GameObject!

The next question then is: which script should we attach the GameObject to? Well technically speaking, the easiest thing might be the Asteroids: DestroyByContact script, because that’s when we know we scored.

However this brings up multiple complications:

  • We would generate multiple asteroids all of which would have the same code. If were to keep track of a total score, each asteroid object would start at 0 and then when it gets destroyed, we would change our text to be 1. Every. Single. Time.
  • From my instincts of a programmer, the DestroyByContact script shouldn’t be the one in charge of keeping our scores, we need a manager of some sort that keeps track of the overall state of the game, or maybe…a controller!

And as we’ll soon see in the video, we’re right. All of the logic is added into the GameController script as you can see here:

using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour
{
    public GameObject hazard;
    public Vector3 spawnValues;
    public int hazardCount;
    public float spawnWait;
    public float startWait;
    public float waveWait;
    public GUIText scoreText;
    private int score;
    void Start ()
    {
        score = 0;
        UpdateScore ();
        StartCoroutine (SpawnWaves ());
    }
    IEnumerator SpawnWaves ()
    {
        yield return new WaitForSeconds (startWait);
        while (true)
        {
            for (int i = 0; i &lt; hazardCount; i++)
            {
                Vector3 spawnPosition = new Vector3 (Random.Range (-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
                Quaternion spawnRotation = Quaternion.identity;
                Instantiate (hazard, spawnPosition, spawnRotation);
                yield return new WaitForSeconds (spawnWait);
            }
            yield return new WaitForSeconds (waveWait);
        }
    }
    public void AddScore (int newScoreValue)
    {
        score += newScoreValue;
        UpdateScore ();
    }
    void UpdateScore ()
    {
        scoreText.text = "Score: " + score;
    }
}

What do we have here? Our GameController script keeps track of our score and we only have one instant of it so we don’t have to worry about the problem discussed above with multiple instances.

We’ll see that we attach our GUIText to the script.

We added UpdateScore() to initialize our text starting state.

But wait!

How do we update our score whenever we destroy an asteroid? We’ll soon see.

Note that we have a public void addScore()

What does it mean for a function to be public? It means that if someone has access to our Script component, they can use the function.

Looking at the DestroyByContact code, that’s exactly what’s being done!

using UnityEngine;
using System.Collections;
public class DestroyByContact : MonoBehaviour
{
    public GameObject explosion;
    public GameObject playerExplosion;
    public int scoreValue;
    private GameController gameController;
    void Start ()
    {
        GameObject gameControllerObject = GameObject.FindWithTag ("GameController");
        if (gameControllerObject != null)
        {
            gameController = gameControllerObject.GetComponent<GameController>();
        }
        if (gameController == null)
        {
            Debug.Log ("Cannot find 'GameController' script");
        }
    }
    void OnTriggerEnter(Collider other) 
    {
        if (other.tag == "Boundary")
        {
            return;
        }
        Instantiate(explosion, transform.position, transform.rotation);
        if (other.tag == "Player")
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
        }
        gameController.AddScore (scoreValue);
        Destroy(other.gameObject);
        Destroy(gameObject);
    }
}

Looking back at our DestroyByContact code, we used Start() to grab the first instance of our GameController object that exists.

We can do this by first setting the Tag: “GameController” on our GameController object.

The GameObject class that we use, just like the Math library, contain static functions that are available to it, meaning we can use them anytime we want.

In this case: FindWithTag() is a static function avialble to use that helps us search for the GameObject with the tag “GameController”

FindWithTag() returns the GameObject if it finds it, otherwise it returns a null object. That’s why we have to first check if the object we get back is null or not, because if we try to do anything with a null object, our game will crash.

Once we’re sure that our GameObject isn’t null, we do the next thing: grabbing the Script Component attached to it.

Once we initialized our gameController variable, we can directly call our public function AddScore() updating our total score for destroying the enemy ship. Fantastic!

Now whenever a ship blows up, we update our points!

Ending the Game

We made it to the end! We have:

  • Our player ship
  • Enemy asteroids being spawned
  • Destruction effects
  • Sound effects
  • UI

There’s only 1 thing left before this tutorial is finished and that’s making the game finish state.

To do this, we created:

  • Two more GUIText labels: the game over message and the restart instructions
  • A Boolean to tell us if the game is over or not

First looking at the GameController script:

using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour
{
    public GameObject hazard;
    public Vector3 spawnValues;
    public int hazardCount;
    public float spawnWait;
    public float startWait;
    public float waveWait;
    public GUIText scoreText;
    public GUIText restartText;
    public GUIText gameOverText;
    private bool gameOver;
    private bool restart;
    private int score;
    void Start ()
    {
        gameOver = false;
        restart = false;
        restartText.text = "";
        gameOverText.text = "";
        score = 0;
        UpdateScore ();
        StartCoroutine (SpawnWaves ());
    }
    void Update ()
    {
        if (restart)
        {
            if (Input.GetKeyDown (KeyCode.R))
            {
                SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
            }
        }
    }
    IEnumerator SpawnWaves ()
    {
        yield return new WaitForSeconds (startWait);
        while (true)
        {
            for (int i = 0; i &lt; hazardCount; i++)
            {
                Vector3 spawnPosition = new Vector3 (Random.Range (-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
                Quaternion spawnRotation = Quaternion.identity;
                Instantiate (hazard, spawnPosition, spawnRotation);
                yield return new WaitForSeconds (spawnWait);
            }
            yield return new WaitForSeconds (waveWait);
            if (gameOver)
            {
                restartText.text = "Press 'R' for Restart";
                restart = true;
                break;
            }
        }
    }
    public void AddScore (int newScoreValue)
    {
        score += newScoreValue;
        UpdateScore ();
    }
    void UpdateScore ()
    {
        scoreText.text = "Score: " + score;
    }
     public void GameOver ()
    {
        gameOverText.text = "Game Over!";
        gameOver = true;
    }
}

Creating our new variables

The first thing you can see is that we created our GUIText objects and Booleans to allow us to check if the game is over or not.

We initialize these new variables in Start().

Creating the restart options

To restart a game, we have to capture a button press input. To do this, we have to put all of our user input code inside the Update function. That’s the only function that runs continuously allowing us to make these checks.

In our Update() function, we check to see if we can reset and if we are, if the user presses R, we would reload the whole application.

I’m sure we’ll see more about the SceneManager in the future, but as you recall, we work in scenes for our games in Unity. What this means is that in the future we might have games with multiple scenes that we can switch between.

In the tutorial, we use Application, but that’s the depreciated version. We now use the SceneManager.

Creating the Game Over state

Just like when we created AddScore(). Our GameController doesn’t know when the game is over. We have to have an external source tell this to us. That’s why we made our GameOver() public.

Inside the function, we set the GameOver text to say game over and set our gameOver flag to be true. But that doesn’t immediately end our game yet!

If you notice in the spawn enemy code, we don’t ever stop creating new enemies, even when it’s game over! We fix that with this:

if (gameOver)
            {
                restartText.text = "Press 'R' for Restart";
                restart = true;
                break;
            }

What this does is that we add our restart instruction and enter into the restart state, which means we can start detecting when the user presses R in Update()

We also break out of while loop so we won’t continue spawning asteroids forever.

The next part…

So great, we added GameOver to our GameController script, but where do we call it?

Inside the DestroyByContact script! Specifically when our ship blows up.

using UnityEngine;
using System.Collections;
public class DestroyByContact : MonoBehaviour
{
    public GameObject explosion;
    public GameObject playerExplosion;
    public int scoreValue;
    private GameController gameController;
    void Start ()
    {
        GameObject gameControllerObject = GameObject.FindWithTag ("GameController");
        if (gameControllerObject != null)
        {
            gameController = gameControllerObject.GetComponent<GameController>();
        }
        if (gameController == null)
        {
            Debug.Log ("Cannot find 'GameController' script");
        }
    }
    void OnTriggerEnter(Collider other) 
    {
        if (other.tag == "Boundary")
        {
            return;
        }
        Instantiate(explosion, transform.position, transform.rotation);
        if (other.tag == "Player")
        {
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
            gameController.GameOver ();
        }
        gameController.AddScore (scoreValue);
        Destroy(other.gameObject);
        Destroy(gameObject);
    }
}

We already have the gameController script component so all we need to do is call GameOver!

And there we go! Now we can have a game over state and restart to the beginning!

Conclusion

Phew, this was a long post for the day! I’m seriously re-considering writing everything. It’s starting to take longer than the actual learning, re-watching, and then implementing!

On the brightside however, I have definelty learned more than I normally would since I have to understand what I’m blogging about!

Also, I think things will be a lot easier once I start working on my own projects and deviate from these long “what I learned from these tutorials posts”.

Anyways, we started 3 days ago with close to nothing in knowledge and we’re now one step closer to making a VR game:

  • Setting up an environment
  • Creating the player
  • Spawning enemies
  • Destroying/Creating objects
  • Creating UI
  • Detecting user button presses
  • Accessing other objects from your script
  • And I’m sure many more!

I’m going to skip the last few modules and enhancing the game and go straight to the next tutorial.

I think this will be the last tutorial before I start messing around with creating a simple game. Until then!

Find the original Day 4 here.

Or visit the 100 days of VR main page.



2 Comments


Recommended Comments

One advantage of using the GUI over the UI in unity is less updates. If you use the Unity UI and change any UI element it will cause the whole UI to update and 3-4 times per frame.

So for example if you used the UI to change a text to create a kind of score increasing animation that you update just using the Uptade() function, you will still get pauses as the UI updates.

With the GUI you have more control over each update, that is why it looks like it's going to be around for some time; at least till the mobiles upgrade. 

Share this comment


Link to comment

That's really great to know! I guess there's a reason why GUI hasn't been deprecated yet.

Thanks for the info Scouting Ninja!

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By Aileen Chen
      2019 Asia VR&AR Fair&Summit (VR&AR Fair 2019)
      Date: May 9-11, 2019
      Venue: China Import&Export Fair Complex, Guangzhou, China
      Address: No. 382, Yuejiang Zhong Road, Guangzhou, China
      Website: http://www.vrarfair.com/index.php?lang=en
      Hosted by: Guangdong Grandeur International Exhibition Group
      Co-organized by: Guangzhou Virtual Reality Industry Association, Guangdong VR Industry Alliance, Shenzhen Virtual Reality Industry Federation, Shenzhen Virtual Reality Industry Association
       
      Overview
      As a thematic exhibition of China Guangzhou International Leisure & Recreation Expo (GILE),VR&AR Fair has been successfully held for two consecutive years (Twice a year, Guangzhou and Wuhan), becoming the One and Only Professional Demonstration and Trade Platform of the VR&AR Industry in Guangzhou. Over the years, we has gathered numbers of famous companies at home and abroad to display their latest products and technologies at VR&AR Fair, including JD.COM, 3Glassess, DP VR, Leke VR, PiXYZ Software (France), ICAROS(Germany), Ai6tech (Taiwan), VRway, Hirain, Royole, Super Captain, TPCAST VR, Shenlinqijing, Foldspace, NineD, TPcast, etc. 
       
      VR&AR Fair 2019 is estimated to host over 250 exhibitors and co-located with 2019 Asia Amusement&Attractions Expo (AAA 2019) on a show floor of 10,0000 sq.m, which are going to cover sectors like VR helmet accessories, VR all-in-one machine, interactive multimedia products, immersive games and devices, immersive digital cinemas, virtual walk-through products, auto stereoscopic (glasses-free), 3D-9D cinema devices, multi-touch devices, AR equipment, AR game, environment modeling technology, realistic sensor (real-time) rendering technology, amusement equipment, Theme Park Facilities,etc. 
       
      Concurrent event:
      2019 Asia Science Museum & Exhibition Hall Facilities Expo
      2019 Asia Multimedia Technology & Interactive Projection Expo
      The 10th Asia Theater & Filming Equipment Fair 2019
      2019 Asia Amusement & Attraction Expo
       
      Why VR&AR Fair 2019?
      1. Win face-to-face business opportunities
      2. Seek professional buyers
      3. Come into contact with business decision makers
      4. Maintain existing clients and find new clients
      5. Effectively improve brand image & awareness
      6. Accurately position your company and brand
      7. Grab market shares
      8. Get access to the latest industrial development
      9. Vigorously expand product and service scope
      10. Establish extensive agent and investment attraction network
       
       
      Discover Your Opportunity at VR&AR Fair 2019!
      Aileen Chen
      Director of Guangdong Grandeur International Exhibition Group
      Tel: 82-20-29806525  
      Fax: 86-20-82579220 
      Mobile/Wechant: 15089702986
      E-mail: grand.fi@grahw.com 
       
       
       
    • By Liam Weston
      Greetings fellow gaming enthusiasts!
      I am the Product Manager for a VR game called Funny Farm VR.
      https://www.funnyfarmvr.com/
      We have already have a proof of concept build of the game that is published on iOS, Android, Oculus Rift, Oculus Go, GearVR, Pico and Niburu platforms. Not a bad start eh? 
      It's a 3D VR game built on the Unity platform. We are now looking to further develop the game concept and build on what we have to achieve the following:
      Introduce a game economy (power ups / rewards / currency) Add more levels We need 1 or 2 developers with experience in developing on the Unity 3D platform as well as a 3D artist and animator.
      This is a great opportunity to work on a fun game and inject your own ideas / personality into it.
      If you're interested in getting involved drop me a message and I'll get in touch.
      Looking forward to putting a team together!
      Liam
       
    • By Questengine
      I made a VR game for Oculus Rift.  It feature complete, but of course some assets are incomplete and balance needs work.  If you have an Oculus Rift and you're looking for something to do with it, you could do worse!
      It's a puzzle/resource management game disguised as a space shooter.  You're in a space ship cockpit and the guns are shooting the aliens, but that's all automatic and passive.  You just maintain ammo supplies, manage heat, recover loot and keep the ship shielded.  There's lots of switches and do-dads scattered all around the cockpit to keep you busy. It's very VR friendly: you have a mostly stationary cockpit, lots of reasons to turn and look and use you motion controllers, and games can be very short.
      This beta is very configurable too.  There's a plain old text file where you can change lots of the static values the game uses.  I'd actually like help balancing everything to be honest as it's a bit complicated.  
      If you have an Oculus Rift I hope you'll take a look.  
      http://www.questengine.com/vercs


    • By OfficePassenger
      Dear frends, I would like to try myself as a composer for games, I make unusual music with an unusual mood and atmosphere, I like to work on sounds and details in music, below I attach examples of my works, [deleted by moderator]
      1.mp3
      2.mp3
      3.mp3
      4.mp3
      5.mp3
      6.mp3
      7.mp3
      8.mp3
    • By sdef Lab
      Hi,
      We have designed VR project with a large area database and when we are making our aircraft to move around the scene , we are facing a spatial jittering when trying to move far from origin
      any solution???.
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!