作者归档:贺 利华

关于贺 利华

正在学习编程,享受编程 热爱文学,闲来读读《读库》 有思想,没理想 正在学会专注

Unity3D Mecanim 中 Transition 的 Atomic 属性是个什么鬼

我们游戏中一直有一个让我非常恼火的问题,关键是查了已经无数次了,根本尼玛不屌我啊,像我如此这般坚强的程序猿都要拜倒了。谁知前几天再次出现,因为已经把 Log 打得有点天罗地网的意思了,而且还持久化到磁盘 Log 文件上了,这下也不担心查着查着突然被其他的事情打断之后,然后再次回来想查问题的时候发现 Log 已经不见了,尼玛不是说好的要保护第一现场的咩?

不扯淡了,先说这个问题是啥。我们的游戏中,对于战斗 NPC 的动作都加了 AnimationEvent 回调,游戏中很多逻辑都依赖于 AnimationEvent 的回调,例如战斗 NPC 释放了一个技能,那么在这个技能动画刚刚开始播放和即将结束的某个时间点,会分别调用 OnSkillStart 和 OnSkillEnd 这两个 AnimationEvent,在这两个回调函数中会有相应的处理逻辑,例如在 OnSkillStart 中给 NPC 加上霸体的 Buf,在 OnSkillEnd 中将 NPC 的 Buf 取消。我碰到的诡异现象是这样的,NPC 的技能 A 的动画时间长度为 3 秒,OnSkillStart 方法和 OnSkillEnd 方法分别处于整个动画时间轴的 0.2 秒和 2.8 秒,NPC 在 AI 判断确定释放技能 A,在技能 A 动画播放到 0.1 秒的时候,受到了主角的攻击,此时 NPC 应该立即切换到受击状态,开始播放受击动画,但是事实上却不是这样,我们能看到的日志信息如下:

10:34:05.565 AM ya_zhang_tie_qi_jun(Clone)001 TriggerSkill ya_zhang_tie_qi_jun_attack_3

10:34:05.573 AM #Network# GameClient Emit a Message: [Action, Ping]

