Core Development Jul 23, 2025

Lesson 4: Player Character & Movement

Learn to create a responsive player character with smooth movement, camera controls, and basic interactions. Build the foundation for your RPG game's core gameplay.

By GamineAI Team

Lesson 4: Player Character & Movement

Introduction

Welcome to Lesson 4 of the "Build an AI-Powered RPG Game" course! In this lesson, we'll create the foundation of your RPG game by building a responsive player character with smooth movement, camera controls, and basic interactions. This is where your game starts to feel alive and playable.

Learning Objectives

By the end of this lesson, you will be able to:

  • Create a player character with smooth movement controls
  • Implement camera systems for RPG gameplay
  • Add basic player interactions and animations
  • Set up input systems for keyboard and gamepad
  • Understand character controller vs rigidbody movement
  • Create responsive and polished player movement

1. Setting Up the Player Character

Step 1: Create the Player GameObject

  1. In your Unity scene, right-click in the Hierarchy
  2. Create Empty and name it "Player"
  3. Add a Capsule as a child (right-click Player → 3D Object → Capsule)
  4. Rename the Capsule to "PlayerModel"
  5. Position the Player at (0, 1, 0) in the scene

Step 2: Add Character Controller

  1. Select the Player GameObject
  2. Add Component → Character Controller
  3. Adjust the Character Controller settings:
    • Height: 2
    • Radius: 0.5
    • Center: (0, 1, 0)
    • Slope Limit: 45
    • Step Offset: 0.3
    • Skin Width: 0.08

Step 3: Create Player Movement Script

  1. Create a new C# script called "PlayerController"
  2. Attach it to the Player GameObject
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [Header("Movement Settings")]
    public float walkSpeed = 5f;
    public float runSpeed = 8f;
    public float jumpHeight = 3f;
    public float gravity = -9.81f;

    [Header("Camera Settings")]
    public Transform cameraTransform;
    public float mouseSensitivity = 2f;
    public float cameraPitchRange = 80f;

    private CharacterController controller;
    private Vector3 velocity;
    private bool isGrounded;
    private float cameraPitch = 0f;

    void Start()
    {
        controller = GetComponent<CharacterController>();

        // Lock cursor to center of screen
        Cursor.lockState = CursorLockMode.Locked;
    }

    void Update()
    {
        HandleMovement();
        HandleCamera();
        HandleJump();
    }

    void HandleMovement()
    {
        // Check if grounded
        isGrounded = controller.isGrounded;
        if (isGrounded && velocity.y < 0)
        {
            velocity.y = -2f; // Small negative value to keep grounded
        }

        // Get input
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        // Calculate movement direction
        Vector3 move = transform.right * horizontal + transform.forward * vertical;

        // Apply speed (walk or run)
        float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;
        controller.Move(move * currentSpeed * Time.deltaTime);

        // Apply gravity
        velocity.y += gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);
    }

    void HandleCamera()
    {
        // Get mouse input
        float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
        float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;

        // Rotate player left/right
        transform.Rotate(Vector3.up * mouseX);

        // Rotate camera up/down
        cameraPitch -= mouseY;
        cameraPitch = Mathf.Clamp(cameraPitch, -cameraPitchRange, cameraPitchRange);
        cameraTransform.localEulerAngles = Vector3.right * cameraPitch;
    }

    void HandleJump()
    {
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }
    }
}

2. Setting Up the Camera System

Step 1: Create Camera Setup

  1. Create an Empty GameObject called "CameraController"
  2. Add the Main Camera as a child
  3. Position the camera at (0, 1.6, 0) relative to the Player
  4. Create a new script called "CameraController" and attach it to the CameraController
using UnityEngine;

public class CameraController : MonoBehaviour
{
    [Header("Camera Settings")]
    public Transform target; // The player transform
    public float followSpeed = 10f;
    public Vector3 offset = new Vector3(0, 1.6f, 0);

