Unity C# Tutorial | Turret Control

Unity C# Turret control tutorial.

In this tutorial, we will build a basic turret controller, This tutorial is made with Unity 2021.3.7f1. You can use any version you want. It’s going to be a turret control system that can be controlled from our camera view.

Content

  • Camera control script
  • Turret control script

Camera control script


We’ll create a simple orbiting camera to follow our turret in third-person mode. Create a CameraController.cs script and put it in the main camera gameobject. Let’s declare some variables.

Orbiting camera.
Main Camera with the Camera Controller component.
  • turretControl: turret control script. This line is going to create an error, but don't worry it will be gone when we construct the TurretControl.cs in a bit
  • targetPos: vector3 target position. The position where the turret is going to aim
  • sensitivity: mouse sensitivity
  • turretParent: the turret parent game object
  • distance: distance value from the barrel
  • heightPosition: height value from the barrel
  • smoothTime: smooth value for camera movement, A smaller value will make the transition faster.

  • rightRotationLimit: right rotation limit value in degree
  • leftRotationLimit: left rotation limit value in degree
  • upwardRotationLimit: upward rotation limit value in degree
  • downwardRotationLimit: downward rotation limit value in degree

  • rotationX: to store the player’s horizontal input value
  • rotationY: to store the player’s vertical input value
  • currentRotation: to store the current rotation value
  • velovity: vector3 value to store velocity value when smoothing the rotation

Start, Update & LateUpdate methods

In the Start method, we want to lock the cursor by setting the cursor lock state with this code, Cursor.lockState = CursorLockMode.Locked, when Locked, the cursor is placed in the center of the view and cannot be moved. The cursor is invisible in this state.

Then we create the Update method to call SetAim and pass the targetPos value to turretControl.csscript. This line is going to create an error as well, it will be gone when we construct the TurretControl.cs and SetAim soon. We use Update method because when the target is moving, its position will get updated every frame.

We use the LateUpdate method to call the CameraMovement which we are going to implement in a bit. LateUpdate is called after all Update functions have been called. This is useful to order script execution. For example, a follow camera should always be implemented in LateUpdate because it tracks objects that might have moved inside Update.

CameraMovement method

This is the method where all of the mechanics happen to the camera.

  • targetPos = transform.TransformPoint(Vector3.foward * 200.0f); : we set the target position 200 units away from the camera’s forward position(z-axis). We transform the camera's local forward position to world space and then assign it to the targetPos . This is because our turret is going to aim targetPos from world space, not from the camera’s forward local position.
  • float axisX = Input.GetAxis(“Mouse X”) * sensitivity; : Get the player’s horizontal input and multiply the input with the sensitivity value
  • float axisY = -Input.GetAxis(“Mouse Y”) * sensitivity; : Get the player’s vertical input and multiply the input with the sensitivity value. We have to make the value negative since the mouse input is inversed
  • rotationX += axisX; & rotationY += axisY; : Accumulate the input value. Otherwise, the values will get reset to 0 when the player stops moving the mouse thus resetting the camera’s rotation.
  • rotationX = Mathf.Clamp(rotationX,-rightRotationLimit,leftRotationLimit); : Limit the horizontal rotation with Mathf.Clamp() function
  • rotationY = Mathf.Clamp(rotationY, -upwardRotationLimit, downwardRotationLimit); : Limit the vertical rotation with Mathf.Clamp() function
  • Vector3 newRotation = new Vector3(rotationY, rotationX,0); : Create a new vector3 newRotation variable to store both vertical & horizontal values in it
  • currentRotation = Vector3.SmoothDamp(currentRotation, newRotation, ref velocity, smoothTime); : Smooth the rotation from currentRotation to newRotation with Vector3.SmoothDamp() function
  • transform.localEulerAngles = currentRotation; : Apply the rotation value to this game object's rotation which is the camera
  • transform.position = (turretParent.position + transform.up * heightPosition) — transform.forward * distance; : Set the camera’s forward position away from the turret parent position with distance & heightPosition values

OnDrawGizmos method

We want to implement gizmos to draw spheres as a crosshair in our game view so we use OnDrawGizmos and DrawWireSphere to do that. Don't forget to turn the Gizmos on in the Game tab.

Turret control script


Now we’ve got the camera working. It’s time to work on our turret and for that, we have to construct a TurretControl.cs to get the turret working. Put this script in the Turret Parent game object.

Turret control script in action.
Turret Control component in Turret Parent gameobject which is SM_Veh_German_Tank_01.

Before we begin, our turret game object hierarchy should be like this: Turret Parrent 👉 Turret Base 👉 Turret Barrel.

  • turretBase : turret base game object
  • turretBarrel: turret barrel game object

  • rightRotationLimit : turret right rotation limit value in degree
  • leftRotationLimit : turret left rotation limit value in degree
  • elevationRotationLimit : turret barrel upward rotation limit value in degree
  • depressionRotationLimit : turret barrel downward rotation limit value in degree
  • turnSpeed : turret turning speed
  • aimPoint : turret aim point

Set Aim & Update method

The SetAim will be called from CameraController.cs and aimPoint will be set here.

In Update method we call HorizontalRotation & VerticalRotation . These methods handle horizontal and vertical calculations respectively.

HorizontalRotation method

  • Vector3 targetPositionInLocalSpace = transform.InverseTransformPoint(aimPoint); : Get aimPointin Turret Parrent’s local space in relation to aimPoint ‘s world space and store it in targetPositionInLocalSpace
  • targetPositionInLocalSpace.y = 0.0f; : Set targetPositionInLocalSpace Y position to zero, since this is horizontal rotation and because we don't need it
  • Vector3 limitedRotation = targetPositionInLocalSpace; : Store limit value of the rotation
if(targetPositionInLocalSpace.x >= 0.0f)
limitedRotation = Vector3.RotateTowards(Vector3.forward, targetPositionInLocalSpace, Mathf.Deg2Rad * rightRotationLimit, float.MaxValue);
else
limitedRotation = Vector3.RotateTowards(Vector3.forward, targetPositionInLocalSpace, Mathf.Deg2Rad * leftRotationLimit, float.MaxValue);

Limit turret horizontal rotation according to its rotation limit.

  • Quaternion whereToRotate = Quaternion.LookRotation(limitedRotation); : Get direction to the aim point and store it in whereToRotate
  • turretBase.localRotation = Quaternion.RotateTowards(turretBase.localRotation, whereToRotate, TurnSpeed * Time.deltaTime); : Rotate the Turret Base

VerticalRotation method

This method is actually similar to HorizontalRotation. These are the lines where they are different.

  • Vector3 targetPositionInLocalSpace = turretBase.InverseTransformPoint(aimPoint); : Instead of getting the aimPoint from Turret Parrent’s local space we get it from Turret Base’s local space because Turret Base is Turret Barrel’s parent.

OnDrawGizmos method

Here in OnDrawGizmos we draw a line 200 units long from Turret Barrel’s forward position(z-axis). This way we can figure out where the turret aims.

Don't forget to assign game objects in Inspector
Assign Turret Parent & Turret Control in Camera Control script.

Assign Turret Base & Turret Barrel in Turret Control script.
Final result.


That’s the end of the tutorial, Cheers!