Announcement

Collapse
No announcement yet.

RTS Camera C++

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    [TUTORIAL] RTS Camera C++

    Hi,

    this mini tutorial is the extension/rework of the RTS camera code you can find at the Wiki. It contains hopefully all the usual camera and keyboard movement and rotation features you may need.

    ---

    After you created a new C++ project (call it RTS or whatever), or if you have an existing one, first set up inputs in the editor as follows:

    For mouse wheel:


    For keyboard:


    Then you need to add some new classes: a Player Controller, and a Spectator Pawn, let's call them RTSPlayerController and RTSPlayerCameraSpectatorPawn respectively. The player controller will be used only for handling RTS unit selection and orders by mouse clicks (not part of this tutorial), and the spectator pawn will contain a camera and handle its movement. This way you can easily replace your default camera with something else during development e.g. a free camera, or the player can select from different camera styles...

    Next, in the Game Mode (in case of a new project called RTS its name is RTSGameMode and automatically created), add a constructor to set your default player controller and pawn:

    .h
    Code:
    #pragma once
    
    #include "GameFramework/GameMode.h"
    #include "RTSGameMode.generated.h"
    
    /**
     * 
     */
    UCLASS()
    class RTS_API ARTSGameMode : public AGameMode
    {
        GENERATED_BODY()
    
            ARTSGameMode();
    
    };
    .cpp
    Code:
    #include "RTS.h"
    
    #include "RTSGameMode.h"
    
    #include "RTSPlayer/RTSPlayerController.h"
    #include "RTSPlayer/RTSPlayerCameraSpectatorPawn.h"
    
    ARTSGameMode::ARTSGameMode()
    {
        // C++ classes
        PlayerControllerClass = ARTSPlayerController::StaticClass();
        DefaultPawnClass = ARTSPlayerCameraSpectatorPawn::StaticClass();
    }
    Jump to the Player Controller to set some basic things, most importantly to have a visible mouse pointer:

    .h
    Code:
    #pragma once
    
    #include "GameFramework/PlayerController.h"
    #include "RTSPlayerController.generated.h"
    
    /**
     * 
     */
    UCLASS()
    class RTS_API ARTSPlayerController : public APlayerController
    {
        GENERATED_BODY()
    
            ARTSPlayerController();
        
    };
    .cpp
    Code:
    #include "RTS.h"
    #include "RTSPlayerController.h"
    
    
    ARTSPlayerController::ARTSPlayerController()
    {
        bShowMouseCursor = true;
        bEnableClickEvents = true;
        bEnableTouchEvents = true;
    }
    Now we are ready to set up our camera handling spectator pawn. As you can see mouse wheel and keyboard handling is separated, because keyboard inputs always have to deal with the delta time of the actual tick to set movement amount according to frame rate. It can be Blueprinted where you can set its default parameters from the editor. Please read through the comments above the variables and functions to have an overview what happens in the .cpp.

    .h
    Code:
    #pragma once
    
    #include "GameFramework/SpectatorPawn.h"
    #include "RTSPlayerCameraSpectatorPawn.generated.h"
    
    /**
     * this is the default RTS camera handling movememnt, rotation, and zoom
     */
    UCLASS()
    class RTS_API ARTSPlayerCameraSpectatorPawn : public ASpectatorPawn
    {
        GENERATED_BODY()
        
    public:
    
        //------------------------------------
    
        /** Constructor */
        ARTSPlayerCameraSpectatorPawn(const FObjectInitializer& ObjectInitializer);
        
        //------------------------------------
    
        /** Camera Component */
        UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
        class UCameraComponent* CameraComponent;
    
        //------------------------------------
    
        //** Camera XY limit */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraXYLimit;
    
        //** Camera height over terrain */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraHeight;
    
        //** Camera min height over terrain */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraHeightMin;
    
        //** Camera max height over terrain */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraHeightMax;
    
        /** Camera Rotation around Axis Z */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraZAnlge;
    
        /** Camera Height Angle */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraHeightAngle;
    
        /** Camera Pitch Angle Max */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraHeightAngleMax;
    
        /** Camera Pitch Angle Min */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraHeightAngleMin;
    
        /** Camera Radius (Distance) From Pawn Position */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraRadius;
    
        /** Camera Radius Max */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraRadiusMax;
    
        /** Camera Radius Min */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraRadiusMin;
    
        /** Camera Zoom Speed */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraZoomSpeed;
    
        /** Camera Rotation Speed */
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
        float CameraRotationSpeed;
    
        /** 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* PlayerInputComponent);
        
        //------------------------------------
    
    public:
    
        /** Zooms In The Camera */
        void ZoomInByWheel();
    
        /** Zooms Out The Camera */
        void ZoomOutByWheel();
    
        /** Rotate The Camera Left */
        void RotateLeftByWheel();
    
        /** Rotate The Camera Right */
        void RotateRightByWheel();
    
        /** Rotate The Camera Up */                                
        void RotateUpByWheel();
    
        /** Rotate The Camera Down */                            
        void RotateDownByWheel();
    
        //---
    
        /** Calculates the new Location and Rotation of The Camera */
        void RepositionCamera();
    
        //------------------------------------
    
    private:
    
        // set them to +/-1 to get player input from keyboard
        float FastMoveValue;                                            // movement speed multiplier : 1 if shift unpressed, 2 is pressed
        float RotateValue;                                                // turn instead of move camera
    
        float MoveForwardValue;
        float MoveRightValue;
        float MoveUpValue;
        float ZoomInValue;
    
        //---
    
    public:
    
        /** Left or Right Shift is pressed
        * @param direcation - (1.0 for Right, -1.0 for Left)
        */
        void FastMoveInput(float Direction);
    
        /** Left or Right Ctrl is pressed
        * @param direcation - (1.0 for Right, -1.0 for Left)
        */
        void RotateInput(float Direction);                                
    
        /** Input recieved to move the camera forward
        * @param direcation - (1.0 for forward, -1.0 for backward)
        */
        void MoveCameraForwardInput(float Direction);
    
        /** Input recieved to move the camera right
        * @param direcation - (1.0 for right, -1.0 for left)
        */
        void MoveCameraRightInput(float Direction);
    
        /** Input recieved to move the camera right
        * @param direcation - (1.0 for right, -1.0 for left)
        */
        void MoveCameraUpInput(float Direction);
    
        /** Input recieved to move the camera right
        * @param direcation - (1.0 for right, -1.0 for left)
        */
        void ZoomCameraInInput(float Direction);
    
        //---
    
    private:
    
        /** Moves the camera forward
        * @param direcation - (+ forward, - backward)
        */
        FVector MoveCameraForward(float Direction);
    
        /** Moves the camera right
        * @param direcation - (+ right, - left)
        */
        FVector MoveCameraRight(float Direction);
        
        /** Gets the roatation of the camera with only the yaw value
        * @return - returns a rotator that is (0, yaw, 0) of the Camera
        */
        FRotator GetIsolatedCameraYaw();
    
        //---
    
        /** Moves the camera up/down
        * @param direcation - (+ up, - down)
        */
        float MoveCameraUp(float Direction);
    
        //---
    
        /** Zooms the camera in/out
        * @param direcation - (+ in, - out)
        */
        void ZoomCameraIn(float Direction);
    
        /** Turns the camera up/down
        * @param direcation - (+ up, - down)
        */
        void TurnCameraUp(float Direction);
    
        /** Turns the camera right/left 
        * @param direcation - (+ right, - left)
        */
        void TurnCameraRight(float Direction);
    
        //------------------------------------
    
    public:
    
        /** Tick Function, handles keyboard inputs */
        virtual void Tick(float DeltaSeconds) override;
        
        //------------------------------------
    
        // detect landscape and terrain static-mesh
        // usage: RTS Obstacle and RTS Building placement onto landscape, terrain static-mesh
        float    GetLandTerrainSurfaceAtCoord(float XCoord, float YCoord) const;
    
        //------------------------------------
    };
    Here comes the heavy part.
    - In the constructor we set the default values of the public parameters, and add a camera component.
    - Then user inputs are assigned to functions.
    - Mouse wheel actions are called directly.
    - Keyboard inputs first set a corresponding value parameter storing their direction. This way it is easy to handle different key combinations.
    - After the input functions you can find the executive functions resonsible for camera movement and rotation.
    - All of them are handled by the Tick function, where first a screen edge region mouse position is checked, which can result in a movement input similarly to pressing a button, then it calles the executive functions with frame rate corrected parameters.
    - Reworked usage of RepositionCamera() resulting in smoother movement
    - Ray tracing of landscape surface using channel ECC_WorldStatic to disable moving under ground.

    .cpp
    Code:
    #include "RTS.h"
    #include "RTSPlayerCameraSpectatorPawn.h"
    
    //////////////////////////////////////////////////////////////////
    
    
    ARTSPlayerCameraSpectatorPawn::ARTSPlayerCameraSpectatorPawn(const FObjectInitializer& ObjectInitializer)
    {
        // enable Tick function
        PrimaryActorTick.bCanEverTick = true;
        
        // disable standard WASD movement
        bAddDefaultMovementBindings = false;
    
        // not needed Pitch Yaw Roll
        bUseControllerRotationPitch = false;
        bUseControllerRotationYaw = false;        
        bUseControllerRotationRoll = false;
    
        // collision
        GetCollisionComponent()->bGenerateOverlapEvents = false;
        GetCollisionComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
        GetCollisionComponent()->SetCollisionProfileName(TEXT("NoCollision"));
        GetCollisionComponent()->SetSimulatePhysics(false);
    
        // set defaults
    
        CameraXYLimit = 25000.f;
        CameraHeight = 1000.f;                    
        CameraHeightMin = 300.f;                // 100 for debugging
        CameraHeightMax = 5000.f;
    
        CameraRadius = 2000.f;                    
        CameraRadiusMin = 1000.f;                // 100 for debugging
        CameraRadiusMax = 8000.f;                
        
        CameraZAnlge = 0.f;                        // yaw
        
        CameraHeightAngle = 30.f;                // pitch
        CameraHeightAngleMin = 15.f;
        CameraHeightAngleMax = 60.f;
    
        CameraZoomSpeed = 200.f;                // wheel
        CameraRotationSpeed = 10.f;                // wheel + ctrl
        CameraMovementSpeed = 3000.f;            // in all directions
        
        CameraScrollBoundary = 25.f;            // screen edge width
        
        bCanMoveCamera = true;
    
        // intialize the camera
        CameraComponent = ObjectInitializer.CreateDefaultSubobject<UCameraComponent>(this, TEXT("RTS Camera"));
        CameraComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
        CameraComponent->bUsePawnControlRotation = false;
        
        RepositionCamera();
    }
    
    
    //////////////////////////////////////////////////////////////////
    
    
    void ARTSPlayerCameraSpectatorPawn::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
    {
        if (!PlayerInputComponent) return;
    
        Super::SetupPlayerInputComponent(PlayerInputComponent);
    
        // action mappings
    
        // mouse zoom
        PlayerInputComponent->BindAction("ZoomOutByWheel", IE_Pressed, this, &ARTSPlayerCameraSpectatorPawn::ZoomOutByWheel);
        PlayerInputComponent->BindAction("ZoomInByWheel", IE_Pressed, this, &ARTSPlayerCameraSpectatorPawn::ZoomInByWheel);
            
        // mouse rotate (+Ctrl or +Alt)
        // unnecessary...
        //PlayerInputComponent->BindAction("RotateLeftByWheel", IE_Pressed, this, &ARTSPlayerCameraSpectatorPawn::RotateLeftByWheel);
        //PlayerInputComponent->BindAction("RotateRightByWheel", IE_Pressed, this, &ARTSPlayerCameraSpectatorPawn::RotateRightByWheel);
        // needed...
        PlayerInputComponent->BindAction("RotateUpByWheel", IE_Pressed, this, &ARTSPlayerCameraSpectatorPawn::RotateUpByWheel);
        PlayerInputComponent->BindAction("RotateDownByWheel", IE_Pressed, this, &ARTSPlayerCameraSpectatorPawn::RotateDownByWheel);
    
        // axis mappings
    
        // keyboard move (WASD, Home/End)
        PlayerInputComponent->BindAxis("MoveForward", this, &ARTSPlayerCameraSpectatorPawn::MoveCameraForwardInput);
        PlayerInputComponent->BindAxis("MoveRight", this, &ARTSPlayerCameraSpectatorPawn::MoveCameraRightInput);
        PlayerInputComponent->BindAxis("MoveUp", this, &ARTSPlayerCameraSpectatorPawn::MoveCameraUpInput);
        PlayerInputComponent->BindAxis("ZoomIn", this, &ARTSPlayerCameraSpectatorPawn::ZoomCameraInInput);
    
        // double speed (WASD +Shift)
        PlayerInputComponent->BindAxis("FastMove", this, &ARTSPlayerCameraSpectatorPawn::FastMoveInput);
        // yaw and pitch (WASD +Ctrl)
        PlayerInputComponent->BindAxis("Rotate", this, &ARTSPlayerCameraSpectatorPawn::RotateInput);
    }
    
    
    //////////////////////////////////////////////////////////////////
    
    
    void ARTSPlayerCameraSpectatorPawn::ZoomInByWheel()
    {
        if (!bCanMoveCamera)    return;
    
        CameraRadius -= CameraZoomSpeed * FastMoveValue;
        CameraRadius = FMath::Clamp(CameraRadius, CameraRadiusMin, CameraRadiusMax);
    
        //RepositionCamera();
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::ZoomOutByWheel()
    {
        if (!bCanMoveCamera)    return;
    
        CameraRadius += CameraZoomSpeed * FastMoveValue;
        CameraRadius = FMath::Clamp(CameraRadius, CameraRadiusMin, CameraRadiusMax);
    
        //RepositionCamera();
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::RotateLeftByWheel()
    {
        if (!bCanMoveCamera)    return;
    
        CameraZAnlge -= CameraRotationSpeed * FastMoveValue;
    
        //RepositionCamera();
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::RotateRightByWheel()
    {
        if (!bCanMoveCamera)    return;
    
        CameraZAnlge += CameraRotationSpeed * FastMoveValue;
    
        //RepositionCamera();
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::RotateUpByWheel()
    {
        if (!bCanMoveCamera)    return;
    
        CameraHeightAngle += CameraRotationSpeed * FastMoveValue;
        CameraHeightAngle = FMath::Clamp(CameraHeightAngle, CameraHeightAngleMin, CameraHeightAngleMax);
    
        //RepositionCamera();
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::RotateDownByWheel()
    {
        if (!bCanMoveCamera)    return;
    
        CameraHeightAngle -= CameraRotationSpeed * FastMoveValue;
        CameraHeightAngle = FMath::Clamp(CameraHeightAngle, CameraHeightAngleMin, CameraHeightAngleMax);
    
        //RepositionCamera();
    }
    
    //---------------
    
    void ARTSPlayerCameraSpectatorPawn::RepositionCamera()
    {
        FVector NewLocation(0.f, 0.f, 0.f);
        FRotator NewRotation(0.f, 0.f, 0.f);
    
        float sinCameraZAngle = FMath::Sin(FMath::DegreesToRadians(CameraZAnlge));
        float cosCameraZAngle = FMath::Cos(FMath::DegreesToRadians(CameraZAnlge));
    
        float sinCameraHeightAngle = FMath::Sin(FMath::DegreesToRadians(CameraHeightAngle));
        float cosCameraHeightAngle = FMath::Cos(FMath::DegreesToRadians(CameraHeightAngle));
    
        NewLocation.X = cosCameraZAngle * cosCameraHeightAngle * CameraRadius;
        NewLocation.Y = sinCameraZAngle * cosCameraHeightAngle * CameraRadius;
        NewLocation.Z = sinCameraHeightAngle * CameraRadius;
    
        // do not allow camera component to go under ground - not enough alone, actor also needed to be limited
        float TerrainSurfaceZ = GetLandTerrainSurfaceAtCoord(GetActorLocation().X + NewLocation.X, GetActorLocation().Y + NewLocation.Y);
        if (GetActorLocation().Z + NewLocation.Z < TerrainSurfaceZ + CameraHeight)
        {
            //FVector NewLocation = CameraComponent->GetComponentLocation();
            NewLocation.Z = TerrainSurfaceZ - GetActorLocation().Z + CameraHeight;
        }
    
        // new camera location
        CameraComponent->SetRelativeLocation(NewLocation);
    
        // new camera rotation
        NewRotation = (FVector(0.0f, 0.0f, 0.0f) - NewLocation).Rotation();
        CameraComponent->SetRelativeRotation(NewRotation);
    }
    
    
    //////////////////////////////////////////////////////////////////
    
    
    void ARTSPlayerCameraSpectatorPawn::FastMoveInput(float Direction)
    {
        if (!bCanMoveCamera)    return;
    
        // left or right does not matter, to set double speed in any direction
        FastMoveValue = FMath::Abs(Direction) * 2.0f;
    
        // used as multiplier so must be 1 if not pressed
        if (FastMoveValue == 0.0f)
        {
            FastMoveValue = 1.0f;
        }
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::RotateInput(float Direction)
    {
        if (!bCanMoveCamera)    return;
    
        // left or right does not matter
        RotateValue = FMath::Abs(Direction);
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::MoveCameraForwardInput(float Direction)
    {
        if (!bCanMoveCamera)    return;
    
        MoveForwardValue = Direction;
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::MoveCameraRightInput(float Direction)
    {
        if (!bCanMoveCamera)    return;
    
        MoveRightValue = Direction;
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::MoveCameraUpInput(float Direction)
    {
        if (!bCanMoveCamera)    return;
    
        MoveUpValue = Direction;
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::ZoomCameraInInput(float Direction)
    {
        if (!bCanMoveCamera)    return;
    
        ZoomInValue = Direction;
    }
    
    
    //------------------------------------------------------------
    
    
    FVector ARTSPlayerCameraSpectatorPawn::MoveCameraForward(float Direction)
    {
        float MovementValue = Direction * CameraMovementSpeed;
        FVector DeltaMovement = MovementValue * GetIsolatedCameraYaw().Vector();
        //FVector NewLocation = GetActorLocation() + DeltaMovement;
        //SetActorLocation(NewLocation);
        return DeltaMovement;
    }
    
    
    FVector ARTSPlayerCameraSpectatorPawn::MoveCameraRight(float Direction)
    {
        float MovementValue = Direction * CameraMovementSpeed;
        FVector DeltaMovement = MovementValue * (FRotator(0.0f, 90.0f, 0.0f) + GetIsolatedCameraYaw()).Vector();
        //FVector NewLocation = GetActorLocation() + DeltaMovement;
        //SetActorLocation(NewLocation);
        return DeltaMovement;
    }
    
    
    FRotator ARTSPlayerCameraSpectatorPawn::GetIsolatedCameraYaw()
    {
        // FRotator containing Yaw only
        return FRotator(0.0f, CameraComponent->ComponentToWorld.Rotator().Yaw, 0.0f);
    }
    
    //---------------
    
    float ARTSPlayerCameraSpectatorPawn::MoveCameraUp(float Direction)
    {
        float MovementValue = Direction * CameraMovementSpeed;
        //FVector DeltaMovement = FVector(0.0f, 0.0f, MovementValue);
        //FVector NewLocation = GetActorLocation() + DeltaMovement;
        //NewLocation.Z = FMath::Clamp(NewLocation.Z, CameraRadiusMin, CameraRadiusMax);
        //SetActorLocation(NewLocation);
        return MovementValue;
    }
    
    //---------------
    
    void ARTSPlayerCameraSpectatorPawn::ZoomCameraIn(float Direction)
    {
        float MovementValue = Direction * CameraMovementSpeed;                
        CameraRadius += MovementValue;
        CameraRadius = FMath::Clamp(CameraRadius, CameraRadiusMin, CameraRadiusMax);
    
        //RepositionCamera();
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::TurnCameraUp(float Direction)
    {
        CameraHeightAngle -= Direction * CameraRotationSpeed * 10.0f;        
        CameraHeightAngle = FMath::Clamp(CameraHeightAngle, CameraHeightAngleMin, CameraHeightAngleMax);
    
        //RepositionCamera();
    }
    
    
    void ARTSPlayerCameraSpectatorPawn::TurnCameraRight(float Direction)
    {
        CameraZAnlge += Direction * CameraRotationSpeed * 10.0f;            
    
        //RepositionCamera();
    }
    
    
    //////////////////////////////////////////////////////////////////
    
    
    void ARTSPlayerCameraSpectatorPawn::Tick(float DeltaSeconds)
    {
        Super::Tick(DeltaSeconds);
    
        // mouse position and screen size
        FVector2D MousePosition;
        FVector2D ViewportSize;
    
        UGameViewportClient* GameViewport = GEngine->GameViewport;
        
        // it is always nullptr on dedicated server
        if (!GameViewport) return;
        GameViewport->GetViewportSize(ViewportSize);
    
        // if viewport is focused, contains the mouse, and camera movement is allowed
        if (GameViewport->IsFocused(GameViewport->Viewport) 
            && GameViewport->GetMousePosition(MousePosition) && bCanMoveCamera)
        {
            //-------------------
            // movement direction by mouse at screen edge
    
            if (MousePosition.X < CameraScrollBoundary)
            {
                MoveRightValue = -1.0f;
            }
            else if (ViewportSize.X - MousePosition.X < CameraScrollBoundary)
            {
                MoveRightValue = 1.0f;
            }
            
            if (MousePosition.Y < CameraScrollBoundary)
            {
                MoveForwardValue = 1.0f;
            }
            else if (ViewportSize.Y - MousePosition.Y < CameraScrollBoundary)
            {
                MoveForwardValue = -1.0f;
            }
    
            //-------------------
            // tweak camera actor position
    
            FVector ActualLocation = GetActorLocation();
            FVector ActualMovement = FVector::ZeroVector;        
    
            // horizontal movement
            if (RotateValue == 0.f)
            {
                ActualMovement += MoveCameraForward(MoveForwardValue * FastMoveValue * DeltaSeconds);
                ActualMovement += MoveCameraRight(MoveRightValue * FastMoveValue * DeltaSeconds);
            }
            ActualLocation += ActualMovement;
    
            // vertical movement
            CameraHeight += MoveCameraUp(MoveUpValue * FastMoveValue * DeltaSeconds);
            CameraHeight = FMath::Clamp(CameraHeight, CameraHeightMin, CameraHeightMax);
            
            // adjust actor height to surface
            float TerrainSurfaceZ = GetLandTerrainSurfaceAtCoord(ActualLocation.X, ActualLocation.Y);
            ActualLocation.Z = TerrainSurfaceZ + CameraHeight;
    
            // limit movement area
            ActualLocation.X = FMath::Clamp(ActualLocation.X, -CameraXYLimit, CameraXYLimit);
            ActualLocation.Y = FMath::Clamp(ActualLocation.Y, -CameraXYLimit, CameraXYLimit);
    
            // move actor
            SetActorLocation(ActualLocation);
    
            //-------------------
            // tweak camera component relative transform
    
            // set rotation parameters
            if (RotateValue != 0.f)
            {
                TurnCameraUp(MoveForwardValue * FastMoveValue * DeltaSeconds);
                TurnCameraRight(MoveRightValue * FastMoveValue * DeltaSeconds);
            }
    
            // set zoom distance
            ZoomCameraIn(ZoomInValue * FastMoveValue * DeltaSeconds);
    
            // adjust camera component relative location and rotation
            RepositionCamera();
    
            //-------------------
            // debug
    
            //DrawDebugSphere(    
            //                    GetWorld(),
            //                    GetCollisionComponent()->GetComponentLocation(),
            //                    GetCollisionComponent()->GetScaledSphereRadius(),
            //                    8,
            //                    FColor::White,
            //                    false,
            //                    -1.f
            //                );
    
            //-------------------
        }
    }
    
    
    //////////////////////////////////////////////////////////////////
    
    
    float    ARTSPlayerCameraSpectatorPawn::GetLandTerrainSurfaceAtCoord(float XCoord, float YCoord) const
    {
        FCollisionQueryParams TraceParams(FName(TEXT("LandTerrain")), false, this);        // TraceTag (info for debugging), bTraceComplex, AddIgnoredActor
        TraceParams.bFindInitialOverlaps = false;                                        // needed
    
        FHitResult Hit;
    
        FVector Start = FVector(XCoord, YCoord, GetActorLocation().Z + CameraRadius);
        FVector End = FVector(XCoord, YCoord, -500.f);
    
        // ECC_ channels should be set properly !!!
        bool bHit = GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECollisionChannel::ECC_WorldStatic, TraceParams);
    
        if (bHit)
        {
            return Hit.ImpactPoint.Z;    // for shape trace it differs from Location
        }
    
        return 0.f;        // water level
    }
    
    //////////////////////////////////////////////////////////////////
    I hope it was detailed enough (anyway the in-code comments should help), ask me if something unclear, or scream if you found a bug/mistake.

    Maybe later I will add unit selection to the player controller...

    Have fun with it!

    Ivan
    Attached Files
    Last edited by sivan; 06-27-2017, 05:25 AM. Reason: update to 4.15, smoother movement
    "Age of Total Heroes" - RTS Pathfinding and Movement System for UE4
    RTS Camera C++ Tutorial

    #2
    I made a little cosmetics on this old stuff, removed some UFUNCTION() macros, as they are not needed...
    "Age of Total Heroes" - RTS Pathfinding and Movement System for UE4
    RTS Camera C++ Tutorial

    Comment


      #3
      Nice, thank you!

      Comment


        #4
        Hi,

        first off all +rep to you Sivan as well as to Connor, really good work. I tried to get it work in UE 4.16 and there are just two things to fix in order to get it work.

        Both at RTSPawn.cpp (or whatever your pawn is named):
        1) Replacement of this line of code:
        CameraComponent->AttachParent = GetRootComponent(); //AttachParent is private
        to
        CameraComponent->SetupAttachment(GetRootComponent());

        2) For some reason you also double initialized GameViewport:
        UGameViewportClient* GameViewport = GameViewport;
        UGameViewportClient* GameViewport = GEngine->GameViewport;
        While the first line of code should be removed. I believe you just forget to delete it guys.

        Once again, nice work! (btw your RTS project looks awesome Sivan!)

        Comment


          #5
          Thanks! I have made a slightly better version having smoother moves, I will update and correct it soon. And if there is interest, I can share my single click and box unit selection stuff too.
          "Age of Total Heroes" - RTS Pathfinding and Movement System for UE4
          RTS Camera C++ Tutorial

          Comment


            #6
            Hey,

            the RTSPlayerCameraSpectatorPawn.h/.cpp codes are updated to UE4.15.

            Moreover, there are two improvements:
            - The movement is really smoother by a different usage of RepositionCamera(), which is now only called once per tick.
            - Vertical ray tracing is introduced to deny the camera to move under ground or inside static meshes. You can set ECollisionChannel::ECC_WorldStatic to a custom channel, e.g. to sense only landscape instead of all static geometries, or as you desire.

            Have fun!
            "Age of Total Heroes" - RTS Pathfinding and Movement System for UE4
            RTS Camera C++ Tutorial

            Comment


              #7
              My engine version is 4.17.

              I have a problem with the following line in the RTSPlayerCameraSpectatorPawn class.
              GetCollisionComponent()->bGenerateOverlapEvents = false; Looks like RTSPlayerCameraSpectatorPawn does not have a collision component.

              Is this an engine quirk in 4.17?

              Comment


                #8
                it's inherited from ASpectatorPawn which has a collision component, namely a sphere. only the mesh is omitted by .DoNotCreateDefaultSubobject(Super::MeshComponentName)
                I use it with 4.17 and fine here.
                "Age of Total Heroes" - RTS Pathfinding and Movement System for UE4
                RTS Camera C++ Tutorial

                Comment


                  #9
                  Ivan, hi!

                  I created an RTS project in 4.17 with the bare minimum code to illustrate the issue that I am facing. See code below.

                  RTSGameModeBase.h
                  Code:
                  #include "CoreMinimal.h"
                  #include "GameFramework/GameModeBase.h"
                  #include "RTSGameModeBase.generated.h"
                  
                  /**
                   * 
                   */
                  UCLASS()
                  class RTS_API ARTSGameModeBase : public AGameModeBase
                  {
                      GENERATED_BODY()
                  
                          ARTSGameModeBase(); //Constructor
                  
                  
                  
                  };

                  RTSGameModeBase.cpp

                  Code:
                  #include "RTSGameModeBase.h"
                  #include "RTS.h"
                  #include "RTSPlayerController.h"
                  #include "RTSPlayerCameraSpectatorPawn.h"
                  
                  
                  ARTSGameModeBase::ARTSGameModeBase()
                  {
                      PlayerControllerClass = ARTSPlayerController::StaticClass();
                      DefaultPawnClass = ARTSPlayerCameraSpectatorPawn::StaticClass();
                  }

                  RTSPlayerController.h

                  Code:
                  #include "CoreMinimal.h"
                  #include "GameFramework/PlayerController.h"
                  #include "RTSPlayerController.generated.h"
                  
                  /**
                   * 
                   */
                  UCLASS()
                  class RTS_API ARTSPlayerController : public APlayerController
                  {
                      GENERATED_BODY()
                  
                          ARTSPlayerController(); //Constructor
                  
                  
                  };

                  RTSPlayerController.cpp

                  Code:
                  #include "RTSPlayerController.h"
                  
                  
                  ARTSPlayerController::ARTSPlayerController()
                  {
                      bShowMouseCursor = true;
                      bEnableClickEvents = true;
                      bEnableTouchEvents = true;
                  }
                  RTSCameraSpectatorPawn.h

                  Code:
                  #include "CoreMinimal.h"
                  #include "GameFramework/SpectatorPawn.h"
                  #include "RTSPlayerCameraSpectatorPawn.generated.h"
                  
                  /**
                   * 
                   */
                  UCLASS()
                  class RTS_API ARTSPlayerCameraSpectatorPawn : public ASpectatorPawn
                  {
                      GENERATED_BODY()
                  
                  public:
                  
                      /** Constructor */
                      ARTSPlayerCameraSpectatorPawn(const FObjectInitializer& ObjectInitializer);
                  
                  
                  };

                  RTSCameraSpectatorPawn.cpp

                  Code:
                  #include "RTSPlayerCameraSpectatorPawn.h"
                  
                  ARTSPlayerCameraSpectatorPawn::ARTSPlayerCameraSpectatorPawn(const FObjectInitializer& ObjectInitializer) //Constructor
                  {
                  
                      // collision
                      GetCollisionComponent()->bGenerateOverlapEvents = false;
                  
                  }

                  Compile fails on this line at RTSCameraSpectatorPawn.cpp:

                  GetCollisionComponent()->bGenerateOverlapEvents = false;


                  Looks like the method is not able to get the CollisionComponent. See compile error message from the compiler:
                  error C2027 : use of undefined type 'USphereComponent'.

                  Intellisense too gives an error with the following message: Pointer to incomplete class type is not allowed.

                  See if anything obvious jumps out at you.

                  If not could you please compile the above simple code on your machine and see if it works?

                  Thanks.
                  ​​​​​​​

                  Comment


                    #10

                    Comment


                      #11

                      Comment


                        #12
                        thanks, it is probably because of the default project includes were changed a few engine versions ago (as I remember in 4.15, include what you use), but my project still includes #include "Engine.h" in RTS.h, which could be not the case in new projects, I have to check it.
                        adding #include "Components/SphereComponent.h" to RTSCameraSpectatorPawn.cpp should solve the problem.
                        "Age of Total Heroes" - RTS Pathfinding and Movement System for UE4
                        RTS Camera C++ Tutorial

                        Comment


                          #13
                          Well, thanks are due to you. Excellent tut. BTW, I included #include "Components/SphereComponent.h" and my test code above compiles. I will test the rest of the camera stuff in 4.17 and post my results. Then you can probably make the changes needed for 4,17 compilation perhaps?

                          Comment


                            #14
                            I will test if there any other additional dependencies like #include "Components/SphereComponent.h" above and post my results soon.

                            Comment


                              #15
                              Ivan, I had to add the following includes to the RTSPlayerCameraSpectatorPawn.cpp in order for the code to compile in 4.17.
                              Code:
                              #include "RTSPlayerCameraSpectatorPawn.h"
                              #include "Components/SphereComponent.h" //Needed for the Collision Component to work
                              #include "Camera/CameraComponent.h" //Needed for the Camera Component to work
                              #include "Components/InputComponent.h" //Needed for the Player Input Component to work
                              #include "Classes/Engine/GameViewportClient.h" //Needed for the GameViewport to work
                              #include "Engine.h" //Needed for Gengine to work
                              Do you want to make the changes yourself in your code above or do you want me to post the whole code?

                              Also, I had to change the Default Game Mode in the Project Settings -> Maps and Modes section:

                              Click image for larger version

Name:	Default_Game_Mode.png
Views:	1
Size:	40.9 KB
ID:	1387160

                              Comment

                              Working...
                              X