    void LateUpdate()
    {
        if (target != null)
        {
            // Smoothly follow the target
            Vector3 desiredPosition = target.position + offset;
            transform.position = Vector3.Lerp(transform.position, desiredPosition, followSpeed * Time.deltaTime);
        }
    }
}

Step 2: Connect Camera to Player

  1. Select the CameraController
  2. In the CameraController script, drag the Player into the "Target" field
  3. Test the camera by moving the player around

3. Improving Player Movement

Step 1: Add Movement Smoothing

Update your PlayerController script with these improvements:

[Header("Movement Settings")]
public float walkSpeed = 5f;
public float runSpeed = 8f;
public float jumpHeight = 3f;
public float gravity = -9.81f;
public float acceleration = 10f;
public float deceleration = 10f;

private Vector3 currentVelocity;
private Vector3 targetVelocity;

void HandleMovement()
{
    // Check if grounded
    isGrounded = controller.isGrounded;
    if (isGrounded && velocity.y < 0)
    {
        velocity.y = -2f;
    }

    // Get input
    float horizontal = Input.GetAxis("Horizontal");
    float vertical = Input.GetAxis("Vertical");

    // Calculate target movement direction
    Vector3 move = transform.right * horizontal + transform.forward * vertical;
    targetVelocity = move * (Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed);

    // Smooth acceleration/deceleration
    if (move.magnitude > 0.1f)
    {
        currentVelocity = Vector3.Lerp(currentVelocity, targetVelocity, acceleration * Time.deltaTime);
    }
    else
    {
        currentVelocity = Vector3.Lerp(currentVelocity, Vector3.zero, deceleration * Time.deltaTime);
    }

    // Apply movement
    controller.Move(currentVelocity * Time.deltaTime);

    // Apply gravity
    velocity.y += gravity * Time.deltaTime;
    controller.Move(velocity * Time.deltaTime);
}

Step 2: Add Movement Animations

  1. Create an Animator Controller for your player
  2. Add basic animation states: Idle, Walk, Run, Jump
  3. Create transitions between states based on movement speed
  4. Connect the Animator to your PlayerController script
[Header("Animation")]
public Animator animator;

void HandleMovement()
{
    // ... existing movement code ...

    // Update animation parameters
    if (animator != null)
    {
        animator.SetFloat("Speed", currentVelocity.magnitude);
        animator.SetBool("IsGrounded", isGrounded);
        animator.SetFloat("VerticalVelocity", velocity.y);
    }
}

4. Adding Basic Interactions

Step 1: Create Interaction System

Add this to your PlayerController script:

[Header("Interaction")]
public float interactionRange = 3f;
public LayerMask interactionLayer = 1;

void Update()
{
    HandleMovement();
    HandleCamera();
    HandleJump();
    HandleInteraction();
}

void HandleInteraction()
{
    if (Input.GetKeyDown(KeyCode.E))
    {
        TryInteract();
    }
}

void TryInteract()
{
    // Raycast forward to find interactable objects
    RaycastHit hit;
    if (Physics.Raycast(transform.position, transform.forward, out hit, interactionRange, interactionLayer))
    {
        // Check if the hit object has an interactable component
        IInteractable interactable = hit.collider.GetComponent<IInteractable>();
        if (interactable != null)
        {
            interactable.Interact();
        }
    }
}

Step 2: Create Interactable Interface

Create a new script called "IInteractable":

public interface IInteractable
{
    void Interact();
}

Step 3: Create Example Interactable

Create a simple interactable object:

using UnityEngine;

public class SimpleInteractable : MonoBehaviour, IInteractable
{
    [Header("Interaction Settings")]
    public string interactionText = "Press E to interact";

    public void Interact()
    {
        Debug.Log("Player interacted with " + gameObject.name);
        // Add your interaction logic here
    }

    void OnDrawGizmosSelected()
    {
        // Visualize interaction range
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(transform.position, 1f);
    }
}

