Home / Legacy / Lesson 6: Creating the Targets

Lesson 6: Creating the Targets

/
/
/
24 Views
This entry is part 8 of 15 in the series Unity 3d Fundamentals Course

In this lesson we are going to learn how to create the moving targets for our game.

Lesson Outline

Create the Target Object

First we need to create the Target GameObject. Please download the following resources or use your own. [wpdm_file id=1] [wpdm_file id=2]

Import the resources into the project and adjust the import settings.

Lesson 6: Creating the Targets

Lesson 6: Creating the Targets

Create a target on your scene and apply the texture.

Lesson 6: Creating the Targets

The Target Object will be able to Move, Rotate and Detect Collisions. Although we could use a single script to deal with all these behaviours, we will create different scripts for each one.

Using the Component Based approach it is always a better choice to have several components, each one responsible for a specific behaviour. In this way we can attach each component only when needed and create more diversity in our targets.

Add Movement

First create a script responsible for moving the targets – TargetMovement.cs.

There are several ways to make an object move. I always prefer the ones that emphasize using the Unity Editor Interface. So first we will create a target duplicate and place it where we want our original target to move. Then we tell the original Target to move into the duplicate position.

So first create the target duplicate and position it near the original one.

Lesson 6: Creating the Targets

Open the TargetMovement script and add a public reference to the duplicate target.

To move the original Target to the duplicate position we will use the Vector3.MoveTowards method.

static Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta);

  • Returns a Vector3 – the current position of the object that is moving;
  • Vector3 current – the position of the object we want to move;
  • Vector3 target – the position where we want to move the object;
  • float maxDistanceDelta – the maximum distance the object will move from its current position to the target position on the given frame this method is called;

This method moves an object in a straight line towards a target position by the specified number of units. When the number of units is greater than the distance between the object and the target, it returns the target position.

So to make the object reach the target position we need to run this method several times in a loop function such as the Update method. Each frame the method is called, the current object will move a specified number of units towards the target until reaching its position.

Add this code to the TargetMovement script.

Add the WeaponMovement script to your current target and assign the duplicate. Enter Play Mode.

Lesson 6: Creating the Targets

What happens is that the target moves really quick to the target position.

Why is the target moving so quickly?

Well this method is being called in the Update meaning it’s called once per frame. Since the game is running at approximately 70FPS (at least in my machine) and we are moving the target 1 unit each time the method is called, in one second the target could move 70 units. Since the distance between the targets is really small, in my case around 10 units, the target reaches the end position almost instantly.

To solve this we need to make the movement frame rate independent. Instead of relying on how many units the target should move per frame, we need to define how many units to move per second. To do we need to multiply the value by the Time.deltaTime.

The Time.deltaTime is equal to the number of seconds it took to finish the last frame. By multiplying our value by this one we get the speed of the object in units per seconds. So change the code to this.

Now the object should move really smooth until reaching the target position. Let’s put this value on a public variable to control from the inspector.

Now that we’ve got our object moving we want to make it go back and forth between its initial position and the duplicate position. To make it return to the original position we need to hold a reference to the object position before moving. We can do this in the Awake method since it’s the first method being called on the Monobehaviour lifecycle.

We want to tell the object to return to the initial position as soon as he reaches the duplicate. This happens when its position is equal to the duplicate position. So we can check this in the Update method right after running the MoveTowards method.

Now we reached a problem in our script. When the object reaches the duplicate we want to send it back to its initial position. So basically we need to run the MoveTowards method again but instead of the target parameter being the duplicate position it should be the initial position of the object. But in our script the target parameter of the MoveTowards method is statically typed to be the duplicate position when it should be able to change dynamically. We need to replace it with a new variable.

So create a new variable to hold the current destination to where the object should be moving.

This variable will change whether the object is moving towards the duplicate or the initial position. At the beginning the destination is the duplicate position. Assign it in the Awake method.

Now change the MoveTowards method to use this new variable and also the method where you verify if the object reached the destination.

When the object reaches the destination, the variable should change to hold the object initial position.

Assign the public references and test it.

Lesson 6: Creating the Targets

The object is now able to go and return to its initial position but it stops when it’s returning. This is because we are always changing the current destination to the initial position.

When the object is moving to the duplicate it works, but when it reaches the initial position the current destination remains the same and so the object does not move.

In order to change this, we need to understand if the object is moving forth or back between the positions. We could use a boolean variable to control the movement direction but I prefer to work with the variables we already have.

So we just need to check what is the current destination and replacing it with the other one. If the current destination equals the duplicate position it should go back to the initial position, and vice-versa.

With this last fix the object is now looping between both positions.

Disable the Mesh Renderer and the Mesh Collider on the duplicate object.

Add Rotation

The method to add rotation to our target is quite similar to the one used for the movement but instead of looping between two different positions, the target will rotate between two different angles.

First we will need to change our object’s pivot. We want the target to rotate around its bottom and not its center.

Lesson 6: Creating the Targets

