Unity入门计划Unity2D动画-脚本与混合树实现玩家角色动画过渡
Posted 九九345
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity入门计划Unity2D动画-脚本与混合树实现玩家角色动画过渡相关的知识,希望对你有一定的参考价值。
目录
3 Update()中定义当前变量与BlendTree变量相关联的逻辑
5.1 animator.SetTrigger/SetFloat
学习的教程
【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)
Unity C#入门教程——动画时钟实现
参考技术A显示当前时间的时钟
为游戏对象创建层级关系
编写脚本并与对象关联
使用命名空间
使用方法更新游戏物体
按时间进行旋转
在此教程中我们编写一个简单的C#脚本来制作时钟指针动画。
要求大家已经对Unity的编辑器有了基本的了解,其实,只需用过Unity几分钟就可以开始该案例了。
很快我们就能完成以下成果。
制作时钟
不用任何Unity资源包,我们从0开始创建Unity工程。缺省的场景中有一个位于 (0, 1, -10), 朝着z轴的负方向的摄像机,为了使摄像机中显示的透视图与场景一致,选择摄像机,并在菜单中选择执行 GameObject /* Align View*将摄像机与当前场景对齐。
啥是 游戏物体 ?
子物体又是哈东东?
我们就简单用立方体来制作时钟的指针,为每个指针使用 GameObject/ Create Other / Cube 创建立方体。对于时针,设置其位置为 (0, 1, 0) ,三个轴上的缩放分别为 (0.5, 2, 0.5) .,对分针位置为 (0, 1.5, 0) ,缩放 (0.25, 3, 0.25) ,秒针的则分别是: (0, 2, 0) 和
**(0.1,4, 0.1)。
**
创建的时钟,以及其中游戏物体间的层级关系。
制作时钟指针动画
要让时钟动起来,就得写点脚本了。在工程视图中右键,选择 Create/ C# Script 新建一个C#脚本,将其命名为 ClockAnimator。 双击打开之,将其中内容清空,从空文件开始。
首先,我们要引用Unity的命名空间,接着就是定义 ClockAnimator 类。将其定义为一个公开的类,继承自[MonoBehaviour]
using UnityEngine;
public class ClockAnimator : [MonoBehaviour]
神马是命名空间?
类又是啥?
关于****[MonoBehaviour]****类?
这样我们就创建了一个可以作为组件的最小的类。保存之,接着从 Project 窗口中把它直接拖放到 Hierarchy (层级)视图的 Clock 游戏物体上,建立其与该游戏对象的关联。当然,也可以使用 Clock 游戏物体的 Add Component 按钮。
将ClockAnimator与Clock关联
要让指针动起来,我们得使用指针组件的 Transform 对象。首先,在刚才的脚本中为每个指针添加一个 Transform 变量,并保存它。这样,这些公开变量就成为可在编辑器中被赋值的组件属性,编辑器会获得这些游戏物体的 Transform 组件,并将它们赋予这些变量,选择 Clock ,将指针对应的游戏物体赋给这些属性。
变量是什么?
using UnityEngine;
public class ClockAnimator : [MonoBehaviour]
为空的ClockAnimator属性和填写属性后的ClockAnimator
下面在脚本中添加一个update方法。这是一个特殊的方法,它会在每一帧播放时调用,我们在这个方法中实现时钟指针的旋转。
using UnityEngine;
public class ClockAnimator : [MonoBehaviour]
方法是啥?
[ Update ] 方法是否应当公开?
保存脚本,编辑器会提示组件中已经有了update方法,并在脚本组件的左侧出现一个复选框,这样我们就能禁用该脚本组件了,当然此时,还是要保持它处于可用状态。
ClockAnimator更新的显示了复选框
每个小时,时针要转动360/12度;分钟指针则是每分钟转动360/60度;而秒针是每秒旋转360/60度。为了程序中使用方便,我们把这些值定义为私有的浮点常量。
using UnityEngine;
public class ClockAnimator : [MonoBehaviour]
常量有什么特殊性?
为了让时钟转动起来,每次update时,我们先得到当前系统的时间。System命名空间中的[DateTime] 结构体 可以用来存储时间,而该结构体中的静态属性Now可以返回当前时间,这样,每次update时,我们就先得到该值并将其存储到一个临时变量中。
using UnityEngine;
using System;
public class ClockAnimator : [MonoBehaviour]
什么是结构体?
属性又是什么?
要让指针转动,就需要更新指针的旋转,而这可以通过设置指针的localRotation实现。使用四元数:[Quaternion]可以定义任意角度的旋转。
因为摄像机朝向是z轴,Unity使用的是左手坐标系统,因此指针应该是绕着Z轴负方向进行旋转。
using UnityEngine;
using System;
public class ClockAnimator : [MonoBehaviour]
时间显示为12:44
四元数quaternion是什么?
旋转又是啥?
改进时钟的实现
很好!在游戏的运行模式中,时钟已经可以显示当前的时间了,但是使用离散的时间值让它看起来更像是一个电子钟。下面增加一个选项,实现模拟时间显示。在脚本中添加一个公开的布尔变量 analog ,用它来决定update方法中的显示方式,这样我们就能在编辑器中直接控制时钟的显示方式了,即使是在游戏运行时。
using UnityEngine;
using System;
public class ClockAnimator : [MonoBehaviour]
添加了analog模式的ClockAnimator
对于模拟现实显示的选项,我们使用和之前稍稍不同的方式。这次不用[DateTime].Now,我们用[DateTime].Now.TimeOfDay,这是一个时间段[TimeSpan]。返回的是自每天午夜开始到现在已经经过的小时,分钟和秒数。由于这个值是双精度数,使用时我们需要将它们转换为float类型。
类型转换的意思是
using UnityEngine;
using System;
public class ClockAnimator : [MonoBehaviour]
按照模拟方式显示的时钟
现在时钟也可以按模拟方式显示了。
unitypackage PDF
**
****原文链接:
** http://catlikecoding.com/unity/tutorials/clock/
以上是关于Unity入门计划Unity2D动画-脚本与混合树实现玩家角色动画过渡的主要内容,如果未能解决你的问题,请参考以下文章