Firing objects by instantiation with forward velocity

A very common game behavior for the player (or a computer-controlled non-player-character (NPC)) is to fire a projectile in the direction they are facing. This recipe presents a way to achieve this behavior.

Although there are several steps in this recipe, there are just three script classes, and most of the work is in setting up the cube wall and red sphere projectile, as seen in the following screenshot:

Firing objects by instantiation with forward velocity

Getting ready

In the 0423_09_05 folder, you'll find a stone image texture file called stones.png shown as follows:

Getting ready

How to do it...

To fire a projectile, perform the following steps:

  1. Create a new scene and add a directional light.
  2. Create a terrain sized 10 x 10, positioned at (0, 0, 0).
  3. Create a new cube named BrickCube, sized (1, 1, 1). Apply the image texture stones.png, give it the tag brick, and add a Rigidbody component from Physics to this GameObject.
  4. Create a new Prefab named Prefab-brick, and drag BrickCube into it.
  5. Add the following C# script class to Prefab-brick:
    // file: DestroyWhenFall.cs
    using UnityEngine;
    using System.Collections;
    
    public class DestroyWhenFall : MonoBehaviour 
    {
      private const float MIN_Y = -1;
      
      void Update () {
        float y = transform.position.y;
        if( y < MIN_Y )
          Destroy( gameObject);
      }
    }
  6. Create a "pyramid wall" of bricks, by placing nine instances of BrickPrefab in the scene at the following positions:
    • bottom row: (2, 0.5, 9) (3, 0.5, 9) (4, 0.5, 9) (5, 0.5, 9) (6, 0.5, 9)
    • middle row: (3, 1.75, 9) (4, 1.75, 9) (5, 1.75, 9)
    • top row: (4, 3, 9)
  7. Create a new sphere named SphereRed, sized (1, 1, 1) with a red material, and add a Rigidbody component from Physics to this GameObject.
  8. Create a new Prefab named Prefab-sphere, and drag SphereRed into the Prefab.
  9. Delete SphereRed from the scene.
  10. Add the DestroyWhenFall script class to Prefab-sphere.
  11. Remove the Main Camera GameObject (since there is already a Main Camera inside the controller that you'll import in the next step).
  12. Import the Unity Character Controllers package, and add an instance of the First Person Controller at (5, 1, 1).
  13. Add the following C# script class to the Main Camera inside your First Person Controller (ensure Main Camera is selected in the Hierarchy panel):
    // file: GameOverManager.cs
    using UnityEngine;
    using System.Collections;
    
    public class GameOverManager : MonoBehaviour {
      private bool gameWon = false;
    
      void Update() {
        GameObject[] wallObjects = GameObject.FindGameObjectsWithTag("brick");
        int numWallObjects = wallObjects.Length;
    
        if( numWallObjects < 1 )
          gameWon = true;
      }
    
      void OnGUI() {
        if( gameWon )
          GUILayout.Label("Well Done - you have destroyed the whole wall!");
      }
    }
  14. Also add the following script class to the Main Camera inside your First Person Controller (ensure Main Camera is selected in the Hierarchy panel):
    // file: FireProjectile.cs
    using UnityEngine;
    using System.Collections;
    
    public class FireProjectile : MonoBehaviour {
      public Rigidbody projectilePrefab;
      private const float MIN_Y = -1;
      private float projectileSpeed = 15f;
    
      /** shortest time between firing */
      public const float FIRE_DELAY = 0.25f;
      private float nextFireTime = 0f;
    
      void Update() {
        if( Time.time > nextFireTime )
          CheckFireKey();
      }
    
      void CheckFireKey() {
        if( Input.GetButton("Fire1")) {
          CreateProjectile();
    
          nextFireTime = Time.time + FIRE_DELAY;
        }
      }
    
      void CreateProjectile() {
          Rigidbody projectile = (Rigidbody)Instantiate(projectilePrefab, transform.position, transform.rotation);
    
          // create and apply velocity 
        Vector3 projectileVelocity = (projectileSpeed * Vector3.forward);
          projectile.velocity = transform.TransformDirection( projectileVelocity );
      }   
    }
  15. Ensuring Main Camera inside your First Person Controller is selected, in the Inspector for the FireProjectile scripted component, drag Prefab-sphere over public variable Prefab-projectile.

How it works...

Both Prefabs (Prefab-sphere and Prefab-brick) contain rigid body components; this allows the physics engine to control instances of such objects. We can apply forces to objects with rigid bodies (for example, we can "throw" this object in a given direction), and the object will collide with and bounce off other objects. Both Prefabs also have an instance of the script class DestroyWhenFall—this adds the simple behavior that when either the red sphere or brick objects fall below the level of the terrain (Y=0), the objects will be destroyed. This prevents the scene from becoming filled up with all of the old sphere projectiles that have been fired. Also, it allows us to detect when the game is completed, that is, when all of the brick objects have been pushed off the terrain. Testing this condition is the responsibility of the GameOverManager scripted component of the Main Camera from First Person Controller. For each frame it counts the number of objects tagged with brick, and once that number is zero, the game completed message is displayed.

At the heart of this mini-game is the Main Camera scripted component FireProjectile of First Person Controller. Variable nextFireTime stores the time when the next projectile may be fired, and for each frame the current time is tested to see if that time has been reached. If it has, then the CheckFireKey()method is called. This variable is initialized to zero, so the user doesn't have to wait at all to fire the first time.

The CheckFireKey() method tests whether the user is pressing the Fire button at that instant. If the Fire button is pressed, the CreateProjectile() method is called, and the next time duration the player can fire is set to FIRE_DELAY seconds in the future.

Note

Unity input settings

The default is the left mouse button, but this can be changed in the project input settings by navigating to Edit | Project Settings | Input.

The CreateProjectile() method creates a new instance of the SpherePrefab (via the public variable Prefab-projectile) at the same position and rotation of the Main Camera of First Person Controller. Note that as the camera scripted component FireProjectile is attached to this camera, any references to transform will retrieve the transform component of that camera.

A velocity vector for the force (movement) to be applied to the sphere projectile is calculated by multiplying the forward vector with the projectileSpeed variable—this variable can be tweaked to achieve the speed of firing desired for a game. To ensure the sphere projectile is moved in the same direction the camera is facing, the TransformDirection() method of the camera's transform component is used to set the projectile's velocity property. The result is that a new sphere projectile instance is created at the camera location, and the force is applied to make it be fired in the direction the user has oriented the camera of First Person Controller.

There's more...

The following are some details you don't want to miss.

Addressing efficiency issues with FindGameObjectsWithTag()

For small games, where efficiency isn't an issue, the use of FindGameObjectsWithTag() is fine. However, when tweaking a complex game to improve efficiency, the use of this method should be avoided as it requires a search of all GameObjects in a scene and needs to test their tag names again. A more efficient approach would be to maintain a count of the number of instances that have been created, and then to ensure that this count (list) is updated each time an object is destroyed. Thus, the count (or size of the list) is all that would be needed to be tested against for our game over condition. Another alternative to FindGameObjectsWithTag() is to maintain a dynamic list of all object references. See Chapter 10, Improving Games with Extra Features and Optimization, for such optimization recipes.

See also

  • The Controlling object-to-object movements (seek, flee, follow at a distance) recipe
  • The Caching, rather than component lookups and "reflection" over objects recipe in Chapter 10, Improving Games with Extra Features and Optimization
..................Content has been hidden....................

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