RTS Camera and unit movement

This time I will focus on another requested tutorial about RTS camera movement and unit movement. In this tutorial I will create:

  • RTS camera supporting: zooming, scrolling and moving,
  • Selecting units,
  • Moving units,

Remember – you can always request tutorial!

This Tutorial has been created using Unreal Engine 4.10
Make sure you are working on the same version of the engine.

Theory

I only shipped one kind-of-RTS game (Artist Colony iOS) so I don’t have to much experience with RTS games.

The main challenge here is to prepare proper communication with spectator (camera), HUD and Controller. It’s complicated and easier to accomplish using C++ but I will use Blueprints as requested.

Creating Interfaces

Let’s start by creating interfaces that will help us with communication.

Create new Blueprint Interface named IMovement. Open it and add new function:

MoveToMouseLoc

With one Vector input named Location.

IMovement

It will be used to tell units where to go.


 

Create another Blueprint Interface named InputInterface. It will be responsible for communicating other classes (eg. HUD) about the input. Open it and add new functions:

OnInputTap

OnInputTap

It will be called each time we start to click.

OnInputHold

oninputhold

Will be called when we are moving our mouse after while holding RMB.  (RMB = Right Mouse Button)

OnInputHoldReleased

With one float input named HoldTime.

oninputholdreleased

Will be called after releasing RMB.


 

Create another Blueprint Interface named SelectionInterface. It will be responsible for communicating select/deselect of units. Open it and add new functions:

OnSelectionGained

onselectiongained

Will be called when we should select an unit.

OnSelectionLost

onselectionlost

Called when unit should be deselected.

Creating Enums

Next step is to create some helping Enums.

Create new Enum named ECameraScroll with these:

  • None
  • Right
  • Left
  • Top
  • Bottom

It will be used to choose in which direction we are scrolling camera.

Create another Enum named EInputType with these:

  • None
  • Tap
  • Hold
  • Scroll

This will store state of input. For example when player is scrolling it will be set to Scroll.

Project Settings

Now we need to configure our project.

Project Settings -> Collision.

Add new Trace Channel named CamScroll. It should be set to Ignore as default.

tracechannel

When dealing with RTS camera movement we need to do some line traces from mouse to the ground. Creating new Trace Channel for this will be helpful as we will only use this channel to check the ground.

Now in your level place BlockingVolume which should imitate ground. Make sure you changed Collision preset to Custom and Block CamScroll. This way our line traces will work with this BlockingVolume. By default this volume is really small and should be scaled up on X and Y.

BlockingVolume

USEFUL TIP: When scaling Volumes it’s always better to use Brush Shape (box extend) instead of normal scale.

brushscale

I have found that scaling sometimes create weird behaviors with overlaps / hit events.


 

When ready add NavMeshBoundsVolume:

navmeshbounds

And make sure it is covering your level. You can show navigation when pressing “P” when in Editor or “Show Navigation” command when in game. You should have green ground like this:

shownavigation


 

When ready go to Project Settings -> Input and add these Action Mappings:

actionmappings

And that’s all that need to be prepared.

Spectator Pawn – Camera Zoom

For my opinion the best Pawn for dealing RTS camera movement is SpectatorPawn as it can be Possessed and don’t have any mesh.

Create new Blueprint extending from Spectator Pawn named RTSSpectatorPawn. Open it and add two new components: SpringArm and Camera.

RTSPawnComponents

SpringArm properties:

  • Rotation: 0, -50, 0,
  • Do Collision Test: False,

Now open event graph and add these variables:

Var Name Var Type Description
MinCamZoom float Default: 300
MaxCamZoom float Default: 2000
CurrentCamZoom float Default: 300
DesiredCamZoom float Default: 300
CamZoomSpeed float Default: 15
DeltaTime float

Create new custom event named SetDefaultSettings:

setdefaultsettings

In BeginPlay call SetDefaultCamSettings:

beginaplay

Now Right Click and find ZoomOut and ZoomIn input events – they should be visible after adding them in Project Settings. (Project Settings -> Input) If you can’t find them please go back to Project Settings step in this tutorial.

zoominput

So basically we are decreasing and increasing DesiredCamZoom float value. Easy as that. Now we need to drive this value somehow.

Create new custom event named UpdateCamZoom:

UpdateCamZoom

This way we have simple zooming functionality.  Now we need to call this event in Tick:

tick

And that’s all here.

Player Controller – Camera/Unit Movement

Next step is to move the camera. There will be one Player Controller responsible for movement. Create new Blueprint extending from Player Controller named RTSPlayerController.

Open it and add these variables:

