Unity Animancer插件(三)运动

2023-11-10

一、根运动

Animancer的根运动系统与原生的工作原理完全相同,但我们可以通过继承Transition类型或实现ITransition接口,来将额外的数据与动画绑定,从而更方便地控制根运动。

在下面这个示例中,我们通过自定义的Transition类实现动画根运动的灵活控制。
首先创建一个脚本RootMotion,并编写如下代码

// 自定义Transition类,将是否启用根运动封装
[Serializable]
public class MotionTransition:ClipTransition
{
	[SerializeField,Tooltip("是否启用根运动")]
	private bool applyRootMotion;

	// 在Play()时调用
	public override void Apply(AnimancerState state)
	{
		base.Apply(state);
		state.Root.Component.Animator.applyRootMotion = applyRootMotion;
	}
}

public class RootMotion : MonoBehaviour
{
	[SerializeField]
	private MotionTransition[] animations;
	[SerializeField]
	private AnimancerComponent animancer;

	private void OnEnable()
	{
		// 启用时播放第一段动画
		Play(0);
	}
	
	/// <summary>
	/// 播放指定动画
	/// </summary>
	/// <param name="index"></param>
	public void Play(int index)
	{
		animancer.Play(animations[index]);
	}
}

接下来给角色挂载这个脚本并指定两个动画,一个动画启用根运动,另一个不启用

再通过UI动态控制角色播放的动画。看下效果

当然,在Animancer中我们也可以通过OnAnimatorMove方法来控制根运动

[SerializeField] private Rigidbody rigid;
private void OnAnimatorMove()
{
	rigid.MovePosition(rigid.position+animancer.Animator.deltaPosition);
	rigid.MoveRotation(rigid.rotation*animancer.Animator.deltaRotation);
}

二、线性混合

通过Animancer,我们可以很方便地控制动画状态机进行播放。通过ControllerTransition可以接收一个动画状态机,并通过Animancer进行播放

public class LinearBlendTreeLocomotion : MonoBehaviour
{
	[SerializeField] private AnimancerComponent animancer;
	[SerializeField] private ControllerTransition controller;

	private void OnEnable()
	{
		animancer.Play(controller);
	}
}

但动画状态机中可能存在许多变量,并通过这些变量控制状态的切换或混合。比如下面的混合树

对于这种情况,Animancer提供了封装好参数的ControllerTransition类型。上面的混合树只需要一个float类型的参数Speed,所以我们可以直接通过如下方式对参数进行控制

public class LinearBlendTreeLocomotion : MonoBehaviour
{
	[SerializeField] private AnimancerComponent animancer;
	[SerializeField] private Float1ControllerTransition controller;

	private void OnEnable()
	{
		animancer.Play(controller);
	}
	
	// 设置Speed参数
	public float Speed
	{
		set => controller.State.Parameter = value;
		get => controller.State.Parameter;
	}
}

在Inspector面板中可以对参数进行绑定

接下来,我们可以通过一个Slider对Speed属性进行控制,效果如下

如果有许多角色应用了相同的动画状态机和参数,我们也不必为每个角色单独设置。只需要将前面的Float1ControllerTransition更改为Float1ControllerTransitionAsset.UnShared类型,就可以直接指定一个配置文件。
右键「Create -> Animancer -> Controller Transition -> Float 1」创建一个配置文件,然后对其中的属性赋值。

接下来就可以复用这个配置文件了,将其拖拽到相应的位置即可。

也可以抛弃动画状态机,完全使用Animancer封装好的混合树(在Animancer中叫混合器)。混合器本质上就是在运行时构建的混合树。它的使用方法如下

public class LinearBlendTreeLocomotion : MonoBehaviour
{
	[SerializeField] private AnimancerComponent animancer;
	[SerializeField] private LinearMixerTransition controller;

	private void OnEnable()
	{
		animancer.Play(controller);
	}
	
	// 设置权重
	public float Speed
	{
		set => controller.State.Parameter = value;
		get => controller.State.Parameter;
	}
}

只是将前面代码中的ControllerTransition类型换成了LinearMixerTransition。通过改变其中的参数可以控制动画的权重,从而实现混合效果。接下来只需要在Inspector面板中指定动画即可。注意:对于Idle这种动画应该关闭Sync选项。Sync是为了同步每个状态的时间,以解决两个状态混合时动作错位的问题(比如walk动画左脚迈出,run动画右脚迈出,此时两个动画混合会产生问题)。但一般情况下Idle动画会比walk或run动画时间长的多,此时如果开启时间同步,就会导致walk动画播放速度慢很多。