10:34:05.667 AM Player OnSkillStart: player_female_suplex_back
10:34:05.792 AM Player OnSkillTrigger (player_female_suplex_back#0)
10:34:05.799 AM ya_zhang_tie_qi_jun(Clone)001 OnHookAttackHit by Player with player_female_suplex_back#0
10:34:05.803 AM ya_zhang_tie_qi_jun(Clone)001 Hit to Hooking from None
10:34:05.817 AM ya_zhang_tie_qi_jun(Clone)001 OnSkillStart: ya_zhang_tie_qi_jun_attack_3
10:34:06.297 AM GameClient Received a Message: [Action, Ping]

10:34:06.338 AM Player OnSkillTrigger (player_female_suplex_back#1)
10:34:06.345 AM ya_zhang_tie_qi_jun(Clone)001 OnSuplexAttackHit by Player with player_female_suplex_back#1
10:34:06.352 AM ya_zhang_tie_qi_jun(Clone)001 is SuperArmored, cannot be Suplex
10:34:06.359 AM ya_zhang_tie_qi_jun(Clone)001 ApplySuperArmoredFX
10:34:06.744 AM ya_zhang_tie_qi_jun(Clone)001 CancelSuperArmoredFX
10:34:06.859 AM Player OnSkillEnd: player_female_suplex_back
10:34:08.589 AM #Network# GameClient Emit a Message: [Action, Ping]

从日志里头看到的信息就是这个牙帐铁骑军已经释放了一个技能,但是在其技能动画播放到 OnSkillStart 的时间点时,已经被主角攻击了,此时其并未立即切换到受击状态,而是继续播放其技能动画并且触发了 OnSkillStart 的回调。所以游戏的逻辑就出问题了,NPC 因为触发了 OnSkillStart 回调进入霸体状态了,然后主角后续的所有攻击都无法让 NPC 做出相应的受击动作了,关键是这个 NPC 在回调了 OnSkillStart 函数进入霸体之后呢,还是会进入到对应的受击状态,这下就奇了怪了。你要么就不进入受击状态呗,要么就别触发霸体啊,这不是要疯吗。

最后仔细分析排查,知道了这个问题是因为 OnSkillStart 的触发时间点处于动画过渡的时间区间内,也就是说 NPC 从 Idle 状态进入到技能 Attack_3 的状态时间长为 0.3 秒,并且这个动画的过渡 Atomic 属性为 True。问题就这么来了,NPC 在从 Idle 状态时释放技能 Attack_3,动画从 Idle 切换到 Attack_3,动画切换到 0.1 秒的时候,受到了主角的攻击,NPC 的整个 AnimatorController 状态图中,受击状态都是直接从 Any State 跳转过去的。按理来说就应该在任意状态下都能切换到受击状态才对啊,为啥动画还会继续往后播放呢,直到 OnSkillStart 回调完了之后再切换到受击状态呢?

最终发现了就是这个 Atomic 属性给闹的,这个 Atomic 属性的字面意思已经很清楚了,那就是当它为 True 的时候,这个过渡是原子的,也就是说只要进入了这个过渡,那么这个过渡就一定会播放完成,不论是发生任何情况这个过渡都会播放完成,而刚好我们的这个 OnSkillStart 回调函数所处的时间点就在这个过渡时间段里头,所以就出现了,虽然最终 NPC 还是进入了受击状态,但是在进入受击状态之前因为已经回调了 OnSkillStart 函数,所以导致逻辑上出现了错误。

这个问题呢,有两个解决方法:

  1. 取消所有的动画状态切换之间的过渡时间,让动画之间的切换不再有过渡的过程,都是直接切换,这样就可以避免出现过渡的时候触发 AnimationEvent 了;
  2. 将牙帐铁骑军的 AnimatorController 中从 Idle 到 Attack_3 状态的 Transition 的 Atomic 选项取消,允许该 Transition 被打断,这个解决方法看上去更有技术含量一些,也更符合我们设计意图,当我们不论处于一种什么状态的时候,受击之后就应该立刻切换到某个受击状态下,这个从语义上也很能说得通,对伐?

霍光这厮创造了一个奇迹

霍光,汉朝名将霍去病的弟弟。霍去病死的时候,他以奉车都尉的身份和金日磾(dī)、上官桀一直侍奉在汉武帝的跟前,是为『内朝』,区别于丞相、御史大夫这种政府部门被称为『外朝』的官员。

汉武帝死前托孤的就是这三位时常侍奉其左右的臣子。年幼的汉昭帝(刘弗陵)即位后(当时年仅八岁),霍光是大司马、大将军,金日磾是车骑将军,上官桀是左将军,形成了三驾马车式的制衡结构。

金日磾的去世,让这个原本还有制衡存在的权利结构崩溃了,而皇帝尚年幼,这就造成了霍光与上官桀两虎相争的局面,最终霍光胜出成为了权倾朝野的独裁者。

在霍光与上官桀争斗之前,两家还有联姻呢,霍光的女儿嫁给了上官桀的儿子上官安,生了一个闺女。上官安还曾私下里跟他老丈人提议让自己的闺女做昭帝的皇后,但是被霍光以『七岁尚早』为由拒绝了他女婿的这个请求。

不过上官家既然也是豪族,不可能完全没有办法的嘛,他们找到了抚养昭帝的蓋长公主,最终还是成功地让上官家的孙女成为了昭帝的皇后。

在后续的斗争中,霍光胜出了。霍光以上官家勾结曾经叛乱的燕王(由于他的同胞姐姐蓋长公主在昭帝面前求情没有被处死)再次发动叛乱为由,把上官家族全给灭了,除了他那年幼的贵为皇后的小外孙女(难道连他自己的姑娘,也就是皇后的母亲也给干掉了?)。

昭帝又是个死得早的皇帝,昭帝过世时,皇后十七岁,他俩还没有子嗣,这样就把昌邑王刘贺(汉武帝时期昌邑王刘髆的儿子,在其父死后继承了王位)给找来继位,认了这小皇后为母亲,最终又以谋反的罪名被废,连王位都给剥夺了,随行入京的随从除三人之外全被杀了(这三人是什么鬼?难道有阴谋?)。

这下又要找谁来当皇上呢?霍光竟然从民间找到了汉武帝时期的太子刘据的孙子——刘病已,即位后为宣帝(这一年他十八岁,比他名义上的母亲太后还大一岁呢)。宣帝在民间的时候已经娶了许氏成家了,在宣帝即位后,许氏莫名其妙地就死了,然后霍光就让宣帝娶了他的小女儿,并册封其为皇后。

这下奇迹出现了,霍光让他的外甥女成功的当上了他女儿的婆婆。也就是说原本太后要管皇后叫小姨,现在成了小姨得叫外甥女母亲了,这难道不是一个奇迹吗?

当然创造奇迹的霍光最终给他的家族带来的也是毁灭,宣帝熬到霍光死了以后,通过各种手段一步步把霍光多年经营的权利集团给瓦解了,并且最牛逼的是立了他在民间娶的许皇后的儿子刘奭(shì)为太子,让霍氏一族的如意算盘彻底落空了。

最终霍氏一族被逼得走投无路了,发动政变被宣帝平定,霍氏一族被灭,霍皇后也被废了,霍光的老婆被弃市。

退避三舍这个成语的出处

晋文公是春秋五霸之中继齐桓公之后的霸主,晋是西周成王(就是那个年幼的成王,经历了三监之乱的幼主)弟弟的封地,晋也是陆续吞并了晋周边的几个小国之后,到文公的时候成为一霸。

文公名叫重耳,因家族内乱,少时出走各国,过了十九年的流浪生活,最终修成大招,回到晋国之后掌握霸权,尼玛还臭不要脸地召唤周襄王到他的领地河阳参加所谓的诸侯会盟,承认其实盟主,简直碉堡了,有木有?

文公牛逼的事迹是啥捏?就是作为南蛮子的楚国(湖南和湖北)牛逼了之后,北上想干掉处在其旁边的宋(河南省),文公这货联合了齐和秦前去救援同属中原北国正统的宋,在城濮(山东省)把楚军给干了。

当然这只能算是军事实力和才能牛逼,远不足以流传千古啦。牛逼的是,这货竟然做了一件牛逼的事情,就是在与楚交战之际,退避三舍了。

原来文公还叫重耳的时候,流浪到楚地时得到了楚成王极大的关照,当时楚成王问他,如果你回国之后当了君主,你丫怎么报答我捏?文公答道:

——晋楚治兵,遇于中原,其辟君三舍。

『舍』是一天行军的距离,约合 12 公里。『退避三舍』就意味着后退 36 公里地。然后在城濮之战中,文公还真就信守诺言退了 36 公里,咱们先不说实际的战争中这个退 36 公里究竟对于战局有何影响,最起码这个重诺的德行是值得称赞和肯定的吧。

问鼎中原的来由

九鼎据传是禹在建立夏朝后,用天下九牧所贡之金铸成九鼎,象征九州。商代时,对表示王室贵族身份的列鼎,曾有严格的规定:士用一鼎或三鼎,大夫用五鼎,而天子才能用九鼎,祭祀天地祖先时行九鼎大礼。因此,鼎很自然地成为国家拥有政权的象征,进而成为国家传国宝器。据说,秦灭周后第二年即把周王室的九鼎西迁咸阳。

秦始皇灭六国统一天下,九鼎已不知下落。有人说九鼎沉没在泗水彭城,秦始皇出巡泗水彭城,曾派人潜水打捞,结果徒劳无功。

由此,我们可以知道在老早的时候,祭祀用的鼎呢,就成为了王权的象征。

而这个楚庄王(就是被晋文公退避三舍的楚成王的孙子)在周国国都洛阳附近的洛水东岸击败了陆浑的戎族,因为平定的是周都附近的动乱,所以周王室天子派人来犒赏一下,使者名叫王孙满。

楚庄王这货直接就问王孙满,周王室祖传的九鼎多重啊?

王孙满一听,不对啊,你这货啥意思,你想夺王权,准备把鼎给运你家里去啊,准备打造马车,想知道运载量还是咋滴啊?就回答道:

——天命未改,鼎之轻重,未可问也。

意思就是说,还没到你能得天下的时候呢,你呀就别问了,即便你知道轻重了,也是不可能把这鼎给搬回家的。

楚庄王一听,明白咋回事了,你丫还瞧不起人,说:

——子,无阻九鼎!国折钩之喙,足以为九鼎。

意思就是说啊,王孙满啊,你也就别藏着掖着了,即便你不告诉我九鼎有多重,也没啥大不了的。我楚国将士武器的戟头上的喙得有多少,我要是用这个喙来熔铸九个鼎那还不是绰绰有余。看看,这称霸还不够呢,夺取周室正统的野心昭然若揭啊。

问鼎轻重,从此就用来喻指图谋夺取政权的言行了。问鼎中原想必就是从此衍生出来的,因为中国传统的正统政权一直是在中原之地,所以问鼎中原也就意味着想夺取中央政权。

对了,这个楚庄王还是那个『一鸣惊人』典故中的楚王,据说这货于公元前 613 年登基,即位之初,沉迷声色,荒于政事,并下令拒绝一切劝谏,违者 “杀无赦”。大臣伍举冒以隐言进谏,称楚国高地有一大鸟,栖息三年,不飞不鸣,不知是什么鸟,当时庄王即位已经第三年,庄王知道伍举在以大鸟讽喻自己,于是回答说,大鸟三年不飞,飞则冲天;三年不鸣,鸣必惊人。然而此后数月,庄王依然如故,仍旧以淫乐为好,大夫苏从冒死再次进谏,庄王终于听从劝告,奋起图治,诛杀小人,任用贤良,使得楚国国力日益强盛 。

后来还真的就成为春秋五霸了,所以如果牛逼的你也有这种机遇,也可如此对那些埋汰你的人说说这个典故,兴许哪天我就『一鸣惊人』了呢,对伐?

周国分封领地的一些小事

周武王伐纣胜利之后,建立周朝,对于自家兄弟和立下大功的臣子自然是分封领地,但是周朝作为一个有着比前朝殷商更为先进制度的新国家,其分封还能看到一些讲仁义之处,或者也可以说为了自身政权合法性寻求更多方的支持。

获得领地并受封的武王兄弟据说有十五人,连那些古代圣人——神农、黄帝、尧、舜的子孙都被寻找到并分封于各地,据说舜的子孙被封于陈(河南省),夏的子孙被封于杞(河南省),连尼玛殷商纣王的儿子禄父(武庚)都受封于宋地,成为一个诸侯,有用殷旧地的一部分,不过给他派了三个监察者(叔鲜、叔度和霍叔),最终在成王刚即位时,还给整出来一个『三监之乱』,简直了。

宋地后来在春秋时期还出了一个疑似春秋五霸的宋襄公。

杞呢,就是那个『杞人忧天』的成语中的那个杞地人所在的地方,成天担心天要塌下来,能干点正事不?