Unity Basics Course: Frame-Based Movement Speed Correction

Unity is one of the most widely used game engines today, providing the necessary features for creating games across various platforms. In game development, movement speed is a crucial factor, and understanding how to adjust movement speed to provide a consistent experience across various frame rates is essential. This tutorial will explain frame-based movement speed adjustment in Unity in detail.

Frame-Based Movement Speed and Time

In Unity, movement speed is primarily determined by two criteria: frame-based movement speed and time-based movement speed. Frame-based movement speed determines how much an object’s position will change each frame. However, since games cannot generally guarantee a consistent frame rate, it is challenging to maintain a consistent speed with just this method.

Therefore, we need to adjust speed based on time. Specifically, Unity provides the time interval between frames through Time.deltaTime. This allows us to adjust movement speed according to time. In other words, we can calculate the distance to move each frame based on Time.deltaTime, ensuring that it is not affected by frame rate.

Basic Script Setup

Now let’s apply the concept of movement speed adjustment through a basic movement script example. First, create a new script in Unity and name it PlayerMovement.cs.

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    public float moveSpeed = 5f;

    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        
        Vector3 direction = new Vector3(horizontal, 0, vertical).normalized;
        
        if(direction.magnitude >= 0.1f)
        {
            float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
            transform.rotation = Quaternion.Euler(0, targetAngle, 0);

            Vector3 moveDirection = Quaternion.Euler(0, targetAngle, 0) * Vector3.forward;
            transform.position += moveDirection * moveSpeed * Time.deltaTime;
        }
    }
}

The above code is a basic movement script. The moveSpeed variable sets the player’s movement speed, and the Update() method takes input to determine the movement direction. It adjusts the position based on the time difference between frames by multiplying by Time.deltaTime.

Code Explanation

1. User Input Handling

The commands Input.GetAxis("Horizontal") and Input.GetAxis("Vertical") in the provided code are responsible for receiving user input. Each function returns a value between -1 and 1, representing left/right and up/down movement.

2. Direction Vector Calculation

Based on user input, we create a Vector3 object called direction. This vector represents the direction of movement in the form of (x, 0, z). If this vector’s length is greater than 0.1 (i.e., if there is a direction in which the user actually wants to move), the next steps are executed.

3. Rotation and Movement Handling

The player’s rotation is calculated based on the input direction using Mathf.Atan2(). Then, the generated rotation value is used to update the player’s rotation state. Finally, in the actual movement handling part, we update transform.position based on the calculated direction.

Advanced Movement Techniques

In addition to basic movement, various adjustment techniques may be necessary depending on the player’s movement style. For example, to prevent different speeds when moving diagonally, we can maintain a consistent speed through vector normalization.

Diagonal Movement Adjustment

When moving diagonally, the speed increases as inputs are given on both axes. To prevent this, it is necessary to adjust the ratio of the diagonal length. We’ll use a conditional statement to detect diagonal movement and normalize the Vector3 vector to adjust the movement speed.

if (direction.magnitude >= 0.1f)
{
    direction.Normalize(); // Normalize the direction vector
    transform.position += direction * moveSpeed * Time.deltaTime;
}

In the above code, the direction.Normalize(); line normalizes the direction vector, preventing the increase in speed during diagonal movement. Now, the player will move at the same speed regardless of the direction.

Physics-Based Movement (Using Rigidbody)

Movement through physics manipulation can be implemented using Unity’s Rigidbody component. This method can better reflect collisions and physical laws, enhancing realism. To implement physics-based movement, follow these steps.

Adding the Rigidbody Component

First, add a Rigidbody component to the player character object in the Unity Editor. This component allows you to set the object’s physical properties, such as gravity, mass, linear and angular velocity, and various other physical attributes.

Movement Code Using Rigidbody

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    public float moveSpeed = 5f;
    private Rigidbody rb;

    void Start()
    {
        rb = GetComponent();
    }

    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        Vector3 direction = new Vector3(horizontal, 0, vertical).normalized;

        rb.MovePosition(transform.position + direction * moveSpeed * Time.deltaTime);
    }
}

In the above code, movement is done through the MovePosition method using the Rigidbody component. In this case, the movement speed adjustment still occurs through Time.deltaTime.

Integration with Animation

After adjusting movement speed, you can integrate animations to create more realistic character movements. In Unity, you can control animations using the Animator component. Animation transitions are usually based on parameters related to movement speed, allowing the character to switch between walking and running animations smoothly.

Setting Animation Parameters

Add a parameter called Speed in the Animator Controller and update this value based on movement speed. This way, you can switch to a running animation when moving above a certain speed.

void Update()
{
    float horizontal = Input.GetAxis("Horizontal");
    float vertical = Input.GetAxis("Vertical");

    Vector3 direction = new Vector3(horizontal, 0, vertical).normalized;

    // Set animation parameters based on movement speed
    animator.SetFloat("Speed", direction.magnitude);

    if (direction.magnitude >= 0.1f)
    {
        rb.MovePosition(transform.position + direction * moveSpeed * Time.deltaTime);
    }
}

In the above code, animator.SetFloat("Speed", direction.magnitude); updates the animation parameter based on the magnitude of the current movement direction. Now the animation will transition smoothly as the player moves.

Performance Optimization

Finally, it is essential to consider performance optimization. Game performance is a critical aspect in Unity, and it is necessary to minimize unnecessary calculations in the movement script.

Update vs FixedUpdate

Physical processes such as movement are typically handled in the FixedUpdate() method. While Update() is called every frame, FixedUpdate() is called at regular time intervals. Therefore, it is recommended to use FixedUpdate() for physics-related code.

void FixedUpdate()
{
    // Place your physics-based movement code here...
}

Additionally, you can further optimize performance by adjusting the physical calculation constants of the physics engine related to movement handling or removing unnecessary components.

Conclusion

In this tutorial, we explored various concepts and implementation methods for frame-based movement speed adjustment in Unity. From basic movement scripts to advanced physics-based movement, animation integration, and performance optimization, we covered many aspects. With these principles, you can provide consistent character movement in your game and it will greatly help in implementing more complex game logic in the future.

Utilize Unity’s various features to embark on your game development journey. Happy Gaming!