Since the 3d model was exported with the pivot in the center, we need to create a root object for place where we want to rotate it – its bottom. Create a new empty GameObject, place it on the target bottom and parent the target.

Lesson 6: Creating the Targets

The root object will be the one rotating. Create a TargetRotation script and apply it to the root object.

Lesson 6: Creating the Targets

To rotate the object we will use the Quaternion.RotateTowards method, the equivalent to the one used for the movement.

static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta);

  • Returns a Quaternion – the current rotation of the object (rotations are stored in quaternions);
  • Quaternion from – the rotation of the object;
  • Quaternion to – the rotation we want to apply;
  • float maxDeegresDelta – the maximum angles the object will rotate on the given frame this method is called;

Since Quaternions are based in complex numbers it’s quite difficult to work directly with them. So we have two methods to get and set the degrees in a Quaternion: Quaternion.Euler and Quaternion.eulerAngles.

  • staticQuaternion .Euler(Vector3 euler) – creates a rotation with the specified angles;
  • Vector3 Quaternion.eulerAngles – returns the angle representation of the rotation in a well known Vector3;

Similar to the movement script we need a public reference to indicate how to rotate the object. In this case we won’t use a reference object but instead we will manually input the degrees. So create a public reference to insert the angles to rotate;

We will also need a target rotation to switch between rotating back and forth. So create a private reference to the target rotation;

Next we need to assign the initial value of the target rotation. It will be equal to the current rotation of the object plus the defined angles. If we only use the angles as the target rotation, the object will rotate to reach that value independently of its initial rotation.

This value should be assigned at the beginning of the script, in the Awake method.

The * operator is used to combine two rotations together.

In the Update method we add the Quaternion.RotateTowards method using the declared variables.

Test the script. Try to rotate your script 90 degrees on the X axis.

Change the movement script to the root object also.

Lesson 6: Creating the Targets

Now the object is rotating 90 degrees on the defined axis but it’s really slow. This is because since we’ve not defined a speed variable, the object is rotating 1 degree per second, so it would take 90 seconds to reach the desired rotation.

Declare a public variable to hold the speed and add it to the RotateTowards method.

Very similar to the movement script we want our target to loop between its initial rotation and the defined one. So we need a variable to hold the initial rotation and assign its value on the Awake method.

The process to make it loop between the rotations is the same as in the movement script. On the Update method we will check when the object rotation is equal to the target and then replace the target rotation value.

Add Collision

I will keep the collision script really simple and just destroy the object when it receives a hit. We are not going to work with triggers or real collisions since all the shooting is done through raycasting. So we just need a public method to destroy the object when the raycast hits the collider.

Create a new script called TargetCollision and apply it to the target object –NOT TO THE ROOT – this script needs to be applied to the object with the collider.

Lesson 6: Creating the Targets

In this script add a public method with no return value or parameters.

Now I don’t want to destroy the object right away. I want it to rotate back to the initial rotation and then destroy itself. To do this we need to modify our TargetRotation script.

So add a public method with no return value or parameters on the TargetRotation script.

In this script we will tell the object to rotate back to the initial rotation. But since each time it reaches the target rotation it rotates back, we need a control variable to know if we should destroy the object or if it should continue its movement.

Create a private variable to control if the object is being destroyed.

Now this variable will be used when the target reaches the defined rotation. If isToDestroy we should destroy the object otherwise continue with the movement.

Add this verification on the Update method.

On the public method created we need to change this variable to true and change the target rotation to the initial one.

Now that our TargetRotation method has the method ready call it from the TargetCollider. But this script it’s not applied to the same object as the collider. Since it is applied to the parent object we need to get the component on the parent object using the GetComponentInParent.

Before testing we need to setup our object as it will be in the game. The target will start on the floor and then it will rotate 90 degrees up.

Lesson 6: Creating the Targets

Now test the collision method using the Mailman extension.

If you don’t have the Mailman editor extension, check our Lesson 02: Create the First Person Weapon.

Now the destroy method is working but the rotation speed is really slow for an object that was just hit. We want the rotation to happen almost immediately so we need to change the speed value on the method in the TargetRotation script.

To finish we also need to disable the target movement when hit. Access the component in the same way we did for the rotation and disable it.

Add the following code to the method Hit on the TargetMovement script.

Full Code

<TargetMovement>

 <TargetRotation>

 <TargetCollision>

Names to keep

What’s next…

Our targets are now able to move, rotate and being destroyed. However, their behaviour is completely automatic.

In the next lesson we will take some control over these behaviours.

Series Navigation<< Lesson 5: Particle Systems and AudioVideo Add-ons: Fixing the Player Weapon >>

5 Comments

  1. I keep getting this from the collider script? It applys to all of the uses of GetComponentInParent ?

    The type arguments for method `UnityEngine.Component.GetComponentInParent()’ cannot be inferred from the usage. Try specifying the type arguments explicitly

    Any idea why?

Leave a Comment

Your email address will not be published. Required fields are marked *