事关Animation Tree的工作随笔(二)
上回说到,游戏项目中客观会遇到逻辑状态的复杂性和动画状态的单一性之间的矛盾,那么Animation Tree是如何解决这个问题的呢?
这又需要引入一个定律:就是逻辑状态无论有多么复杂,但一套逻辑状态组合一定唯一对应一个具体的动画。
举例来说:已知控制当前游戏对象的逻辑状态有是否技能中、是否受击、是否中毒、是否眩晕。
那么我们可以建立一个下面的关系:
技能中 | 否 | 是 | 否 | 否 | 否 | 否 | 是 |
受击 | 否 | 否 | 是 | 否 | 否 | 否 | 是 |
中毒 | 否 | 否 | 否 | 是 | 否 | 是 | 是 |
眩晕 | 否 | 否 | 否 | 否 | 是 | 是 | 否 |
最终动作 | 默认站立待机动作 | 当前技能动作 | 当前受击动作 | 中毒待机动作 | 眩晕动作 |
眩晕动作 (设眩晕动画优先级高于中毒) |
技能动作 (设技能动作优先级高于受击) |
给定每个逻辑的输入,最终的动画结果基本一定是唯一的。
这就是Animation Tree的原理。把整个角色动画,依照不同的逻辑单元组合,最终在给定既定的逻辑取值的情况下,能得到唯一的动画结果。无论是使用什么具体的方案,万变不离其宗。
这中间有几个需要注意的问题:
1、优先级,这个优先级一般与逻辑无关,只是动画本身的优先级,例如上表中的倒数第二列,中毒和眩晕全都触发时,一般眩晕的优先级会高于中毒的优先级。再如最后一列,当中毒和技能同时触发时,技能有限级一般会高于中毒优先级。这就意味着眩晕和技能条件的判断要早于中毒的判断,并享有对整个树的优先处理权,眩晕条件一旦达成,其之后的中毒条件甚至根本就不用去判断了。
2、融合,角色当前属于眩晕动画状态,眩晕解除后,一般不会直接跳转到接下来的状态,而是需要走一个动作融合,这时需要注意接下来到底要转到哪个状态的问题。表面上看,人们不可能知道一个状态解除后接下来会转向什么状态,但实际上在既定的项目中,这一点是可以做到的。
3、逻辑状态本身的互斥性,例如,上表中,不太可能出现眩晕和技能同时存在的情况,因为眩晕的目的旨在剥夺玩家的行为控制能力。在制作具体的Animation Tree时可以灵活根据这些条件进行一些优化和优先级的合理排布。
Animation Tree,目前接触过UE3的Animation Tree思路和CE3的Animation Graph思路。这两个思路是分别从两个不同的出发点入手解决同一个问题。
UEAT的出发点是:逻辑状态无论有多么复杂,但一套逻辑状态组合一定唯一对应一个具体的动画,也就是说,无论AT有多么复杂,最终的结果一定是单一的。这种具有单一结果的情况,一般人们应该都会选择树来做为数据结构。所以,UEAT看起来像下面这个样子(图片来自UDK官网):
4中,被一堆绿色节点包围的那个节点,就是UEAT的根,所有的条件依照这个根以树状排布。
AG不是特别熟悉,这里只是根据笔者现在的经验来写,可能会有疏漏,望懂CE的大牛补充。
CEAG的出发点与UEAT不同:逻辑状态无论有多么复杂,但一套逻辑状态组合一定唯一对应一个具体的动画,所以CE把重点放在了这些状态组合上,AG的编辑界面里排布着大量的关联节点,每个节点的进入条件就是状态的各种组合,从一个节点跳转到另一个节点,如果有路径,就顺路径混合,否则直接按照节点混合直接跳到目标节点(图片来自CE官网):
上图是节点间的转移路径,下图是某个单一节点的条件,一个是Action条件,一个是当前角色速度的条件。
个人推荐使用UEAT的方案,图表一旦大起来的时候,树状逻辑组织的可读性显然高于图状逻辑组织,而CEAG更糟糕的是将节点间的不同性隐藏在了节点内部的属性上,不把节点全部点开一次你是不可能明白这张图到底想干嘛的,而UE则直接从树根追溯到叶子就知道这张图是怎么回事了。但是UEAT的缺点是任意两个动画叶子之间的Blend并不是特别好设置,因为叶子间的切换,需要首先追溯到两个叶子的共同树枝,然后根据这个共同树枝来进行混合,如果有下面这种情况,就很难对付了。
顺便做个广告,Surface Pro的1024级压感电磁笔爽死了,谁用谁知道。
如图所述,1到3、1到4的公共根是Blend节点,节点Blend时间0.1s,所以如果要这两个路径的Blend区分,这里的处理就需要麻烦点。AG处理这个只需要走两条不同的Blend路径就可以。
但是笔者仍然推荐AT,道理很简单,可读性太重要了,而且Blend这种看起来对就是对的的坑爹玩意儿,一般不会有人揪着0.1和0.3不放的,真需要叶子间那么精确到0.1和0.3的可能性不是很大,即便有了,也可以通过回调或者自定义等手段避免掉。但是,学AT的时候,半个小时就明白这玩意儿是怎么回事了,学AG的时候,把整个例子的所有节点走一遍就是一上午,还没来得及排布各个条件的优先关系。AG这种思路太过于直接地考虑程序的实现性了,而忽视了整个体系的方便性,感觉像给自己用的东西,不像给人用的东西。
逻辑状态中最重要的关系就是优先级关系,各个不同体系的逻辑条件,其优先级基本直接决定了最后的结果,所以,像UEAT这样的组织方式,最直接地表述了这样的关系,必然产生的结果就是优良的可读性和可控性。
关于AT的组织和概念就基本上差不多这样了。
基于AT的基本原理,用户可以定制各种辅助的条件和Blend模式来达成自己的目的:
例如速度。
可以把当前角色的相对朝向速度作为条件导给AT,如果速度是正前方的,则角色播放正前方跑步动画,如果是正后方的,则播放后退动画,如果是左右的,则播放向左跑向右跑动画(不是直接转朝向)。如果有前有左呢?在当前节点混合吧!这样就很容易地处理了方向移动的混合问题。与此相同,游泳、飞行这种可能带方向的需求都可以这么做。还可以根据速度的大小,进行走和跑的切换。
例如头部一直向着某目标(Look IK)。
可以把角色的Look IK做成专门的节点,并制作一个专门的处理头部与身体混合的节点,这样,下半身该什么操作还什么操作,跟头部合理融合就行了。
上下半身融合可以采取同样的机制来处理。
在AG里,这么做可就痛苦了,因为AG是既定输入得到既定的唯一结果,而得到两个结果,并使这两个结果之间能够混合……稍微会有点麻烦。
诸如此类的桥段还可以想出很多,再举一个特殊的:
技能动作成百上千,难道全都要放在AT里面去连吗?
不可能,那样的话整个图会变成什么样子呢?设想一下几千个节点,各种连线,这已经超越蜘蛛网,甚至超越乱麻,达到神经网络的层次了。
技能动作再多,其优先级相对却是很简单的,大部分技能优先级一般都高于走跑跳,小于击倒、击飞、眩晕什么的。扩展一个节点,这个节点的动画并不是直接填写在节点里的,而是可以从技能系统中取得当前的技能动画就可以了。上下半身分离技能什么的,可以采用类似的机制来做。
总而言之,AT是个非常强大的概念,使用这套系统基本上就可以做到把程序从繁重而且毫无意义的动作状态机编制过程中解放出来了。而且,您难道不觉得“这玩意儿本来就该这样”吗?逻辑和表现分离,就正如M和V分离一样天经地义,有时候反而搞不明白为什么动作状态表这样的奇葩思路在中国居然还这么流行。
尽早集成,尽早使用,您一定会体会到它彪悍之处的。
希望下个项目的时候,笔者不会再遇到长1000行宽1000行的动作状态表这样的坑爹玩意儿了……
(完)
事关Animation Tree的工作随笔(二)的更多相关文章
- 事关Animation Tree的工作随笔(一)
最近的业务上,又回到Animation Tree这块了. 众所周知的是Animation Tree这些概念已经提出很久了,但是使用有着AT支持的CE引擎的项目,却依然义无反顾地没有使用AT,而且,连某 ...
- JavaSE 第二次学习随笔(二)
循环结构中的多层嵌套跳出 targeta: for(int i = 0; i < 100; i++){ for (int j = 0; j < 100; j++) { if(i + j = ...
- <hdu - 3999> The order of a Tree 水题 之 二叉搜索的数的先序输出
这里是杭电hdu上的链接:http://acm.hdu.edu.cn/showproblem.php?pid=3999 Problem Description: As we know,the sha ...
- [LeetCode] Trim a Binary Search Tree 修剪一棵二叉搜索树
Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so that a ...
- Alpha冲刺随笔二:第二天
课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(十天冲刺) 团队名称:葫芦娃队 作业目标:在十天冲刺里对每天的任务进行总结. 随笔汇总:https://www.cnblogs ...
- Hadoop周边生态软件和简要工作原理(二)
转自: http://www.it165.net/admin/html/201307/1532.html Sqoop: sqoop在hadoop生态系统中也是应用率比较高的软件,主要是用来做ETL工具 ...
- 一个HTML5培训班毕业生的找工作随笔
昨天刚参加完一个面试,通过了.写个随笔记录一下. 先介绍一下背景. 我是今年十月份的时候从某个培训机构的HTML5 Web前端培训班毕业的,是一个刚进入IT行业的新人. 本人毕业于某三流学校,在参加培 ...
- Android Animation动画详解(二): 组合动画特效
前言 上一篇博客Android Animation动画详解(一): 补间动画 我已经为大家介绍了Android补间动画的四种形式,相信读过该博客的兄弟们一起都了解了.如果你还不了解,那点链接过去研读一 ...
- vue 工作随笔
现在工作要做一个电商项目,将工3作的笔记记在这儿,以后方便结总 本套项目用的前端方案 是: vue vue-router Element -ui Axios Echarts 后端技术采用node.js ...
随机推荐
- FoxOne---一个快速高效的BS框架
FoxOne---一个快速高效的BS框架--(1) FoxOne---一个快速高效的BS框架--(2) FoxOne---一个快速高效的BS框架--(3) FoxOne---一个快速高效的BS框架-- ...
- 常用的JS数据类型转换方法
JS 数据类型转换的方法有以下3种:1)使用转换函数2)强制类型转换3)利用js变量弱类型特性进行转换 1:js提供了parseInt()和parseFloat()这两个转换函数. 这里输入内容par ...
- css单位rem---移动端至宝
1.rem是什么? rem(font size of the root element)是指相对于根元素的字体大小的单位.简单的说它就是一个相对单位.看到rem大 家一定会想起em单位em(font ...
- C/C++中的拷贝构造函数和赋值构造函数
代码: #include <iostream> #include <cstdio> using namespace std; class A{ public: A(){ cou ...
- 利用System.Net.Mail和多线程实现邮件发送
对于邮件发送,一般来说,程序会响应超过1秒,这样对于用户体验来说,让用户等待的时间过长,而且发送的邮件越多时间就越长,所以这里我利用了线程的来处理邮件发送这种耗时的工作,废话不多说,直接上代码 pri ...
- jquery1.9学习笔记 之选择器(基本元素四)
ID选择器("#id") 描述: 选择与给出ID属性匹配的单元标签. 对于ID选择器,jquery使用JS的函数document.getElementById(),当一个标签附加到 ...
- symfony 从request中获取当前登陆用户
$usernameToken = unserialize($request->getSession()->get('_security_main')); $user = $username ...
- Google Analytics:为链接点击设定事件追踪的方法
在 Google Analytics 中,可以使用 Event Tracking 功能跟踪自定义的事件.但是,如果你要跟踪的是一个链接点击,那么单纯这样写则很有可能导致漏掉许多事件: <a hr ...
- 『安全科普』HTTP协议讲解及手工模拟发送
学习,熟悉HTTP协议,便于以后进行HTTP重放攻击! 0x 01 HTTP协议 查看HTTP协议 先查看鼠标点击一个链接后,浏览器发出了怎样的HTTP请求. Chrome浏览器下,按F12进入开发者 ...
- NGINX配置小随笔
达到以下效果: 1,特定目录被指定IP访问 2,不是指定的IP地址不能执行URI中特定字符串 3,特定目录中不能执行PHP文件 set $self_visit ''; if ( $request_ur ...