概述

在2D游戏中,角色移动是玩家交互的核心。一个流畅、响应迅速的角色移动系统可以显著提升游戏体验。本教程将引导你一步步构建一个完整的2D角色移动系统。

1. 设置2D环境

首先,确保你的Unity项目设置为2D模式:

  • 在Unity编辑器中,选择 Edit > Project Settings > Editor
  • Default Behavior Mode 设置为 2D
  • 确保摄像机设置为 Orthographic(正交投影)

创建玩家对象

在Hierarchy面板中,右键点击选择 2D Object > Sprite 创建一个精灵对象。

为这个对象添加以下组件:

  • Rigidbody2D - 处理物理
  • BoxCollider2D - 碰撞检测
  • Animator - 动画控制

2. 基础移动脚本

创建一个名为 PlayerMovement.cs 的C#脚本:

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    [Header("移动参数")]
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float jumpForce = 10f;
    
    [Header("地面检测")]
    [SerializeField] private Transform groundCheck;
    [SerializeField] private float groundCheckRadius = 0.2f;
    [SerializeField] private LayerMask groundLayer;
    
    private Rigidbody2D rb;
    private float horizontalInput;
    private bool isGrounded;
    private bool canJump = true;
    
    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }
    
    private void Update()
    {
        GetInput();
        CheckGround();
        
        if (Input.GetButtonDown("Jump") && isGrounded && canJump)
        {
            Jump();
        }
    }
    
    private void FixedUpdate()
    {
        Move();
    }
    
    private void GetInput()
    {
        horizontalInput = Input.GetAxisRaw("Horizontal");
    }
    
    private void Move()
    {
        rb.velocity = new Vector2(horizontalInput * moveSpeed, rb.velocity.y);
    }
    
    private void Jump()
    {
        rb.velocity = new Vector2(rb.velocity.x, jumpForce);
        canJump = false;
        Invoke(nameof(ResetJump), 0.2f);
    }
    
    private void ResetJump()
    {
        canJump = true;
    }
    
    private void CheckGround()
    {
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
    }
    
    private void OnDrawGizmosSelected()
    {
        if (groundCheck != null)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
        }
    }
}

3. 高级移动功能

3.1 冲刺功能

为角色添加冲刺功能,让移动更加灵活:

[Header("冲刺参数")]
[SerializeField] private float dashSpeed = 15f;
[SerializeField] private float dashDuration = 0.2f;
[SerializeField] private float dashCooldown = 1f;

private bool canDash = true;
private bool isDashing = false;

private void Update()
{
    // ... 之前的代码
    
    if (Input.GetKeyDown(KeyCode.LeftShift) && canDash)
    {
        StartCoroutine(Dash());
    }
}

private System.Collections.IEnumerator Dash()
{
    canDash = false;
    isDashing = true;
    float originalGravity = rb.gravityScale;
    rb.gravityScale = 0f;
    
    // 根据方向冲刺
    float dashDirection = Mathf.Sign(horizontalInput);
    if (dashDirection == 0) dashDirection = transform.localScale.x > 0 ? 1 : -1;
    
    rb.velocity = new Vector2(dashDirection * dashSpeed, 0f);
    
    yield return new WaitForSeconds(dashDuration);
    
    isDashing = false;
    rb.gravityScale = originalGravity;
    
    yield return new WaitForSeconds(dashCooldown);
    canDash = true;
}

3.2 墙体跳跃

实现墙体跳跃功能,增加游戏的操作深度:

[Header("墙体跳跃")]
[SerializeField] private Transform wallCheck;
[SerializeField] private float wallSlideSpeed = 2f;
[SerializeField] private Vector2 wallJumpForce = new Vector2(10f, 15f);

private bool isTouchingWall;
private bool isWallSliding;

private void CheckWall()
{
    isTouchingWall = Physics2D.OverlapCircle(wallCheck.position, groundCheckRadius, groundLayer);
    
    if (isTouchingWall && !isGrounded && horizontalInput != 0)
    {
        isWallSliding = true;
        rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlideSpeed, float.MaxValue));
    }
    else
    {
        isWallSliding = false;
    }
}

private void WallJump()
{
    if (isWallSliding && Input.GetButtonDown("Jump"))
    {
        // 向远离墙体的方向跳跃
        float wallJumpDirection = -Mathf.Sign(horizontalInput);
        rb.velocity = new Vector2(wallJumpDirection * wallJumpForce.x, wallJumpForce.y);
    }
}