演示效果与前面相同,这里不再赘述。

三、定向混合

除了前面的1D混合树效果,Animancer也可以通过MixerTransition2D实现2D混合树的效果。

下面我们来通过MixerTransition2D实现一个机器人跟随鼠标移动的效果。首先将之前写过的SpiderBot脚本拿过来,将move的类型替换为MixerTransition2D

public class SpiderBot2 : MonoBehaviour
{
	public AnimancerComponent animancer;
	public ClipTransition wakeUp;
	// 将原本的ClipTransition替换为MixerTransition2D
	public MixerTransition2D move;
	private bool _isMoving;

	public bool IsMoving
	{
		get => _isMoving;
		set
		{
			if (value)
				WakeUp();
			else
				GoToSleep();
		}
	}
	
	/// <summary>
	/// 睡眠
	/// </summary>
	private void GoToSleep()
	{
		if (!_isMoving) return;
		_isMoving = false;
		// 反向播放Wake Up动画
		var state = animancer.Play(wakeUp);
		state.Speed = -1;

		if (state.Weight == 0 || state.NormalizedTime > 1)
		{
			state.NormalizedTime = 1;
		}
	}

	/// <summary>
	/// 唤醒
	/// </summary>
	private void WakeUp()
	{
		if (_isMoving) return;
		_isMoving = true;
		// 正向播放Wake Up动画
		var state = animancer.Play(wakeUp);
		state.Speed = 1;

		animancer.Playable.UnpauseGraph();
	}

	private void Awake()
	{
		animancer.Play(wakeUp);
		// 暂停整个图
		animancer.Playable.PauseGraph();
		// 计算第一帧
		animancer.Evaluate();

		wakeUp.Events.OnEnd = OnWakeUpEnd;
	}

	private void OnWakeUpEnd()
	{
		// 速度大于0时唤醒
		if (wakeUp.State.Speed > 0)
		{
			animancer.Play(move);
		}
		// 否则是睡眠
		else
		{
			animancer.Playable.PauseGraph();
		}
	}
}

将这个脚本挂载到机器人身上,即可在Inspector面板中看到2D混合树界面

接下来编写机器人的移动脚本SpiderBotController

public class SpiderBotController : MonoBehaviour
{
	[SerializeField] private SpiderBot2 spider;
	[SerializeField] private Rigidbody body;
	// 移动速度
	[SerializeField] private float moveSpeed;
	// 旋转速度
	[SerializeField] private float rotateSpeed;
	// 加速倍率
	[SerializeField] private float sprintMultiplier;

	// 2D混合器
	private MixerState<Vector2> _moveState;
	// 移动方向
	private Vector3 _moveDir;

	private void Awake()
	{
		// 获取混合器
		var state = spider.animancer.States.GetOrCreate(spider.move);
		_moveState = (MixerState<Vector2>) state;
	}
	
	/// <summary>
	/// 获取移动方向
	/// </summary>
	/// <returns></returns>
	private Vector3 GetMoveDir()
	{
		// 射线检测
		var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		
		if (!Physics.Raycast(ray, out RaycastHit hit))
			return default;
		// 移动方向
		var dir = hit.point - transform.position;
		dir.y = 0;
		// 一帧移动的距离
		var movementThisFrame = moveSpeed * sprintMultiplier * Time.fixedDeltaTime;
		var distance = dir.magnitude;
		// 目标距离小于一帧移动的距离,则返回zero
		if (distance <= movementThisFrame)
			return default;
		return dir.normalized;
	}

	private void Update()
	{
		_moveDir = GetMoveDir();
		spider.IsMoving = _moveDir != default;
		
		if (_moveState.IsActive)
		{
			// 旋转
			var eulerAngles = transform.eulerAngles;
			var targetEulerY = Camera.main.transform.eulerAngles.y;
			eulerAngles.y = Mathf.MoveTowardsAngle(eulerAngles.y, targetEulerY, rotateSpeed * Time.deltaTime);
			transform.eulerAngles = eulerAngles;
			
			// 设置混合器参数
			_moveState.Parameter = new Vector2(
				Vector3.Dot(transform.right, _moveDir),
				Vector3.Dot(transform.forward, _moveDir));
			
			// 鼠标左键按下奔跑
			var isSprinting = Input.GetMouseButton(0);
			_moveState.Speed = isSprinting ? sprintMultiplier : 1;
		}
		else
		{
			// 混合器未激活,将速度和参数归零
			_moveState.Parameter = default;
			_moveState.Speed = 0;
		}
	}

