Choosing destinations – find the nearest (or a random) spawn point

Many games make use of spawn points and waypoints. This recipe demonstrates two very common examples of spawning—the choosing of either a random spawn point, or the nearest one to an object of interest (such as the Player's character), and then the instantiation of an object at that chosen point.

Getting ready

This recipe builds upon the previous recipe. So, make a copy of this project, open it, and then follow the next steps.

How to do it...

To find a random spawn point, follow these steps:

  1. Create a Sphere sized as (1,1,1) at (2,2,2) position, and apply the m_red Material.
  2. Create a new Prefab named Prefab-ball, and drag your Sphere into it (and then delete the Sphere from the Hierarchy panel).
  3. Create a new capsule object named Capsule-spawnPoint at (3, 0.5, 3), give it the tag as Respawn (this is one of the default tags that Unity provides).

    Note

    For testing, we'll leave these Respawn points visible. For the final game, we'll then uncheck the Mesh Rendered of each Respawn GameObject, so that they are not visible to the Player.

  4. Make several copies of your Capsule-spawnPoint by moving them to different locations on the terrain.
  5. Add an instance of the following C# script class called SpawnBall to the Cube-the player GameObject:
    using UnityEngine;
    using System.Collections;
    
    public class SpawnBall : MonoBehaviour {
      public GameObject prefabBall;
      private SpawnPointManager spawnPointManager;
      private float destroyAfterDelay = 1;
      private float testFireKeyDelay = 0;
    
      void Start (){
        spawnPointManager = GetComponent<SpawnPointManager> ();
        StartCoroutine("CheckFireKeyAfterShortDelay");
      }
    
      IEnumerator CheckFireKeyAfterShortDelay () {
        while(true){
          yield return new WaitForSeconds(testFireKeyDelay);
          // having waited, now we check every frame
          testFireKeyDelay = 0; 
          CheckFireKey();
        }
      }
    
      private void CheckFireKey() {
        if(Input.GetButton("Fire1")){
          CreateSphere();
          // wait half-second before alling next spawn
          testFireKeyDelay = 0.5f;
        }
      }
    
      private void CreateSphere(){
        GameObject spawnPoint = spawnPointManager.RandomSpawnPoint ();
        GameObject newBall = (GameObject)Instantiate (prefabBall, spawnPoint.transform.position, Quaternion.identity);
        Destroy(newBall, destroyAfterDelay);
      }
    }
  6. Add an instance of the following C# script class called SpawnPointManager to the Cube-player GameObject:
    using UnityEngine;
    using System.Collections;
    
    public class SpawnPointManager : MonoBehaviour {
      private GameObject[] spawnPoints;
    
      void Start() {
        spawnPoints = GameObject.FindGameObjectsWithTag("Respawn");
      }
    
      public GameObject RandomSpawnPoint (){
        int r = Random.Range(0, spawnPoints.Length);
        return spawnPoints[r];
      }
    }
  7. Ensure that Cube-player is selected in the Inspector for the SpawnBall scripted component. Then, drag Prefab-ball over the public variable projectile called Prefab Ball.
  8. Now, run your game. When you click on the mouse (fire) button, a sphere will be instantiated randomly to one of the capsule locations.
    How to do it...

How it works...

The Capsule-spawnPoint objects represent candidate locations, where we might wish to create an instance of our ball Prefab. When our SpawnPointManager object, inside the Cube-player GameObject, receives the Start() message, the respawns GameObject array is set to the array, which is returned from the call to FindGameObjectsWithTag("Respawn"). This creates an array of all the objects in the scene with the tag called Respawn — that is, all our Capsule-spawnPoint objects.

When our SpawnBall object GameObject Cube-player receives the Start() message, it sets the spawnPointManager variable to be a reference to its sibling SpawnPointManager script component. Next, we start the coroutine method called CheckFireKeyAfterShortDelay().

The CheckFireKeyAfterShortDelay() method uses a typical Unity coroutine technique that goes into an infinite loop using a delay controlled by the value of the testFireKeyDelay variable. The delay is to make Unity wait before calling CheckFireKey() to test if the user wants a new sphere to be spawned.

Tip

Coroutines are an advanced technique, where execution inside the method can be paused, and resumed from the same point. The Yield command temporarily halts the execution of code in the method, allowing Unity to go off and execute code in the other GameObjects and undertake physics and rendering work and more. They are perfect for situations where, at regular intervals, we wish to check whether something has happened (such as testing for the Fire key, or whether a response message has been received from an Internet request and so on).

Learn more about the Unity coroutines at http://docs.unity3d.com/Manual/Coroutines.html.

The SpawnBall method CheckFireKey() tests whether, at that instant, the user is pressing the Fire button. If the Fire button is pressed, then the CreateSphere()method is called. Also, the testFireKeyDelay variable is set to 0.5. This ensures that we won't test the Fire button again for half a second.

The SpawnBall method CreateSphere()assigns variable spawnPoint to the GameObject returned by a call to the RandomSpawnpoint(…) method of our spawnPointManager. Then it creates a new instance of prefab_Ball (via the public variable) at the same position as the spawnPoint GameObject.

There's more...

There are some details that you don't want to miss.

Choosing the nearest spawn point

Rather than just choosing a random spawn point, let's search through array spawnpoints, and choose the closest one to our player.

To find the nearest spawn point, we need to do the following:

  1. Add the following method to the C# script class called SpawnPointManager:
    public GameObject NearestSpawnpoint (Vector3 source){
      GameObject nearestSpawnPoint = spawnPoints[0];
      Vector3 spawnPointPos = spawnPoints[0].transform.position;
      float shortestDistance = Vector3.Distance(source, spawnPointPos);
    
      for (int i = 1; i < spawnPoints.Length; i++){
        spawnPointPos = spawnPoints[i].transform.position;
        float newDist = Vector3.Distance(source, spawnPointPos);
        if (newDist < shortestDistance){
          shortestDistance = newDist;
          nearestSpawnPoint = spawnPoints[i];
        }
      }
    
      return nearestSpawnPoint;
    }
  2. We now need to change the first line in the C# class called SpawnBall so that the spawnPoint variable is set by a call to our new method called NearestSpawnpoint(…):
    private void CreateSphere(){
      GameObject spawnPoint = spawnPointManager.NearestSpawnpoint(transform.position);
    
      GameObject newBall = (GameObject)Instantiate (prefabBall, spawnPoint.transform.position, Quaternion.identity);
      Destroy(newBall, lifeDuration);
    }

In the NearestSpawnpoint(…) method, we set nearestSpawnpoint to the first (array index 0) GameObject in the array as our default. We then loop through the rest of the array (array index 1 up to spawnPoints.Length). For each GameObject in the array, we test to see if its distance is less than the shortest distance so far, and if it is, then we update the shortest distance, and also set nearestSpawnpoint to the current element. When the array has been searched, we return the GameObject that the nearestSpawnpoint variable refers to.

Avoiding errors due to an empty array

Let's make our code a little more robust, so that it can cope with the issue of an empty spawnPoints array—that is, when there are no objects tagged Respawn in the scene.

To cope with the no objects tagged Respawn we need to do the following:

  1. Improve our Start() method in the C# script class called SpawnPointManager, so that an ERROR is logged if the array of objects tagged Respawn is empty:
    public GameObject NearestSpawnpoint (Vector3 source){
    void Start() {
      spawnPoints = GameObject.FindGameObjectsWithTag("Respawn");
    
      // logError if array empty
      if(spawnPoints.Length < 1) Debug.LogError ("SpawnPointManagaer - cannot find any objects tagged 'Respawn'!");
    }
  2. Improve the RandomSpawnPoint() and NearestSpawnpoint()methods in the C# script class called SpawnPointManager, so that they still return a GameObject even if the array is empty:
    public GameObject RandomSpawnPoint (){
      // return current GameObject if array empty
      if(spawnPoints.Length < 1) return null;
    
    // the rest as before ...
  3. Improve the CreateSphere()method in the C# class called SpawnBall, so that we only attempt to instantiate a new GameObject if the RandomSpawnPoint() and NearestSpawnpoint()methods have returned a non-null object reference:
    private void CreateSphere(){
      GameObject spawnPoint = spawnPointManager.RandomSpawnPoint ();
    
      if(spawnPoint){
        GameObject newBall = (GameObject)Instantiate (prefabBall, spawnPoint.transform.position, Quaternion.identity);
        Destroy(newBall, destroyAfterDelay);
      }
    }

See also

  • The same techniques and code can be used for selecting spawn points or waypoints. Refer to the NPC NavMeshAgent control to follow waypoints in sequence recipe in this chapter for more information about waypoints.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.74.66