Tank move and barrel mouse follow script
- Get state of input keys (we will use WASD for moving).
- Get position of the mouse.
- Add force to the tank according to the W (forward) and S (backward) key.
- Add rotation to the tank according to A (left) and D (right) key.
- Calculate the rotation to rotate the barrel so it targets the mouse.
The following script is used to get the tank rollin’:
using UnityEngine;
using System.Collections;
public class PlayerTank : MonoBehaviour {
/// <summary>
/// The maximum speed the tank can achieve
/// </summary>
public float Speed = 80f;
/// <summary>
/// Maximum turn speed of the tank
/// </summary>
public float TurnSpeed = 1.3f;
/// <summary>
/// A game object which represents the barrel. It is used for the mouse follow function
/// </summary>
public GameObject Cannon;
/// <summary>
/// Maximum turn rate of the cannon
/// </summary>
public float CannonTurnRate = 30f;
/// <summary>
/// Holds the current "power input" (-1..1). -1 Backwards, +1 forwards (S and W key)
/// </summary>
private float _powerInput;
/// <summary>
/// Holds the current "turn input" (-1..1). -1 turn left, +1 turn right (A and D key)
/// </summary>
private float _turnInput;
/// <summary>
/// Holds the current mouse position
/// </summary>
private Vector3 _mousePosition;
/// <summary>
/// The actual tank body where the force and rotation will be applied
/// </summary>
private Rigidbody2D _tankBody;
public void Awake()
{
// Get the rigidbody
_tankBody = GetComponent<Rigidbody2D> ();
// If no cannon is assigned we throw an error. This means using a cannon is mandatory
if (Cannon == null) {
throw new MissingReferenceException("No cannon attached!");
}
}
public void Update()
{
// The input of W and S key
_powerInput = Input.GetAxis ("Vertical");
// The input of A and D key
_turnInput = Input.GetAxis ("Horizontal");
// Current mouse position
_mousePosition = Input.mousePosition;
}
public void FixedUpdate()
{
MoveTank ();
CannonMouseFollow ();
}
private void MoveTank()
{
// Add a relative force according to power input and speed settings
_tankBody.AddRelativeForce (new Vector2 (0f, _powerInput * Speed));
// rotate the tank according to the turn input and turn speed
_tankBody.MoveRotation (_tankBody.rotation - _turnInput * TurnSpeed);
}
private void CannonMouseFollow()
{
// Translate the mouse position into a point in the world
var position = Camera.main.ScreenToWorldPoint (_mousePosition);
// Calculate the rotation to make the cannon look at the mouse position
var rotation = Quaternion.LookRotation (position - Cannon.transform.position, Vector3.back);
// Since we don't want to do a instant rotation, calculate the rotation towards the target rotation according to the turn state
var targetRotation = Quaternion.RotateTowards (Cannon.transform.rotation, rotation, CannonTurnRate);
// Assign the new rotation to the cannon
Cannon.transform.rotation = new Quaternion (0, 0, targetRotation.z, targetRotation.w);
}
}
What is happening here? At first some public fields are declared. Those fields are visible within the editor and we can manipulate them from other scripts, too.
PlayerTank script
It allows me to use this script for other tanks and change the values to get different behaviors (think of another type of tank for example). The public fields are:
Speed
: Maximum speed of the tank (more a maximum applied force to the tank to make it move).TurnSpeed
: Maximum turn rate of the whole tank.Cannon
: The GameObject which is used as a cannon. We will use it for rotating the cannon individually and let it follow the mouse.CannonTurnRate
: The turn rate of the cannon when following the mouse. It simulates an inertia to not move instantly around.
The next fields are some private fields needed to save the input values and the actual tank body, where all forces (speed and turn rate) will be applied to:
_powerInput
: A float value from -1 to 1 of the vertical input axis. In our case this is W (_powerInput = 1
) and S (_powerInput = -1
). The value is a float since a controller could be used for input allowing all other values between -1 and 1._turnInput
: A float value from -1 to 1 of the horizontal input axis. In out case this is A (_turnInput = -1
) and D (_turnInput = 1
)._mousePosition
: The position of the mouse on the screen. Be careful: The mouse does not have to be within the game window when running the game to get the position. Its input is always grabbed regardless where the mouse is – as long as the game window has focus._tankBody
: A Rigidbody2D, where our forces will be applied.
Fields setup done! Let’s move on to the implemented methods:
Awake
: Called when the GameObject gets instantiated and initializes the_tankBody
field. It also checks if aCannon
has been set, otherwise it will throw an exception making the cannon a mandatory object for the script.Update
: Is called on every frame and sets the input fields to the most recent values from the input axis.FixedUpdate
: Is called every fixed framerate frame. It should always be used when using a RigidBody component. In this case we call the two following methods:MoveTank
: This method simply adds a force to the_tankBody
in order to make it move. Therefor the input state is multiplied with the speed.CannonMouseFollow
: This method is used for making the cannon follow the mouse. At first we will translate the mouse coordinates to world coordinates by using theCamera.main.ScreenToWorldPoint()
-method. Then we useQuaternion.LookRotation()
to calculate where we need to rotate the cannon to, to let it look in direction of the mouse. Since we don’t want to rotate the cannon instantly, we useQuaternion.RotateTowards()
. Its third parameter is a delta of how much degree rotation per fixed update is allowed. We use theCannonTurnRate
to limit this. In the default settings we allow a maximum of 30 degrees rotation per fixed update.
That’s the magic behind the move and follow script. :-) I’m pretty sure it can be tweaked and optimized in much ways, but for a first start, I’m really happy with tank’s behavior.
Check out the full source code at GitHub and a running sample.