章节目录

  • 前言
  • 制作角色的动画状态机
  • 加入控制代码
  • 修改动画和状态设置
  • 后言
  • 参考链接

前言

  为了提高玩家的游戏体验,我们常常需要在角色运动的时候,根据它的运动状态播放对应的动画。为了能够控制当前播放什么动画,我们首先需要制作动画状态机,再用代码去控制动画状态机切换当前的状态,从而切换当前播放的动画。


制作角色的动画状态机

  我们在给角色制作动画状态机来控制我们之前制作好的角色动画之前,我们需要确定我们有哪些状态,以及控制这些状态转移的条件是什么。

各个角色动画之间的状态转换规则如下:

  1. 当角色不着地时,不播放任何状态
  2. 角色着地时播放Idle播放动画
  3. 当角色着地水平移动时,播放Walk动画
  4. 当角色触发跳跃事件时,播放Jump动画
  5. Jump动画播放完之后,着地播放Idle动画
  6. 当角色触发射击事件时,播放Shoot动画
  7. Shoot动画播放完之后,着地播放Idle动画
  8. 当角色触发死亡事件时,播放Death动画
  9. Death动画播放完之后,自动播放Falling动画

  根据状态转换规则,我们可以得出控制状态转换的所有参数

状态转换参数:

  • Grounded:Bool
  • Speed:Float
  • Jump:Trigger
  • Death:Trigger
  • Shoot:Trigger

  接着,我们在Unity顶部菜单栏选中Window->Animator打开动画状态机编辑窗口,然后在Hierarchy窗口中选中Player编辑角色的动画状态机。在Unity中,一个状态控制一个动画。根据上面状态转换规则,我们还需要创建一个没有任何动画的空状态,并将其作为整个状态机的默认状态。我们在Animator窗口中点击鼠标右键选择Empty创建一个空状态,将其命名为Empty之后,点击鼠标右键编辑这个状态,然后选择Set as Layer Default State将其设置为默认状态。最后,我们根据上面得到的控制状态转换的所有参数来创建状态转换参数,并构建角色的动画状态机如下:

  创建完动画状态机之后,我们为状态之间的转移边设置条件。点击状态之间的转移边,然后在右侧的Inspector面板进行设置。

每条转移边的具体设置如下:

  • Empty -> Idle:

    • Has Exit Time: false
    • Interruption Source": Next State
    • Conditions: Grounded(true)
  • Idle -> Empty:
    • Has Exit Time: false
    • Conditions: Grounded(false)
  • Idle -> Walk:
    • Has Exit Time: false
    • Conditions: Speed(Greater 0.1)
  • Walk -> Idle:
    • Has Exit Time: false
    • Interruption Source": Next State
    • Conditions: Speed(Less 0.1)
  • Walk -> Empty:
    • Has Exit Time: false
    • Conditions: Grounded(false)
  • AnyState -> Jump:
    • Has Exit Time: false
    • Can Transition To Self: false
    • Conditions: Jump(Trigger)
  • Jump -> Empty:
    • Has Exit Time: true
    • Interruption Source": Next State
    • Conditions: List is Empty
  • AnyState -> Shoot:
    • Has Exit Time: false
    • Can Transition To Self: True
    • Conditions: Shoot(Trigger)
  • Shoot -> Empty:
    • Has Exit Time: true
    • Interruption Source": Current State then Next State
    • Conditions: List is Empty
  • AnyState -> Death:
    • Has Exit Time: false
    • Can Transition To Self: false
    • Conditions: Death(Trigger)
  • Death -> Falling:
    • Has Exit Time: true
    • Conditions: List is Empty

加入控制代码

  角色的动画状态机编辑完成之后,我们还需要使用代码来控制动画状态机的状态转移。打开PlayerController.cs,插入动画控制代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(AudioSource))]
