Turret rotation with mouse input, decoupled from tank rotation

I think the cursor functionality is so cool where it shoots where your cursor is at, but I really wanted to set my camera more over-the-shoulder and looking forward, and for that to really work I found I had to do a few things.

  • Have the camera attach to the TurretMesh instead of the RootComponent,
  • Control the Turret’s rotation with mouse X input, and
  • Make sure turning with A/D doesn’t also pull your view away (so the turret is always looking where you’re aiming, regardless of how the tank is turning).

Here it is in action:

To do this, first I needed to make the TurretMesh component in the PawnBase class accessible to the TankPawn class. That was easy enough just by moving it from private to protected.

Next, I wanted to attach the Camera Spring Arm to the TurretMesh rather than the Capsule component. Maybe I didn’t need to do that in the end, but at the time it felt like the step I needed to take, so I made the following change:

APawnTank::APawnTank()
{
	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Camera Spring Arm"));
    // Changed this from RootComponent to TurretMesh.
	SpringArm->SetupAttachment(TurretMesh);
	Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
	Camera->SetupAttachment(SpringArm);
}

Then, I needed a way to take the mouse input and use it to rotate our view. Since the camera arm is now attached to the TurretMesh, I would do:

void APawnTank::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	PlayerInputComponent->BindAxis("MoveForwardAndBack", this, &APawnTank::MoveTank);
	PlayerInputComponent->BindAxis("TurnRightAndLeft", this, &APawnTank::RotateTank);
    // Added this line for mouse input.
	PlayerInputComponent->BindAxis("RotateTurret", this, &APawnTank::RotateView);
	PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &APawnTank::Fire);
}
/// Rotate turret with mouse (alternative to aiming at cursor).
void APawnTank::RotateView(float Input)
{
	FRotator Rotation = TurretMesh->GetRelativeRotation();
    // The += is important here, otherwise my view would never move very far 
    // before snapping back to 0 once you stopped moving the mouse.
	Rotation.Yaw += Input * MouseSensitivity * GetWorld()->DeltaTimeSeconds;
	TurretMesh->SetRelativeRotation(Rotation);
}

With all of that done, finally I needed to couteract the rotation of the root component so that the turret and thus our camera wasn’t pulled along for the ride every time we hit A or D, making aiming a lot more difficult.

/// Determine rotation speed and direction on y-axis (spinning/yaw). Update RotationDirection.
void APawnTank::RotateTank(float Input)
{
	float Pitch = 0;
	float Yaw = Input * TurnSpeed * GetWorld()->DeltaTimeSeconds;
	float Roll = 0;

	FRotator Rotation = FRotator(Pitch, Yaw, Roll);
    // An exact opposite rotation with negative Yaw result.
	FRotator CounterRotation = FRotator(Pitch, -Yaw, Roll);

	RotationDirection = FQuat(Rotation);
    // Here is where the Tank actor is rotated.
	AddActorLocalRotation(RotationDirection, true);

	// And here is where we counter rotate the turret the same amount 
    // so the view stays the same.
	TurretMesh->AddLocalRotation(CounterRotation, false);
}

And that’s it! I just felt proud of myself for figuring out the fact that I needed to add that counter rotation to keep the view centered. It took me a bit lol.

I don’t know if any of this is actually explored later in the lecture but either way, hopefully this is interesting to someone.

3 Likes

I’m so proud of you! This is a lot of work. Great job problem solving :100:

1 Like

How do I get the MouseSensitivity value? in the RotateView()?

Privacy & Terms