5. Input System Setup

Step 1: Configure Input Manager

  1. Go to Edit → Project Settings → Input Manager
  2. Verify these inputs exist:
    • Horizontal: A/D keys and Left/Right arrow keys
    • Vertical: W/S keys and Up/Down arrow keys
    • Jump: Space key
    • Mouse X: Mouse X axis
    • Mouse Y: Mouse Y axis

Step 2: Add Gamepad Support

Update your PlayerController to support gamepad input:

[Header("Input Settings")]
public bool useGamepad = true;

void HandleMovement()
{
    // Get input (keyboard or gamepad)
    float horizontal = Input.GetAxis("Horizontal");
    float vertical = Input.GetAxis("Vertical");

    // Apply deadzone for gamepad
    if (useGamepad)
    {
        float deadzone = 0.1f;
        if (Mathf.Abs(horizontal) < deadzone) horizontal = 0;
        if (Mathf.Abs(vertical) < deadzone) vertical = 0;
    }

    // ... rest of movement code ...
}

6. Testing and Polish

Step 1: Create a Test Scene

  1. Add some basic geometry to your scene (cubes, planes)
  2. Create ramps and platforms to test jumping
  3. Add an interactable object with the SimpleInteractable script
  4. Test all movement features:
    • Walking and running
    • Jumping
    • Camera controls
    • Interactions

Step 2: Performance Optimization

[Header("Performance")]
public bool enableMovementSmoothing = true;
public int maxCollisionChecks = 10;

void HandleMovement()
{
    // Limit collision checks for performance
    if (controller.isGrounded)
    {
        // ... movement code ...
    }
}

7. Common Issues and Solutions

Issue: Player Sliding on Slopes

Solution: Adjust the Character Controller's Slope Limit and Step Offset values.

Issue: Camera Jitter

Solution: Use LateUpdate() for camera movement and adjust follow speed.

Issue: Movement Feels Sluggish

Solution: Increase acceleration and deceleration values, or adjust input sensitivity.

Issue: Jumping Through Ground

Solution: Ensure the Character Controller's Skin Width is appropriate for your ground colliders.

8. Mini-Task: Create Your First Level

Task: Create a simple test level with:

  1. A ground plane for the player to walk on
  2. Some obstacles to navigate around
  3. A platform to jump onto
  4. An interactable object to test the interaction system

Requirements:

  • Player can move smoothly around the level
  • Camera follows the player properly
  • Jumping works on the platform
  • Interaction system responds to the interactable object

9. Pro Tips for RPG Movement

Movement Feel

  • Responsive Input: Keep input lag minimal
  • Smooth Transitions: Use lerping for acceleration/deceleration
  • Visual Feedback: Add particle effects for footsteps, dust, etc.

Camera Design

  • Comfortable FOV: Use 60-75 degrees for RPG games
  • Smooth Following: Avoid camera snapping or jittering
  • Collision Avoidance: Implement camera collision with walls

Performance

  • Optimize Collision Detection: Use appropriate collision layers
  • Limit Update Frequency: Don't update every frame if not needed
  • Profile Your Code: Use Unity's Profiler to identify bottlenecks

Conclusion & Next Steps

Congratulations! You've successfully created a responsive player character with smooth movement, camera controls, and basic interactions. Your RPG game now has a solid foundation for gameplay.

What you've accomplished:

  • ✅ Player character with Character Controller
  • ✅ Smooth movement with acceleration/deceleration
  • ✅ Camera system with mouse look
  • ✅ Jumping and gravity system
  • ✅ Basic interaction system
  • ✅ Input handling for keyboard and gamepad

In the next lesson, we'll dive into "Basic Combat System" where you'll learn to implement:

  • Attack mechanics and animations
  • Health and damage systems
  • Combat UI and feedback
  • Enemy AI basics

Community Support

Stuck? Have questions? Share your progress or ask for help in our Discord community! Join Discord