[RequireComponent(typeof(Animator))]
public class PlayerController : MonoBehaviour {[Tooltip("角色初始朝向是否朝向右边")]public bool FacingRight = true;[Tooltip("移动时角色加速的力大小")]public float MoveForce = 365f;[Tooltip("角色移动的最大速度")]public float MaxSpeed = 5f;[Tooltip("跳跃时向上加速的力大小")]public float JumpForce = 1000f;[Tooltip("检测角色是否落地")]public Transform GroundCheck;[Tooltip("跳跃音效")]public AudioClip[] JumpClips;// 记录角色当前是否处于准备跳跃状态private bool m_IsReadyToJump;// 记录角色当前是否正处于跳跃状态private bool m_IsJumping;// 记录角色当前是否处于着地状态private bool m_GroundedStatus;// 组件引用变量private Rigidbody2D m_Rigidbody2D;private AudioSource m_AudioSource;private Animator m_Animator;private void Awake() {// 获取组件引用m_Rigidbody2D = GetComponent<Rigidbody2D>();m_AudioSource = GetComponent<AudioSource>();m_Animator = GetComponent<Animator>();}private void Start() {// 监测变量是否正确赋值if(GroundCheck == null) {Debug.LogError("请先设置GroundCheck");}// 初始化变量m_IsReadyToJump = false;m_IsJumping = false;m_GroundedStatus = false;}private void Update() {// 通过检测角色和groundCheck之间是否存在Ground层的物体来判断当前是否落地m_GroundedStatus = Physics2D.Linecast(transform.position,GroundCheck.position,LayerMask.GetMask("Obstacle"));// 设置动画状态机控制参数m_Animator.SetBool("Grounded", m_GroundedStatus);// 着地时,如果当前不处于跳跃状态且按下了跳跃键,进入准备跳跃状态if(m_GroundedStatus && !m_IsJumping && Input.GetButtonDown("Jump")) {m_IsReadyToJump = true;}// 刚刚落地,退出跳跃状态if(m_GroundedStatus && m_IsJumping) {m_IsJumping = false;}}private void FixedUpdate() {//获取水平输入float h = Input.GetAxis("Horizontal");// 设置动画状态机控制参数m_Animator.SetFloat("Speed", Mathf.Abs(h));// 若h * m_Rigidbody2D.velocity.x为正数且小于MaxSpeed,表示需要继续加速// 若h * m_Rigidbody2D.velocity.x为负数,则表示需要反向加速if(h * m_Rigidbody2D.velocity.x < MaxSpeed) {m_Rigidbody2D.AddForce(Vector2.right * h * MoveForce);}//设置物体速度的阈值if(Mathf.Abs(m_Rigidbody2D.velocity.x) > MaxSpeed) {m_Rigidbody2D.velocity = new Vector2(Mathf.Sign(m_Rigidbody2D.velocity.x) * MaxSpeed,m_Rigidbody2D.velocity.y);}//判断当前是否需要转向if(h > 0 && !FacingRight) {Flip();}else if(h < 0 && FacingRight) {Flip();}// 跳跃if(m_IsReadyToJump) {Jump();}}private void Jump() {// 进入跳跃状态m_IsJumping = true;// 设置一个竖直向上的力m_Rigidbody2D.AddForce(new Vector2(0f, JumpForce));// 设置动画状态机控制参数m_Animator.SetTrigger("Jump");// 退出准备跳跃状态,避免重复跳跃m_IsReadyToJump = false;//随机在角色当前所处的位置播放一个跳跃的音频if(JumpClips.Length > 0) {int i = Random.Range(0, JumpClips.Length);AudioSource.PlayClipAtPoint(JumpClips[i], transform.position);}}private void Flip() {// 修改当前朝向FacingRight = !FacingRight;// 修改scale的x分量实现转向this.transform.localScale = Vector3.Scale(new Vector3(-1, 1, 1),this.transform.localScale);}
}

修改动画和状态设置

  点击运行游戏,可以看到角色已经有了动画。但是,我们发现角色跳跃的时候,一直在重复播放Jump动画。这是因为我们在制作动画的时候,为了便于预览,Unity默认将我们的动画设置为循环动画。我们需要修改一下JumpShootDeath这三个动画的动画设置。打开Assets\Animation\Player,分别选中这三个动画,然后取消Loop Time的勾选。

  此外,为了让优化动画效果,我们还可以在Animator设置各个状态的动画播放速度。这里,我们将JumpShoot这两个状态的Speed分别设置为2.54

  修改完成之后,再次运行游戏,可以看到Jump动画的播放速度变快了。


后言

  至此,我们已经制作好了角色的动画状态机,并使用代码控制IdleWalkJump这三个动画的切换,剩下的动画控制将在后面提到。最后,本篇文章所做的修改,可以在PotatoGloryTutorial这个仓库的essay6分支下看到,读者可以clone这个仓库到本地进行查看。


参考链接

  1. Animator Controller
  2. Animation States
  3. Animation transitions

