This is the first installment of a tutorial series about controlling the movement of a character. Specifically, we'll slide a sphere based on player input.

This tutorial is made with Unity 2019.2.9f1. It assumes that you've gone through the Basics tutorials first.

Controlling Position

Many games are about a character that has to move around to accomplish some goal. The player's task is to guide the character. Action games give you direct control by steering the character, usually by pressing keys or turning a stick. Point-and-click games have you indicate a goal position and the character moves there automatically. Programming games make you write instructions that the character executes. And so on.

In this tutorial series we will focus on how to control a character in a 3D action game. We start simple, by sliding a sphere around on a small flat rectangle. Once we have a good grip on that we can make it more complex in the future.

Setting the Scene Start with a new default 3D project. We don't need anything from the package manager at this point, although you could use the render pipeline of your choice. I always use linear color space, which you can configure in the project settings via Edit / Project Settings / Player / Other Settings. Linear color space. The default SampleScene scene has a camera and a directional light, which we'll keep. Create a plane to represent the ground, plus a sphere, both positioned at the origin. The default sphere has a radius of 0.5, so set its Y coordinate to 0.5 to make it look like it sits on top of the ground plane. Scene hierarchy. We're limiting ourselves to 2D movement on the ground, so let's position the camera above the plane looking down to get a good view of the play area in the game window. Also set its Projection mode to Orthographic. That gets rid of perspective, allowing us to see the 2D movement without distortion. Orthographic camera looking down. The only thing left that muddles our view is the sphere's shadow. Get rid of it by setting the light's Shadow Type to None, or No Shadows depending on the Unity version. Light casts no shadows. Create materials for the ground and sphere and configure them as you like. I made the sphere black and the ground dull light gray. We'll also visualize the movement with a trail, so create a material for that as well. I'll use an unlit reddish material for it. Finally, we need a MovingSphere script to implement the movement. Project assets. The script can start as an empty extension of MonoBehaviour . using UnityEngine; public class MovingSphere : MonoBehaviour { } Game view. Add both a TrailRenderer and our MovingSphere component to the sphere. Leave everything else as it is. Sphere object with components. Assign the trail material to the first and only element of the Materials array of the TrailRenderer component. It doesn't need to cast shadows, though that's not essential as we've disabled those anyway. Besides that, reduce the Width from 1.0 to a more reasonable value like 0.1, which will generate a thin line. Trail renderer. Although we haven't coded any movement yet, we can get a preview of how it would look by entering play mode and moving the sphere around in the scene window. Movement trail.

Reading Player Input To move the sphere we have to read the player's input commands. We do this in the Update method of MovingSphere . The player input is 2D so we can store it in a Vector2 variable. Initially we'll set both its X and Y components to zero, then use those to position the sphere in the XZ plane. Thus the input's Y component becomes the position's Z component. The Y position remains zero. using UnityEngine; public class MovingSphere : MonoBehaviour { void Update () { Vector2 playerInput; playerInput.x = 0f; playerInput.y = 0f; transform.localPosition = new Vector3(playerInput.x, 0f, playerInput.y); } } The simplest way to retrieve directional input from the player is by invoking Input.GetAxis with an axis name. Unity has both a Horizontal and Vertical input axis defined by default, which you can inspect in the Input section of the project settings. We'll use the horizontal value for X and the vertical value for Y. playerInput.x = Input.GetAxis("Horizontal") ; playerInput.y = Input.GetAxis("Vertical") ; The default settings link these axes to the arrow and WASD keys. The input values are also tweaked so the keys behave somewhat like a controller stick. You could adjust these settings as you like, but I keep the default settings. Using arrow or WASD keys. Both axes also have a second definition, which link them to the input of a joystick or left controller stick. This allows for much smoother input, but I'll use the keys for all animations except the next one. Using controller stick. Why not use the Input System package? You could do that, but the principle is the same. All we need is to retrieve two axis values. Also, at the time of this writing the package is still in preview, so not officially released and supported yet.

Normalizing the Input Vector The axes return zero when they are at rest and −1 or 1 at their extremes. As we use the input to set the sphere's position it is constrained to a rectangle with the same range. At least, that's the case for key input because the keys are independent. In the case of a stick the dimensions are linked and we are usually constrained to a maximum distance of 1 from the origin in any direction, thus limiting the position to lie within a circle. The advantage of the controller input is that the maximum length of the input vector is always 1, no matter the direction. So movement can be equally fast in all directions. This is not the case for keys, where the maximum is 1 for a single key but √2 when both keys are pressed, meaning that diagonal movement is the fastest. The maximum for keys is √2 because of the Pythagorean theorem. The axis values define the lengths of two sides of a right triangle and the combined vector is the hypotenuse. Hence, the magnitude of the input vector is `sqrt(x^2+y^2)`. We can make sure that the vector's length never exceeds 1 by dividing the input vector by its magnitude. The result is always unit-length vector, unless its initial length was zero in which case the result is undefined. This process is known as normalizing a vector. We can do this by invoking Normalize on the vector, which scales itself and becomes a zero vector if the result would be undefined. playerInput.x = Input.GetAxis("Horizontal"); playerInput.y = Input.GetAxis("Vertical"); playerInput.Normalize(); Normalized key input.