	private void FixedUpdate()
	{
		// 利用刚体移动
		body.velocity = _moveState.Speed * moveSpeed * _moveDir;
	}
}

看下效果

四、参考资料

[1]. https://kybernetik.com.au/animancer/docs/examples/locomotion/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity Animancer插件(三)运动 的相关文章

  • Unity-AR 简介

    Unity AR 简介 现有Unity AR Sdk ARKit 苹果推出的AR开发平台 ARCore Google 推出的增强现实 SDK ARFoundation ARFoundation是ARKit XR插件和ARCore XR插件
  • Unity Shader入门精要第七章 基础纹理之遮罩纹理

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一 实践 参考 前言 遮罩纹理 mask texture 是本章要介绍的最后一种纹理 它非常有用 在很多商业游戏中 都可以见到它的身影 那么什么是遮罩呢 简单来讲 遮罩允许我们
  • Unity中按钮检测鼠标状态

    改方法主要是用于按钮检测鼠标的进入 滑出 点击 抬起 长按 长按停止 1 先将下面这个脚本挂载到需要检测鼠标状态的按钮上 using System Collections using System Collections Generic u
  • Unity中UI框架的使用1-添加面板、显示Loading页面

    其中BasePanel和Canvas都是挂在面板的预制物上的 1 导入我们的UI框架 本篇文章中有用的是两个UIPanelType NUIManager和NBasePanel 会放在文章最后供大家使用 2 先将我们做好的Panel设置成预制
  • unity中创建询问弹出窗口

    在开发过程中进程会遇到需要弹出一个窗口询问用户是否进行的操作 今天就来制作一个这样弹出窗口 然后根据弹出窗口的选择内容不同进行不同的操作 本例中主要是为了删除一个数据 而在删除数据操作前需要得到用户的一个确认操作 这里面主要用到了Notif
  • Unity学习笔记(一)—— 基础知识

    一 基础知识 1 开发团队组成 2 unity特点 图形界面 所见即所得 入门简单 支持C 比OC C 更友好 js 国内外资源丰富 因为使用的人多 跨平台性好 PC端 移动端等 对VR AR的支持最完善 3 成功案例 游戏 炉石传说 神庙
  • 【IMGUI】 各种辅助类 EditorGUIUtility、EditorUtility、GUIUtility、GUILayoutUtility

    EditorGUIUtility class in Editor 继承自 GUIUtility EditorGUI 的各种辅助程序 EditorGUIUtility currentViewWidth 我尝试打印了下这个值和position
  • unity: C#的Action Event Delegate的异同

    目录 一 Action 二 Event 三 Action和Event区别 四 Delegate 总结 Action Event Delegate的异同 前言 Action Event和Delegate都是C 语言中的重要概念 分别用于管理函
  • Unity保存图片到相册

    Unity保存图片到Android相册 Java 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
  • unity 性能查看工具Profiler

    文章目录 前言 profiler工具介绍 菜单栏 帧视图 模块视图 模块详细信息 通过profiler分析优化游戏性能 最后 前言 每次进行游戏优化的时候都用这个工具查看内存泄漏啊 代码优化啊之类的东西 真的好用 但是之前也就是自己摸索一下
  • Unity打包WebGL的优化常用操作?

    1 贴图部分优化 如果贴图格式时2048 在不影响画面效果的情况下 改成1024或者5 12 还可以缩小包体 2 压缩和解压缩问题 WebGL打包的时候分三种压缩情况 gzip 比Brotli文件打 但打包快 http和https都支持 B
  • 【原神游戏开发日志1】缘起

    原神游戏开发日志1 缘起 版权声明 本文为 优梦创客 原创文章 您可以自由转载 但必须加入完整的版权声明 文章内容不得删减 修改 演绎 相关学习资源见文末 大家好 最近看到原神在TGA上频频获奖 作为一个14年经验的游戏开发行业的老兵 我就
  • 【转载】【Unity】WebSocket通信

    1 前言 Unity客户端常用的与服务器通信的方式有socket http webSocket 本文主要实现一个简单的WebSocket通信案例 包含客户端 服务器 实现了两端的通信以及客户端向服务器发送关闭连接请求的功能 实现上没有使用U
  • Unity学习笔记

    一 旋转欧拉角 四元数 Vector3 rotate new Vector3 0 30 0 Quaternion quaternion Quaternion identity quaternion Quaternion Euler rota
  • Unity中URP下的指数雾

    文章目录 前言 一 指数雾 雾效因子 1 FOG EXP 2 FOG EXP2 二 MixFog 1 ComputeFogIntensity 雾效强度计算 2 lerp fogColor fragColor fogIntensity 雾效颜
  • 游戏开发常见操作梳理系列之——玩家信息的显示系统

    在游戏中 有不少游戏在左上角会出现玩家的头像和等级以及血量 这就是玩家的信息显示系统 那么这些是如何制作的呢 接下来我将讲讲代码的操作 其它操作我会在其它笔记中一一说明 敬请期待 信息的显示相当简单就是控制一些UI 然后在其它系统里面填写相
  • 游戏开发常见操作梳理之NPC药品商店系统(NGUI版)

    后续会出UGUI Json的版本 敬请期待 游戏开发中经常会出现药品商店 实际操作与武器商店类似 甚至根据实际情况可以简化设置 废话不多说 直接上代码 药品商店的源码 using System Collections using Syste
  • 游戏开发常见操作梳理之小地图的制作

    游戏中一般存在小地图系统 实际上就是设置一个新的摄像机放置在玩家的正上方 然后在小地图上显示新摄像机看见的东西就可以了 在小地图上一般存在放大地图和缩小地图的按钮可以方便放大和缩小地图 这些操作是如何实现的呢 接下来直接上核心代码 usin
  • 游戏开发之常见操作梳理——武器装备商店系统(NGUI版)

    游戏开发中经常出现武器商店 接下来为你们带来武器装备商店系统的具体解决办法 后续出UGUI Json版本 敬请期待 武器道具的具体逻辑 using System Collections using System Collections Ge
  • 游戏开发中常见系统梳理之背包系统的实现一

    游戏中几乎都存在大大小小的背包系统 接下来我将讲述背包系统具体是如何实现的 完整源码 以下是使用unity NGUI实现 使用txt配置的方法 后续更新UGUI Json实现的背包系统敬请期待 背包中的物品我们常常将其制作成预设体 通过改变

