TLDR; Find the full script here
We will be covering the swerve input from the popular runner mechanics. This input has been used over and over again in hyper-casual games and is probably the most used one. Runner games have been around in the hypercasual game market for a long time
First, let’s break it down to understand how we will start coding it.
- The user taps and holds anywhere on the screen
- While holding, the user can move their finger horizontally to make the player move
- The player only moves when the finger is moving
Let’s overview the steps on how we will approach this
- Initial setup
- Get the input from the user’s tap
- Calculate the movement amount of the player in the game
- Move the player
Step 1: Setup
The setup should be pretty straightforward. We will keep a parent gameobject where the forward movement of the player will take place. We will keep our Player under the parent and that is where our swerving will take place. The reason is simple.
- We want the parent to take care of the progression of the player. i.e. the player can simply move forward, or follow a certain path using a spline. We don’t care how the progressive movement takes place. We only take care of the horizontal movement. Hence, separating them using different gameobjects simplifies our process
- We still want our swerve movement to follow the progressive movement
Step 2: Input
We will keep a variable named _anchorPositionwhich will keep track of the point where the user’s finger was on the last frame—the difference between the Input.mousePosition and the _anchorPosition will give us the amount of movement that the user made in the current frame. We will make sure that the input is 0 when the user is not touching the screen
private float GetInput()
{
var inputX = 0f;
if (Input.GetMouseButtonDown(0))
{
_anchorPosition = Input.mousePosition;
}
else if (Input.GetMouseButton(0))
{
inputX = (Input.mousePosition.x - _anchorPosition.x);
_anchorPosition = Input.mousePosition;
}
return inputX;
}
private float GetInput() { var inputX = 0f; if (Input.GetMouseButtonDown(0)) { _anchorPosition = Input.mousePosition; } else if (Input.GetMouseButton(0)) { inputX = (Input.mousePosition.x - _anchorPosition.x); _anchorPosition = Input.mousePosition; } return inputX; }
Step 3: Calculate Movement
From Step 1, we get a float that contains the x input amount of the user. We can use that to get the move amount. First, we will multiply it with the Time.deltaTime value and use the result for the player’s movement in the x-direction. At this point, the general swerve movement should work but with no discipline.
private float GetDisplacement(float inputX)
{
var displacementX = 0f; displacementX = inputX * Time.deltaTime;
return displacementX;
}
If you move the finger too fast, you will see that the player will follow that speed to move very fast and cause a jerking behavior that is not intended. The movement should be smooth even if the user is moving the finger too fast
We create a variable named maxDisplacement. Now, if the player tries to do a displacement greater than the maxDisplacement we will stop it right then and there.
private float SmoothOutDisplacement(float displacementX) { return Mathf.Clamp(displacementX, -maxDisplacement, maxDisplacement); }
Great! Let’s use the final displacement value to move the player by adding it to the position.x of the player.private Vector3 GetNewLocalPosition(float displacementX) { var lastPosition = transform.localPosition; var newPositionX = lastPosition.x + displacementX; var newPosition = new Vector3(newPositionX, lastPosition.y, lastPosition.z); return newPosition; }
Generally in a swerve runner, we want to keep the player within the bounds of the path. To do that we need to make sure that our position.x is within the limits. For example: if the path is 6 units wide. We want to make sure our position.x value is clamped from -3 to 3.
private Vector3 GetLimitedLocalPosition(Vector3 position)
{
position.x = Mathf.Clamp(position.x, -maxPositionX, maxPositionX); return position;
}
Step 4: Move the Player
We apply the movement to the local position of the player.
private void Update()
{
var inputX = GetInput();
var displacementX = GetDisplacement(inputX);
displacementX = SmoothOutDisplacement(displacementX);
var newPosition = GetNewLocalPosition(displacementX);
newPosition = GetLimitedLocalPosition(newPosition);
transform.localPosition = newPosition;
}
That’s about it. You can find the full script here