Var Name Var Type Description
StartSwipeCoords Vector Storing OnTap mouse coordinates.
EndSwipeCoords Vector Storing OnHoldingEnded mouse coordinates.
CurrentInputState EInputType Holding current state of input. Make sure it’s NONE as Default.
isScrollingCamera bool Is currently scrolling camera?
DeltaTime float
InputHoldingTime float  Accumulated time while holding mouse.
isHoldingInput bool Is currently holding mouse?
SelectedPawns Actor Array Reference This will store all selected Actors reference.
MoveClickTime float Accumulated time for moving units.
WantToMove bool Want to move units?
MoveToLocation Vector Location to move the units

Lot of variables. It’s easier to create in C++.

Now in your Event Graph create new Functions.

OnSwipeStarted

OnSwipeStarted

This will store hit location with CamScroll channel.


OnSwipeEnded

OnSwipeEnded


OnSwipeUpdate

One Output: Vector named Delta.

Two local variables:

  • LocalNewSwipeCoords – Vector,
  • LocalDelta – Vector,

OnSwipeupdate

This function is adding and returning offset from starting vs current mouse position. Thanks to this we will be able to move our spectator pawn.


IsScrolling

With one Output: ECameraScroll.

This function is responsible for letting know if we want to scroll the camera.

IsScrolling

Doing such “IFS” in Blueprint isn’t the best way. In C++ you would have simple if statement with else if. That’s why it’s easier to do such stuff in C++.

This is basically checking if our mouse is near top/bottom/right/left corner and if yes – return ECameraScroll enum.


SetSelectedPawns

With one input named SelectedPawns: Actor Reference Array.

setselectedpawns


GetScrollDirection

With one Output named Direction: Vector.

GetScrolLDirection

With IsScrolling function you can add movement input with some direction but I want to add some smoothing. This function is able to return direction based on mouse position rather than top/bottom/left/right side of screen. Thanks to this scrolling will be smoother.


Now let’s make some use of these functions. Create new custom event named UpdateCamera:

UpdateCamera

This will actually scroll and move our camera. You can call this in Tick to check it out. Be sure your RTSSpectatorPawn is possessed by this controller.


Add another event – PanCamera Input:

PanCameraInput

This is responsible for swipe camera movement.


And another input – SelectInput:

SelectINput

This is responsible for selecting units input.


Now create custom event named UpdateSelection:

UpdateSelection

It’s letting know HUD that we are holding mouse button.


And add last custom event named UpdateClickToMove

updateclicktomove

It’s just updating accumulated time in MoveClickTime so we won’t move the units when trying to move camera.


Now everything need to be called in Tick:

PCTick

And now last thing. MovePawns input:

MovePawns

This is creating 2×2 grid (just for testing) and calling MoveToMouseLoc interface on SelectedPawn. Which will be null for now but this will change in a sec.

That’s all in PlayerController. Make sure your Game Mode default pawn is RTSSpectatorPawn and default controller is RTSPlayerController!

HUD

Create new Blueprint extending from HUD named RTSHud. Make sure it is set in your GameMode as default HUD.

Open it and make sure it implements InputInterface! When ready add those variables:

Var Name Var Type Description
OnTapMousePosition Vector2D Hold mouse position on click.
HoldingMousePosition Vector2D Holding current mouse position.
isDrawingSelection bool Is currently drawing selection box?
FoundActors float Stores all actors in selection.
SelectedActors float Stores actors that implements IMovable interface.

Now let’s add InputInterface events. Event On Input Tap:

EventOnInputTap

Event On Input Hold:

OnInputHoldEvent

Event On Input Hold Released:

OnInputHoldReleasedEvent

Those should be self explanatory.

Create new custom event named DrawMouseSelection:

DrawMouseSelection

This is for debug – drawing selection box so you can check if it’s working correctly.

Now add Receive Draw HUD event:

DrawHud

Look at Get Actors in Selection Rectangle function. It will return all actors in box selection that I’m doing. After that I’m just checking if the actors implements Selection Interface and call Selection Gained on Them.

After releasing mouse SelectedActors will be passed to PlayerController.

That’s all in HUD!

Test Unit

Now let’s make a test. Create new blueprint extending from Character named Pawn. Open it and make sure its implementing Selection Interface and IMovement interface as well!

When ready add new static mesh component named SelectionMesh. Make sure visibility is set to False. It’s just debug mesh that will be visible when this Pawn will be selected.

Now in Event Graph add On Selection Gained and On Selection Lost events:

pawn_selection

This should be place when you want to change material / add mesh when unit is selected – or maybe show some UMG widget. It depends on you and your game.

Last part is unit movement. Just add Move To Mouse Loc interface event:

movetomouse

And that’s all! If you like you can select CharacterMovement component and enable RVOAvoidance.

RVOAvoidance

Final Result

You can download whole project (4.10) here.

 

