Hello, and thanks for stopping by for the this week’s entry in my Weekly WaveShine series! In this week’s entry you can look forward to:
Unreal Project: Simple Camera Movement
I’m currently working on a project in Unreal that I’ve provisionally named Metal Wings. I’m maintaining a few different pieces of documentation for this project:
Per the vertical slice spec, the camera will need to exhibit the following behaviors:
So for this increment, I focused solely on panning the camera around using the arrow keys. I kept the other related features in the back of my mind and did my best to ensure the implementation would support these various configuration options.
The current implementation provides the following:
Free Camera Movement
Just a quick clip of what the movement looks like. Nothing ground breaking, but baby steps!
Thus, it’s reasonable to say that the player is able to pan around using the arrow keys as detailed in the Vertical Slice Spec. While not the most revolutionary of behaviors, it was definitely a good starting point to start getting more familiar with Unreal and the architecture that’s in place within the game. You can see more about this in the section below.
Unreal Project Architecture Overview
Architecture Diagram
I promise I know how to make things pretty. This is not one of them.
Right now, the Game Mode is implemented as a blueprint. I might implement this as a custom C++ class if initialization becomes more complex and maintenance of overall game state starts to become unmanageable. I hope to keep this all decentralized, but we’ll see as development progresses. The key responsibilities of this part of the architecture are:
The reason why the Game Mode is responsible for the Camera Actor is because I don’t think that the movement of the free camera should be tightly coupled to the Player Controller. I haven’t decided if I’m going to implement multiple Player Controllers to represent fundamental changes in a player’s gameplay capabilities or not. If this were the case, and the free camera implementation were tightly coupled to the initial Player Controller, it would require additional refactoring to accomplish.
Game Mode
A quick look at the blueprint for the Game Mode asset. On BeginPlay, the function on the right is invoked which instantiates a free camera actor and then sets a variable containing a reference to said actor.
The Player Controller is implemented as a blueprint. Similarly to the Game Mode, it’ll become a C++ class if it starts to get out of hand. The key responsibilities of this part of the architecture are:
The Player Controller is naturally in charge of initializing both the Enhanced Input Component and Ability System Component as it owns both of them. Going forward, I think it will continue to be responsible for adding and starting all of the different Abilities that the player is supposed to possess when the game starts. This way, the Player Controller will know what it’s able to do, but the exact nuts and bolts of what those things are will be abstracted away into Gameplay Abilities.
Player Controller
On the left is the function that initializes the input system, and the function on the right initializes the ability system. They get invoked in that same order on the BeginPlay callback.
The Camera Actor is also implemented as (surprise) a blueprint. In general, I’ve tried to keep things that connect things together as blueprints, but things that perform any complex operations as C++ classes. This way, it’s (hopefully) easier to visually see how things are connected while keeping complex implementations abstracted away. The key responsibilities of this part of the architecture are:
The reason why there is a separate Camera Actor instead of manipulating a camera that is placed directly on a Pawn is because the movement of a free camera is (at least to me) independent of the actual entity that the Player is attempting to manipulate. Additionally, the vertical slice spec does call for a follow-style camera as well, which would be easier to implement using a Camera Component that is actually on the Pawn. This way, it’s easy to transition between the free and follow camera modes by simply blending between the two camera poses. Finally, because the camera is not a child of the Pawn, it won’t be subject to relative movement due to the movement of the parent Pawn.
Camera Actor
You can see the components owned by the camera actor on the right as well as some of the configuration options available to the camera component on the right.
The Input Action is a blueprint type defined by the Enhanced Input Plugin. It is responsible for the following:
Some of you may be thinking “This is just a vertical slice. Why bother with the Enhanced Input plugin?”. I generally find making tightly coupled bindings between inputs and game behavior to be very shortsighted. I don’t even think that it really saves a whole lot of time once you do understand the input frameworks that are available in a given engine, so I personally think it’s just super lazy and borne out of a desire to not have to learn a new input framework. Considering that I actually want to be employed and good at my job as an entry-level game designer where I’ll probably have to actually implement features I figure it makes sense to know how to actually leverage the framework.
Input Action
The Input Action and it’s accompanying Input Mapping Context are both pretty simple. The Input Action itself is pictured above.
This Gameplay Ability is implemented as a blueprint type defined by the GAS and represents the player’s intention to move the camera. It is responsible for the following:
Right now, the custom Gameplay Task I mentioned directly connects an Input Action to a custom Movement Component, but I think that I can actually abstract this away and move the definition of that connection to the blueprint. I think this would be desirable because it would reduce how rigid and coupled the implementation of the ability is without actually adding any meaningful complexity to the blueprint level. You can see the source code for the task here.
Gameplay Ability
The blueprint for the gameplay ability can be seen above. The Poll Free Player Camera Input is the custom task that’s mentioned. The ability gets started by default at the beginning of the game and the polling task pulls the input data as it ticks.
The custom Movement Component is implemented as a custom C++ class and specifically defines how the free camera movement should behave. It is responsible for the following:
This is probably one of the pieces of this implementation that I like the least. The boundaries for camera movement are pretty hard-coded in and don’t actually perform any validation on their values. Also, the boundaries make the assumption that the boundaries should be a box. For the sake of the vertical slice, this should be sufficient, but in reality, its possible more complex boundary detection will be needed. Second, I’m uncertain whether the polling timing for player input and the position updating for the camera are lined up correctly or even if their ordering is deterministic. Based on some quick playtesting, it seems fine, but might be something to check if there’s excessive latency or inconsistent behavior. The good news is that because its implemented as a component, it can easily be re-implemented and swapped out in the scenario that a complete re-work is needed. Also, it doesn’t use a reference to an Input Action Value because that would directly couple this movement behavior to the Enhance Input system, and would make it harder if I wanted to inject input values for some reason. You can see the source code for the component here.
Movement Component
Here’s the Movement Component that’s owned by the Camera Actor. You can see the configuration options to the right.
The Test Pawn is a Pawn blueprint. It is responsible for the following:
This part of the architecture is thankfully quite simple.
Test Pawn
As one can see, the test pawn is indeed, quite simple right now. It’s definitely not going to stay that way.