Unity Animancer插件(三)运动

news2024/12/27 10:05:00

一、根运动

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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/99578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jsp+ssm计算机毕业设计订单管理系统【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

Linux操作系统~信号处理的底层原理

目录 1.信号在内核中的表示 信号处理的方法 2.信号的递达&#xff0c;未决&#xff0c;阻塞 3.信号集操作函数 &#xff08;1&#xff09;.sigset_t&#xff08;信号集类型&#xff09; &#xff08;2&#xff09;.信号集函数 &#xff08;3&#xff09;.sigprocmask函数…

基于asp.net194校园火车票预订系统-计算机毕业设计

项目介绍 asp.net版火车票查询系统主要有用户注册&#xff0c;在线查询&#xff0c;在线订票&#xff0c;后台管理等功能。用户只有先注册才能登录火车票查询系统的首界面&#xff0c;然后可以进行在线查询、车票订购的功能。可对车次信息的查询和客户对已定车票信息的查询&am…

【云原生进阶之容器】第一章Docker核心技术1.6节——UnionFS

1 UnionFS综述 1.1 什么是 UnionFS 联合文件系统(Union File System),2004年由纽约州立大学开发,它可以把多个目录内容联合挂载到同一个目录下,而目录的物理位置是分开的。UnionFS可以把只读和可读写文件系统合并在一起,具有写时复制功能,允许只读文件系统的修改可以保…

二叉排序树详解及实现

二叉排序树详解及实现1.什么是二叉排序树2.二叉排序树的数据结构2.1二叉排序树的节点类型2.2二叉排序树中插入某个元素2.3 二叉排序树中按值查找元素2.4 找排序二叉树中的最小值2.5返回排序二叉树中ptr中序遍历的后续节点2.6 寻找排序二叉树中的最大值2.7 寻找二叉树中中序遍历…

《痞子衡嵌入式半月刊》 第 68 期

痞子衡嵌入式半月刊: 第 68 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。 上期回顾 :…

【分布式事务之spring实践】分布式事务选型实战

Spring中使用事务 Spring是一个伟大的框架&#xff0c;从一开始只是一个容器框架&#xff0c;到现在已经发展成为了一个包含企业开发中的方方面面的很多框架的总称。它不但从复杂度上&#xff0c;发展出了用于各个方面的子框架。它还从易用性出发&#xff0c;推出了像Spring-B…

多旅行商问题:世界杯优化算法(World Cup Optimization,WCO)求解多仓库多旅行商问题(提供Matlab代码)

一、世界杯优化算法 世界杯优化算法&#xff08;World Cup Optimization&#xff0c;WCO)由Navid Razmjooy等人于2016年提出&#xff0c;该算法模拟了国际足联世界杯比赛&#xff0c;思路新颖&#xff0c;收敛速度快&#xff0c;全局寻优能力强。 算法原理参考&#xff1a;智…

[附源码]Node.js计算机毕业设计河南美丽乡村旅游信息网Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

[附源码]Nodejs计算机毕业设计基于远程协作的汽车故障诊断系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

DevOps实战系列【第十四章-完结篇】:Jenkins Pipeline最佳实践案例

个人亲自录制全套DevOps系列实战教程 &#xff1a;手把手教你玩转DevOps全栈技术 我们将之前的案例&#xff0c;使用流水线构建一下 搭建一个Jenkinsfile模型 pipeline {agent anystages {stage(拉取gitlab项目代码) {steps {echo "拉取git代码"}}stage(构建代码) {…

FRP进阶篇之解决方案

目录 一、前言 二、多客户端使用 1、场景 2、解决方案 3、使用 3.1、服务端配置 3.2、客户端配置 3.3、结果验证 三、dashboard 仪表盘使用 1、场景 2、解决方案 3、使用 4、验证 5、使用Java调用API接口获取设备列表 5.1、样例代码 5.2、结果验证 一、前言 通…

27. SAP OData 框架里的缓存(Cache)设计专题讲座

本教程前一篇文章,25. 答疑 - SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器,我们介绍了 SAP OData 框架处理 metadata 请求的流程,如下图所示: 其中左边的分支,当读取本地服务器的 metadata 时,OData 框架会从 Share Memory 即共…

npm-开发自己的包并发布

目录 1.开发自己的包 1.1. 需要实现的功能 1.2. 初始化包的基本结构 1.3. 初始化 package.json 1.4. 在 index.js 中定义格式化时间的方法 1.5. 在 定义转义 和还原HTML 的方法 1.6. 编写包的说明文档 1.7包的入口文件 2.发布自己的包 2.1注册npm账号 2.2登录npm账…

Jmeter(二十):jmeter对图片验证码的处理

jmeter对图片验证码的处理 在web端的登录接口经常会有图片验证码的输入&#xff0c;而且每次登录时图片验证码都是随机的&#xff1b;当通过jmeter做接口登录的时候要对图片验证码进行识别出图片中的字段&#xff0c;然后再登录接口中使用&#xff1b; 通过jmeter对图片验证码…

Qt下载安装及配置教程

进入qt中文网站&#xff1a;https://www.qt.io/zh-cn/ 下载开源版 往下滑&#xff0c;下载Qt在线安装程序 它已经检测出我的是windows系统&#xff0c;直接点击download就好。如果是其它的系统&#xff0c;需要找到对应自己系统的安装包。 然后跟网速有关&#xff0c;等…

计算机毕业设计springboot+vue大学生个人财务管理系统

项目介绍 本大学生个人财务管理系统设计目标是实现收入和支出的信息化管理,提高管理效率,使得支出收入管理工作规范化、科学化、高效化。 本文研究的大学生个人财务管理系统基于springboot架构,采用JSP技术、JAVA编程语言和MYSQL数据库设计开发。通过本系统,实现了管理员和用户…

设计模式之享元模式

Flyweight design pattern 享元模式的概念、享元模式的结构、享元模式的优缺点、享元模式的使用场景、享元模式的实现示例、享元模式的源码分析 1、享元模式的概念 享元模式&#xff0c;即运用共享技术来有效的支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少…

基于nodejs博客系统的设计与实现.rar(论文+源码+ppt文档+视频录制)

第一章绪论 3 1.1项目开发的背景和意义 3 1.2国内外研究的现状 4 1.3研究的主要内容 4 第2章系统相关技术介绍 5 2.1 B/S结构技术介绍 5 2.2nodejs技术介绍 6 2.3mysql数据库 7 第三章系统分析 8 3.1可行性分析 8 3.2功能需求分析 8 3.2.1登录模块需求分析 9 3.2.2分类博客模块…

NetSuite 如何统一用户的时区

在NetSuie的原始设置中&#xff0c;用户可以设置自己的时区&#xff0c;这在单一国家的环境中实际上是个缺点。例如&#xff0c;有些客户并没有注意到自己的时区是否是本国时区&#xff0c;所以在查看系统日志时&#xff0c;发现时间不对头&#xff0c;产生了困扰。所以&#x…