Creating ShooterTutorial takes a lot of my free time.
donateIf you want you can help me out! I will use your donation to buy better assets packs and you will be added to Credits /Backers page as well. Implementing game is taking time but writing about it is taking much more effort!

20 thoughts on “RTS Camera and unit movement

  1. Great tutorial, some things here are done a little different to my methods for a UE4 RTS. Unfortunately the downloaded project is not working for me 🙁 I’m using 4.10.4 on OSX. I get a blackscreen on play, and then blueprint errors when I stop gameplay. See this image. http://i.imgur.com/VOeDV7u.jpg

    • And after reading the whole article, I needed to set the gamemode up properly. After setting the default pawn, hud, spectator and controller, it all worked.

    • Project is rared with 4.10.2 version. I’m using Windows so maybe that’s the issue. But you can recreate this stuff using Tutorial!

  2. Hi there! i tried to make this tut, but i’m stucked. In part of “And another input – SelectInput:” i can’t add/i have no “On input tap” and “On Input Hold Released” node. When we create it?

    Regards!

  3. Lets see here. Seems like you missed quite some bits. To start in the HUD Sections Varibles “Found Actors and Selected Actors says they are floats but they are supposed to be Actor Reference Array.

    Im not sure if this one is missed, but you need to imploment the BPISelecttionInterface in the HUD class to be able to call Selection Lost. This is from the Recieve Draw HUD Event.

    As Kiss said, There is a function in the BPIInput interface missing, On Input Hold Released.

    Just a heads up to let you know.

  4. Really appreciate the work you do here. The small variable fixes stated in the comments got this working almost perfectly for me. The one issue I’m having is that when i make a selection and click to make the pawns move, far too many debug spheres appear and the pawns move to the last sphere so I might point them towards the center of a level and they will move far away. Any idea what could be causing so many destination points to appear?

  5. Last parts of “Spectator Pawn – Camera Zoom” section.

    Where is the Target under Target Arm Length?
    Is it another component? I don’t know how to get that part in a variable…

    I created a new variable and named it “Target Arm Length” but cannot get the “Target” part of it.
    Sorry, I am new to Blueprints and I am a worthless artist :(. Gomeh.

  6. Yup I found the error. You missed one custom event not explained in the tut. Download the Tut from the Link above, open it in Unreal (works even with 1.14) and open the RTS HUD. There is one new custom Event Called “CallOnSelectionLost”. It clears the Array. (All the red dots appeared because there were too many entries in the Array). After creating that custom Event in your own RTS HUD call it on the “Event Receive Draw HUD” on the “Then 1” Pin of the Sequence (like it is shown in the tut).

  7. Hello. Could you please help me. I did everything according to the tutoril. Everything is working, it’s just not working perfectly. When drawing rectangle, units/actors/pawns get selected even before the rectangle touched them. Other problem, and it is the biggest one, is that ALL units in a small radius get selected when I press only one. I tested this several times. The array is cleared, they are just too close to each other. I even made their collision capsules smaller. I don’t know what to do to make the selection better. Please help

  8. Hello. Thank you for the tutorial. I followed it entirely and I have 2 problems, and I hope someone can help. First is that units becom selected even before the rectangle touches them. This is not a big problem but if there is a larger number of units crammed, then it will be harder to do at least a little bit more precise draw selection.
    Second one is a bigger problem. When no unit is selected, but they are all crammed in one place, when I select (Just LMB quick click) 1 unit in that group, all other units in a small radius become also selected. I have been trying to resolve this for the last 4 hours without any success.
    Please help

  9. Hello, I’m just wondering. For “Is Scrolling” function, there’s a boolean function where it says “mouse x location is not less than 0.1 screen height and greater than 0.9 screen height”… Can’t I just pull the output directly from “mouse x location is greater than 0.9 screen height”? The two events are obviously mutually exclusive, no? Or did I miss something?

  10. Hello. Thank you for the tutorial, i have a question ,When holding the mouse right button Camera Scrolling to the Blocking volumes , Camera got flacker, how can i fix that 🙁

  11. Hello, Thank you for the tutorial.i have a question, Camera got flacker when holding mouse right button scrolling screen meet the blocking volume ,i m keeping trying fix that 🙁

  12. Excellent tutorial, keep up the good work.
    Would you mind uploading the project to github so we can help you maintain it?
    I can help with PR-s to ensure it works on newer engine versions and occasional bug fixes.

  13. Hello, thanks for the tutorial, I stumbled on a problem on your project, everything works fine, until you draw a selection rectangle, then the rts spectator pawn moves weird, and change it’s own z axis when you move, when it shouldn’t, I followed your tutorial and remade it on 4.23 version, but the same problem occurs, can anyone help me?

Leave a Reply to Ahmad Deedat Amadeus Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.