柚子/Unity HD2D 角色控制脚本

Created Sun, 12 May 2024 00:00:00 +0000 Modified Thu, 05 Dec 2024 15:58:30 +0000
By Yoyo 1537 Words 7 min Edit

前言

最近对 HD2D 风格的游戏特别感兴趣,便开始着手制作一个简单的角色控制脚本。

实现步骤

1. 创建角色刚体和胶囊碰撞器

首先在角色的对象上创建一个刚体和胶囊碰撞器,并设置好参数。

2. 创建角色动画

在角色的对象上创建一个动画控制器,并添加相应的动画。

3. 创建角色控制器脚本

新建一个脚本,开始一步步实现吧!

3.1 定义变量

首先定义一些变量,包括速度、跳跃力度、地面检测忽略的图层、动画组件、Sprite Renderer 组件、人物移动、人物身上的胶囊碰撞体、点、半径、是否在地面。

public class PlayerMovement : MonoBehaviour
{
    // 获取人物的刚体组件
    private new Rigidbody rigidbody;

    // 人物的速度
    public float speed = 4F;

    // 跳跃力度
    private float jumpForce = 3F;

    // 地面检测忽略的图层
    public LayerMask ignoreLayer;

    // 动画组件
    private Animator anim;

    // Sprite Renderer组件
    private SpriteRenderer spriteRenderer;

    // 人物移动
    private Vector2 input;

    private Vector3 moveInput;

    // 人物身上的胶囊碰撞体
    private CapsuleCollider capsuleCollider;

    private Vector3 pointBottom, pointTop;

    private float radius;

    private bool isGround;
}

3.2 人物行走

我们要让人物正常行走,首先需要获取刚体组件和动画器组件。

    void Start()
    {
        // 获取刚体组件
        rigidbody = GetComponent<Rigidbody>();
        // 获取动画器
        anim = GetComponent<Animator>();
    }

然后我们需要在 Update 中获取输入,并将其赋值给 input 变量。

    void Update()
    {
        // 获取水平垂直输入
        input.x = Input.GetAxisRaw("Horizontal");
        input.y = Input.GetAxisRaw("Vertical");
        moveInput = new Vector3(input.x * speed, rigidbody.velocity.y, input.y * speed);
    }

接着我们需要将 input 变量乘以速度,将其赋值给刚体的速度。

    void FixedUpdate()
    {
        // 人物的移动
        rigidbody.velocity = moveInput;   
    }

接着在 void Update () 中播放动画。

// 设定isRunning的值,是true还是false。根据返回向量的长度来决定的。
anim.SetBool("isRunning", input.magnitude > 0 ? true : false); 

3.3 人物精灵图翻转

刚刚实现的效果我们可以发现一个小问题,由于我们只制作了单个方向的行走动画,导致向左向右行走时都是播放相同方向的东西,显得异常突兀。

为了解决这个问题,我们需要让角色的精灵图翻转,具体的做法是,判断 input. X < 0 时,将角色的 Sprite Renderer 组件的 flipX 属性设置为 true,否则设置为 false。

首先我们需要在 void Start () 中获取 Sprite Renderer 组件。

// 获取Sprite Renderer组件
spriteRenderer = GetComponent<SpriteRenderer>();

然后在 void Update () 中判断 input. X < 0,并设置 spriteRenderer 的 flipX 属性。

// 根据水平输入来翻转Sprite
if (input.x != 0)
{
    spriteRenderer.flipX = input.x < 0;
}

现在我们获得了一个看上去正常得多的行走动画。

3.4 人物跳跃

接下来我们要实现人物跳跃,首先我们需要判断角色是否在地面上,如果在地面上,我们就允许跳跃。如果不在地面上,则播放悬空动画,并禁止再次跳跃。

这一部分参考傅老師MrFu的黑魂DarkSouls複刻經典教程

首先我们需要在 void Start () 中获取胶囊碰撞体组件。

