【Unity入门计划】Unity2D动画(2)-脚本与混合树实现玩家角色动画过渡

发布于:2023-01-22 ⋅ 阅读:(16) ⋅ 点赞:(0) ⋅ 评论:(0)

目录

1 玩家角色移动伴随的简单动画 

1.1 行走

1.2 停留

1.3 攻击敌人(触发型)

1.4 受伤(触发型)

1.5 跳跃

1.6 下蹲

2 动画间的过渡

3 过渡的判断逻辑

3.1 行走与停留:移动速度

3.2 受伤&攻击:bool变量

4 根据实例展示Blend Tree与脚本的协作

4.1 在状态机中创建BlendTree

4.2 创建连接:Make Transition

4.3 创建所需变量并在Transition中加入逻辑

4.4 搭建每个混合树变量与Motion的关系

4.5 脚本中加入玩家键盘控制与角色动作的逻辑关系

1 获取动画组件

2 定义变量

3 Update()中定义当前变量与BlendTree变量相关联的逻辑

4 在脚本中关联bool变量Hit

5 效果展示

5 涉及到的Unity中C#用法

5.1 animator.SetTrigger/SetFloat

5.2 .Set()

5.3 .magnitude

5.4 .Normalize()

5.5 Mathf.Approximately()

5.6 new


学习的教程

【unity2021入门教程】80-2D游戏开发教程系列-03-RubyAdventure2DRpg官方教程-28-添加Ruby动画控制脚本_哔哩哔哩_bilibili

本篇博客依旧是建立在RubyAdventure项目上来写的,2D动画内容很多、覆盖面很广,因此以下局的一些动画例子几乎都是这个项目里涉及到的,没办法涵盖完全,以后有机会会补充清楚,这里就先围绕着Ruby角色的动画来谈谈如何实现他的动画过渡。 

1 玩家角色移动伴随的简单动画 

第一章就简单谈一谈,对于一个传统的RPG 2D小游戏,玩家操控的主角角色在游戏场景中会需要哪些动画。

1.1 行走

如果一个游戏视角是俯视的上帝视角,角色就只有向上、向下、向左、向右四个方向的行走空间,一般如果改变方向,直接瞬间切换。

还需要注意的一点是,既然行走有四个方向,那就决定了接下来的所有动作,四个方向都应该有不同的表现过程,因此一般动画片段每个状态都会由Left、Right、Up、Down四组组成。

1.2 停留

为了让细节感更足,很多游戏会把人物停止不动时喘气动作做出来,就会有起伏感。因此除了行走动画,还会有人物停止不动时的动画效果。

当然,停留动画对于四个不同的方向都需要有自己的动画片段。

1.3 攻击敌人(触发型)

除了基本的移动动画,一般角色还有发射炮弹、飞镖等等攻击时的动画效果,而且是在发射炮弹的瞬间触发的,例如下面这个向右发射的动作演示。

攻击动画与上述行走、停留动画不同的是,攻击动画属于触发型动画,由于攻击这个动作具有瞬时性,因此在角色脚本中一般以一个bool变量体现。

同样的,攻击也需要有四个方向不同的动画。

1.4 受伤(触发型)

一般RPG会有敌人和陷阱的设置,场景中如果角色触碰到敌人、受到敌人炮弹攻击、误入陷阱等,都会受到伤害,此时也需要一个受到伤害瞬间的动画效果。

与攻击动画相同,受伤也属于触发型动画,在角色脚本中一般以一个bool变量体现。

同样的,受伤也需要有四个方向不同的动画。

1.5 跳跃

对于一些2D横轴游戏,玩家角色一般会把移动分为左右行走、向上跳跃和下蹲的四种运动状态。

这里我认为跳跃是一种长久的状态,在角色从离开地板到落向地板的过程中都会维持一个跳跃的姿势。

1.6 下蹲

下蹲也是横轴游戏一个常见的动作状态,一般是长按某个键一直处于下蹲动作状态。 

2 动画间的过渡

游戏中不可避免会遇到处理动作过渡的情况,这也是Unity中Blend Tree混合树的最大的功能——通过某些参数处理动画的混合。

下面是我正在学习教程中角色混合树中动作片段之间的Transition关系:

其中:Moving、Idle、Hit、Launch分别表示移动(行走)、停留、受伤、攻击的动作,可见动作之间的混合情况是非常多的,例如:

  • 走着走着停下来
  • 停下来的时候受到攻击、受伤后停下来
  • 走着走着开始攻击敌人

我们需要梳理清楚,采用什么变量来进行两个动作间的过渡判断才合适呢?

3 过渡的判断逻辑

3.1 行走与停留:移动速度

这个很好解决,当移动速度大于0时就判断在行走,不移动时判断为停留。

3.2 受伤&攻击:bool变量

前面提到了受伤和攻击其实都是一个瞬时的东西,因此其实在判断是否过渡的时候用一个bool变量就可以跟其他动画相互跳转。

4 根据实例展示Blend Tree与脚本的协作

4.1 在状态机中创建BlendTree

创建了4个混合树并按需命名

4.2 创建连接:Make Transition

右键Make Transition创建混合树之间的联系。

4.3 创建所需变量并在Transition中加入逻辑

  • 在Animator窗口左侧的变量区,新建混合所需变量