《土豆荣耀》重构笔记(八)添加角色的动画相关推荐

  1. 《土豆荣耀》重构笔记(三)创建游戏场景

    章节目录 前言 导入资源 调整Unity布局 创建游戏场景 设置Aspect 放置Sprite Sorting Layer 组织场景中的物体 后言 参考链接 前言   为了记录重构笔记的进度,我在Gi ...

  2. 《土豆荣耀》重构笔记(五)创建角色以及怪物的动画

    章节目录 前言 创建角色和怪物 创建怪物的动画 创建角色动画 制作Prefab 后言 参考链接 前言   在游戏中,角色和AI的移动都需要配上相应的动画,以免让玩家产生游戏中的角色或者AI在平移的不真 ...

  3. 牌类游戏使用微服务重构笔记(八): 游戏网关服务器

    网关服务器 所谓网关,其实就是维持玩家客户端的连接,将玩家发的游戏请求转发到具体后端服务的服务器,具有以下几个功能点: 长期运行,必须具有较高的稳定性和性能 对外开放,即客户端需要知道网关的IP和端口 ...

  4. Unity2d学习笔记(一)添加角色地图并且实现人物移动

    写这个系列的目的 最近在学习unity2d游戏制作,这个系列的文章就是想将我学到的知识记录下来,方便自己以后复习回顾. Unity2d基础知识 众所周知,Unity是一个游戏引擎,就我的理解来说,游戏 ...

  5. MongoDB 学习笔记八 复制、分片、备份与恢复、监控

    MongoDB 学习笔记八 复制.分片.备份与恢复.监控 MongoDB复制(副本集) 什么是复制? MongoDB 复制原理 MongoDB 副本集设置 副本集添加成员 MongoDB 分片 分片 ...

  6. 黑马程序员_java自学学习笔记(八)----网络编程

    黑马程序员_java自学学习笔记(八)----网络编程 android培训. java培训.期待与您交流! 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无 ...

  7. 牌类游戏使用微服务重构笔记(四): micro框架使用经验

    项目依赖 推荐使用go module, 我选择go module的最主要原因是足够简单,可以脱离gopath,就跟写nodejs一样,随便在一个地方新建一个文件夹就可以撸代码了,clone下来的源码也 ...

  8. 吴恩达《机器学习》学习笔记八——逻辑回归(多分类)代码

    吴恩达<机器学习>笔记八--逻辑回归(多分类)代码 导入模块及加载数据 sigmoid函数与假设函数 代价函数 梯度下降 一对多分类 预测验证 课程链接:https://www.bilib ...

  9. ROS学习笔记八:创建ROS msg和srv

    ROS学习笔记八:创建ROS msg和srv 本节主要讲述了如何创建和建立ROS msg和srv,同时使用命令行工具rosmsg.rossrv和roscp. msg和srv简介 msg:描述ROS m ...

  10. 《MFC游戏开发》笔记八 游戏特效的实现(二):粒子系统

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9360993 作者:七十一雾央 新浪微博:http:// ...

最新文章

  1. Extjs 登录界面源码
  2. SpringBoot2 整合Ehcache组件,轻量级缓存管理
  3. 机器人动力学与控制_大负载协作机器人难关怎么攻克?3大技术细节解析!
  4. Anaconda多环境多版本python配置笔记
  5. 献给初学者,[winform]中如何设计高效全局的快捷键?[ShortcutKeys]
  6. 使用visio画数据库模型图
  7. sockaddr_in结构体、socklen_t类型、bzero函数简要记录
  8. php 解析今日头条视频,今日头条的视频地址解析方法
  9. 【前端框架】当前基于bootstrap框架的几种主流前端框架
  10. 实战项目 — 爬取中国票房网年度电影信息并保存在csv
  11. netbeans如何导入java项目_netbeans导入java项目
  12. 什么是现汇买入价、现钞买入价、卖…
  13. 【游戏逆向】《**明月刀》BUFF及技能预判
  14. js replace 与replaceall实例用法
  15. 有关 RichEdit line insertion error 报错的问题解答
  16. qlv 格式转换为MP4
  17. Delphi中ActionList组件在菜单编制中的应用
  18. 情景闹钟-情景随心换-手机情景模式闹钟1.0发布
  19. 海外社交媒体营销之Facebook如何进行推广?
  20. 三维目标检测---BtcDet论文解读 Behind the Curtain: Learning Occluded Shapes for 3D Object Detection

热门文章

  1. 操作系统课程设计(linux操作系统)
  2. 纯干货内容:关于ivx和mendix的对比 还在犹豫选择那个低代码平台的小伙伴看过来
  3. Fiddler4抓包工具使用教程
  4. Win10校园网锐捷客户端犯病 可能有用的2个措施
  5. java 拦截器实现接口调用频率限制
  6. c语言课设作业 计算器,C语言课程设计-计算器设计
  7. 1、DSP28335 硬件概述与寄存器描述
  8. 软考-网络工程师复习资料
  9. 2015 年 5 月软考网络工程师真题与参考答案
  10. 什么是SIT测试,、UAT测试?