capsuleCollider = GetComponent<CapsuleCollider>();
radius = capsuleCollider.radius;

然后创建一个 bool OnGround () 函数用于判断角色是否在地面上。

    bool OnGround()
    {
        pointBottom = transform.position + transform.up * radius - transform.up * 0.1f;
        pointTop = transform.position + transform.up * capsuleCollider.height - transform.up * radius;
        Collider[] colliders = Physics.OverlapCapsule(pointBottom, pointTop, radius, ~ignoreLayer);
        Debug.DrawLine(pointBottom, pointTop, Color.green);
        if (colliders.Length != 0)
        {
            isGround = true;
            return true;
        }
        else
        {
            isGround = false;
            return false;
        }
    }

然后我们需要在 void Update () 中调用 OnGround () 函数,并在角色在地面上时播放跳跃动画。

anim.SetBool("isJumping", !OnGround());

接着我们在 void Update () 中获取按键输入,当角色在地面上并且按下键盘空格键或者手柄 A 键时,给予角色一个向上的跳跃力度。

        // 检查跳跃输入
        if (Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.JoystickButton0))
        {
            if (isGround)
            {
                // 应用向上的力
                rigidbody.AddForce(new Vector3(0, jumpForce, 0), ForceMode.VelocityChange);
            }
        }

现在我们就能获得一个比较完美的跳跃效果了。

完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    // 获取人物的刚体组件
    private new Rigidbody rigidbody;

    // 人物的速度
    public float speed = 4F;

    // 跳跃力度
    private float jumpForce = 3F;

    // 地面检测忽略的图层
    public LayerMask ignoreLayer;

    // 动画组件
    private Animator anim;

    // Sprite Renderer组件
    private SpriteRenderer spriteRenderer;

    // 人物移动
    private Vector2 input;

    // 人物身上的胶囊碰撞体
    private CapsuleCollider capsuleCollider;

    private Vector3 pointBottom, pointTop;

    private float radius;

    private bool isGround;

    // Start is called before the first frame update
    void Start()
    {
        // 获取刚体组件
        rigidbody = GetComponent<Rigidbody>();
        // 获取动画器
        anim = GetComponent<Animator>();
        // 获取Sprite Renderer组件
        spriteRenderer = GetComponent<SpriteRenderer>();
        capsuleCollider = GetComponent<CapsuleCollider>();
        radius = capsuleCollider.radius;
    }

    void FixedUpdate()
    {
        // 人物的移动
        rigidbody.velocity = new Vector3(input.x * speed, rigidbody.velocity.y, input.y * speed);  
    }

    // Update is called once per frame
    void Update()
    {
        // 获取水平垂直输入
        input.x = Input.GetAxisRaw("Horizontal");
        input.y = Input.GetAxisRaw("Vertical");

        // 根据水平输入来翻转Sprite
        if (input.x != 0)
        {
            spriteRenderer.flipX = input.x < 0;
        }

        // 设定isRunning的值,是true还是false。根据返回向量的长度来决定的。
        anim.SetBool("isRunning", input.magnitude > 0 ? true : false);
        anim.SetBool("isJumping", !OnGround());

        // 检查跳跃输入
        if (Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.JoystickButton0))
        {
            if (isGround)
            {
                // 应用向上的力
                rigidbody.AddForce(new Vector3(0, jumpForce, 0), ForceMode.VelocityChange);
            }
        }
    }

    // https://www.bilibili.com/video/av21513489/?p=19
    bool OnGround()
    {
        pointBottom = transform.position + transform.up * radius - transform.up * 0.1f;
        pointTop = transform.position + transform.up * capsuleCollider.height - transform.up * radius;
        Collider[] colliders = Physics.OverlapCapsule(pointBottom, pointTop, radius, ~ignoreLayer);
        Debug.DrawLine(pointBottom, pointTop, Color.green);
        if (colliders.Length != 0)
        {
            isGround = true;
            return true;
        }
        else
        {
            isGround = false;
            return false;
        }
    }
}