4. 动画控制

4.1 创建动画控制器

为角色创建动画状态机:

  • 在Animator窗口中创建以下状态:
    • Idle (空闲)
    • Run (奔跑)
    • Jump (跳跃)
    • Fall (下落)
    • Dash (冲刺)
  • 设置适当的转换条件和参数:
    • Speed (Float)
    • isGrounded (Bool)
    • isDashing (Bool)

4.2 动画控制脚本

创建一个脚本处理动画状态:

using UnityEngine;

public class PlayerAnimation : MonoBehaviour
{
    private Animator animator;
    private PlayerMovement movement;
    private Rigidbody2D rb;
    
    private void Start()
    {
        animator = GetComponent<Animator>();
        movement = GetComponent<PlayerMovement>();
        rb = GetComponent<Rigidbody2D>();
    }
    
    private void Update()
    {
        UpdateAnimation();
    }
    
    private void UpdateAnimation()
    {
        // 设置速度参数
        animator.SetFloat("Speed", Mathf.Abs(rb.velocity.x));
        
        // 设置地面状态
        animator.SetBool("isGrounded", movement.IsGrounded);
        
        // 设置跳跃/下落状态
        if (!movement.IsGrounded)
        {
            animator.SetFloat("VerticalVelocity", rb.velocity.y);
        }
        
        // 冲刺动画
        animator.SetBool("isDashing", movement.IsDashing);
        
        // 翻转角色
        if (rb.velocity.x > 0.1f)
        {
            transform.localScale = new Vector3(1, 1, 1);
        }
        else if (rb.velocity.x < -0.1f)
        {
            transform.localScale = new Vector3(-1, 1, 1);
        }
    }
}

5. 优化和调试

5.1 移动平滑处理

添加移动平滑,防止角色移动过于僵硬:

[Header("移动平滑")]
[SerializeField] private float acceleration = 10f;
[SerializeField] private float deceleration = 10f;
[SerializeField] private float velPower = 0.9f;

private void MoveWithSmoothing()
{
    float targetSpeed = horizontalInput * moveSpeed;
    float speedDiff = targetSpeed - rb.velocity.x;
    float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : deceleration;
    float movement = Mathf.Pow(Mathf.Abs(speedDiff) * accelRate, velPower) * Mathf.Sign(speedDiff);
    
    rb.AddForce(movement * Vector2.right);
}

5.2 空中控制

调整空中移动控制,提供更好的游戏体验:

[Header("空中控制")]
[SerializeField] private float airAcceleration = 5f;
[SerializeField] private float airDeceleration = 5f;
[SerializeField] private float airControlMultiplier = 0.5f;

private void AirControl()
{
    if (!isGrounded)
    {
        float targetSpeed = horizontalInput * moveSpeed * airControlMultiplier;
        float speedDiff = targetSpeed - rb.velocity.x;
        float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? airAcceleration : airDeceleration;
        float movement = Mathf.Pow(Mathf.Abs(speedDiff) * accelRate, velPower) * Mathf.Sign(speedDiff);
        
        rb.AddForce(movement * Vector2.right);
    }
}

6. 测试和调试

重要提示:在测试角色移动系统时,请注意以下要点:
  • 确保地面检测点位置正确
  • 调整碰撞器大小以匹配角色精灵
  • 测试不同平台上的表现(Windows、Android等)
  • 使用Unity Profiler监控性能

调试技巧:

  1. 使用 Debug.DrawRay 可视化射线检测
  2. 在Inspector中添加调试信息显示
  3. 创建调试模式开关,方便测试
  4. 使用Unity的Frame Debugger分析每帧状态

总结

通过本教程,你已经创建了一个完整的2D角色移动系统,包含:

  • 基础移动和跳跃
  • 冲刺功能
  • 墙体跳跃和滑墙
  • 完整的动画控制
  • 移动平滑和空中控制优化

这个系统可以作为大多数2D平台游戏的基础。你可以根据自己的游戏需求进行扩展和修改,比如添加二段跳、爬墙、特殊技能等。

下一步建议:

  1. 添加声音效果(脚步声、跳跃声等)
  2. 实现粒子特效(冲刺特效、落地灰尘等)
  3. 创建状态机管理角色状态
  4. 添加输入缓冲改善手感
  5. 实现镜头跟随和镜头效果

希望这个教程对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言讨论。

Happy Coding! 🎮