随机推荐

  • 关于github在vscode上的认证以及密钥缓存机制

    今天在向GitHub仓库提交代码的时候收到了这封邮件 说是使用密码的认证将要被舍弃了 提醒我换成两步验证 2FA 切换成两步验证很顺利 突然很好奇GitHub密码在Mac上是怎么保存的 vscode的设置里有两个选项 如下图 保存密码的地方
  • html如何添加环绕标签,html给定标签选项并添加标签(附代码)

    这篇文章给大家介绍的内容是关于html给定标签选项并添加标签 附代码 有一定的参考价值 有需要的朋友可以参考一下 希望对你有所帮助 HTML haveTags addTags 返回的数组 CSS havetags span addtags
  • ctfshow 萌新web系列--3

  • Linux shell判断含有通配符的文件是否存在

    方法一 使用 ls jpg gt dev null 命令 if ls jpg gt dev null then echo 当前文件夹下 未找到 jpg文件 else echo 当前文件夹下 存在 jpg文件 fi 方法二 使用 ls jpg
  • Descriptors cannot not be created directly

    1 Descriptors cannot not be created directly 在运行诸如深度学习python等程序时 如mmdetection mmdetection3d中的程序 会出现报错 Descriptors cannot
  • 后氧传感器正常数据_氧传感器电压多少正常?氧传感器数据流分析介绍

    氧传感器作用是什么 氧传感器用以检测排气中氧的浓度 并向ECU发出反馈信号 再由ECU控制喷油器喷油量的增减 从而将混合气的空燃比控制在理论值附近 氧传感器是利用陶瓷敏感元件测量汽车排气管道中的氧电势 由化学平衡原理计算出对应的氧浓度 达到
  • Redis启动与关闭

    安装redis之后 在命令行窗口中输入 redis server redis windows conf 启动redis 关闭命令行窗口就是关闭 redis redis作为windows服务启动方式 redis server service
  • Xilinx_RAM_IP核的使用

    Xilinx RAM IP核的使用 说明 单口RAM 伪双口RAM 双口RAM的读写 以及RAM资源占用的分析 环境 Vivado2018 3 IP核 Block Memory Generator 参考手册 UG473 7 Series F
  • 人力资源平台项目总结(2)

    目录 1 路由和页面 1 1 左侧菜单的显示逻辑 设置菜单图标 重点 2 组织架构 2 1 认识组织架构 2 2 将树形的操作内容单独抽提成组件 2 3 获取组织架构数据 并进行树形处理 重点 2 4 删除部门功能实现 2 5 新增部门功能
  • 使用presto+airpal+hive打造即席查询工具

    0X01 前言 即席查询怎么做 怎么选型 这次用的是presto来做尝试 缘起 公司是Impala的深度用户 我主要负责Impala的各方面的工作 最近因为一些特殊原因需要对现有的体系进行一些调整 需要做出来即席查询的组件 在spark s
  • 基于matlab的多元线性回归分析

    二 多元线性回归原理 2 1 数学模型 在社会生活及生产实践中会经常遇到一种问题 即我们非常关注一个量的变化 而这个量受到另一个或是多个因素的影响 我们想要了解这些因素是如何影响我们最为关注的这个量的以及这些因素对我们最为关注的这个量的影响
  • 【C语言进阶】实现atoi函数

    1 函数介绍 atoi的函数功能 将string所指向数字字符串转化为整数 注意 1 会跳过前面的空白字符 例如空格 tab缩进 等 2 如果不能转换成 int 或者为空字符串 那么将返回 0 特别注意 该函数要求被转换的字符串是按十进制数
  • 数字图像处理-小波变换小白解释基本原则

    内容完全转载 小波理论的基本概念及概述 第二版 欢迎阅读此份关于小波变换的入门教程 小波变换是一个相对较新的概念 其出现大约是在20世纪80年代 但是有关于它的文章和书籍却不少 这其中大部分都是由数学专业人士写给其他同行看的 不过 仍然有大
  • Java解析cron表达式

    概述 Cron表达式是一个字符串 以5或6个空格隔开 分为6或7个域 每一个域代表一个含义 即两种语法格式 Seconds Minutes Hours DayofMonth Month DayofWeek Year 即 秒 分 时 天 月
  • rp学习1---web页面左侧导航栏收缩

    一 首先使用几个矩形框将所有的导航栏按照需要和层级画出来 如下 二 将父菜单和子菜单分别转化为动态面板 具体转化动态面板方式如下 选择要转为面板的部分 如两个子菜单 鼠标画框框住两个菜单即可 会将框内的所有内容作为一个面板 右击 三 选择父
  • 算法训练营第三十二天(8.16)

    目录 Leecode 435 Non overlapping Intervals Leecode 763 Partition Labels Leecode 56 Merge Intervals Leecode 435 Non overlap
  • pycharm问题求解

    为什么我的pycharm下面会弹出在 init 中找不到某个函数 我不知道在哪里设置了这个就都成这个样子了 重新安装一个模组可以暂时解决这个问题 但是切个屏就又变成这样了 正常的好像是这样的 求解
  • graph 图数据结构

    树 和 图 辨析 1 树的父节点和子节点之间是一条路单向可达 2 图的的节点之间存在多条路可达 基本概念 1 顶点 2 边 3 邻居节点 只有一条边连接的顶点 4 度 degree 一个顶点有几条边 就有几度 图的区分 1 无向图 边没有方
  • 【Shell】expect解决脚本中交互时自动输入的问题

    日常和shell相关的工作中 经常遇到要在脚本中连接其他服务器进行文件传输等操作 这些命令通常会要求和用户交互输入验证 信息 那么在脚本中如何实现自动输入口令之类的信息 这里就要用到expect 以ubuntu20为例 首先要安装这个软件
  • Unity Animancer插件(三)运动

    一 根运动 Animancer的根运动系统与原生的工作原理完全相同 但我们可以通过继承Transition类型或实现ITransition接口 来将额外的数据与动画绑定 从而更方便地控制根运动 在下面这个示例中 我们通过自定义的Transi