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.
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.
Let’s start by creating interfaces that will help us with communication.
Create new Blueprint Interface named IMovement. Open it and add new function:
With one Vector input named Location.
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:
It will be called each time we start to click.
Will be called when we are moving our mouse after while holding RMB. (RMB = Right Mouse Button)
With one float input named HoldTime.
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:
Will be called when we should select an unit.
Called when unit should be deselected.
Next step is to create some helping Enums.
Create new Enum named ECameraScroll with these:
It will be used to choose in which direction we are scrolling camera.
Create another Enum named EInputType with these:
This will store state of input. For example when player is scrolling it will be set to Scroll.
Now we need to configure our project.
Project Settings -> Collision.
Add new Trace Channel named CamScroll. It should be set to Ignore as default.
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.
USEFUL TIP: When scaling Volumes it’s always better to use Brush Shape (box extend) instead of normal scale.
I have found that scaling sometimes create weird behaviors with overlaps / hit events.
When ready add NavMeshBoundsVolume:
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:
When ready go to Project Settings -> Input and add these Action Mappings:
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.
- Rotation: 0, -50, 0,
- Do Collision Test: False,
Now open event graph and add these variables:
|Var Name||Var Type||Description|
Create new custom event named SetDefaultSettings:
In BeginPlay call SetDefaultCamSettings:
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.
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:
This way we have simple zooming functionality. Now we need to call this event in 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?|
|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.
This will store hit location with CamScroll channel.
One Output: Vector named Delta.
Two local variables:
- LocalNewSwipeCoords – Vector,
- LocalDelta – Vector,
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.
With one Output: ECameraScroll.
This function is responsible for letting know if we want to scroll the camera.
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.
With one input named SelectedPawns: Actor Reference Array.
With one Output named Direction: Vector.
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:
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:
This is responsible for swipe camera movement.
And another input – SelectInput:
This is responsible for selecting units input.
Now create custom event named UpdateSelection:
It’s letting know HUD that we are holding mouse button.
And add last custom event named 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:
And now last thing. MovePawns input:
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!
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:
Event On Input Hold:
Event On Input Hold Released:
Those should be self explanatory.
Create new custom event named DrawMouseSelection:
This is for debug – drawing selection box so you can check if it’s working correctly.
Now add Receive Draw HUD event:
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!
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:
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:
And that’s all! If you like you can select CharacterMovement component and enable RVOAvoidance.
You can download whole project (4.10) here.