由上面讨论的过渡的判断逻辑可知,本实例所需变量有:当前移动速度Speed、攻击和受伤的触发bool变量Hit和Launch。

同时,由于玩家角色做某种动作的时候还需要一个变量控制他面朝的方向,因此还需要一个Vector2类型的变量,这里分别创建一个Look X和Look Y,二者组成了二维变量(Look X, Look Y)

  • 还需要在混合树的属性栏添加变量控制的逻辑——Hit

Animator窗口中指向Hit的Transition,还需要调用上面创建的Trigger也就是bool类型的变量Hit,

点击箭头,进入Transition属性栏,在Conditions中加入Hit变量 

  •  在混合树的属性栏添加变量控制的逻辑——Idle

状态idle判断的条件是看Speed是否接近于0(由于浮点数没办法全等于0),因此可以设定某个限值,例如0.1,条件给Speed < 0.1即可,

 在混合树的属性栏添加变量控制的逻辑——Run

Run的判断条件是,Speed > 0.1

4.4 搭建每个混合树变量与Motion的关系

这里就是将每个混合树代表的动作合集,与其关联的动作片段,通过变量联系在一起。

例如停止状态idle,它包含四个不同方向idle的动作效果,因此需要通过(Look X, Look Y)这个二维变量的值来控制。

其他三个状态同理,都是基于(Look X, Look Y)这个二维变量的值来控制的。

4.5 脚本中加入玩家键盘控制与角色动作的逻辑关系

首先,玩家操作的角色和场景中不需要被操作的角色不同,还需要将不同状态的动画与玩家键盘输入的轴值关联在一起,才能实现动作与玩家移动角色同步。

项目中的玩家角色都会有一个专门控制其移动的脚本,例如实例项目中就有Ruby Controller这个脚本。

注意:以下出现的脚本代码只会展示当前涉及到的代码段,篇幅原因省略了其余的代码内容~

1 获取动画组件

与修改和操作其余任何组件一样,动画对于游戏对象来说也是由Animator组件控制的,因此在进行所有操作之前,需要先获取当前游戏对象的动画组件。

public class RubyController : MonoBehaviour
{
    Animator animator;
    
    private void Start()
    {   
        animator = GetComponent<Animator>();
    }
}

2 定义变量

这里需要定义2个变量:

  • 一个获取当前角色移动时轴输入值的二维变量move
  • 一个用以储存移动时当前面朝向的二维变量lookDirection
public class RubyController : MonoBehaviour
{
    //当前移动使的轴输入
    Vector2 move;
    //移动时的面朝向,初始朝向右
    Vector2 lookDirection = new Vector2(1, 0);
}

3 Update()中定义当前变量与BlendTree变量相关联的逻辑

由于玩家操作轴是在Update()中完成的,因此需要在Update()里加入定义的变量move、look与BlendTree中的几个变量的逻辑关系。

public class RubyController : MonoBehaviour
{
    void Update()
    {
        //确定静止时的面朝向
        //确定当前的移动的朝向,通过按键确定
        move = new Vector2(horizontal, vertical);
        //如果当前Ruby正在移动:
        //Approximately表示判断浮点数是否近似相等
        if (!Mathf.Approximately(move.x, 0.0f) || !Mathf.Approximately(move.y, 0.0f))
        {
            //赋予移动时当下的朝向
            lookDirection.Set(move.x,move.y);
            //归一化
            //blend tree 中表示方向的参数范围是[-1,1],因此需要归一化
            lookDirection.Normalize();
        }
        //动画
        //传递面朝方向
        animator.SetFloat("Look X", lookDirection.x);
        animator.SetFloat("Look Y", lookDirection.y);
        //传递矢量长度,作为速度
        animator.SetFloat("Speed", move.magnitude);
    }
}

4 在脚本中关联bool变量Hit

当前没有涉及到攻击的设定,因此先不考虑角色攻击的效果,仅考虑受伤害时的动画。受伤混合树中定义的bool变量Hit控制玩家是否触发受伤动画。

脚本中,在减血的方法中添加下列语句

    //更改生命值
    //amount是游戏中加血/减血的操作
    public void ChangeHealth(int amount)
    {
        if (amount < 0)//if >0加血,则不进入无敌时间
        {
            //受伤的动画
            animator.SetTrigger("Hit");
        }
    }

5 效果展示

5 涉及到的Unity中C#用法

5.1 animator.SetTrigger/SetFloat

定义动画组件中的变量值,调用格式:

animator.SetTrigger(String)

animator.SetFloat(String, value)

5.2 .Set()

给某个Vector变量直接赋值

vector2.Set(x, y)

5.3 .magnitude

vector2.magnitude

返回一个浮点数,表示当前相对速度大小(保留大小丢失方向信息)

5.4 .Normalize()

vector2.Normalize()

将一个向量归一化操作,变成大小为1仅表示方向的单位向量(保留方向丢失大小信息)

5.5 Mathf.Approximately()

Mathf.Approximately(float, float)

浮点数中近似相等比较,由于浮点数有很多位小数点,如果直接用全等判断“==” 就永远不可能相等,这里相当于给判断多了一个Epsilon的误差值。

5.6 new

由于Vector2不能像方法一样使用,一般给一个Vector2类型的变量赋某个二维值的时候需要用到,例如:

Vector2 sample = new Vector2(1, 0)


网站公告

欢迎关注微信公众号

今日签到

点亮在社区的每一天
签到