RTS Camera

From Epic Wiki

Details

RTSCamera.png

I will add more details to this page when I have time, just thought I would share my code for an RTS Camera for those of you who need a place to start! Let me know if you have any questions or suggestions to improve my code! The cursor is not locked into the viewport, I am still looking for a way to do this.


Also, for zooming with the mouse wheel, add the inputs for mouse wheel up and down with the names:

WheelMouseUp WheelMouseDown

Hope this helps!

Author: Connor Brewster

FESpectatorPawn.h

#pragma once

#include "GameFramework/SpectatorPawn.h"
#include "FESpectatorPawn.generated.h"

/** AFESpectatorPawn
 * This Pawn Will Move Like An RTS Camera
 */
UCLASS()
class FE_API AFESpectatorPawn : public ASpectatorPawn
{
	GENERATED_UCLASS_BODY()

public:
	/** Camera Component */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
	TSubobjectPtr<class UCameraComponent> CameraComponent;

	/** Camera Z Angle */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraZAnlge;

	/** Camera Radius From Pawn Position */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraRadius;

	/** Camera Height Angle */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraHeightAngle;

	/** Camera Zoom Speed */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraZoomSpeed;

	/** Camera Radius Max */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraRadiusMax;

	/** Camera Radius Min */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraRadiusMin;

	/** Camera Movement Speed */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraMovementSpeed;

	/** Camera Scroll Boundary */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float CameraScrollBoundary;

	/** Should the camera move? */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	bool bCanMoveCamera;

private:
	/** Sets up player inputs
	 *    @param InputComponent - Input Component 
	 */
	void SetupPlayerInputComponent(class UInputComponent* InputComponent);

public:
	/** Zooms In The Camera */
	UFUNCTION()
	void ZoomIn();

	/** Zooms Out The Camera */
	UFUNCTION()
	void ZoomOut();

	/** Gets the roatation of the camera with only the yaw value 
	 * @return - returns a rotator that is (0, yaw, 0) of the Camera
	 */
	UFUNCTION()
	FRotator GetIsolatedCameraYaw();
	
	/** Moves the camera forward
	 * @param direcation - (1.0 for forward, -1.0 for backward)
	 */
	UFUNCTION()
	void MoveCameraForward(float direction);

	/** Moves the camera forward 
	 * @param direcation - (1.0 for right, -1.0 for left)
	 */
	UFUNCTION()
	void MoveCameraRight(float direction);

	/** Repositions The Camera */
	UFUNCTION()
	void RepositionCamera();

	/** Tick Function, Called Every Frame */
	UFUNCTION()
	virtual void Tick(float deltaSeconds) override;
};

FESpectatorPawn.cpp

#include "FE.h"
#include "FESpectatorPawn.h"


AFESpectatorPawn::AFESpectatorPawn(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	//Disable Standard WASD Movement
	bAddDefaultMovementBindings = false;

	//Set Default Camera Values
	CameraRadius = 1000.0f;
	CameraZAnlge = 0.0f;
	CameraHeightAngle = 70.0f;
	CameraZoomSpeed = 32.0f;
	CameraRadiusMin = 750.0f;
	CameraRadiusMax = 2000.0f;
	CameraMovementSpeed = 2000.0f;
	CameraScrollBoundary = 25.0f;
	//TODO: While selecting units, the camera CANNOT move!
	bCanMoveCamera = true;

	//Intialize The Camera
	CameraComponent = PCIP.CreateDefaultSubobject<UCameraComponent>(this, TEXT("RTS Camera"));
	CameraComponent->AttachParent = this->GetRootComponent();
	CameraComponent->bUsePawnControlRotation = false;
	RepositionCamera();

	//Enable Tick function
	PrimaryActorTick.bCanEverTick = true;
}

void AFESpectatorPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	check(InputComponent);
	
	//Bind Mouse Wheel Zooming Actions
	InputComponent->BindAction("WheelMouseUp", IE_Pressed, this, &AFESpectatorPawn::ZoomIn);
	InputComponent->BindAction("WheelMouseDown", IE_Pressed, this, &AFESpectatorPawn::ZoomOut);

	//Bind WASD Movement
	//FOR TESTING PURPOSES ONLY!!!
	//InputComponent->BindAxis("MoveForward", this, &AFESpectatorPawn::MoveCameraForward);
	//InputComponent->BindAxis("MoveRight", this, &AFESpectatorPawn::MoveCameraRight);
}

void AFESpectatorPawn::ZoomIn()
{
	//Don't execute any further if the camera can't move
	if (!bCanMoveCamera)
		return;

	//Decrease the CameraRadius but clamp it between the min and max radii
	CameraRadius = FMath::Clamp(CameraRadius - CameraZoomSpeed, CameraRadiusMin, CameraRadiusMax);
	
	//Reposition the camera in the local space
	RepositionCamera();
}

void AFESpectatorPawn::ZoomOut()
{
	//Don't execute any further if the camera can't move
	if (!bCanMoveCamera)
		return;

	//Increase the CameraRadius but clamp it between the min and max radii
	CameraRadius = FMath::Clamp(CameraRadius + CameraZoomSpeed, CameraRadiusMin, CameraRadiusMax);
	
	//Reposition the camera in the local space
	RepositionCamera();
}

FRotator AFESpectatorPawn::GetIsolatedCameraYaw()
{
	//Return a FRotator containing (0, CameraYaw, 0)
	return FRotator(0.0f, CameraComponent->ComponentToWorld.Rotator().Yaw, 0.0f);
}

void AFESpectatorPawn::MoveCameraForward(float direction)
{
	//Don't execute any further if the camera can't move
	if (!bCanMoveCamera)
		return;

	//Calculate how much to move the camera by
	float movementValue = direction * CameraMovementSpeed;

	//Create a delta vector that moves by the movementValue in the direction of the camera's yaw
	FVector deltaMovement = movementValue * GetIsolatedCameraYaw().Vector();

	//Add the delta to a new vector
	FVector newLocation = this->GetActorLocation() + deltaMovement;

	//Set the new location of the pawn
	SetActorLocation(newLocation);
}

void AFESpectatorPawn::MoveCameraRight(float direction)
{
	//Don't execute any further if the camera can't move
	if (!bCanMoveCamera)
		return;

	//Calculate how much to move the camera by
	float movementValue = direction * CameraMovementSpeed;

	//Create a delta vector that moves by the movementValue in the direction of the right of the camera's yaw
	FVector deltaMovement = movementValue * (FRotator(0.0f,90.0f,0.0f) + GetIsolatedCameraYaw()).Vector();

	//Add the delta to a new vector
	FVector newLocation = this->GetActorLocation() + deltaMovement;

	//Set the new location of the pawn
	SetActorLocation(newLocation);
}

void AFESpectatorPawn::RepositionCamera()
{
	//Create variables to hold the new values
	FVector newLocation(0.0f, 0.0f, 0.0f);
	FRotator newRotation(0.0f, 0.0f, 0.0f);

	//Find Cos and Sin of the Camera Z Angle
	float sinCameraZAngle = FMath::Sin(FMath::DegreesToRadians(CameraZAnlge));
	float cosCameraZAngle = FMath::Cos(FMath::DegreesToRadians(CameraZAnlge));

	//Find the Cos and Sin of the Camera Height Angle
	float sinCameraHeightAngle = FMath::Sin(FMath::DegreesToRadians(CameraHeightAngle));
	float cosCameraHeightAngle = FMath::Cos(FMath::DegreesToRadians(CameraHeightAngle));

	//Set newLocation X to cosCameraZAngle * sinCameraHeightAngle * CameraRadius
	newLocation.X = cosCameraZAngle * cosCameraHeightAngle * CameraRadius;

	//Set newLocation Y to sinCameraZangle * sinCameraHeightAngle * CameraRadius
	newLocation.Y = sinCameraZAngle * cosCameraHeightAngle * CameraRadius;

	//Set newLocation Z to cosCameraHeightAngle * CameraRadius
	newLocation.Z = sinCameraHeightAngle * CameraRadius;

	//Set the new rotations
	newRotation = (FVector(0.0f,0.0f,0.0f) - newLocation).Rotation();


	//Set the camera's location and rotation to the new values
	CameraComponent->SetRelativeLocation(newLocation);
	CameraComponent->SetRelativeRotation(newRotation);
}

void AFESpectatorPawn::Tick(float deltaSeconds)
{
	Super::Tick(deltaSeconds);

	//Create variables to hold mouse position and screen size
	FVector2D mousePosition;
	FVector2D viewportSize;

	//Get mouse position and screen size
	UGameViewportClient* gameViewport = GEngine->GameViewport;

	//Make sure viewport exists
	check(gameViewport);
	gameViewport->GetViewportSize(viewportSize);
	
	//Make sure the viewport has focus(contains the mouse)
	if (gameViewport->IsFocused(gameViewport->Viewport) && gameViewport->GetMousePosition(mousePosition) && bCanMoveCamera)
	{
		//Check if the mouse is at the left or right edge of the screen and move accordingly
		if (mousePosition.X < CameraScrollBoundary)
		{
			MoveCameraRight(-1.0f * deltaSeconds);
		}
		else if (viewportSize.X - mousePosition.X < CameraScrollBoundary)
		{
			MoveCameraRight(1.0f * deltaSeconds);
		}

		//Check if the mouse is at the top or bottom edge of the screen and move accordingly
		if (mousePosition.Y < CameraScrollBoundary)
		{
			MoveCameraForward(1.0f * deltaSeconds);
		}
		else if (viewportSize.Y - mousePosition.Y < CameraScrollBoundary)
		{
			MoveCameraForward(-1.0f * deltaSeconds);
		}
	}
}