15.游戏引擎的Gameplay玩法系统基础 | GAMES104-现代游戏引擎:从入门到实践 - P1 - GAMES-Webinar - BV1u34y1H7jd
大家好,欢迎大家回到games104,现代游戏引擎的理论与实践,我是王希,那个过去的两周大家休息的怎么样,我是终于又可以得一个星期的假期,然后可以出去玩一玩,然后那个现在又被拉回来。
开始给大家上我们的games104,那么在开始课程之前的话呢,首先给大家那个反馈一下,我们社区女同学们的那个各种声音,首先的话很多同学说,我们现在第二节课的作业,和第三节课的作业还来不及做。
比如说第二节课的作业是什么,大家记得吗,就是我们那个啊用LUT去做各种各样,后期的效果对吧,我们还做出那个叫什么那个,那个那个叫音节模式的视觉效果,然后呢第三节课的话呢是有动画。
作为一个最简单的动画的混合,实现人走走向跑,然后呢还有一个简单的control效果,那么就是说课程组同学们商量了一下的话呢,因为考虑到每个同学进入我的课程的时间,有先有后。
然后呢还有一些可能有些东西很难,所以我们把整个提交时间统一到啊,下个月的月底就是8月31号,这样的话和我们第四节课的作业的话一起来收,这样给同学们更多的时间去准备,而且大家如果觉得很难的话。
你也不用所有的作业都做,你可以选择你选你觉得能上手的作业可以做,因为很少有人能写全部的游戏引擎对吧,你每个东西的专家也实在是太厉害了,所以说哎我是做rendering的,我是做动画的对吧。
你可以选一个自己感兴趣的题材去做,那么另外一个的话呢,就是哎这个是大家反映比较多的一个问题啊,就是我们作业的提交好像老是失败,然后呢,我今天才跟我们那个课程组的小伙伴一起,在在商量。
然后现在我们猜测的主要的原因,就是那个在那个games的课程提交网站,因为那个不是我们课程组做的是games课程组,games课程做的那个网站,当大家提交完自己的作业的时候呢。
还要单独再去点一下那个提交按钮,所以很多同学是上载完附件,但是呢就没有去点那个小按钮,导致这个作业提交就失败了,然后呃后来我们想了一个办法,就是在我们的课程的那个官网上,又写了一个文档。
在那文档的第九页的话呢,就是专门教大家怎么去提交作业的那个地方,我们告诉大家注意这件事情,所以同学们如果还有什么作业提交的问题的话,也及时跟我们课程组联系,然后我们课程组的小姐姐真的很好。
她那个大家好多同学提交失败的朋友,把通过微信传给他之后,他帮大家一一的就提交上来了,所以的话,哎,我觉得这里面真的要给我们科室的小小姐姐们,小伙伴们点个赞,然后所以大家如果课程提交作业。
提交还有任何困难的话,就及时跟我们联系,反正我们会尽我们所能给大家提供帮助,那么最后一个同学们也问我们一些问题了,这些问题我觉得蛮好玩的,就比如说有同学问我们说。
哎我们的pick a引擎要不要做这个scripting,脚本系统,这个这个东西其实呢是我们这节课要讲的,就这节课我们讲那个game play会讲脚本系统,那其实游戏引擎接入脚本系统还是蛮复杂的。
就是包括啊他你是怎么去架构接口,怎么去设计,包括你是不是要实现一个,就是比如像graph这样的visual scripting,那这件事情,其实目前的话,就整个课程组会很专注的把课程准备好。
就像大家跟大家汇报过,就是说实际上我们写这个课件,是非常非常压力很大的,因为我们没有任何参考资料,所以每节课都要临时现写现编,然后确定提纲,所以这是个巨大的工作量,然后我们最近连开发任务都受了一些影响。
那所以的话呢,我觉得可能在9月份课程结束之前的话,我们会专注于把课程打造好,同时和大家一起把PUI引擎本身的一些问题,功能做完善,可能是等我们90月份这个基础课程做完之后,我们会和大家一起商量。
如果同学们有兴趣的话,要么我们课程组来写,要么我们和社区的小伙伴一起来写,看给我们的这个皮UI引擎设计脚本系统,但同学们提的这个问题是对的,就是说如果真的要做一个好的游戏引擎的话。
那一定是要有脚本系统的,否则的话它很难去做它的扩展性,那么另外有同学也在问我们说,诶那个为什么皮克AI引擎用的是c make up for x Mac,那个实话实说,我自己没有用x Mac。
然后呢我今天还专门向我们的小伙伴们请教,我说x max是什么东西啊,然后那个他们给我看了一下,然后我说哦原来是这样的,好像Mac确实是比c Mac简单一点,那但是呢当时我们为什么选用c make呢。
就是说实际上课程组觉得,c Mac是现在开源社区里面应该来讲是最popular的,就是说最大家广泛接受的一个build的工具,那么它本身的话宫内比较完善,也比较强大,而且支撑的文档也足够多。
确实当时我们在做皮卡小引擎的时候,就有一个小小的野心,就是我们一上来就想跨平台,所以一上来就选了一个最标准的一个解,但是呢其实未来如果同学们有兴趣的话,大家有人说哎我要做个更简单的AI Mac版本的话。
如果有这个想法的话,我觉得我们也会帮助大家支持大家来做这个事,但目前的话呢就是说我们会先集中精力,把一个版本搞好,因为就算c Mac的话,很多同学理解起来啊,学习起来还需要花一点点时间。
那么如果大家觉得有问题的话,c Mac其实有官方的网站呢,大家可以去查一下,如果大家找不到那个网址的话,我们到时候会在微信上把这个网址给大家,就是c Mac在哪查找他的资料。
那最后呢有同学也在问我们说诶,为什么这个小引擎每一次都没改代码,但每一次编译都要重新编译一下,这个其实是我们目前也是我认为小引擎,我们还有很多事情要做的地方,因为虽然一行代码没有改。
但是呢我们不是有那个反射系统吗,就是他每一次编译的时候,首先会调那个反射的compiler,他会首先渲染出很多的这个generate code,那这个code的话,你会编译器当成一个脏code。
他又开始重新编译了这个地方,其实我们未来是可以优化的,就是如果你的这个数据的,就是schema的定义没有改的话,我们可以不触发变异,但这个写起来讲起来很简单,写下来需要很多的处理和编译的处理的。
这里面需要花一点点时间,所以其实就这样,大家也能意识到就是一个大型引擎,它就挺复杂的,有的时候叫自己要做自己的预编译,然后呢这个预编译本身呢。
它的比如说怎么样实现incremental compiling啊,实际上是比较复杂,我记得以前我在做那个就是hero引擎的时候,我们当时最痛苦的一件事情是,我们那个引擎编译一下。
大概要花掉呃半个小时左右,然后呢,后来我们专门找了一个一个一个很厉害的,技术的老哥,大概花了有一年的时间,终于把我们的编译时间从半个小时,减到了10分钟,实际上你会发现就在写这个引擎的时候。
有很多的include对吧,这个include1大堆的类,那结果有些文件啊,你基本上一编译的时候,会被编译几百甚至上千次,就会导致整个编译速度特别的慢,所以其实当引擎做到最后的时候。
当你面对几百万行代码到上千万行代码的时候,如何能够实现分布式的IMPLEMENTAL的编译,实际上是个很重要的工程问题,否则的话整个引擎开发组的话,大家每天可能有一半的时间在等编编译了。
那个过程是怎么说呢,既可以划水,很快乐,但同时又会觉得很痛苦,太无聊了对吧,所以说同学们问的这几个问题,我觉得都蛮有意思的,所以我在这里也给大家做一些我们的回答好,那今天跟大家的社区互动的部分就到这了。
接下来我们就开始我们今天课程的正题了,今天课程讲什么呢,讲这个game play,game play这个翻译成中文叫什么呢,我我想了半天啊,我其实脑子我今天上课之前。
我都不知道game play中应该怎么说,后来我想了想,应该叫玩法系统对吧,这个听上去翻译起来还是有点信达雅的,这个小小的给自己自夸一下,然后这个玩法系统其实是个很庞杂的,很庞杂的系统了。
就真的是你想到游戏里面几乎都almost everything,它都叫玩法系统哦,所以这一节课准备起来,我们的那个团队真的是觉得头疼肚子疼,就是该讲啥呢。
好像这个玩法系统有一部分好像在render也讲过,好像有些东西在物理也讲过对吧,有些东西在其他地方都讲过,那到底什么东西属于玩法系统呢,然后我们花了很长时间,把这个玩法系统分成了两大块。
第一大块呢大家可能也比较熟悉了,就是玩法系统的基础,我们会讲A真正更详细的event系统是怎么驱动的,就是整个这个世界里这些game object互相怎么去通讯,怎么去互动对吧,这是做玩法的基础。
然后呢,诶你怎么去用scripting写你各种各样的玩法规则,对不对,比如说打砍一刀上去,这个血量到底怎么扣对吧,这些东西都属于玩法系统,那这东西也是属于那个scripting系统,一个重要发挥的地方。
那还有大名鼎鼎的,就是可视化的这种挂那个script系统,比如说像大名鼎鼎的BLUPIN的蓝图系统,那这里面的话我们可以去讲,然后最后呢会给大家讲,这个也是非常这个这个如雷贯耳的,叫著名的3C系统。
如果大家同学们那个稍微浅强一点,游戏的设计的话,都知道啊,要做一个好游戏,做一个好的动作游戏对吧,你一定要理解什么叫3C啊,3C是什么呢,Character control and camera。
就是角色控制已相机,那3C系统到底是什么,今天给大家做一个简单的科普,那这些就是杂七杂八东西,讲完的话,基本上就是玩法系统的一个骨架了,但其实玩家系统里面有很多的细节啊。
我们今课程上是没有办法跟大家一一展开的,那么这节课呢我们这就这个玩法系统,我们是准备了两节课嘛,那另外一个第二节课的话,哎只讲一件事,就是AI对人工智能怎么样,AI还是比较霸气吧,就我连提纲都写不出来。
我就觉得我用了我蛮喜欢的,斯皮尔伯格的那部电影就是AI,大家如果没没有看过的话,一定要看亚洲眼,因为他非常的有深刻,他就讲了一个小机器人,小男孩,每天想着自己能变成一个真实的real boy。
也就是一个真实的男孩的故事是吧,那其实的话那个就是我我哎呀这个跑题了,我在想我以前看那个AI那个电影的时候,后来我玩那个底特律变人的时候,我记得我就一下子戳中了我的灵魂共鸣对吧,我就觉得哇。
很能理解这些机器人为什么要变成人,那么其实AI系统的话呢,讲起来就有很多很有意思的东西,而且呢你说讲的很浅,也可以讲的很深也可以,所以我们想想专门做一节课,跟大家好好的去玩玩一下AI系统。
所以说整个我们的玩法系统就分成这两趴,那么大家的话呢可以根据自己的喜欢对吧,你如果很感兴趣AI,你可以跳过这节课不听的,但是相信我,你如果只听AI了,你这个玩游戏玩法一定做不出来。
为什么你没有前面的这些系统的支撑的话,光有个AI这个游戏也是跑不起来了,所以此处给你们挖一个小坑好,那我们就回到今天我们课程的主题了,那我们就基础的玩法系统是怎么来的,首先玩法系统呢。
实际上啊大家觉得做一个游戏一定要玩吧系统,但文化系统其实挺难做的,为什么呢,他的第一个挑战就是说它实际上就是叫设计师,动动嘴,程序员跑断腿,因为一个玩法,它实际上会和游戏里面的各个系统都会打交道。
比如说对吧,我们这个这个这个就是武林至尊对吧,可那个我们的卡圣人,他们做的这种动作和打击感,那简直是这个,其实我很期待这款产品,我今天一直在问说这游戏今天上线了没有,他们告诉我没上线。
哎呀我我我就只能默默的等待了对吧,因为我是他的这个铁粉,那么好,那其实像我们的这个就是说这种一个动,很有动作打击感的这样一个游戏的话,你要做他什么,你首先要跟动画系统起作用嘛,对不对。
动画系统呢你还要跟特效系统对,打击感的核心是什么,特效嘛,对不对,还有什么呢,生效对吧,你一定要跟some effect起作用,你还跟那个control控制的那个那个那个。
无论是手柄还是鼠标键盘都要起作用,对不对,还有各种UI的提示对吧,double kill什么暴击对吧,这种东西它它在屏幕上啪啪啪弹出来,那么还有什么呢,还有大家著名大名。
大名鼎鼎的叫动作感的那个要素叫卡针,叫打击针,你要把那个画面啪啪啪整个卡住,所以你还要跟渲染系统起作用,所以其实我作为一个非常简单的一个玩法,就是一个有打击感的东西。
实际上你作为games play的engineer,就作为玩法工程师的话,他虽然这些每个系统都不需要他写作animation,animation系统的工程师对吧,做sound effect系统。
有sound effect系统工程师,做这个UI系统,UI系统工程师等等等等等,但是我们的game play的engineer,他要和所有人在一起talk,然后呢他才能够满足我们设计师的需求。
否则的话啊他就是做不出来,所以说其实啊game plan engineer,他很多时候都是个杂学家,就是什么东西都得懂那么一点点,这是做game play的第一个挑战。
那么做game play的第二个挑战是什么呢,就是说其实啊在游戏里面,game play真的是多种多样的,我们不要说不同的游戏类型啊对吧,你一会儿有RTS,一会儿又是这个FPS。
一会儿又是ACT游戏对吧,就算在同一个游戏呢,比如说大家去玩巫师的时候对吧,核心战斗是这个样子的,我看见怪我就砍我卡斯特,我的魔法,我的各种技能特效打的砰砰砰,打到怪物上很有打击感,对不对。
但是你玩昆特牌的时候,你发现啊,这又是一个很好玩的一个游戏,我一直认为巫师是一个被动作耽误了的一个,昆特牌游戏对吧,那很多人都觉得昆特牌比这个巫师本体好玩,当然这个有点夸张了,但是我们再想象一下。
就是如果我们想做一个像,比如说像荒野大镖客这样的游戏,你想这个事件里面你能做的丰富的玩法,机制类型有多少,比如大家如果玩网游的话,那真的网游里面是什么什么能搞,比如说你在里面可以抛绣球,可以踢足球。
可以这个大家一起玩吃鸡,还有大家还可以一起去什么打牌,下五子棋,还有什么还可以参加这个什么啊,科举你还可以考状元,所以这些所有的东西我们都要统称玩法啊,这些玩法的话呢。
哎我们很多时候要把它做进一个游戏里面去,音响,这个时候玩法系统的架构,包括玩法系统的这种叫做可扩展性,其实要求是非常高的,这也是玩法系统一个很挑战的一个东西,那么第三个的话呢就是玩法系统。
我们认为比较有意思的东西是什么呢,叫快速迭代,大家知道这个这个这个four night堡垒之夜很厉害,对不对,但是不知道堡垒之夜最开始的时候,他不是现在这个吃鸡模式,它是一个mo对吧。
当时最早EPIC是想把它做成一个,就末世题材的生存类mo,但是呢有一天这个就是我们的team同学,叫team smiai,就是那个EPIC的老大,他觉得哎好像这种重度的mm好像没有什么市场。
我们还不如做个轻量的,以吃鸡为题材的这样的一个游戏对吧,就出现了PUB啊,不是PVG啊,就是那个for night对吧,battle royal这样的一个一个模式,好,从老大下定决心到团队搞出来。
给的时间多久呢,两个月,所以说其实你在做game play的时候,你想这么大一个游戏两个月就搞定了,真的就两个月搞定,为什么就game play系统还有一个很大的特点,他和我们做rendering。
做animation不一样,比如说rendering,我一个feature对吧,像我之前讲的叫sky啊,讲的那些,比如说TURING啊,一个系统从决定要干到他做下去,很多时候是半年以上。
甚至有的系统要做一年以上,因为它的算法很复杂嘛,你就一步无数的敌它迭代调整,然后呢跟美术去磨整个流程,所以这个过程是一个很怎么说呢,很漫长,很专注,但是呢又是相对稳定的过程。
但是gap edge near他面对的情况是什么,前两天设计师跟你讲说,哎我们要做这个玩法好,大家一起做出来了,诶,出来玩了两天之后,大家觉得不对劲了,好马上要改,我有了一个新的想法。
好两天之后又要变出一个新的玩法,所以其实game play它是整个游戏系统里面,迭代速度最快的一趴,所以当我们设计这样的一个game play系统的时候,我们在引擎侧一定要支持它快速迭代的功能。
所以说回想一下,就是说我们要做game play怎么样,第一它是要有能力和各个系统进行talk对吧,它是能够把各个系统的AI功能全部能调用,第二个是什么呢,就是说哎它具有很强的玩法扩展性对吧。
你在这里面可以玩一个非常叙事性的内容,也可以玩一个打击感对抗性很强的内容,对不对,也可以玩一些哎比较有意思的探索性的内容,这都是跟UI系统我们要支持支撑的,第三个就是这么多乱七八糟的玩法诶。
都是要求相对快的速度,可能是比如说几几天几周对吧,一般很少GAMEPLAY会做几个月以上,那是非常难的那种games play,但是那种game play都是迭代一个几个月以上的。
game play的研发都是拆成很多很多的那个小的,这个sprint就是小的一个突击,我们把它做出来,然后再看它的效果好坏,再去迭代它,所以这其实就是game play系统的一个要求。
就是说他就我我我总结一下叫做,就是他就要求第一要砸血,第二个什么呢,要价格便宜,量又足对吧,这才能支撑我们一个丰富的游戏体验,那么怎么去做game play呢,那首先的话回想一下。
我记得我们在第二节课跟大家讲说,哎游戏系统怎么搭的,其实我一直在想说,如果你想理解游戏引擎,那个第二节课听完之后,你就可以出去跟人吹了,我大概知道游戏引擎咱们加了,因为在那节课里面呢,我们就讲到了。
说这个游戏世界是有无数的机油object,game object构成的对吧,那game object之间的话呢,其实如果能让他玩起来,首先他们彼此是要能talked的对吧,我一个炸弹能告诉你们说。
不好意思,我要炸了你们对吧,我一个坦克发射一个炮弹对吧,我会告诉你们说一只炮弹正在飞来,那么这些game object之间的话呢,我们认为要能talk,但是talk的话,如果你把它写死成我的代码中的话。
那是不是就像我们的第二课,第二节课讲的就是说你就是一堆if else if else哇,这个或者是switch case对吧,这样的一个branch的一个循环,那这个代码写到后来肯定不是给人看的。
基本上就崩掉了,整个系统都不知道怎么维护了,所以呢在第二节课我们就讲了,就说哎其实你应该用event message的一个机制,就是把所有在这个事件发生的事情,变成一个event或者叫message。
其实我自己很多时候很迷乱,虽然我们经常一直叫它event系统,但实际上呢在游戏引擎写的时候,我们会经常把它写成message,好像觉得message很有感觉,我记得我在第二节课讲了。
这个男女朋友要分手的那个例子对吧,就是那个你们家门口有个邮箱,互相递那个分手信,其实我觉得message很有感觉,就很有那种物理的实体感,确实就是在游戏世界里面,在每个tick我们的co之间诶。
互相给对方写信,然后呢收到信的人,每个人做出自己的响应,这其实就是个最基础的诶,我们对这个世界的互动的一个方法,就相当于我们之间建立了这样的一个通讯协议,那今天的这节课里面我们讲game play系统。
那我会稍微讲的细那么一点点,就是说如果你真的想做一个玩法系统,你想做一个能够应对相对复杂的,一个游戏机制的话,我们的引擎测疑问的系统它该怎么去做,那在这里面的话呢,其实我们上升一个相对高的一点高度啊。
其实这个模型啊它是一个它的设计范式,叫做publish,Subscribe pattern,就是说发行者和这个注册者,Secreation,就是那个注册者的这个pattern。
就相当于就是说有人负责发布信息,另外一群人注册一些信息,就这些信息来的时候我就注册了,就是这个这个叫发行或者订阅吧,中文应该叫订阅,订阅可能更好一点,就相当于是说每个人都出自己的校园小报。
但是我喜欢那几个同学的文笔,我就订订阅那几个同学的小报,所以它是这样的一个标准的一个pattern,那在这样的一个pattern里面的话呢,实际上它有几个很重要的元素。
第一个就是我扔出去的各种各样的events对吧,各种各样的事件,第二个呢这event需要有一个系统,这个系统叫什么呢,叫event dispatcher,你可以认为它是个物流公司。
就是你每天这个你在淘宝上订的东西,那那那个就是商家把东西都发出来了,就是你买的东西,然后呢这个物流公司它其实很强大,大家不要千万不要低估物流啊,物流行业其实是一个技术密度很高的一个行业。
但因为他的这些信息的分拣派送,实际上是很在乎效率,而且要进行一些算法的,所以event dispatui它就像物流公司一样,他做好这个信息的分拣派送,然后呢,唉再到我们各个的接收者进行相应的动作。
这个我们叫什么叫QUEBEC,所以呢其实在一个poption subscription model,就是发行者和订阅者的这个pattern里面的话。
三个key component就是第一个就是消息的定义,你的event definition,然后呢你的所有的哎这种消息来的时候,我该怎么去处理呢,对不对,这个处理的函数我们叫做call back。
call back呢,我们在第二节课讲的都很简单,实际上call back呢是有一个注册的过程呢,就是说你得告诉别人说,当有这类消息来了之后,你调我这个动作对吧,这个是要有一个注册关系,注册在这儿了。
然后呢,另外一个的话呢,就是我一每一帧会产生几百甚至上千的,各种各样的消息,那这个event这个DISPUI就像物流公司一样的,他要把消息送到各个对应的那个机构那边去,所以这三个要素呢。
实际上是我们一般系统的核心好,这里面有什么坑呢,第一个就是event呢,首先你要去定义它,就event definition对吧,那最简单的就是说首先每个一般它有个type。
大家能想到最简单的方法是什么,诶,我定义一个枚举类型,就用数字对吧,这枚举类型是字符串,你看起来像磁场,它实际上是一个唯一的标识号,那这个东西呢实际上在系统里面,有的时候在早期引擎的时候呢。
是这个程序员自己手打的,我定义了100种不同的event,根据我们的策划的需求,但是你会发现到后来,连even的type都是需要这个交给设计师去创建的,然后呢每个event呢它还有很多很多什么呢。
它的它的数据叫event arguments,或者叫有的时候叫payload,就是说产生这个event了,比如说发生了一个爆炸,那你得告诉我说你爆炸的地点在哪里呀对吧,你给我带来的伤害是多少啊。
伤害什么类型啊,那这些东西它又变成event的数据好,那最简单的解决方法是什么,如果大家学过面向对象的话,是不是觉得哦这很简单,我知道怎么写对吧,那我就是第一个event的鸡肋对吧,然后呢。
其他所有的event都派生自他在这上面的话呢,我会去定义他各自独自的这种数据类型,这个呢其实没有问题的,如果你整个游戏引擎game play都是用C加加写的,或者用某种面向对象语言去写,就是可以的。
但是呢就像我前面讲的就是做游戏,它最复杂的情况是什么,90%的情况下,玩法不是由我们程序员决定卡,所以说当我们决定在做一个游戏之前的话,我们基本上不知道这个游戏,会产生多少种类型的这种event。
因为很多都是在我们的设计师在研发过程中,他们觉得诶我需要有一个比如说大boss残血之身,那个一半,大boss,马上要切到他的这个这个第二段状态的时候,这样的一个消息对吧。
那这个时候不可能你写代码的时候知道了,所以呢诶我们要允许就是设计师去定义,各种各样的event类型,所以呢,这里面我展示了一个荣耀里面的一个定义,定义这个event的数据的方法其实非常简单。
就是你去定义A你的event这个inner是什么,当然它会形成一个唯一的引导,这个这个算法其实并不难,然后呢,A你在定义A我需要有哪些不同数据结构,然后用我们前面的方法。
大家还记得我们在上节课讲工具的讲过什么诶,讲过代码渲染对吧,现在大家看到这个大家就知道了,我用这个方法其实很容易的,就可以渲染出一个诶引擎的代码,就把这些这些新的这些event类型,把它定义进去了。
但因为其实啊我讲的是一个最简单的做法,但实际上大家想想看,如果我这么做的话,是不是要求设计师每改一次这样的event,我的整个引擎的代码需要重新编一遍,这样是不是很麻烦,大家想,对不对。
所以实际上呢在真实的游戏设计中的话,我们有一个很重要的能力,就是允许设计师在我的核心引擎代码之外,能够扩展它的event的定义,所以有些event它可能是通过纯数据的方式定义。
在我的工具链和我的游戏的game play系统里面去,所以game play他的这个东西很有意思啊,他就是说呃,我觉得在作为引擎做玩法系统的时候,很多我们只是提供一些最基础的工具。
但是game play,经常写着写着就写成具体的游戏系统了,那我们在这个设计中的话,就是引擎这个侧要提供的功能是什么呢,就是说你在这个游戏相关的引擎呃,游戏代码中或者游戏相关的数据中。
都能去扩张我的gay系统,但这里面怎么做到的,其实讲起来就比较复杂了,我举个简单的例子,比如说像AI里面的话,这个event你可以这样去定义,你用这个可视化的方法去定义。
然后呢它实际上还是生成CCR的代码,但是呢它的引擎架构就可以允许说,在这引擎运行时的wrong time,他可以把这一段新编译出来的一个C加代码,当成一个DIAI,我注入进去,那这件事情。
他就是花了很多功夫去解决这个问题嘛,但有有的引擎它用什么方法呢,哎我的上层做了一层,比如说其他语言,比如像C12F这样的接口对吧,它很容易动态的挂件和扩展,还有的语言,还有的引擎怎么办呢。
哎我用脚本语言对吧,我在脚本里面定义一些event,所以各自都有各自的方法,但是这个东西我就不展开,但是其实光event的定义就是具有一个可扩展性的,event的定义,这件事情其实不简单的。
就大家如果真的有兴趣去研究引擎的时候,你会发现这一趴很值得你去设计,而且这个东西呢也关系到我们后面的东西好,那当我们有了这样的一些event之后呢,接下来我们要注册我们的响应函数了,对不对。
我们要call back了,那么QUEBEC这个东西呢,其实这个名词很有意思啊,叫回调函数,其实说实话当时我听到这个名字的时候,我就觉得啊这个翻译的这个这个不能怪翻译,翻译,我看到这个英文名。
我也觉得叫毁掉,但是我总觉得这玩意像是一个,其实我蛮喜欢他另外一个叫法叫invoke,就是说哎我去激活它,我去这个这个这个就是触发这种感觉,其实我一直觉得回调不是回调,是触发。
就相当于我提前准备好了一堆堆的小的,piece of code对吧,一些小的功能模块,你不是回调的,我还是说你把我激活了,但没关系,这个是这个既然游戏行业已经这么。
这不仅是我们整个计算机行业都已经这么定了,那我们就follow这个paradise对吧,我们叫做回调函数,但是我更喜欢的叫invoke这个词,就是说当有一个什么消息来了之后。
你把我一段处理方法这个激活了就赢过我了,那这个回调函数呢你是需要提前注册好的,就是比如说我收到一个damage的这个event,那我就毁掉一个就damage这个handle对吧,Handler。
那我去handle这个AI的事情,比如说我遇到了一个叫做什么,这个这个这个什么color change,或者说我的status change,比如说move on change。
或者说reloading这样的一个消息过来之后诶,那我就调动我reloading这个装,重新装弹这样的一个操作,那这个事情也是对的,都很简单诶,有意思的事情就来了,就是说其实啊在这个回调函数里面。
有一个特别容易踩的坑是什么呢,就是说你会发现回调函数的注册和它的执行,不是在一起的,这很好理解,对不对,一般来讲我们都是先注册后执行,但是在这个中间间隔里面,其实各种幺蛾子都会发生。
这就相当于你跟你朋友约好了,说哎那个等什么时候有空,我们俩一起喝茶对吧,好女朋友说好,我把这个喝茶的这个这个这个这个这个时间点,什么那个预约全部定好了,但是呢到那个礼拜六的时候。
你那个朋友可能会发生各种各样很奇怪的事情,对吧,他会突然说我有事好了,你那个手上那一张这个入场券,是不是就等于白买了,其实在我们的引擎里面,最复杂,最容易出问题的,就是说一个对象的生命周期。
和它回调函数的安全性,就经常你注册了一个回调函数,如果这个东西我没有设计好的话呢,哎就会导致整个系统就是在那边就会犯错,甚至会导致整个系统的崩塌,为什么,比如说你毁掉了一个东西,它的物体不存在了。
这里面举个例子吧,比如说我们注册了一个函数对吧,这个函数呢就专门收这种,就是对我的这个血量有伤害的,这样的一个一个一个处理好,那这个函数呢注册都没有问题,但是如果在下一帧的时候。
这个对象他可能已经死亡了,这个对象已经被销毁掉了,诶这时候如果有个手雷在旁边爆炸对吧,这个回调函数你再去调它的时候,它可能就掉到了一个野指针,那这个东西它就会出问题,那大家会说那没关,没关系啊对吧。
你的回调,你这个这个对象的回调还是注册在这,如果你对象删除掉我这个回调函数也一并删掉,对不对,这个方法是有没有问题的,有问题没问题,你可以这么做,但实际上的话你会发现,当你一个游戏引擎里面。
管理的几千上万个机油对吧,就是游戏object,你又注册了相近,可能是他十倍以上的这样一个call back函数的时候,他的这种管理就会变得非常的复杂,而且很多时候很容易写出bug,那东西怎么办呢。
其实这里面其实,就引入了一个很重要的一个概念,这个概念其实大家如果去学习C加11的时候,就学到了诶,我们最早以前讲指针对吧,指针非常简单,就是一段地址嘛,但是呢A在现代C加加语言。
其实很多现代语言里面都会引入这个这个pointer,有什么叫weak pointer对吧,还有各种各样的什么reference啊,还有什么各种各样的这个还有什么。
我忘了好几个好几种那个reference,他其实都在解决一个问题,就是一个对象的生命周期的问题,有些概念比较复杂,我先在这里面我就讲两个非常简单的概念,就是说第一个就是强引用,什么意思。
就是说我的回调函数注册在这儿了,不好意思,你这个对象就不能删除掉,为什么因否则我下次调到了之后,你删除了我怎么办对吧,这个做法呢会保证就是说整个饮用的安全性,但是的话呢它会导致一个问题。
就是说有些对象真的应该被销毁了,但是呢因为你有一些函数注册了引用了,我就不能被销毁,那这个做法就会导致系统的内存就会越来越大,越来越大越来越大,所以现在已经很少这么做。
但这个这种reference在游戏中有没有用的,其实还是有用的,就是说其实这个词strong reference,我个人觉得呃就是他跟你语义我,我比如像我当时在学引擎的时候。
我会把它定义成叫persisted,就是说你拥有它控制权,比如说我一个小的一个房间里面对吧,这个房间里所有的物体,那我对他的关系是个拥有关系,就是包含关系,那好只要我这个房间不要销毁。
那我这里面的物体就不能被销毁对吧,随便你物体就算被那个依赖一句怎么样,我都不销毁你,除非我这房间整个offload了,但是呢还有一种reference呢,他就做的诶,相对来讲就没有那么霸道。
我们叫做wake reference,什么意思,就是说这个游戏对象唉,我这个回调函数确实要依赖于这个对象,但是呢我你如果要删除,你就真的删除,但是呢当我去调这个reference的时候。
其实我只要加额外的一步,就是判断一下你那个物体,这个那个对象有没有被删掉对吧,其实简单来讲,就是说其实大家如果学过那个C加11啊,或者一些其他的,比如像我记得。
好像很多smart pointer干的就是这个事儿对吧,就是说你会检验一下,我的这个pointer到底有没有效,比如说我的owner已经没了,那我的pointer就无效了。
那么我首先下面所有的处理我都不做了,而且把自己制成无效,这样大家都知道了,那这个做法呢,实际上就会使得肌肉的释放会变得比较的easy,比较高效,那么其实呢我们在游戏世界里面,就是物体之间的引用关系。
包括函数call back函数和物体引用关系,两种引用都是非常有用的,比如说经常物体和物体之间,如果像刚才我举的那个例子,就是比如说在游戏中啊,GO期间是可以形成一个嵌套关系的对吧。
那么如果我的父物体没有被销毁的话,我的子物体就不能被销毁,那这种就是一种strong reference,我其实表达了一种内存的锁定关系,那么但是呢机油之间,比如说唉我想存一下现在我所有能看见的人。
那这种reference的话呢,我们一般就用wake reference,什么意思,就是说这个人我觉得我应该可以看见的,比如说我便利我现在所有能看见的敌人,我选择其中一个去攻击我。
如果是下一帧或者前几帧算出来这个值的话,这一帧我决定要发起攻击动作,但那个时候的reference的话呢,我们就用micro,为什么呢,因为也许那个人在你前几帧里面,你觉得好像他还在你面前,对不对。
你还想攻击他,但是这个时候那个人其实已经死了,已经GC就是那个garbage collection,已经把它们那个内存释放掉了,那你实际上就可以知道说这个对象已经无效了。
所以真在你真实的在写一个游戏引擎的时候,其实strong和weak reference都是非常重要的,一个支撑性的一个功能,而且这个东西的话呢事实上是一定要用对,千万不要用错。
那么对于call back function的话呢,其实很多时候我们WAKRAFT用的比较多一点好,这个其实是啊一个非常重要的,就是当我们注册我们的call back函数的时候,特别要注意的一件事情。
因为你很多时候会遇到整个环境的不确定性,其实在真实的游戏引擎中特别做玩法的时候,我们的一个游戏对象,有一个游戏对象放在那儿,实际上有几十个系统,用各种不同的逻辑对它进行操作,一会儿让他生,一会儿让他死。
所以呢你在任何一个系统里面去应用它的时候,其实你就不能假设那个物体一定是有效的,但是呢你要是做大量的这种判断,诶,你又会让你的这个引擎速度变得很慢,所以这是个很两难的一个问题,Anyway。
这就是我们讲QUEBEC的注册,那么其第三个系统呢,就是那个我们的消息分发系统,这个情况大家觉得应该很简单对吧,那我反正就是来了什么消息,我就问你们所有人都去,比如说我每一帧产生100个消息,然后呢。
我每个机油就依次的根据我注册的quebec function,然后呢把所有的消息去扫一遍,然后呢少到了相应的函数,我就去分发,对不对,大家想想这样可不可以,事实上这样的你写也可以的。
但是呢他的问题是它的效率非常非常的低,这里面有很多的问题了,比如说假设我每一帧产生N个消息,然后呢每个机油假设我产生NM个,这个这个这个就是这个这个就是它的那个,call back函数。
然然后呢我有K歌这个GO,那你这个计算辅导的多少呢,就M乘N乘K,大家想想这个是不是很麻烦对吧,还有一个就是你的内存,是不是当这些消息过来的时候,这个这个这个消息比如一开始放一个去存。
那以后使用包括这个消息释放掉,那这个每一次都从头这样扫的话,是不是会就会变得非常的慢,所以实际上的话呢就是说在我们的消息分,事实上是有一个分发机制的,最简单的分发机制就是我消息来了,马上就去扩扩别人。
但这样会出一个什么问题呢,就会出现你前面一个函数执行中间会打断出来,来一段,它就会形成一小段,中间的这个这个这个就相当于你得等别人,就是大家如果学过那个KROUTINE的话。
就知道那个时候哎我的主线程要停下来,我要等另外一件事做完,我再回来,我的还是在继续继续执行对吧,但是如果你是单线程的话,那也真的就等在那儿了,等他回来,那这个地方的话呢,它就会产生一个很有意思的问题。
就是说假设我们这个世界在发生一个爆炸,比如说一个手雷炸了,他把旁边的另外一些手雷引爆了,那个手雷再去引爆,那你就会一个手雷产生那个引爆的event,就会触发另外一个另外几个手雷的,这个爆炸的这个疑问。
然后就一层层一层层掉下去,最后你去看那个codex的时候,你就会发现它深不见底,我不知道大家有没有有没有这个debug,一些比较大型系统啊,就是比如说我们一个大型系统部署出去之后,然后呢他如果宕机了。
你去看他的cost,有些时候如果系统架构都不好,你会发现你cos AI非常非常深,你就一路掉下去,而且呢同样名字的函数,它只是因为对象不同,在疯狂的在被重新扩,那个时候debug起来其实是非常的痛苦。
所以呢就是如果你想立即执行这些,call back函数的话,它其实就有这样的一个问题,然后另外一个呢他还有一个很有意思的问题,就是刚才我讲的就是说你的每一个操作,如果他瞬时能完成对吧。
这个动作我觉得还可以,问题不大,比如说我减一下血量,无非就是你的heal什么加减一个数值的事情,这个执行起来也非常的快,对不对,但是这个时候说哎因为我手雷要炸了,所以我现在要加一个爆炸的烟雾的效果。
我要AI effect,那么在前面我们讲过离子系统,对不对,那么大家知道离子系统这个玩意儿,可是很费的对吧,我要创建的几百个particle,要算它的运动,而且要把它渲染出来。
这个东西它实际上是并不能够瞬间做完了,但是呢其他所有的游戏逻辑,都在等着这个离子系统做完,那就会导致什么问题呢,就是在有些时候,你的游戏的帧率会突然一下子很不稳定,所以这样的架构呢。
其实啊在游戏引擎很早的时代,大家就觉得不能这么干了,所以直接派发,其实它在这个地方产生的问题是蛮深,蛮蛮严重的,那么这个时候呢还有一个问题是什么,就是说我产生大量的事情。
它在时间轴上不停的产生不同的event,这样就会导致就是说我我的调用是一层套一层,一层套一层,我没有办法把这个运算把它整个并行化对吧,大家还记得我们在前面讲过,就是其实后面会跟大家讲一些最简单的。
比如说面向数据编程的架构,实际上在现代计算机,我动辄就是八个核甚至16个核,那如果我依次就是像串珠串一样吊在一起的话,那这个调用的话,它是很难形并行化的,所以这都是就是immediate调用。
产生了一个很大的问题,那么所以呢,其实在现代的这个游戏引擎架构中的话呢,我们要做这样的一个叫这个这个event系统的话,一定要构建一个叫event q,就是我们会把所有的event全部。
这一帧的event全部catch到一起,变成一个Q,然后呢我下一帧开始的时候,我各个机构依次来处理它,但这里面我要一个分发的过程,那么这个event q呢其实啊蛮有意思的,他首先呢大家会想象在内存中。
或者说在我的引擎中,我们要有一个小小的,这个就是叫序列化和反序列化的系统,因为刚才讲的就是每个疑问它的类型不同,它的数据都不一样,对不对,但最后我的内存内存中存储的时候呢。
我们是希望给他分一个叫block of memory,就是一一大块内存对吧,每一个疑问的邦邦邦邦邦,一个连一个的连起来连起来,所以这个时候我去把几百个event拍成一块内存块,然后呢拿到这个内存块之后。
我要把它反向的拆成几百个event,其实这里面是有一个小小的,序列化和反序列化的过程的,那这个过程怎么实现的呢,以前大家会觉得很难对吧,那我要用无数的C加类型去派生和继承。
但大家学过了我们讲的那个反射对吧,我们就知道每一个数据结构它的内存是多少,所以我只要拿到event它的描述结构的话,实际上我们就知道它内存有多大,所以这个时候你看反射就起作用了。
那好我知道每个event的反射描述的话,其实你给我一段二进制的空间,一个一一段空间,我也可以反向的填回去,我一个C加的结构,所以其实event这里面对反射要的那个,就是用的地方是非常非常多的。
那么我有了这么多event之后,接下来我怎么去管理内存,这里面大家一定要注意,就是我们一般用的是一个rain buffer,这个rain buffer的概念,大家这个在学数据结构的时候没有学过。
其实非常的简单好用诶,你会发现我们在读书的时候,学的所有的数据结构,其实当我们去做大型工程的时候,它一定是有用的,所以像event这个事情就完美的适合这个ring buffer。
就是说我们会假设说我每一帧最多,比如说我同时要存1000个一半的,或者是5000个一半的,那我就分配个rain buffer,这样的话呢我就不用每次再去申请新的内存。
而我直接一直在重用这个rain的内容,这样我每次一个一般的处理完之后呢,其实我就可以把它head往前移一步对吧,如果我后面再去要加薪的疑问,我把它tell往后移,当然hit tell重合在一起。
说明我的buffer已经overflow了,已经炸掉了,但这种情况的话,在游戏中很多时候都是你写出bug的一个标志,比如说你有一个机,错误的在不停的发出这个疑问的,这是有的。
但是呢这个时候我们就会把这些U穷截断,这样的好处是什么呢,就是你的游戏逻辑可能会错误,甚至错只错误那么几帧,但是你整个引擎不会崩崩塌掉,所以的话呢,就是一般我们会推荐用ring buffer的方法。
去管理这个event的这个这个这个池子好,那么其实呢就刚才讲的,就是说真正在这个现代游戏里面啊,你问的数量是蛮高的,那么如果我们不假思索的,把所有的event都放到了一起。
那这个处理就是它便利的过程就很麻烦很费,我们讲过就是说A计算机是图灵机结构对吧,我们要把一秒钟就一帧里面,我要把几百个甚至上千元1万的过过滤完的话,而且给他根据在表里面去查收哦,因为你是这种疑问的。
所以我要叫你那个call back,这个来回的调用其实是比较慢的,那怎么办呢,其实在游戏引擎架构中的话,我们一般会把event跟着几大类分掉,比如说网络event其实非常的多对吧。
但是它的处理和其他的event没有什么关系,那我们就把所有的network event全放到一起,有单独的一个rain buffer来管理,单独的一个DISPU处理诶,所有战斗相关的。
一般我们也把它放在一起,然后呢动画系统它有的时候也是个独立系统,我们也要放到一起,那这样的话实际上我们相当于把一个大的数据,把它分而治之了,这个呢在实践中,其实无论是对于提高我们的引擎的效率。
还是提高我的引擎的这个DEBUGABILITY,就是说当你写这么复杂的引擎的时候,很多时候你会写错,那当你把消息呢这样分成几大类之后,再去追踪再去处理的话,实际上这个你一旦出错之后,你查起来真的很轻松。
因为你很多时候就发现诶,此处应该有一个动画消息怎么没有产生,那你就不会买这个event q的去寻找,你只需要在那个就是说动画那个科研寻找,所以这个实际上是一个非常好的一个架构,所以同学们。
我们在加我们的引擎的event系统的时候,会强烈的推荐大家,就是把这个消息分成几大类对吧,虽然理理想上原则上是这样,你是不需要分类,但是呢它会导致效率下降很多。
那么其实呢event q呢它是还是有蛮多的问题的,就是刚才讲了哎,虽然我一上来就讲了,这个直接处理模式有很多的坏处对吧,大家也想是啊,这个简直就是一拍脑袋嘛,来了一件事,我就马上处理对吧。
然后这样的话我一件事产生另外一件事,我就形成一个链式反应,最后这个东西很麻烦,所以还不如全部收集好,下一帧,我一起来处理,这个听上去是非常好的,但是呢它产生一个比较大的问题。
第一个就是你这个消息执行的顺序,我是不能保证的,为什么呢,举个例子啊,比如说啊我动画系统对吧,我的AI系统,我的战斗系统,其实在很多游戏引擎里面,它的执行顺序都是固定死的,但是有的时候我的有些业务逻辑。
我是希望先处理动画再去处理物理,再去处理战斗,有的有的人呢我是希望先处理战斗,再去处理动画对吧,有的时候我是战斗行为决定了我的动画行为,但是的话呢你的event系统去处理的时候,他统一到下一帧了。
下一帧之后同一类的消息全集中到一起,其实你并不能保证它的这样的一个执行的效率,而执行的顺序,这时候当我们在写这样的逻辑的时候,我们就要保证这个逻辑具有一定的健壮性。
其实这也是很多游戏特别容易产生bug的地方,其实这个问题说句实话,在现在游戏引擎里面,一直都是一个非常难以解决的问题,甚至有时候我们为了解决这个问题,我们会hard扣的一些东西。
所以其实在引擎里面有的时候有些疑问,我们甚至保留立即处理的能力,但这里面都是要很精心的设计过的,比如说我会有一个疑问,我故意塞在那边,还有就是有些函数我故意放在里面,我会去去安全性的改一些数据。
所以其实真正的一般在TK的时候比较复杂的,就是我记得有三种,一种是啊,正常的就是你下一帧一上来就处理完对吧,还有一种呢是你在我这一帧TIK完之后,在我的post tik的地方,我把它处理完。
所有的有的有的一般是在叫post tik处理,有的在critic处理,有的呢就是真的是immediate的处理了,所以实际上一个现代游戏引擎的话呢,它三种方法都要支持的,但这三种方法怎么去用。
其实是要求这个程序员,他对这个系统的调用方式非常的理解,这个地方就特别特别容易产生bug,其实到目前为止,我觉得还有一个很难的问题,我自己也没想明白,就是说啊我作为一个程序员,我是能理解诶。
什么时候该用那个PRE,什么时候用post,什么时候用media immediate,但是的话我怎么把这件事情告诉我们的设计师,让我们设计师非常用错,设计师很多时候是很蛋疼的,觉得这个是很麻烦。
那么另外一个的话呢,就是这个啊event这种处理机制呢,大部分情况下,所有的行为都会是在下一帧再处理,所以呢当我一个复杂的,这个就是说递进关系的逻辑,就是因为EVA触发了even的B。
even的B出问C他整个过程都是一次要慢一帧,所以在有一些你希望时时能发生的事情的时候,诶这个疑问那就比较麻烦了,我举个例子吧,比如说我们做打击感对吧,你这一刀在这一帧检测到。
我这刀砍到了那个我们的敌人,唉那个时候你希望比如说正品,你希望标选一个效果,几乎在同一帧渲染到你屏幕上,这样你的打击感才能到最好,对不对,但是呢如果按照event的方法的话呢。
很多时候比如说你这一针砍上去之后,诶,你下一帧可能先是health系统,去这个线下一帧那个particle能够出来了,你看到飙血这个时候在这个这个时候,health系统也能检测到说哦这是这是一次暴击。
好,那我再发出疑问的说,我要屏幕抖一下好,再下一帧屏幕抖一下,你想这个一刀砍上去,我看到飙血是一针之后了,然后呢在我看到那个屏幕抖动的时候,已经是两针之后了,那么两帧是多少呢。
两针几乎是100ms之后了,那其实你的打击感是不是感觉就不对了,这其实也是就是event的系统,当我们在用的时候,要非常小心地规避这个问题,所以很多时候你这个地方需要hard code直接绕过去。
这个就是他的latency对吧,所以这是invent q在用的时候,在真实的做做游戏引擎中,大家一定要小心的东西,但是呢不妨碍invent q是一个非常重要的一个,Game play。
就是玩法系统的基础系统好,那么其实这个地方相当于,我们把第二节课提的这个概念,就是这个消息分发处理的逻辑,这次算彻底跟大家讲清楚了,就是你要做一个游戏文化系统,你首先需要有这样的一个消息处理机制。
作为你的foundation,那么大家现在也能大概能明白,就是说消息的处理其实有三种方法,一种是就是叫马上处理,另外一种呢就是这个下一帧处理,下一帧处理呢又分为两种叫PRE和post对吧。
更细节的东西呢同学们自己去掌握了好,那么有了这样一个东西之后,接下来就说诶,那我们是不是可以开始写游戏的逻辑了,游戏的玩法了对吧,我就可以按照我的想法设计各种各样,我想要的游戏了呢啊原则上是可以的。
那最简单的写法怎么样呢,我就直接用C加写死了,对不对,那么在早期的游戏开发中,还真的就是这么干的对吧,因为那个时候说实话游戏还是蛮简单的,我们可以做的,就像你做一个FPS游戏。
实际上你用C加加写真的能写出来,而且它的效率肯定是最高的,那么我自己在当时读高中的时候嘛,那时候我刚刚在学习编程,我印象特别深,就是我用汇编写了个俄罗斯方块哇,这个我当时还记得那些代码大概是几千行代码。
那个汇编写下来,然后我就觉得哇,这个汇编语言真的是世界上最工整的语言,因为汇编语言每一行的指令它的长度都差不多,不像你写高级语言,就是像狗啃的一样,一会长,一会短,一会长,一会短。
汇编语言看起来非常非常工整,然后一段一段一段一段哇,这个感觉特别好,所以特别有成就感,但是呢大家知道这样的东西它是难以为继的,因为当我们的游戏变得越来越复杂的时候,那这样的高级语言的话。
或者是这样的编译语言,它其实是有非常多的问题了,那么最大的问题是什么呢,就是说你每改一点点的游戏的这个game play,你的整个游戏引擎就得重新编一次对吧,这个是很蛋疼的,很头疼的一件事。
那么第二个很头疼的问题是什么呢,就是说其实啊当我们的游戏真的发布出去之后,大家知道最容易出的问题是什么,有各种各样的bug对吧好,你经常玩买了一个游戏有bug,特别是你玩网游有个bug对吧。
我这个跳起来暴击别人始终达不到,这个肯定是游戏系统写了bug,或者说一个装备掉落总是错的,这种情况怎么办,诶如果作为游戏开发者,你说他急不急,他很急的,但是他再给你推一个新的版本吗,把这bug修好嘛。
那这个事情你肯定不愿意,对不对,那怎么办,其实有个很重要的东西叫做hot update,叫热更新,其实大家如果现在在玩网游啊,就几乎所有的网游,他们都是一个刚需的东西,叫做热更新。
就是你为什么会经常玩一游戏,玩着玩着玩的过程中间,你突然发现诶官方推了一个东西,你的bug就好了,其实这个东西就是用热更新的机制,而所有的编译语言,比如像C加加,它就非常难以热更新。
但是你真的硬生生的要hack其实也是可以的,但那个就会变得非常的麻烦对吧,所以其实编译型语言的话呢,在热更新方面的这个挑战是蛮多的,而且每一点改动都要重新编译,甚至把整个游戏宁可一遍。
就像我刚才讲的时候,宁可不好,你肯定要link半小时过去,那对开发的效率影响非常非常的大,所以这个时候呢,这个天降这个,这个这里面还有一个第三个挑战是什么呢,就是说那个在游戏开发中啊。
我们总觉得玩法是我们程序员去想的对吧,我们做过玩法系统,但实际上它的最大的用户不是程序员,是设计师,而设计师呢,他本身并没有受过非常良好的这样的一个,就是编程训练,你让他去写C加代码对吧。
人家设计师学的不是这个东西,人家学的是如何设计一个好玩的游戏,那么好,那设计师的话,他需要有能力自己去定义各种各样的玩法,各样的东西,那其实你如果让他直接改那个C加代码,这件事情也是很痛苦的。
包括有些艺术家,他也需要去定制一些游戏的功能,一些玩法,那怎么办,你怎么去驱动这个事情,所以呢诶这个时候就天降猛男了,就是这个脚本语言,脚本语言呢诶这个时候真的是就是特别特别好。
他首先的话呢就是非常简单易学易上手,第二个的话呢它热更新很方便,很多消费语言都是这种解释知识性语言,所以说你把代码一更新,诶,他的整个这个逻辑全部就变了,反正他代码是走到一步看一步嘛。
大家以前有没有学过那个有个语言叫basic语言,对吧,这是最简单的语言,现在我不知道现在应该在vb了,那么basic语言就是我以前印象特别深,我以前学pasta学C的时候,他告诉我说这是编译语言。
basic语言就是个解释语言,所以解释言的好处是什么,就是内行函数执行之前你把函数换掉了,它可以继续按照新的逻辑去运转,所以非常的方便,另外一个呢就是说它非常适合是什么呢。
就是说脚本语言它一般运行在一个虚拟机上面,所以其实它是在一个沙箱里面运行,所以脚本语言很多时候他可以自己crush,但是呢他不会把你的引擎的本体crush掉,就很多时候你会发现。
比如说我写了一个脚本的逻辑,但这个逻辑如果crash掉之后的话呢,哎我大不了再重新rise这个东西有逻辑就好了,我给大家举个例子啊,比如说这个东西在什么地方特别有用呢。
就是在写非常复杂的网络游戏的服务器端,大家可能不能想象,就是我们一个大型的M游戏的话,它一旦跑起来,背后可能是几百个这个进程,同时在启动,做各种各样的工作,有的人专门负责聊天,有的有的人专门负责发邮件。
这几百个进程或者县城在各个机器上,其实随时都有可能会挂掉,但是为什么你们去玩那些大型网络游戏,好像很少宕机呢,实际上我们背后都会做这样一个系统,就是我们一旦发现某个服务挂掉了之后。
我们会把那个服务重新启动,重新通过脚本重新把它接入进去,所以说这个其实也是脚本语言很核心的一个,一个好处,就是它能够就是说A不会把本体crush掉,所以这个呢其实是诶,刚才我们抛的问题的一个很好的解。
那么脚本语言呢它是怎么工作的呢,其实也蛮简单的,就是说你写出脚本,它本质上就是一些文文本对吧,文本通过他的自己的编译器编译成一堆的bad code,那个这个bad code呢。
其实一般来讲都是他们自己定义的,非常的简单,就比如说每个instruction,就是诶我到底做过什么操作,然后呢我的这个这个这个到底是哪些数字,相加相乘,然后内存应该怎么移动,非常非常简单。
然后变成一段那个二进制的一个blog,然后呢它有一个就是虚拟机,虚拟机呢就去执执行他自己编出来bad code,然后呢就可以这样整个run起来了,所以你可以认为就是它整个是在,你这个怎么说呢。
可以说是在游戏操作系统之上,又跑了一个虚拟机,这就是为什么它本身可以变得非常的鲁棒,当然了,它也是有代价的,代价就是它速度可能会比较慢,那么其实呢当脚本语言,它提供了这样所有的服务之后呢。
它其实就可以把很多的业务逻辑移到脚本里面,比如大家现在去看很多的那个游戏啊,特别是网游,他的脚本语言用的特别特别多,比如说大名鼎鼎的这个魔兽争霸对吧,那么他就用一己之力,带带带来了整个中国游戏行业。
大家都喜欢用UI这个脚本语言,对不对,所以我们的脚本语言你就会写很多很多逻辑,比如说啊开宝箱对吧,大家最恨的开宝箱的东西,比如说我打装备,打个东西刷装备对吧,这个东西我一个玩法。
比如说我交一个任务会触发什么什么事情,这些东西很多时候都是用脚本去写的,那么而且脚本还有个好处,就是策划也可以写,那这里面的话呢就是作为引擎测,我们去想,就是在脚本语言和我们的引擎之间。
一个最难最难的一个问题是什么呢,是对象的管理,就是游戏里面的一个机诶,我们一直在讲游戏引擎里面都是有机油,对不对,但这节课你们就会讲到一个有意思的问题,就是机油到底归谁管,比如说在我的脚本语言。
你比如UI里面,我每一个游戏里面的NPC诶,我都有一个他的肌肉对象,对不对,然后呢我在引擎里面的每个NPC呢,它也有一个自己的机会对象好,那这个NPC是谁负责管理他的整个生命周期,谁负责去销毁它。
这件事情其实并没有那么简单,原因很简单,就比如说你走出了一个主城对吧,你走进一个主人,你面前蹦蹦蹦出了这个几十个NPC好吧,你看他交任务对话完了之后,你走出去之后,这NPC是不是要被销毁掉了。
否则你的内存一直不得占用了吗,哎问题就来了,到底是咱们C加代码写的游戏引擎呢,还是我们交给这个就是脚本,你的系统去管它,其实这件事情,在游戏引擎设计的时候是非常关键的,那么这里面有两种不同的流派了。
比如说有一类人认为我的这个引擎代码,C加写的效率是最高的,而且呢我做了大量的这个安全性测测试,那我把这些机油呢,全部把它放到这个就是引擎内核去管理对吧,我100个NPC我玩好了,NPC逻辑坏了。
你给我发个指令,我把它删掉,这种做法呢其实是啊,应该来讲是没有什么太大问题的,但这些要求我的脚本里面,每一次去访问这些机油的时候,我得问问这个对象有没有销毁掉,而且呢就是引擎写这个对象的管理的时候。
他要写得相对比较严谨一点,那么因为大家知道,就是很多引擎是用C加加写的嘛,那C加引擎它的内存管理一旦做不好之后,就很容易造成内存泄露对吧,那么这个它它但是产生的问题是什么呢,就是说。
其实随着我们在脚本里的业务逻辑越来越复杂,很多机构的创建消亡,实际上是由游戏玩法决定的,打个比方,比如说你扔出了一个什么道具,啪啪啪,他突然变成了三个小怪物出来对吧,三个小怪物呢。
它还能向你去砸那个什么这个石头,打个比方,那这个三个小怪物它是不是G对不对,那这个机构的创建我还需要到引擎再绕一圈吗,好吧,而且这个创建的逻辑,其实在小本里写的最简单的对吧好,这小怪物开始看见你了。
就不停的打,你就变出石头来砸你变出来的石头,它又是机油对吧,这个机油又是脚本系统自身产生的那好,那难道这些小石头也要在我的引擎里的,整个转一圈吗,那这个过程它就会变得非常非常的麻烦。
所以你会发现一个很有意思的事情,就是说在一些玩法非常丰富和复杂的,游戏里面的诶,大家会说我把机油它的创建和管理,交给脚本系统,就脚本系统来负责创建这些对象,这些对象并且去交互,然后呢。
引擎呢只是引用到这些脚本系统创新的机构,但是它本身不负责他生命收集的管理,那脚本系统怎么去销毁这些东西呢,哎这里面就讲了一个大名鼎鼎的系统,就前面已经提到了叫GC。
GGC的英文全称叫garbage collection,就是垃圾回收器对吧,它会就是几乎所有的脚本系统都有这个GC系统,就是说如果我发现我的这些对象,没有人再需要用它了,好。
我就专门找个时间把这个对象给清掉,那么大家如果写过c#语言的对吧,写过java语言的,写过其他所有脚本语言,都知道这个GGC系统,cc系统是一个让大家又爱又恨的一个系统,爱它就是真的省事。
不会像C加加对吧,动不动会遇到什么野指针啊,动不动会遇到什么这个内存泄漏这种问题对吧,真的是无脑的创建对象,无脑的用它,然后呢用完就扔,不用管,过一段时间期,最后把你处理掉。
但是GC会产生的问题是什么呢,就是他这个算法特别的慢对吧,他每一次要整个扫描这个这个context,里面的引用关系,所以大家如果去分析一下,比如说举个例子,像UI你去看,如果你的脚本写的非常的复杂的话。
那个GC系统运转的效率会非常低的,甚至在一些复杂的游戏产品里面,GC本身要吃掉整个游戏时间的,可能是1/10左右,我去收集这所有的那个这个资产,其实我跟所有的错,那个就是说这种就是重度脚本型的游戏的。
这种特别复杂的MO的这种游戏的团队,交流的时候,大家很核心的在一起聊的就是如何优化GC,让这个GC不要占用我太多的时间,所以这两种方法呢都有各有利弊,但是完全取决于你的游戏类型。
那么我觉得就是取决于你的引擎,如果我们的引擎是做的本身啊,对象没有那么多,没有那么复杂,但更多的是一种很重度的这种啊,角色细节的表现,比如说我相信你就这么几十个对象,但是呢它有很多很丰富的动作。
AI的这种细节,那这个东西的话呢,我会倾向于把它的生命周期用引擎去管理对吧,那这样的话呢,就是说他其实在欧美单机游戏动画,很多时候对象他都是在这个引擎里面管理的,但是对于比较复杂,就是业务特别特别复杂。
而且经常会因为玩法会产生大量的新的GO的,这样的一个游戏,比如说像最典型的就是MRPG对吧,那这里面的对象都特别多,而且非常多的系统,各种各样的玩法,那一般来讲的话呢。
哎我们会把生命周期放在脚本里面去管理,这样的话呢在玩法系统里面,我们可以很容易地创建和销毁,各种各样的物体对象,所以其实我很难说哪个方法好,其实各有利弊,那我也非常希望就是在下一代的引擎设计中。
这个问题呢彻底的解决,但目前真的是如果我们引入脚本的时候,这个对象到底归谁管,其实是一个两边会打拉扯的一个东西,那么其实还有更有意思的问题,就是说大家想象一下,如果我们今天去用脚本系统,在我们的引擎中。
大家最自然的想法是什么,哎我的引擎写的很厉害,很牛逼对吧,我的引擎每每一帧都是tick,当我到有些业务逻辑的时候,本来比如说我想写的这个AI系统,我想写的一个玩法,比如说我走过去把门一开。
这个门上会有些灯去闪一闪这些东西诶,我可以把它变成一个脚本,所以是引擎去调用脚本,到了某个特定特定的,比如说component的时候,我把那一段它相应的处理写成脚本对吧,这个逻辑是成立不成立成立的。
比如像unity引擎里面就是这个啊,它的原生态,就是说哎你每个component可以扩展成一个脚本,然后用c#去写写的就可以了,但其实啊还有一类这个引擎非常有意思的架构,就是它是用脚本包引擎。
这个架构呢现在大家谈的不多啊,但是我一直觉得,这也是值得大家去思考的一个东西,就是说实际上你可以把引擎变成一个SDK库,就是引擎提供了各种各样基础的服务,然后呢唉是由脚本来控制整个tick的flow。
到了那个时候调侃说tick animation好,引擎傻乎乎的把所有的机构animation tik1遍,脚本说嗯你们这些animation可以,这些机构可以休息了,那就应该干掉。
然后呢他给一个名名单过去,引擎把这些机构给干掉,所以其实呢还有一类架构,就是说我有脚本反过来的去爆音擎,现在具体的游戏我已经不记得了,但是我记得在早期的游戏游戏产品,而且是很著名的大作里面。
他用的架构还真的就是交不爆音情,但是现在的话呢这种结构我记得已经有点少了,就是说但这两种思路的话呢,我觉得大家都可以考虑,其实如果你走方法二的话,实际上你就把引擎变成了一个什么呢,变成了一个啊。
我可以认为是个SDK集吧,就是用用脚本反过来去扩他的service,各种各样的服务,所以这两种架构的话,大家在做自己的引擎架构的时候可以考虑,如果你想走方案二的话,其实你引擎暴露出什么接口。
是要认真的设计过的,那么其实呢今天要讲的就是说,脚本有个很重要的特性,就是它能够热更新,那这里面我就不展开复杂了,就讲个非常简单的点,就是热更新怎么去实现呢,其实讲透了是非常的简单。
就是脚本语言的热更新,很多时候他就是把那个函数时间给修改掉了,就是说他以前老的函数有个函数指针对吧,我我扩这个函数的时候,那我就到这个指针执行这个函数,诶,你来了一个新的函数的时候。
我把它指成重新指向我的新的视线,那我的热更新就已经实现了,但注意一点,就是说在引擎在那个脚本里面有很多的local,variable啊,或者是函数里面的这些vb,有些人你viable,它是这个全局变量。
那这些变量的话,当我们在写脚本的时候,热更新的时候,这些变量注意一定要重置,或者一定要把数据这个进行必要的处理,所以否则的话呢你热更新不好,就会导致整个游戏的崩塌,那大家会觉得哎我去写热更新的时候。
我肯定会小心了,对不对,写错了,我大不了重新启动一下,大家在所有在做游戏引擎的同学都会这么讲,一开始入行的时候,但是我要跟大家讲,就是当这个引擎,真正作为一个商业级的游戏引擎的时候,你就没有那么快活了。
因为音响啊就是你的一个游戏,现在有上百万人同时在玩对吧,然后的话呢很多人在做很重要的操作,比如有人正在做充值操作,有的人正在点开他的那个时装箱,对不对,有人正在打一个很重要的boss。
你老人家这个地方假设热更新把人家搞坏了,那这可能就有几千甚至上万人同时找你,讨要我的装备对吧,讨要我那个boss,那个boss我已经快打完了,突然你把我的引擎崩掉了,所以其实在游戏引擎架构的时候。
特别考虑的就是工程上的一种鲁棒性,这个也是当我们就是从,其实今天我们讲的这个脚本系统,实际上就已经讲了,游戏引擎一个很有意思的概念,就是很多人在问我说,哎你们的游戏引擎是用什么语言去写的呀。
然后我每次就很尴尬,我说这个很难讲啊,因为我们会用到十几种完全不同的语言,就是在每一个不同的场景下,肯定有个语言更适合他,比如说服务器你会用go语言对吧,那么你在这个就是说呃脚本那个地方。
你可能会有UI,你可能用CSHUI,你还会有一些地方你管理服务器,你可以用Python,对不对,然后你有的地方用CC加加对不对,然后包括也会用一些java,包括你前端还要用一些语言。
所以其实一个真正的现代游戏引擎啊,它是一个复合体,各种音调在一起混合去编程,所以当我们去实现我们的引擎和脚本系统,对接的时候,其实就是大家第一次能感受哎,这个脚就混合编程,做这种大型系统这种概念。
所以反过来讲的话,就是在我们今天开课的第一开始,同学跟我们说,AIKA要不要实现脚本系统,我我刚才也在讲的,就是说这件事情的话呢,其实支持一个简单的,比如UI脚本,其实难度并不大的,但是呢我会觉得。
就是等我们这个PUI本身做的再稳定一点之后,同学们有兴趣的话,我们一起可以来设计一下,就是哎对PUI这么小小的引擎,它的脚本系统该怎么接入,该怎么去开放哪些接口,包括那些接口函数怎么去注册。
这些地方有很多很有意思的小细节,那我们大家一起探讨,所以说你想把一个脚本系统和引擎,结合的好的话呢,哎真的是要花那么一点点的时间去好好想想它,它的架构,那么其实呢脚本语言有很多好处了对吧。
但脚本语言最大的坏处其实也非常明显的,就是第一个也是最这个大家最诟病的是什么,就是它的这个效率太低对吧,大家知道就是比如说啊C车哪个哪个圆最快,C加肯定是最快的,对不对,那你java肯定会慢一点的。
c#其实速度还可以,但是你如果用了UI的这种语言的话,它的速度就会比那个原声语言慢很多,但是呢就是这个有一种很有意思的东西,叫什么呢,叫jet,就just in time。
就是说像ui jet是大名鼎鼎了,就是说诶我虽然是解释执行的,但是呢我可以一边解释,我一边编译,等下一次的时候,我就直接run那个机器码,其实jet这个这种方式啊,其实本身是非常好的一种方式。
就是呃我记得以前我们以前讨论过一个问题,就是到底是编译语言快还是js in time的语言快,大家会说那肯定是编译好的语言快,对不对,CAI写好了肯定是最快的对吧,但事实上呢。
就是JAI语言有一个很厉害的一个属性,就是说他一边运行一边编译的时候,他可以知道你代码执行的路径,比如说假设我代码中有大量的if else if else分支,你会发现有些分支,在99%点几的情况下。
他都不会出道,就把可能你整个这个脚本乱完之后,那个分支都会出不到,唉,借他他就知道,他就会直接把那些分支全部优化掉,所以理论上讲的话呢,就是在很多情况下,jet如果做得好。
他理论上是可以超过你的编译语言的,但是我没有那么贪心的要求啊,就是我觉得啊,你只要比那个解释执行快就可以了,但实际上这一点gt我觉得做的还是挺不错的,就是现在的比如像ui jet这个语言的话。
它实际上比原生态的UI大概能快一个数量级,是没有什么太大问题的,所以的话呢其实这个脚本语言的话呢,这个问题其实解决的还是蛮好的,就是像UI1些简单语言,那接下来还有个很有趣的问题是什么呢。
就是说哎我们在写C语言或c#,这种语言的话呢,它面向对象的结构做的非常的好,但是的话呢就是像很多的脚本语言,他们是这个叫wk type的,就叫弱类型语言,比如像UI里面,你创建一个对象,它是没有类型的。
你根本不知道什么东西对吧,它的好处就是什么东西都往里面塞对吧,你无论是一个结构还是一个函数,还是什么东西都往里面塞,意思,反正都都可以调用,但是它的坏处就是说,哎你去反射它里面真正到底什么数据的时候。
还是挺麻烦的,你得去抠到UI里面的结构,你是能抠出来的,但这个可能效率会比较低,所以这也是我们在用脚本语言的时候,有的时候会遇到一些比较头疼的一些问题,那么其实呢所以说不同的引擎啊。
它会根据自己的选择选择引擎,比如说刚才提到的,就是说比如说这个我们一般挑语言的时候,我们看它自身的这个build的feature是不是足够好,比如有的语言它的内裤非常的丰富。
很多操作一行指令就就处理完了,对不对,那么有些的话呢就是它的效率非常的高,很轻量,内存占用很低,那么还有一些脚本语言的话呢,就是说哎它的面向对象封装的特别好,这样你不用自己再去分。
这些比较复杂的数据结构,所以其实我们再去这个寻找这个游戏引擎的,脚本语言的时候,这些因素都要去考虑,对面举几个例子吧,比如说像这个魔兽世界对吧,他用一己之力把这个UI这个语言,介绍给了整个中国游戏行业。
那么UI语言它的最大特点是什么呢,就是轻量,它的虚拟机真的非常非常的轻,非常的节约你的内存,然后呢效率也很高,因为它和C加加的接口效率也做的非常高,但是它的特缺点是什么呢,就非常的啊。
基本上的内裤都没有,就是各种各样的这个扩展都没有,很多东西都需要你自己去写,那么这里面的话呢,另外一个语言就反其道而行的就是Python,对大名鼎鼎的Python那个我印象特别深。
就是我以前学Python的时候,那本书上面画了一条巨大的舌音,Python的意思就是大蛇嘛,那么Python语言大家如果现在去学一些,我忘了,前段时间大家特别火,都要学Python了。
人人都要学Python,对不对,学数据处理,学大数据你都要用Python,对不对,因为Python的好处就是什么,他的库特别的强大和丰富,但是它的缺点是什么呢,就是说哦他还有object。
它是面向对象的,我记得当时我们在最早在接UI,这个这个脚本的时候,我们还犯了一个错误,就是我们觉得UI哎呀这个弱类型对吧,实在是太难管理了,作为程序员的小故事,我们把UI整个改造成了面向对象。
最后出了什么问题呢,就会导致UI的GC就是那个垃圾回收特别的慢,后来我们上了ui jet之后,发现哎呀这个BGT基本上不能加速,因为你没有按照他最原生态的那个方法去写。
那这一点反而Python是做的很好,Python面向对象天然的对吧,而且呢有非常强大的扩展库去支撑它,但是Python的问题是什么呢,就是非常的重对他虚拟机一旦跑起来之后,你可能几百兆内存就没有了。
所以只Python的问题,那么现在在引擎里面呢,大家也开始慢慢的,因为有mono这样的库,包括当net现在也官方的支持这样的库,就是说我们可以把c#,这种原生态的编译语言变成一个脚本语言。
那么C72P有很大的好处,就是学起来成本非常的低,基本上你学过C加加和C语言之后,其实你会非常容易理解C12,第二个的话呢就是说cc sharp的话,它本身的社区各种库的支撑也是非常强大的。
所以其实现在已经有越来越多的这个团队,也在考虑用c#作为它的扩展语言,那么最大名鼎鼎的就是unity对吧,unity就是完全依托于CJB的可扩展性,这里面其实我认为是选这个选择。
还是很smart的一件事情,那么其实各种语言的话,你会发现当我们去比较它的时候,诶比较它的性能对吧,那这里面毫无疑问,UI一般是比较最慢的,Python也好不到哪去,Python有时候也慢。
但是c#的话呢,他去执行的时候效率会高很多,但是呢在以内存为基的话,诶这个UI是最省内存的,那么Python还是一样很费内存,那是CCCHP呢,相对来讲中间找到了一个平衡。
所以说呢我们在设计游戏引擎的时候,我们确实要非常仔细的寻找,我们的脚本语言应该是什么,所以到时候同学们可以投票啊,就是我们如果PULLA后面开始,决定做他的脚本语言的时候。
大家会选说我们到底是用UI还是用Python,还是CH对吧,说不定我们可以根据大家的投票,选择我们PICOLA的脚本语言,那讲完了脚本语言,大家基本上知道了脚本之后呢。
就知道怎么去写各种各样的游戏玩法了,因为你已前面有一个max系统,然后脚本就相当于是你可以对各种各样的玩法,对吧,写坏了也没关系,大不了一个热更新,重新再更新上去,所以这是脚本语言很棒的一个东西。
那接下来就再讲一个大家更加喜闻乐见的,就是visual scripting,就是说哎可视化的脚本语言对吧,可视化脚本,那什么叫可视化脚本呢,那其实就是这个大名鼎鼎的blueprint蓝图系统,对吧啊。
还有什么呢,就是现在UI现在也要退的,就very scripting这个系统对吧,那这些东西其实不只是两个引擎了,其实基本上现在的游戏引擎,都要提供这种可视化的这个脚本功能。
那为什么我们需要可视化的脚本功能呢,其实很多时候是我们忽视了一件事情,比如像我我自己是程序员出身,我很多时候不太理解,说哎呀明明写一个加减乘除,写个if else判断对吧,我用语言写起来不是很快嘛。
很方便嘛,实在不行,我写脚本嘛对吧,程序员其实蛮喜欢写脚本的,但是为什么我们要做一个这么复杂的一个,就是可视化的这个编程系统的,其实后来我我们才意识到说,因为对于很多非程序背景的游戏开发者。
比特别像设计师和艺术家,那么你让他去编程这件事情其实成本非常的高,而且你可以想象一个设计师,他想写一行,写几行代码,真的能编译,通过这个事儿就已经哭爹喊娘了,很多时候他根本不知道变量的声明啊。
不知道变量的生命周期管理呀,实际上这件事情就变得非常的不可控,而且呢就是说我们写代码经常会写错对吧,逻辑写错了,因为你比如说对象内心用错了呀,函数这个调的不对呀,这些问题你在写这个五写这个代码的时候。
其实你看不出来的,你只有执行的时候知道,但是的话呢可视化的这种脚本的话,实际上通过各种提示,各种关系锁定,把这些所有的error提前给你屏蔽掉,这样的话又会极大的方便,我们的艺术家和设计师。
来创造他想要的游戏玩法,所以说这就是为什么在现代引擎中,可视化的脚本变得越来越这个受欢迎,也就是这个道理,那么可视化的脚本是怎么去做的呢,接下来我们讲一下这个它的设计设计结构,OK稍等,我先喝点水。
好嗯然后呢其实怎么去理解可视化的脚本呢,其实啊你就把它想象成一种编程语言,大家如果学过任何一种最简单的编程语言,你知道编程语言有哪些要素呢,首先你要定义一个变量,对不对,然后呢你要去写函数。
你要去写表达式对吧,表达式是什么呢,就加减乘除含一些函数,是不是函数啊,就是比如说一些一些语句,这些语句什么执行一些动作,比如print在屏幕上打印一个东西对吧,或者在屏幕上画画一条线。
那么这些这个这些语句还有什么呢,还有这个控制流对吧,If else,对不对,我要控制他整个行为还有什么呢,诶我把一一大堆的操作,把它包装成一个包体,包装成一个函数,这样的话方便我以后更加多次的。
方便的去调用它,然后呢,类似的功能和它的这个,就是说数据我打成一个包,变成一个类的定义,那么其实就是我们很多,几乎是所有的现代语言的一个定义吧,但这个是一个呃不是这个叫面向函数编程。
就是我们传统的就是传统式的编程,我们就有这些要素,对不对,那这些要素呢,其实就是当我设计一个可视化编程系统的时候,你要对对抗,你要看这个要素,那这里面的话呢。
我们接下来就举用unreal的blueprint为例,因为BOEPRINT我个人比较下来,我认为是做得最成熟的,也是最老牌的这样的一个可视化脚本语言,所以它里面很多概念基本上是完整的实现了。
那么首先你看一个变量,那其实变量呢最核心的就是说诶,我每个变量它到底是什么类型的对吧,它是个布尔型,它是个int型,它是个还是个颜色对吧,还是一个向量,还有一个是什么呢,诶这个变量的作用域。
这个听起来有点复杂了,对不对,这个变量是在我这个本地自己算算的,还是我的变量是来自于外面的,比如说A啊,这个角色的血量是跟这个角色相关的,对不对,那么整个世界的温度,那是整个世界的相关的。
所以每个变量它实际上是有自己的作用域的,对吧,你一个角色的血量的变化,不会影响其他角色的血量,更不会影响世界的温度,所以每一个变量有自己的scope,所以其实呢在这个就是编程语言里面。
就是在这个这个script语言里面,那个这个graph那个图里面的话呢,哎我们就通过拐角的方法去定义了,各种各样的变量,那这里面变量之间它会进行赋值吗,其实几乎所有的可视化的编程语言。
它都会用不同形状或者颜色的拐角,来定义数据类型,这样就避免你把错误的数据接入到他的输出了,当然这里面还有一个特殊的处理,就是说比如说有些可以直接进行类型转换的,比如说你float可以直接转成int对吧。
int可以转成float,那像AI你们的做法是什么呢,诶他会给你直接加一个类型转换器,但这个都是细节了,所以其实这个拐角的设计啊,实际上对艺术家来讲非常的方便,为什么呢。
因为它只要看到绿色就绿色蓝色蓝色就好了,它就不用再去管其他的事情了,那么包括它的鼠标移上去的时候,如果你正在运行的时候,debug的时候,你能看到它的数值到底是多少,大家想想,这个时候大家的理解就是说。
为什么可视化编程态度它有很大的一个优势了,因为我们以前在写C语言的时候,经常会把数据的类型复杂,当然了,编译器会告诉我们这个事错了,但现在你用这种就是可视化编程的话,它直接就不让你连了。
这样就更简单对吧好,有了这个变量之后呢,其实我们就可以进行语句或者是表达式,那其实表达式呢其实相对比较简单一点,就是说比如说加减乘除,包括现在所有的visual script语言的话,他会都给你。
会给你highlight,说你是做个加法,你还是做个大小比喻,那个比较你还是做个乘法,就是都有一个巨大的一个符号的明显显示,那这里面的话一个很重要的节点,就是这个语句就是做一个动作。
其实在几乎所有的steam machine啊,或者说蓝图啊这种语法里面,你会看到一个类似三角形的这样一个符号,这个符号就表示说这个地方我要执行的语句,这个语句是个动作,我这个动作做完了之后。
我再回去做下一个动作,像刚才我举个例子,就比如说我想我想在屏幕上打印出一行字,这是什么,这是一个动作,对不对,那我指向去那边,Excement print as a string。
然后呢这里面你可以输出一个变量,你的你的这个string,就是你这个字符串到底是什么东西,哎你这个语这个语句执行完之后,可能在游戏上面的那个就是控制面板,会打印出一行字出来,当然我们可以做的更复杂一点。
比如说我这个动作是让屏幕相机抖三抖对吧,一样的抖三抖完了之后,我再去走下一个flow,所以说statement就是说在我们的整个这个可创一年,你实际上真正的执行的flow是用这个语句走。
但是呢还有很多的就是说进行这种表达式运算,那么在可视化引擎,另外一类呢就是我们的control flow,就各种各样的control flow,那其实呢也比较简单,比如说哎比较大小对吧。
然后的话呢我们要求你们就比如说依次执行,那这里面其实他就用刚才那个语句,那边的那个三角形的符号,你会发现control flow里面很多时候,有的时候是数据的比较,有的时候是执行的比较。
这两种东西呢都会区分开来,所以大家去理解这个,就是说可视化种编程语言的时候,实际上你把这些东西能够串起来的话,你基本上就能形成任何一种你想要的逻辑,那这里面我选的全是blue处理为例啊。
但是如果你真的理解这个基础结构的话啊,实话实说,你做一个简单的类似于那个不print系统,其实难度并不像大家想的那么大对吧,特别像现在你有了WPF非常方便的帮你做UI,然后呢你又懂脚本语言的话。
其实你用这些东西呢,形成一个基本上一个简易的可视化编程语言,其实比如说像有些游戏引擎啊,我见过比如说当我们做这个叙事结构,做了个叫store flow,那其实也可以用这种方法去形成它的整个flow。
其实也是类似于一个脚本,那么在实战中的话呢,我们也会发现就是两个问题,第一个就是诶我有很多的运算,我把它我会经常会调用它,而且呢我希望他有更清晰的输出和输出,那我们在高级语言中引入了一个东西叫做函数。
大家想象一下函数出来之前的话,我们写一段代码,是不是从头写到尾,几千行就一路写下来,对不对,然后呢我不能执行的时候,我就降不过去,那么后来大家引,我就在汇编里面引入了函数的概念,那好那函数的话呢。
它就可以很方便地把一系列的这种这个操作,打包到一起,然后呢它就会抽象出哪些是我的输入,但是呢我还注意函数的执行入口只有一个对吧,然后呢A函数的执行出口可能有很多个,然后呢进入到我的函数的外面的东西。
那这样我的函数就可以形成一个接电单元,那么再往后的话就是说,诶如果我希望这个数据和我的这个这个处理,形成一个闭包,那形成一个整个打包在一起的体验,就类似于像我们高级语言的类这个概念。
那其实呢这就是这个在大家如果学习这个,就是像unreal的这样的一员的时候,它的一个blue print其实非常就像一个类,而且你会发现到blueprint里面会定义很多诶。
只属于local variable对吧,就是我这个类自己用的viable ui web,我可以变成private,我只能自己看到,有些val变成别人看到,所以其实比如说我举个例子,比如像虚幻引擎。
是一个非常深受C加思想影响的这么个引擎,其实我们去看他的blue print,这个体系结构的设计啊,就非常像把C加的一些概念用,这个就是说脚本化的,用这个图形化的方法把它表达出来了。
所以你一旦有这样的一个基础之后,理解它的难度就并不大了,那么所以这里面就点出了这个,当然了,他有很多这种辅助性的设置,就比如说哎你移上去,给你进行一种非常快速的帮你测试这些节点。
包括当你的一个点拖出来之后,他直接给你提示,这一点,我个人觉得是unreal做的非常好的东西,如果大家在做自己引擎的这个系统的时候,如果你做完基础的功能,你再做一些这些功能的话,我认为是非常有必要的。
因为它会极大的提高你的使用者的效率,就是我们一直讲引擎是什么吗,很多人很多很多人认为引擎是一些高深的算法,但是呢我们自己做引擎的,这个这个行业从业者的话,我们会认为引擎的本质它是生产力工具。
就一个引擎的好坏不是由你多少种功能决定的,而是由什么呢,由别人用起来的效率决定的,其实这一点你就是如何做一个让别人用起来,效率很高的引擎,这件事情是门槛非常高的一件事情,真的他这里面有很多很多事情。
需要我们学习和努力的,所以那你有了这样的一个scripting的话,那接下来的话其实很重要的一个东西,就是说你要去debug debug起来,其实他就因为你把整个逻辑全部变成,可视化之后。
当我的任何一个对象在什么状态的时候,那个节点就会被highlight,然后呢你鼠标一上去,它会告诉你各种web,我们程序员也非常习惯于第八个,对不对,我们是习惯于什么呢,加一个断点。
在那个时候通过print数值,我能看到他所有的东西,但是大家想想看,对于艺术家来讲,对于这个设计师来讲,这件事情是很难,那么唉你一旦变成了这个可视化的语言的时候,它就变得非常的简单了。
很直觉我确实观察过,就是说艺术家他怎么去用这个这个,这个就是说不UI这样的系统的时候,我会发现确实虽然作为程序员来讲,我们会觉得BLUPRINT系统特别的麻烦对吧,没有写代码写的那么舒服。
但是对于一个没有编程基础的人,他真的符合他所有的直觉,包括第八个这个过程好,那么其实可视化编程系统呢,它也不是就是特别好啊,就是没有问题的,事实上我们在工程实践中发现,这个东西问题也是挺多的。
比如说对于一个大型游戏产品来讲的话,如果这个脚本或者这个这个可视化脚本,不是一个人去改的话,那么几个人同时改的时候,他们之间的merge就会变成一个很痛苦的问题,因为如果是代码之间。
merge其实是比较简单的,大家如果写过这种,比如说一些beond compare这些软件的时候,你用过的话,你就知道还是挺方便的对吧,但是而且你很容易理解,就是说这一行代码加上去语义的变化。
但是对于一个图来讲的话,这个节点添加删除,包括移动之后,他们之间的merge,实际上就算你可以merge,默认出他的语音已经没有一个人能讲的清楚了,这也是我们在跟很多就是啊。
做国内大型商业产品的团队去交流的时候,你会发现一个很有意思的特点,就是说大家在用虚幻引擎,就这种就是我们只是举个例子啊,就是用这种有可视化编程的,这种语言的那个引擎的时候,他做PTAP阶段。
用这种啊visual scripting系统非常的快,能快速put type,但是他一旦进入了产业化生产的时候,他们的这个工程师一定会把它变成,就是要么用C加代码原生态去写。
要么就用其他的脚本语言替代掉,这样的话方便他们进行管理,进行merge,但是还有一个考虑,就是说那个早期的VIUI语言执行效率比较低,他们希望用更快的语言去写,这样效率更高一点。
但这其实呢也反映出了就是威尔斯克T系统,它的本身的一个问题,那么另外一个的话就是risk,UI系统的一个比较大的问题,就是代码它是线性的,你可以依次读下对吧,我们这么多年的眼镜的话呢。
就是说A代码它的就是分块性,比如说函数的定义类的定义,分成不同的文件上下文,你能看得非常的清楚清晰易读,但是当你面对一个真正的商业级游戏的,这个这个这个这个就是说reception的结果的话。
他真的有的时候就像一团乱麻一样的,那么除非创作者就是写这个脚本的人,自己就画这个的人,自己他是知道怎么回事,很多时候比如别人接手的时候,是一下子根本看不清楚是怎么去用的,所以这也是这个就是可视化脚本的。
一个很很很难的一个问题,所以说它是一个非常好的,给这个设计师和艺术家做put type的工具,但是你真的到工业化应用的时候,它还是要用很多方法把它变成一个可规范的,可追踪的,而且可第八个的这么一个东西。
那么讲到这儿的话呢,其实我觉得就是威尔UI系统的话,大家仔细看,其实他并没有大家想象的那么神秘和复杂,但是呢它到底和脚本什么关系,其实这里面我想大家只要记住一个概念就好了,实际上啊。
VIOUI系统和脚本就是一模一样的东西,大家想象一下,就今天我们看到了一个,比如说一个比如像unreal的蓝图,或者是unity rescripting,你可以直接把它编译成一个LUA脚本。
你可以把获了一个c#的脚本,就大家可以完全先写一个脚,你自己的脚本系统,然后呢,你再去做一个,你的这个就是可图形化的这个逻辑系统,然后呢其实你只要做一件事情,就是把你的这个图形化的逻辑。
这个这个这个就是vo graph的这个东西,把它就是渲染成一个脚本的代码,然后呢你前面所有的逻辑都不用动,那这两个东西就可以直接去执行,当然现在的这个就是很多的引擎,它不再去走脚本这一步了。
他直接把图编程这个机器码变成bad code,这样效率会更高一点,但是如果你想增加这中间的第八个,第八个ability,甚至少写一些代码的话呢,其实你可以把一个图编译成一个脚本。
然后再用脚本编译成机器码再去执行它,其实效果是完全一模一样的,所以这个呢实际上就是这个visual graph的本质,它本质上其实就是一种可视化的脚本,所以同学们讲到这1part的话,大家就知道了。
就现代引擎最引以为傲的可视化编程系统,它的底层是这个问题,但这东西讲起来非常简单,入门你你去理解它的基本概念也很简单,但是呢大家如果真的去做它,包括把里面很多的小细节处理完的话,其实它的复杂度蛮高的。
比如刚才我讲的一件事,你就说诶我在我这个可视化的系统里面,我可以定义各种各样的新的event,扩展各种各样游戏的功能,我还可以兼容其他的一些脚本,一些额外的功能,那这个就要求你系统做的非常的复杂了。
所以说就像我刚才讲的是,你实现一个简单的可视化编程系统啊,我觉得大家做几个月应该能做得出来,就一个人做,做几个月是真的能做出来的,但是呢你想做一个商业级可用的,那可能是一群人干个一两年是很正常的。
那你比如说像荣耀的blue print,已经迭代了这么多年了,那现在他们还在快速的演进中,这里面其实我们自己去分析的话,就会发现这里面有很多很有意思的东西可以带,值得大家去去理解和学习,基本上讲到这儿。
我们的游戏逻辑的核心基本上就讲完了,那为什么讲到这儿就不讲了呢,因为很简单,就是再往下讲,就是太碎太杂了,就是已经基本上怎么说呢,就是今天我讲了,你真的要做个game engineer的话。
你其实要掌握的东西还是蛮多的,很多很细碎的东西,那所以最后呢我们在这一趴,因为今天这节课内容比较多啊,我们就讲一个比较简单的东西吧,就是叫3C系统,因为这也是游戏玩法里面大名鼎鼎的,就很心动。
那首先呢在讲这1part之前呢,哦我就像一个我一个很认可的一个游戏,去致敬嘛,就是这个意思,takes two对吧,双人成行,因为我觉得如果大家去理解,什么叫3C系统的话。
我觉得双人成行是一个最好的案例,可以说做的非常的极致,很多时候我觉得他比很多3A大作做的都到位,那么就是什么叫一个3C系统呢,简单来讲就是角色控制和相机,那么它基本上是最重要的。
这个element对于我们的游游戏的体验,就是玩家对游戏世界的感知,其实就是通过角色,然后你通过控制这个角色,然后你怎么看到这个角色感知的角色,你是通过相机的,所以这三个东西形成了我们游戏的。
很多体验的核心,它不是所有玩法的核心,玩法有很多东西对吧,那么但是的话呢,就是说这个体验的最直接的体验,是来自于这个3C系统,那首先讲角色,角色大家会觉得哦是不是讲角色的模型,角色动画其实不是的。
就角色它是讲什么呢,就是讲这个角色的移动,你怎么让他动起来,你怎么让角色战斗起来对吧,包括就是他的这个各种各样的,就是A他的healthy啊,他的放出魔法的各种技能这种体系啊,那么这些东西。
这些feedback系统对你对游戏的感知是最直接的,这个影响,那么举个最简单的例子,就是最容易大家忽略的就是它的移动哎,这里面举个例子就是我们举一个巫师的例子,大家看到就是说。
如果大家还记得我们在讲小引擎的时候对吧,移动诶它无非就是向前走,向左走,向右走,然后呢也有一个加速,有个减速,对不对,但是如果真的我们去看一个,就是啊3A游戏的一个角色移动的时候。
你会发现它的细节非常的多,因为在很多时候你会碰到障碍物,你会上坡,你会下坡,你会这个时候走着突然就停了,然后呢你会穿穿过一道门或者怎么样,你会面前有个小坎儿,这时候角色的移动就会有很多。
很多的细节需要你去响应,因为你如果不这样做的话,这个角色你看起来就很僵,因为没有人喜欢玩一个游戏,整个角色就很像robot一样,就这样永远这样走,然后的话看见山也是这样走,看见这个平地也是这么走。
那你的感觉就很奇怪,所以说角色的移动实际上对于好的游戏来讲,有无数的细节,那么另外一个的话呢就是说其实角色的话呢,它实际上是要和游戏世界里的各种环境,各种物体进行互动的,比如说诶我走着走着。
我突然抓一个东西吊起来对吧,比如说我走着走着,我走到一个冰面上,诶我是时候开始变成滑冰的这个效果了,那这个动作是不是整个就完全变掉了,对不对,那还有比如说我跳到水里面了对吧,我的行为又会不一样。
所以其实角色的这个就角色的这个运动的话,实际上是非常非常复杂的,那么另外一个的话呢,就是说角色的话呢它和环境各种互动,比如说你要和你的这个terrain系统,你地面上是一个沙地对吧,你还是个雪地。
那么还有就是说我在不同的环境下,我要产生各种各样的音效,各种各样的这个这个particle效果,实际上都是要角色系统跟这个环境产生了互动,那么另外一个的话呢就是说其实角色的移动呢。
很多时候它和物理系统系关系,比如说当我去滑翔的时候对吧,我要去模拟空气的flow,所以这个时候我既要有我的控制,又要有物理的输出,所以真实的在游戏中的一个角色系统的话,怎么去做呢。
一般来讲我们都是用状态机的方法去做,那这里面随便举个例子,这里面这个这个例子不算复杂,这不是个3A游戏的一个结果,这是unity的一个案例,但这个其实都已经算是很简单的,一个角色系统的一个控制。
实际上在我们就大家还记得,我在讲动画系统的时候讲过,就是说现代动画系统,比如动画树的这个这个设计,它有个叫AISM动画状态机,对不对,其实动画状态机里面你点进去,你会发现它每一个节点。
它又可以是一段脚本系统,这就是说,当我在各种各样的动画之间去切换的时候,实际上它上面还挂载很多很复杂的业务逻辑,所以说当我们再去写一个游戏的时候,一个好的游戏啊。
它的character系统实际上是用大量的动画的,这个这个这个script或者是graph,在内嵌了大量的逻辑的script tograph,形成了这样一个综合的结果,所以我们在做引擎的时候。
我们怎么去想这个问题呢,其实做引擎很多时候我们也做不了太多的事情,我们可以做物理系统对吧,我们可以做动画系统,我们也可以做这个脚本系统,但是呢最核心的是说,我们在设计的时候。
要允许这些系统互相之间的状态,都可以互相通讯,然后呢这个通讯具体发生什么,逻辑的话,我们要把它开放给我们的设计师,让他们来定义,这个时候它才能产生一个我们想要的,这样一个丰富的角色行为系统。
其实后面讲的这个就是说control和camera也是一个道理,那么control是核心是什么呢,就是哎最简单的第一条,就是他要处理各种各样的输入设备,大家可能有时候意识不到,就是在游戏里面。
这个行业里面,我们遇遇到的输入设备是很多很多的,比如说可能是这个方向盘,手柄对吧,鼠标键盘对不对,各种各样奇怪的,还有那个你那个飞行控制模拟器的这些东西,那么你各种各样的,这个包括那个做VR的时候手势。
其实这些输入和游戏并不是直接关联的,而control系统就是要把这种输入设备的input,变成我们真正的game play的这个device,就变成我们游戏的game play。
而且不同的game play你的感受还不一样,所以control系统就干这个事儿,听上去是不是很简单诶,但是其实你真的做到一个好的CTRL系统,它的细节特别多,这里面就举这个例子。
就比如说我射钉子这个例子对吧,这个例子很多同学应该有感觉诶,做的真的是很舒服,那为什么你会觉得这个操作就那么的舒服呢,难道他用的鼠标对吧,用的手柄已经跟我们不一样吗,肯定是一样的,对不对。
不同的游戏用的都是一样的手柄,一样的鼠键盘鼠标,但为什么他做的让你感觉好,这里面讲几个细节啊,比如说哎当你去鼠标右键按下去,我要瞄准诶,开始移动它,左键按下去,我要输出的时候。
它是上在前面的一定系统讲的时候,我会产生一个AAI和shoot的这样的event,对不对,然后我的行行为就发生变化了,这个听上去很简单的,但实际上这里面的细节就在魔鬼,那魔鬼就在这个细节里面。
那么当我们去一按这个瞄准的时候,其实这个时候我的状态就变掉了,就这个时候我的相机就会UI就会说诶,把这个更加聚焦,大家记得我们在讲render的时候,怎么去做这个望远镜的效果,就是把FA变小。
而且相机的位置也会发生变化,这个时候我们就会更加的聚焦,看我们标点好,当我们的鼠标开始去移动寻找的目标点的时候,实际上大家注意到没有,它其实有一个小小的吸附的效果,这个吸附效果大家千万不要小瞧它。
其实这个地方我们在引擎这一侧是要开放一个,就是说编辑系统交给设计师和艺术家去编辑的,这个时候,当你的这个curse移移动到你的目标点的时候,首先我要的定义哪些东西是我的目标。
然后我要能判断说我的脚本系统,能判断说我和他之间的夹角小于多少度的时候,我就触发这种吸附系统,而且这个吸附不是一瞬间的,它上市也有一个过程过去了,其实这件事情对你游戏的手感,影响是非常非常大的。
给大家举个例子啊,比如说嗯嗯射击游戏,其实大部分的在主机上的射击游戏,都会带或多或少的吸附系统,这就是为什么你在主机上玩一些,做的不是特别好的设计游戏的,你的感觉就是我很难瞄中人,为什么呢。
如果我完全依据你手柄的输入去移动你的鼠标,左右移动的时候,你那时候玩起来其实是很难受的,你基本上套路中的,你很多时候你觉得你要套住这个人的时候,你会发现这个角色你你那curse就划过去了,为什么呢。
因为你眼睛看到那个东西和你反映到你的手上,然后呢再到这个手柄传输到那个主机,主机再去处理,这个中间的latency可以达到将近一两百毫秒,这个数据是大家很敬业的,大家没有想到这个延迟有这么大。
所以因因为那个东西移动它是有惯性的嘛,所以很多时候如果不做吸附处理的话,你是基本上套不中任何一个人的,更不要说我去枪枪狙瞄苗头了,而这种AI assistant这个系统的话就是CTRL系统。
一个非常核心的一个设计,而这个系统的话就需要我们在前面工具也好,包括我们的脚本也好,都要提供相应的数据编辑功能,教给我们的艺术家,那么另外一个的话呢就是反馈,那反馈的话就这里面的话。
比如像双人成行里面那个剧的感觉,对不对,大家觉得太想去了,它实际上通过什么呢,诶通过让你的手柄有那种震动感,当然这里面我们要小小的提示一下,那些用鼠标键盘的同学,你们是感受不到那种快乐的对吧。
那么中反馈只有手柄有吗,其实没有诶,比如说那个我们的那个就是,如果你把那个模拟飞行,你用那个飞行手柄,其实手柄很多好,高级手柄是有力反馈的,比如说你要玩那个赛车游戏,你插入的是那个高级的。
这个赛车的键盘和整个座套,其实那个很多那个车车的方向盘,有的游戏是有力反馈的,就感觉非常好,所以其实control的一个很核心的点,就是给你的反馈感诶,我这边反过来问大家一个问题,那如果我就是鼠标键盘。
那我有没有反馈可以做呢,大家想想有没有,其实这里面是有的,有是什么东西呢,哎就是大名鼎鼎的RGB啊,大家是买一些就是游戏本的时候,你发现有一个特点,就是说当你打到游戏紧张的时候。
那个游戏键盘或者是个那个那个游戏本身,那个背光就会发生变化,对不对,当你残血的时候,突然变成了一片血红之色,就什么东西,只要上了RGB就变得很高级了,是的,其实在实在没有办法做力反馈的情况下。
其实有些游戏都用最新最简单的光效,也让你觉得有一点点那么的这个代入感,所以反馈对于control来讲非常重要,其实我一直在讲游戏和电影行业最大的区别,这也是同学们的喜欢做游戏引擎啊,我想讲的一件事。
就是为什么我这么热爱做游戏引擎,就是说我一直觉得就是做电影艺术,也是个视觉艺术对吧,但是呢他是个我叫做被动媒体,叫papal media,什么意思,就是说我做了,你们也不要给我有任何的这个反馈。
你们就看吧,我做啥你看什么呀,但游戏的特点是什么,就是根据用户的输入,我会跟他有各种互动和反馈,所以游戏是一个proactive,是一个主动媒体,就是说它最核心的精髓。
就在于游戏世界和我的行为之间的互动,所以control呢就是解决了这个互动最核心的一个。
一个一个系统,所以control系统其实本身并不简单,那么他真的我们要去做这样的CTRL系统的话,实际上它还要区分,比如说你在不同的环境下对吧,同样一个案件,它可能产生的操作是完全完全不一样的。
那这个地方设计又是,你觉得我们程序员能控制吗,程序员肯定控制不了,对不对,那怎么办,我们只能在引擎里面提供这样的一个接口,让设计师让艺术家可以去定义它,通过什么定义呢。
通过刚才前面讲的visual scripting,通过那个脚本系统来定义他,各种各样不同的行为,所以在这里面的话呢,就是我们去定义用户的输入的时候,它其实一定是多肽的,其实很多用户的输入它可以是组合键。
对吧,叫CORE,也可以是一串按键,大家以前如果玩过街霸的话,就应该知道那个按键的那个序列,那可是我们反复苦练的东西,对不对,所以所以说其实这也是control系统,一个很核心的一个东西。
所以其实呢就是说呃哦所以说control系统的话呢,其实我一直认为就是很多时候会被大家低估,大家都觉得做过游戏,是不是就鼠标键盘按下什么键,我就做个什么东西,其实不是的,如果这样直接的翻译。
它不是不会上升到一个系统,但是如果你真的做到一个3A游戏,那种感觉的东西的话,其实control系统反而是你丝滑手感的由来,它它实际上是真的决定了你整个游戏的手感,包括刚才我讲的feedback对吧。
那么现代游戏的设备会越来越注重于,就是反向对用户的多生态的这种反馈,比如说像那个像PS5的手柄上还带了个小喇叭,我经常玩恐怖游戏的时候,玩着玩着突然小喇叭就想起来了,把我吓了一跳。
哼然后而且那个小喇叭上还有麦克风,经常一不小心突然发现那边有一个,就是那个其他的玩家在跟你说话,然后你然后你又很想跟他说话,所以这种感觉,其实这种control系统是蛮有意思的一个东西,好啊。
最后今天也是最后一个系统,就是我们的camera系统了,其实相机系统大家会觉得我们讲了rendering,相机系统不就有了嘛,对不对,不就是这个相机系统想起来非常的简单。
就是说哎你设定一个相机的位置叫POV对吧,然后再设定一个FOV相机的张角,我的一个相机系统就有了,是的它就是这么简单,但是呢,其实相机很多时候是要和你的角色绑在一起的,但是大家如果仔细观察。
一个有好的游戏的相机啊,你观察它并不是固定死在你身体后面的,某一个固定长度,只是由你的视角决定了,实际上一个是当你角色在跑动走动,这个相机的距离远近都会发生变化,而这个才是相机系统一个很精妙的东西。
那么其实在相机系统的话呢,有个很著名的东西叫弹簧臂,大家可能也听说过,就是说当我这个左右动的时候,如果是第三人称视角的游戏的话,我会保证我的相机尽量不要穿墙,但是很多游戏做不到对吧。
今天我有这样的bug,其实这个东西大家也会知道,处理起来还是挺麻烦的,因为各种各样的情况都要,有的时候是你不能穿墙,有的时候呢你还可以穿墙,但是把那个墙要透明度,这里面有很多的小细节在处理。
那么包括呢就是当你进入focus模式的时候,诶这个相机要变得更加聚焦一点,这都是我们经常很熟悉的东西,但其实呢就是相机真正有意思的东西是什么呢,就是说呃当我在不同的速度,在我不同的游戏模式下。
当我看到不同方向的时候,它相机的远近大小都要发生变化,这个时候呢其实有一个很著名的东西,叫做camera cage,什么意思,就是说或者叫camera track。
就是设计师会设计我这个角色在什么状态下,我的相机的参数,包括它的位置都要精心的定义过,他最后在空间上可视化出来,就像一个很奇怪的形状的一个一个笼子,那为什么这东西这么重要,因为相机啊,实际上在游戏里面。
表达的是一种非常强的人的主观感受,你你的眼睛瞳孔是会放缩的,比如说我们恐惧的时候对吧,瞳孔会放大,然后呢你看的东西会更,其实你的FV会变缩小,你会更聚焦在你眼前的敌人,当你在放松的时候。
你的瞳孔会A会缩小,你看的地方会更更宽泛,然后那个时候的话呢,你可能就会看的面更广一点,让很多周围的事情你能看得到对吧,当然还有各种各样的状态,所以所以相机,它其实是一种非常非常强的主观感受。
而所谓的相机系统的话,就是让设计师有能力,精准的去表达人的主体感受,因为实际上我们在打游戏的时候,我并不是真的在那个游戏世界里面对吧,但是的话呢我隔着那个屏幕也能感受到,如同身临其境的紧张感或者代入感。
所以这就是相机系统很核心的一个,一个解决的问题,那么其实相机系统的话呢,它还会触发很多的相机的效果,比如最著名的抖屏,其实大家我们在讲经常游戏讲叫打击感吗,打击感其实这里面用到了很多相机系统的技术。
比如说屏幕的震动对吧,打到一下屏幕的震动,比如说我打了一下屏幕,突然就全屏的那种诶一下子白掉了,或者一下子黑掉的这种效果,那这种或者camera effect这种效果,其实这也是相机系统很核心的一个啊。
功能要开放给我们的设计师,那还有卡相机桶是什么呢,就是说哎其实在真实的游戏里面,它有很多很多的相机,但是呢每一次你你以为你只能看到一个相机,其实它会在很多的相机之间来回切换,比如说举个例子啊。
比如说啊一款设计游戏,当我端起枪的时候,我进入了FPS模式对吧,那个时候哎我就看到了一个FPS,我看不到我,其实我那个相机是用了一个,那个就是说一个FS第一认真设计的游戏,但这个时候我把枪装起来。
然后呢我这个角色开始走的时候,有的游戏他就会切回到第三人称诶,当我在地上捡到了一颗加特林重机枪,我把重机枪拎在手上的时候,诶你看发现那个相机的位置又发生了变化,它当你拎着向左看右看的时候。
它的相机视角又会发生变化,其实在这么一个简单的过程中,已经切换了三次套相机了,比如说我急喷的时候,又是一条相机,比如说我开车对吧,我我端起把枪,我开始狙击镜的时候,我的相机就变掉了。
当我进入了一辆赛车里面,诶,我的相机位置和它的逻辑又会发生变化,所以其实在真实的游戏里面的话,你会发现你有很多很多的相机,根据你游戏不同的状态,而且呢所谓的他就需要一个camera system。
就camera manager去管理它,而且呢kai major还有一个很重要的任务,就是什么呢,在这些相机里面来回的让你去实现它的差值,所以一个camera的话,实际上比大家想象的要复杂得多。
那么这个camera system的话呢,其实它要解决的一个非常重要的一个东西,就是这个我们叫subjective feeling,就是说主观感受,比如说像这里面你要去表达一个滑道的时候。
其实我那个时候通过我的motion blur对吧,就是那个运动模糊,通过相机的这种抖动抖动,包括他的视视野的放缩,就给你形成一种非常强的,身临其境的这样的一种代入感,速度感和代入感,而这件事情的话。
其实都是相机系统设计所实现的,那另外一个的话呢,就是说比如说我们去表达一个角色,在跑动的时候,其实相机并不是机械的绑在角色身上,它实际上相机会有自己的惯性,自己的逻辑。
这样你会觉得这个角色的运动感觉是活的,如果相机只是机械地绑在角色身上,你不会觉得相机死,你会觉得那个角色很僵,包括相机,有的时候也会负责产生一种电影般的画面,所以说其实相机呢其实对于游戏的这种表现力。
的影响是非常非常大的,所以说我我记得我在讲render时候讲过,就是说哎后处理的look up table,这个方法是一个性价比很高的系统,让你游戏一下子上档次,对不对。
那这里面我们在讲游戏玩法系统的时候,我要跟大家讲的就是说camera系统,其实是我认为一个性价比非常高的,让你的游戏有这种大作感的这样的一个系统,那其实呢就是说camera系统,那刚才讲的那些细节。
大家听到更像是讲游戏设计,对不对,是的,我讲了很多游戏设计的东西,但是呢作为游戏引擎来讲的话,我们在设计camera系统的时候,就要考虑到就是说我们的任何一款游戏产品,它可能有很多的相机。
而且我在定义相机行为的时候呢,它实际上是有很多的功能的对吧,各种各样的状态,我要读取角色的速度,我要去调用各种各样的camera的效果,我要调整相机的FOV,我要是相机抖动起来。
那么这些所有的功能呢要允许用一个脚本系统,或者是一个very good scripting,就是说可视化的这样的一个,比如像BLUPRINT这样的一个可视化的图,图形编程系统。
能对这个相机的行为进行编辑,那你引擎测必须要提供这样的一个camera,系统的功能的话,别人才能在你上面开发一个高质量的游戏,所以这也是引擎对于相机系统的一个,重要的一个支撑吧。
所以这就是大家去理解什么叫3C,这样大家听我讲完之后是不会意识到诶,其实3C为什么对现代游戏这么重要,而且现代游戏引擎也是一个非常重要的一个啊,要支撑这个复杂的散热系统,才能决定我们游戏产出的质量。
现在大家就只能理解这里面的很多,很有趣的妙处了,其实很多同学听完我们这门课呢,并不一定会做游戏引擎,比如说你们是做游戏的,但是呢大家听了这些东西之后,再结合引擎开放的功能就能理解哦。
如何做一个操作感体验感很强的一个游戏好,所以呢讲到这一趴,基本上game play基本上讲完了,就是所以其实我在准备这节课的时候哈,其实我记得是昨天下午的时候,我跟我们课程的小伙伴们有个对话。
我我在说我准备完这门课,我觉得好像我也没有讲什么高深的东西,像我们之前很多课都有很多很高端的,这种高屋建瓴的提炼对吧,好像跟play系统讲的都是一些很工程实践的东西,很很很简单,好像每个东西都不是很难。
我说game play系统,玩法系统到底它的本质是什么东西,然后我们课程的小伙伴大家讨论了半天,最后的结论是什么呢,就是说其实game play真的就是一个everything。
就是游戏玩法的几乎所有东西都是game play系统,它确实就是一个很难被定义边界的,这么一个系统,它不像物理,不像这个rendering对吧,它的边界特别的清楚。
game boy系统它的边界其实并不清楚,那么其实我们整个行业总结了这么多年,好像也就提炼了这么些东西,比如说我们要用event系统对吧,用这个模式去统一游戏内机构之间的communication。
这样的话让这些肯定开始变得更加有序可管理,那我们知道很多逻辑,我没有办法在引擎里把他写死,那怎么办,我只能开放出这个scraping的系统,让大家去让设计师也好,让艺术家好。
可以定制这个游戏各种各样的玩法,那么那screaming系统又太难学了,怎么办,那我们就用VSCOT系统对吧,让大家更加方便地掌握这些,各种各样玩法的开放,但是今天我真的要做一个SG。
我真的要做个卡牌游戏,我真的要做一个PS游戏,我真的要做一个RTS游戏,实际上它的game play差别还是非常非常大的,他的很多上层代码,包括引擎有些东西都要被改,所以这也是就是大家在这个理解玩法。
系统的时候,要有这个敬畏之心,就这里面的话,至少以我的经验和浅见的话,我很难找到一个叫做一招鲜,就是一个架构,能够把所有的游戏全部都能够cover住的,这么的一个一个系统。
所以的话呢基本上每一个优秀的作品,他在跟play系统上都会有自己的一些想法和突破,所以的话呢这也是为什么,游戏引擎其实很多时候啊并不能够是万能啊,大家很多人会认为我能不能做一游戏引擎,支持所有的游戏。
理论上可不可以可以的,但是在现代实践中,你会发现就是很多不同类型的游戏,他会有自己的引擎的preference,也是这个道理也在这个地方,所以的话呢这也是就是GAI系统,它非常美妙。
非常值得大家去思考的一个东西,好的,以上呢就是我今天关于玩法系统的第一节课,帮助大家建立我game play系统的一个基础的概念,好那今天的课程我们先讲到这部分,那就是那个再次感谢一下。
我们的课程组的小伙伴,就是同志们还是非常的辛苦,就是哈,这个虽然说我们上一节课到现在有两周时间啊,但是这两周基本上大家都没休息,都在这个,我到昨天前天全部都在加班,然后我们就在这大家一起弄。
然后今天我大概是最后到五点才,把今天的课程全部准备完成,所以的话呢就是希望能够帮助到同学们,那接下来又到了我们传统时间,就是大家有没有什么问题,关于游戏玩法系统,好好的,第一个问题。
同学问我说接入了可视化脚本之后,是不是就不需要用传统脚本了啊,原则上来讲确实是可以这么干的,但是呢实际上游戏的开发者很少会这么做,其实我们看到很多工程实践中的话,可视化脚本的话呢。
大家会作为早期的PTAP,特别是开放给艺术家,开放给设计师用,但是的话呢脚本一般大家都会留,其实你们看到比如举个例子吧,这个可视化脚本做的最好的是虚幻引擎对吧,但是很多虚幻用虚幻引擎的团队。
一定会再开发一套自己的脚本系统,作为自己的开发的需要,所以其实可视化脚本和脚本之间呢,它不是一个替代关系,就是在有些时候脚本写起来效率更高,而且更好维护,在有些时候呢可视化脚本就够用了对吧。
而且我可以方便很多其他人都用起来,所以这两个在引擎实践中的话,我会觉得就是说如果你想支持一个可视化脚本,我都会非常建议第一步,你先把脚脚本系统给支持了,我你有了脚本系统的知识,你再去支持可视化脚本。
然后呢你有了可视化脚本之后,也不要这个喜新厌旧,我建议两套系统都要保留啊,哇这个问题好难啊,有同学问我说,未来平台是不是趋向于禁止热更啊,这个实际上是很专业了,其实在传统的比如说PC游戏的时候。
那真是个快乐的时代,就是我们会非常方便的热跟所有的游戏,特别是对于大家经常玩的,比如像魔兽世界啊,像那种大型的MRPG,它没有热根,基本上这个游戏是没法运行的,因为这种复杂产品它一定会有很多的问题。
大家想象不到的,但是呢我们也看到,就在现在的一些平台上举个例子啊,比如说像控制的比较严的IOS平台,就是我们想像手机上的移动端平台对吧,IOS平台,那他为了防止呢,你就是在他的这个一审核完的游戏内容后。
又在注入一些他不受审核的新内容,他就不是不想让你去热根,所以这个时候其实很多开发者,特别是做这种online game的开发者就非常痛苦,那么包括像做主机游戏,举个例子,Plain xbox。
我记得我们花了很久时间,试图跟这些爸爸们沟通,说其实如果我们做个online gaming是非常需要热更的,但是呢他平台有很多安全性的问题,举个例子。
比如说PLAYSTATION他的那个那个你看到的游戏,它是被加密过的,那这个加密过程呢不受我们控制,只受平台来控制,那这个时候热更起来就很麻烦,所以啊我不认为,就是未来的游戏平台都一定会禁止热更。
这件事情,实际上是对开发者来讲,是非常不方便的一件事情,但确实现在有很多平台真的不让我们热梗,但是未来到底大家是越来越封闭,说OK大家都不知干了对吧,还是说大家都明白了,没有热根。
那我对一个超复杂的游戏是没法做的情况下,大家都慢慢的想一个方法打开你授权去做,我觉得这是两种可能发生的未来,但我作为一个游戏开发者,作为一个引擎开发者的话呢,那我肯定是希望平台将来要开放。
越来越方便的热梗了,因为热根对于游戏来讲真的非常的重要,哇这个问题好难啊,这个这个问题是有点难,同学问我说,就是有可能采取逻辑和表现分离的方法,开发游戏吗,实际上在游戏开发中。
大部分时候逻辑和表现是分离的,特别是它其实取决于你的游戏的类型,比如说啊像传统的MRPG游戏,比如说哎我们这种呃,大家知道那种修仙练级打怪这种游戏,那么他的表现其实相对来讲比较简单。
就是说我的各种技能啊打出去对吧,要打出多少伤害结算,那其实很多时候,游戏的逻辑就可以由我们设计师团队单独去写,然后呢产生各种各样复杂的业务结算,那么其实它的表现那一块和逻辑这块,分的是非常非常开的。
那么包括就是有的时候,我觉得他的策策划,就像跟那个我们的艺术家下订单一样的说,我要这几个特效,每个特效打多远对吧好,这个依次从低级到高级怎么样,然后他去配数值,他去配结算,配各种音质的东西。
然后呢相应的这些数据配好了之后,下面的表现直接调用,你就能产生你的效果,这样它的玩法开发效率是非常非常高的,所以在这样的类型的游戏里面的话,逻辑和表现分的还是挺开的。
而且这是有的时候团队规模可以做的很大,一般来讲在产品研发中的话,我们认为就是如果组织里面的这种啊,就不同工种的同学,这个分离的越快,就decoupling越好的话,它的开发效率就越好。
确实这种游戏里面这种效率是很高的,但是对于现代游戏来讲,大家现在玩游戏,很少有人在玩,喜欢玩那种纯素质的游戏了对吧,大家要求的是打造打击感动作的表现,整个角色和环境的互动越来越多的这种细节。
特别像3A游戏这种游戏,那这个时候的话呢,其实你会发现,这个逻辑和表现之间的关系就会越来越密切,因为你会发现,就是说对于现代游戏玩家来讲的话,他会越来越讲究,就是他不再是简单的说哎我递上一个装备。
啪就到我背包里面,背包那个旁边那个背包里面出现了一个装备,你希望看到这个人攻下身来捡起这个东西,捡起东西看两眼,嗯不错,然后呢要放到我的包里面对吧,然后这个东西它如果有发光,有什么东西的时候。
我身上还有一些效果会出来,然后呢这个角色身上背的这个东西有点重,我走路的姿态好像发生了一些变化,但我这讲的有点夸张了,但实际上对于现代游戏发展来讲的话,我们的用户其实对于这种这个这个角色的。
细节的要求已经是越来越高了,这个时候呢客观上就要求引擎,它能越来越方便的,让我们不同工那个工种的同学,就设计师也好,艺术家也好,包括程序也好,能够合力在一起,去把一个玩法和他的这个表现糅合在一起去做。
那这里面的话呢其实无论对游戏开发团队,对引擎来讲都提出了更高的要求,所以我个人觉得的话呢,就是在未来的这种类型的游戏里面,特别是大家如果做主机游戏的话,那基本上来讲的话,游戏玩法和表现比较难以分开。
这是我自己个人的一个实践,如果有更好的架构能分开,我觉得我会非常乐意尝试,因为这样的效率会更高嘛,但是在实操中的话,就像刚才我讲3C系统一样的话,其实他的表现和玩法其实高度相关,因为3C不仅仅只是表现。
他实际上跟玩法也很有关系,对所以的话那个这个的话呢是现在开发中的话,大家一定要注意,包括引擎架构的时候也要注意的,就是我建议是保持这种开放性,就像刚才第一个问题,同学跟我说,脚本和可视化的脚本。
两个是不是那个可以彻底的,这个这个这个一个是替代一个的关系,我个人觉得是有点难,他两个都有作用,其实大家如果理解现代游戏引擎啊,他就明白,它并不是一个大家想象中一个完美的理想架构。
它实际上是一个呃大量的妥协,然后呢保持了各种可能性的一个工程基础平台,然后根据某种各种类型的游戏,你可以去选取其中的一些模块去强化,去表现你游戏的内容对,所以这个问题我的答案是这样的,好的。
今天好像一下子又讲了将近两个小时,那我们今天要不先到这样,那再次谢谢同学们,然后呢就是休息了两个星期,我感觉我也回血回的非常多了,然后呢下个星期我们同学们再见面,那个时候我跟同学讲什么呢。
就会讲特别可爱的,特别有趣的AI系统。
那我们下周见,谢谢大家好。
15.逆问题建模与ADMM算法求解 | GAMES204-计算成像 - P1 - GAMES-Webinar - BV1j8411Y7zY
好今天给大家分享了一个主题是ADMM,这个是很有意思的一套算法哈,它是早些年在这个大数据分析的时代,这个特别是那个在我们神经网络出来之前,这个DMF是大放异彩,因为它又简单又实用,收敛速度还比较快。
最后结果还比较好,所以说那个时候唉这个很多大家做计算摄影啊,做computation啊啊,还包括像很多数据优化呀,哪怕像operation in search,在这些地方。
都会把ADM当成一个非常好用的一个工具,大家把这个ADM快速的来总结过来,诶这套一下那套一下发了非常多的论文,所以说那个年代大家觉得这个ADMM啊,简直就是神器,它本身这个东西呢是一个叫交替乘子法。
它算是机器学习中比较广泛的一个,约束问题的最优化方法,这个ADM本身是arm算法的一种延伸,只不过是他把那个无约束的一个优化部分,用块坐标下降,也就是block curtain descent。
或者是alternative minimization来做优化,这个当年在那个我们非常有名的一个做优化的,一个老师啊,斯坦福的一个stephen boy诶。
他在一个他的一个文章叫disputed of an,Optimization and statistical learning by alternating direction。
Method of multiple,也就是DM最早出现的这么一个方法,这种方法本身,它的出现是主要是为了,弥补我们二次乘方向的一个缺点,特别是在一些问题中,我们用二次惩罚项来近似的约束问题。
在最优点附近,需要惩罚项的一个系数趋近于无穷,这个时候就会使我们的一个海森矩阵,变得非常非常大,而且因为这个海森矩阵变得非常大的时候,这个时候它近似的目标函数就不是很稳定,所以为了解决这么一个问题吧。
就我们sting board就引入一种叫线性逼近的部分,通过线性系数不断的接近它的最优解,也就是我们所说的一个duality的一个上升,使得二次乘方像在系数很小的情况下,也能得到满足精度的解。
这DM目前是已经变得非常成熟的一个啊,求解带有约束问题的一个通用的优化框架,这个如果大家啊同学有兴趣的话,也可以去翻一翻这个steam boy的这个原文,在我们课前先扯一扯这个DIMM历史。
但这个交替乘子法确实是一种非常简单实用,又性能优越的一种呃,求解分布式图优化的一个问题,就是它主要是通过分解协调,也就是decomposition coordinate,一个过程。
把很多的全局问题分解为啊,一个个的小的一个容易求解的局部子问题,唉,然后再协调这些子问题之间的解,来得到一个全局的一个解,这个也很有意思哈,也大家推荐一下,大家读一下最早最早的这个论文。
这个求解这个问题呃,之后哈就是大部分大家把这个理解透之后,大部分这种带约束的一个图优化问题,其实都可以得到一个比较好的一个解,而且这个时候呃,其实大家构建一个image formation model。
也通常是构建这么一个带约束的一个优化问题,这个还是建议大家精读一下这一部分,这个很有意思,今天要给大家通过这么一个ADM,结合两个例子吧,第一个我们是结合一个非常简单的一个叫single。
pixel camera的一个例子,也就是我们所说的一个压缩感知,这个压缩感知是怎么样通过一个单点的像素,来恢复出来这么大一张图像呢,大家有没有想到我测量我就一个像素点。
我只能到感受到这个图像光强弱的变化,我这个是怎么样才能恢复出来这么一张图呢,诶这个时候我们就需要构建,像我们最开始说的,我们要构建我的image formation model。
也就是构建AX减B这么一个问题,这里面的B呢就是我们的一个观察observation,我们在传感器上就是一个像素点嘛,就拿到一个BA,这个我们就可以变成一个调制矩阵,变成调制矩阵,后面这个。
当然这个时候,因为我们最后只能听到一个强弱的微小变化,这个时候我们是非常非常多的解哈,这个时候我们就需要加约束,也就是我们image prior。
这个我们会以这个single pixel camera为例子,再给大家讲解ADMM这个实现的过程,那除此之外啊,给大家举了一个嗯一个高维的一个例子,就是我们怎么样实现一个阵列啊,或者是一个阵列。
再加上一个三维的一个slice和4D,这个情况下我们怎么做一个压缩感知,怎么样去有效地解它来,这篇论文是我当年就上读博二时候写的,一个4D的一个呃,这个压缩感知的一种呃套路吧。
这个希望大家同学也可以了解一下,这个举一个基本的例子,再举一个比较高级的例子,这样的话大家对整套的一个逆问题怎么构建,还有我怎么样通过我们今天讲解的这个算法,叫ADMM去来去解它,这个呃就慢慢慢慢。
大家就对整个就成像的一个嗯就是问题的抽象,再到问题的解决,有了一个完整的印象,这个时候啊,大家也就具备,初步的具备和构建和解决问题的能力,啊这个,还差一分钟,我们到八点整正式开始,这个先给大家测测背景。
这个背景也确实是非常有意思,好那我们现在正式开始诶,亲爱的同学们,大家好,欢迎回到games204,今天给大家继续讲解,我们的一个计算的一个工具盒,诶,上一上一节课。
我们通过一个DECONVOLUTION的例子来给大家分享了,Half quadratic eting,如何求解一个带约束优化问题,今天呢我们会通过两个例子吧,就是single pixel camera。
特别是压缩感知这个例子来给大家讲一个呃,更加通用实用一些的算法,也就是非常广泛应用的一个算法,叫the alternating direction method of multiple。
大家通常就是简称DMM,想必这个算法大家也听过很多回了,诶这个到底是怎么实现的啊,本来大家开始想是先从duality给大家直接讲,后来想了想,就只讲数学,太枯燥了,还是结合一些实例来给大家分享。
这个算法到底是怎么解的,然后到底是怎么用的,然后我们也可以有一个构建我们这个成像模型,然后再加上我们需要的约束,然后再去求解这个问题,一个完整的一个闭合的回路来培养大家嗯,真正的抽象。
就从物理世界中的现实问题,抽象出它一个带有模型的问题,然后再去求解它啊,要经历这么一个过程,首先呢呃今天的我们分为四个方向来讲,这个问题刚开始是给大家介绍一下。
什么是逆问题,我们这个逆问题到底是怎么回事哈,这个呃大家需要了解一下,然后给大家分享的是通过我们single pixel camera,也就是单像素的一个相机,通过压缩感知,我们怎么样通过ADIM。
实现这么一个单向素的一个相机,我们这个整个的实验构架构啊,实验构想啊到底是怎么实现的,这是一个非常有意思哈,这个最早是零四还是05年,这个single pixel camera问世。
想必大家同学们新闻上也会看到,我们对这个问题求解完之后,要给大家分享一下,我们这个问题本身它的一个bin perspective,就是我们这个用概率图模型来去,怎么样去表示我们这一个问题。
它到底有什么样的意义,这个啊也是非常有意义的,那最后我们把这个inverse problem,升级到一个高的维度,这个高的维度嗯,大家有时候一看就非常棘手,但实际上它解起来。
跟你一尾二维并没有任何区别啊,并没有任何区别,啊这个从gotten那暴露了很多surprise,这个credit属于golden re,首先我们说到文艺问题嗯,大家想一想我们成像中的逆问题大概有哪些呢。
就是在大家的一些呃正常的一些印象里面,就我们有哪一些呃,相机问题是可以直接嗯想象得到的呢,就举个例子哈,大家生活中可以遇到的,比如说我们拍CT这个T是咋实现的呢,T实际上它是通过在这个轴。
通过下面发出一个平平行光平行的X光诶,我这边有一排探测器,哎我这个探测器在这一个面上扫那么一圈诶,就会得到这整个它的一个投影的一个频谱图,然后再通过一个render transform一个逆变换唉。
就可以把这一层的一个啊形状解析解析,就是信号解析出来,就解析出来之后,我们再通过在Z轴方向的运动很多层层析嘛,如果说叫tomography,就是断层成像,一层一层的把这个slice切出来。
最后形成一个三维的一个模型,这个时候就是我们大家拍这个要看到内脏啊,或者是脑子里有啥缺陷呢,当然脑子可能看不太清,分辨率不够哎,这个时候我们就通常用competition table graphy。
这是一个非常经典的一问题,这也是成像领域第一个诺贝尔奖,应该是第一个诺贝尔奖,但是我相信后面还可能成像,会有非常非常多的一个诺贝尔奖啊,也希望大家是其中拿奖的一员,那除此之外。
在医疗中还有一些其他的应用,比如像我们的一个MR,也就是电磁波成像,这个原理其实大致跟cg差不多,也都是一个投影到一个分裂分裂域来实现它,还有像我们啊,比如说这种不是很常见的一个高光高光谱。
好像一般的高光谱,是直接通过一个光栅进行扫描,唉这就形成一个呃XY,然后在一个多通道的颜色的这么一个data cube,但这个地方是用了一些呃。
binary Python一个调制来获得了一个多光谱的逆问题,因为我们最后的传感器啊,要么是大家用的黑白的,要么就是RGGB彩的,顶多顶天了,大家都多光谱九个通道,这个呢我们拿到的通道数量是非常有限的。
但是我们的高光谱我要获得一个,比如说像几百个通道,上千个通道诶,我们怎么样通过这么少的数据来,恢复出这么多的一个通道的一个数据,这个时候就出现了一种逆问题啊,就我们要信息有损失,但我要逆回去。
这个怎么逆回去呢,这是一个高光谱的例子,但除此之外,我们本身的一个competition photography,还有像我们的光场成像啊,像我们的热成像啊,很多地方都是要求解这么一个逆问题。
因为本身我们信息有损失,这个是我们recall一下,第一节课讲了这么个问题啊,像我们很多啊计算摄影的问题,都可以通过这么一种方式来表示,这个方式是什么呢,就是ax减B唉,再加上一个约束。
我们假设这个B是我在一个图像传感器,我哪哪怕是一个单点的一个光电传感器吧,就是我们传感器上捕捉到的一个信号,就是BX呢是我本身要求的那个信号,也可能是我们干净的一个图啊。
那xx呢实际上就是我们最后要求得的解,这个时候我们整个无论是前面的整个成像的,一个前面的一个系统啊,包括我们的光学系统,我们的科学照明,包括我们前面有可能会自己加调制系统,这些部分啊,哪怕我有。
也有可能是在呃这个传感器表面加一些pattern,来做调制,或者在嗯app上做调整,或者在前面做调整,都有可能,我们统一把这个调制用一个matrix a来表示,我A来调制我们这个X。
所以实际上就是最后因为调整完之后,我们在图像传感器上拿到的东西就是B吧,这个时候我们第一步要保证,经过我们的这个呃真实这个模型A啊,我们这个图像会变换成B。
这个要保证它的data就是data fability,要正确,后面呢因为通常啊我们从这个成像过程A啊,通常是有信息损失的,这个时候我们通常是有一个高维向低维的映射,比如说我们物理的世界。
首先我们生活是一个三维的世界,我们有各种各样的光源啊,光源不一样的光谱啊,光谱不一样的形状,物体自己的形状有自己表面的一些材料性质啊,比如说我们啊图形学里面,通常把它描描绘成BRDF和BSDF。
还有运动啊,还有观察者呀,有这么多的,还有甚至还有偏振,还有不一样的光谱,哎这么多的因素都可以在一起,我们要举一个例子,就是普通的流量传感器,这个时候只是拿到一个二维的图,对不对。
这个时候我们的imazing model a是有信息损失的,它是一个高维向低维的映射,但假设我们要恢复出来一些,其中一些维度的信号,这个时候你要符合这个ax等于B的这种情况,那就太多了。
这说明我们有很多很多的一个解空间,来符合我们这一个ax等于B的情况,这个时候呢,为了让我们得到最优的一个,符合我们要求的一个解,我们就需要根据我们要求解的性质,来给我们这个最后要求的图像做一个约束。
也就是我约束过的解空间,举个例子哈,这个图像A这个因为这个model加上,就是不让我把这个图搞糊了,一个PSF,我在图像传感器上拿到的一个观察,就是一个bad image,我们记为B。
这个时候我们通过后面比如说加一个啊,total viation的一个regular,Or,我们就可以求解这么一个x sharp image,假设这个是呃这个tomography的问题呢。
比如说A这个时候就变成一个projection matrix,然后B呢就是我们在投影的这么一个观察,这是B,然后X就是我最后要重建的一个三维的volume,当然你这个这种套路啊。
就可以基本上往任意的这个图像的一个模型上,就成像的一个模型来套,然后再去加不一样的regular rider来求解它,这个是通用的一个套路啊,怎么样解这个问题呢,实际上就是到了今天的一个主角。
就是我们所说的ADMM,这通常啊像我们这个image formation,他除了在我们我们的这个图像传感器上,拿到了这个b observation,除了像经过啊这个这个image model a本身。
它还会受噪声的影响,这个也就是这里即为这个一塔,但是它会受到一个噪声的影响,这个时候,比如说,假设我们到一个遇到一个压缩感知的问题,这个压缩感知我们观察到的一个数据量。
是远远小于我最后要恢复出来的信息量的,这个时候就像刚才讲到,就符合ax等于B的这种解,那基本上就是无穷无尽,对不对,这个时候我们通常把这个问题描述为病态问题。
也就是you post problems要求解它,就需要一个合适的一个约束项,来把这个解约束在一个我们想要求解的范围内,像一般的一个啊求解问题啊,比如说我们要求解这个ax等于B。
我们可以是把它当成一个求解一个list square,的一个problem,就我们知道这个a a transports本身是一个,就是半正定的一个矩阵,我们这个时候,就可以通过。
这么一个求解一个list square problem,来去把它这个整个的一个解直接解出来,当然这个时候,我们通常是用嗯minimize这个X的一个r two norm。
然后它要符合ax等于B的这么一个人说项,当然这是一般的一个就是L2的一个嗯,我们假设一个ATGA就是这个X的值啊,实际上就是尽可能的一个小吧,但这些也不是特别好哈。
后面我给大家讲一下这个single pixel的一个问题啊,这个我们用这个问题描述呢,就是我们我们假设要直接求解,这个list square的一个问题的时候,假设这个时候我们要做压缩感知,哎。
我们要压缩到两倍,就是图像XY方向各压缩到一倍的时候,哎我们发现这个这个least square求解的问题,好像这个PSNR不是特别高,特别是当四倍的时候,我这个图基本就看不清了,八倍的时候。
那更是找不到了,就是也就是说在这种直接求解一个list square,的一个problem的时候,我们的解,他收敛到了一个我们不是很喜欢的一个地方,就是它是一个本身是一个非凸的问题哦,简约非常多。
诶这个地方我不是特别喜欢这个说,因为这个结果不是符合我们真实的一个要求,这个时候怎么办呢,诶这个时候这个图像里面就是聪明的,经过几十年的发展啊,这些科学家们就发现我图像自己啊,有非常多的一些性质。
就比如说我们的稀释性,图像边缘的梯度的系属性,这都是非常经典的一些就是图像的一些性质,这个时候我们可以把我们的一个正式的,就出现了我们这个AX减B的一个l two norm。
也就是前面这一块data fidelity,这个要保证这个时候后面再加一个约束,这个时候整个的一个啊优化的一个,objective function就构成了,当然有时候之前人们开玩笑哈。
如果你能求解这个问题,基本上你就是可以求解,任何在计算成像之中的问题啊,当然这个不是特别严谨,这也说明了就这个问题啊,真的是有非常普遍的一个普适性,为啥说不是很严谨的。
因为有很多时候这个image model a啊,他他搞不清,他搞不清哈,他不一定是可以直接描述出来,比如说我那个一个普通的一个单透镜,这个相差啊,随着深度跟视野都不都在不断变化。
你这个有时候这个一个简单的一个matrix a,可能就搞不定,所以这两年就大家有干过啥事儿呢,就有一些呃组啊,就是通过一个小神经网络来模拟这个相差,哎,这个A啊不光是一个matrix啊。
我可以用一个小神经网络来表示啊,对不对,这也是完全没有问题的,然后再去求解这整个的一个呃约束的,在约束的一个优化问题,当然我们要求解这么一个问题。
上上节课我们讲了一种方法叫half coretic spleeting,这个是怎么样呢,我们可以把这一个优化的问题,通过half coredis bleeting的一个方法。
来把它重写成这种B减ax加了一个regular tom,然后再把后面的这一个约束,然后拆解成一个嗯,subject to dx等于Z这一个问题,然后这个时候我们就可以,在我们的拉格朗日函数写出来的时候。
加了一个判别项,判决项叫penalty term,哎,来通过交替的求解,这个X跟Z我们就引入一个Z哈,这引入通过引入这个Z来优分别的优化,这个XNZ,这个时候直到它收敛到一个比较好的一,个结果为止。
当然这个求解的过程上节课也给大家呃讲过了,就是我们是通过啊不断的变化它的一个鬼脸,Decent,在XZ分别求解它的一个鬼脸descent,来最后啊收敛到一个比较好的结果。
这个我们要遇到一个比如说LYLOL,我们就可以用一个呃soft啊,stress to的这个方法,来求解这个l one norm的一个极值,r two呢,那直接啊AX减B吧。
就是control gradient这个事就解决了,基本上这两大套路哈,Control gradient,还有一个soft threshold,这是你求解每部的一个啊好用的一些方法。
当然我们最后整个问题通过求解之后,其实X啊就是呃一个N啊,信号一个长,我们把一个image配拍成长条A,实际上就是嗯比如说一个模糊吧,上节课讲的模糊就是一个condition对吧。
这个时候我们要比如说我要恢复它的一个边缘,我可以还可以加一个total,variation的一个REGUIER,这个时候就是对XY方向分别求导,我们假设它的gradient是稀疏的。
这个时候可以加一个TV的regular x,我要去造呢,哎就是可以用一个DC matrix来做一个RIRATHER,就是对它进行简单的一个趣造是吧,当我们再回顾一下这个x update。
就是我们要优化X我要求它的一个PROXIM,这个时候我们这边有两个,你看我们这个时候X是变量,把Z当成常数,这个地方我们就实际上就求解了一个啊,也是一个X等于B吧,大a hat小AB这么一个问题。
我们有一个简单的control gradient,就可以非常快速的求解这个问题,这个有库哈,有库这个Python这个spicy sparse里面有一个control,电影库。
大家有兴趣的话就不要有兴趣了,这个作业里面会会会有的,这个大家一定会解决这个问题的,当然我也推荐大家自己写一下,这个CONTROB的鬼脸,当然我们这个可以做要求,可以做一个要求。
这个他这个规定的本身自己还是要实现一下的,然后UPDZ呢这个就好办,这个更好办,这个是一个l one norm,加一个l two norm,这个时候我们用一个soft threshold。
就可以非常快速的求解它的一个值,那这个TV跟啊这个OY这个问题都不大,这个就是把这个项目换换掉就可以了,当然这个时候就不知道我们这节课的主题,一个单像素的相机是怎么实现的呢。
来我们先回顾一下这个单像素相机的一个,实现的一个过程啊,它是怎么样构建它的一个成像装置呢,首先我们一个场景,假设是我们一个自然的场景哈,我们通过一个透镜在我的透镜的下面,这个时候我先不放一个图像传感器。
我先通过一个叫DMD,你甭管这个DMD叫什么,这个DMD本身是叫地质图,Mac minor array,也就是数字微透镜阵列啊,这个说到DMD,大家就先说一下吧,这个DMD本身哈是在做投影仪里面。
那个投影的一个仪器器件,它是一个可以零一开合的一个数字微透镜,就是我们把透镜通过max的工艺,做成一个可以开合的零一的,这个时候就可以调这个光,有还是没有DLP啊,就是以前就得录仪器。
这个公司叫texas instrument,经过了十几年的研发,砸了几10亿美金,从80年代一直到零几年诶,最后终于成功了,当时很多时候就觉得这个德语经,这个公司已经挺不住了。
就其他的业务已经没办法支撑这个公司发展了,他还是继续投钱在这个点MD上,因为之前呢我们投影里面通常都是用l cos,后来呢这个DMB问世之后,就发现我们这个用透镜就微镜啊做反射,亮度可以做得非常高。
我可以承受非常高的光强,也就是很多高端的投影仪里面,特别是亮度非常高的一些投影里面,通常是用DMD就是它的核心器件,这个它是有一个像素的阵列,每个像素都可以零一开合就可以是亮是暗,大家就可以这么理解哈。
所以这个时候05年,那时候DMB也刚出来没多久,就通过这个DMD,在我的相面上进行一个零一的一个,随机般的调制对吧,我然后我再通过另一个透镜,把这个经过随机的,就是我这一个像素嘛,这是0101。
我成了一个mask,相当于,再通过一个单像素的一个,就是一个感光元器件嘛,叫photo die,这个时候就可以捕捉到这个经过调制的信号,当然我们经过一系列的这个这个这个这个,首先这个先先静止哈。
假设这个场景不动,经过一系列的不一样的一个随机的斑调制,我们就可以拿到一个vector,就是记录了N个观测值,这个时候我们通过这个观测值来结合。
我们之前我们TMD上呈现了一个binary pattern,来恢复我们这个场景,当时大家是想有非常多很贵的一些探测器啊,比如像我们的这个光电倍增管,很灵敏,速度很快,但是非常贵。
这个时候不大能做成一个很大的阵列,当时大家都觉得诶,这种single pixel camera就可以实现一个阵列的成像,但是只用一个成像的一个起点,这个是怎么做的呢,这个是当年他们搭的第一套装置。
好这跟刚才大家看那个原理是实际上是一样的,这个物体经过透镜打到DMB上,一个DMB上会呈现随机的斑,然后再经过一个被一个射手大的,然后来捕获的,然后不断变换这个DMD上,这个Python就可以。
直到捕捉到一个足够的一个N个MANAGMENT位置,啊实际上就是我们你看我们这个图像,假如它是一个R,而点乘上这么一个这个一个随机班。
然后再加到一起,拿到一个绘图纸诶,换一个随机的一个码点成这个图像值,哎又获得了一个新的值,直到就是我们这个叫M的merriment吧,就是积分的这个值吧,加起来这个这个值。
我的managment matrix是啥呢,就是我是怎么样对这个图像传感器,朝向信号进行调制呢,实际上这个就很简单,就我们就是这个binary map,就我们这个二值图啊。
就是对我这个图像的一个调制是吧,我这个里面A就把这个二值图,这个二维的图拍成一个长条了,那一样的拍成一个长条一样的哎,我们这个M的值,然后就有M个marement matrix。
要构成一个大managment vector吧,就构成一个这么多vector,构成一个marim matrix,就是呃这个A等于B,有位同学说,这不就是奇函数的图像吗,还有一些mal Python啊。
这这很多机G图像都是这大差不差,本质上就是大家可以想一想,为什么我通过这么一个binary map,就可以把不断的调制,就可以把这些信号诶完整的记录下来了,大家可以想一想啊,确定方程,这个我不是很清楚。
但本身确实是u pose,什么叫签定方程啊,这个没听,以前没听说过,然后这个因为我们这个随机班本身啊,它不断变换,这种,实际上是我对整个图像信号,在不同频率下的一个采样,这个大家想一想啊。
就是在不同频率下的采样,我们现在已经构建了这么一个,ax等于B的一个问题了,这以前呢大家也试过用这个half correct lippt,加上一个TV来解这么一个问题哈。
这个时候我们比如说我们最后的一个信号,是M的值,但是我们实际的观测值只有N个,这个到时候那时候大家去恢复二倍啊,TSNR可以恢复出来,3737。33。7个分贝,那就是在TV的约束下。
然后呢啊后面是后来加了一个deny的,一个DCN的一个约束,这是后来的四倍,八倍的时候,我们发现这个half cos x pet加TV,这个rap非常厉害,这就是整个图像变换的特别厉害。
恢复的不是特别理想,当然这个DSN这时候就变得稍微好一些,但也没有好太多,就是我们这种half correct expedition,在那个时候,对我们这一个压缩率非常高的一种情况,就是四倍啊。
四倍就是十六四乘46,八倍就是886 14,压缩率64了,这个有点太大,这个时候恢复的不是特别好,说先在讲这个ADL详细之前,我们就直接从这个half corec splitting,过渡到ADMM。
其本质上求解的问题都一样哈,就1as减B加上一个约束对吧,然后这个时候我们去怎么样,我们看一下区别啊,本身啊我们这个拉格朗日从拉格朗日上看,我half corey是不in的一个拉格朗日函数。
我是引入了这么一个变量Z,因为通过隐居的一个penalty method,就penalty项就判逆向,来构建这么一个拉格朗日函数,但是ADMADMM呢,它是构建了一个增广型的一个拉格朗日函数。
叫AUGMENTITY,拉格朗,它是引入了这么一个变量Y来,通,过把这所有的这些约束,都写到一个拉格朗日函数里面,这个时候就是我们就是希望加强我们本身的,这个就是这个我们这个函数目标函数吧。
目标函数的一个凸性啊,就让它更容易啊求解到一个global mining里,但是不大可能global嘛,但是他收敛的会更好一些,这个时候我们想构建了这么一个增广型钢,拉格拉格朗日函数啊。
我们可以看到后面这个项目本身都是一个凸的,这么一个像,对不对,它的一个凸性会比一个panic term本身是更强的,然后我们构建了这个增广贤的拉格朗日诶,这个时候我们去怎么去解它呢。
当然我们这个该不知道的,这个X还是不知道matrix a还是这个A,而TV啊就可以就是XY方向,gradient deoy可以加一个ISP就没问题,这个怎么解它呢,啊这个套路是差不多的。
这个时候我们有XZU这三个变量对吧,我们要对这三个变量分别的啊,进行交替求解对吧,我们这个X把X当变量,X当变量,这个有的像就是第一项和第三项,这个X变量是有的对吧,我们第一步就求解X的PROXIM。
也就minimize这个函数,那第二步呢把这个Z提出来,也就是第二项和第三项里面有这个Z对不对,这个时候我们就可以把这个Z提出来,然后去minimize就是求解这一步的一个pro。
然后第三步实际上就是把我们的U提出来对吧,U我们知道这个实际上就是一个L2的一个,这个这个就很简单了,大家一求一求导,然后等于零,就发现这个AU就等于一个U加上DX减Z是吧,这个是嗯比较容易理解的。
当然我们这个整个update的一个套路,跟half codec cppt也没有太大区别,基本差大差不差,这第一步呢,实际上我们就是一个list square problem吧对吧,我们就可以求导之后。
让这个导数等于零,这个时候我们就可以构建出一个AHAT等于AX,ha hat x等于B唉,实际上就是一个control CD,规律性的解了一个线性方程是吧。
那第二步呢实际上也跟half project exceppt,一样对吧,只是我们之前这里边有个不是很常见的一个U,对吧,这个时候也是要,假如说我们是一个TV的regular rather。
这个时候很简单,我们用一个呃threefold,就叫这个soft threshold,一个method来去求解这么一个问题,然后要是遇到其他的REGUIZON呢。
比如说像denzing base rearm,哎,这个时候我就可以把这个通过DNOTION,这个regular or来把它这一步求解到这一步,这个基本上这两步哈。
跟那个half project meeting就一样对吧,基本上就没有,基本上没有任何区别,大家一看到他的一个伪代码哈,这两个比如说TV的伪代码跟这个DNOISE,伪代码本身就是两步。
King half correct,就是两步变三步哎,求x proximal,求Z的proximal,然后再把这个new update一下,然后知道他我们满足我们收敛的一个条件,其实大家看来看去哈。
这个就发现这个无论是half codec,SPEETING还是ADMM,基本上都是非常非常简单的一个问题,每一步第一步求它的program,要么就是求一个list square,Problem。
list square呢我们求解的问题就用CTRL鬼链子,要么就是一个TV的一个约束,TV约束呢我们就可以用一个soft rice holding,要么就是一个noise约束。
我们就可以用我们列入这个noise的一个problem,来求解,它基本上就套路就这几个,然后两三步一拼,这么一个复杂的一个带约束的优化问题,就求解了,大家觉得是很神奇,其实这个真的是非常简单。
但我们可以看到这个ADMM跟TV的比较好,我们发现这个ADM,因为我们对这个拉格朗日函数进行了增广,它的凸性变强了,这个时候我们就可以很明显的运行到,凸性变强了,我求解的效果应该是会变好哈。
大家可以看到这个ATMM加上一个TV的一个约束,相比half cox beeating加一个TV的约束,我们可以提高将近十个分贝的一个PSNR,当然也没啥约束,就listen square。
基本上就是解不出来了,就基本上解不出来了,然后呢就哪怕你压缩率很高的时候,呃,这个是压缩率很高的时候,这个也不行了,这ADM这个也贵了哈,所以后来就同学们加了一个DM,加一个DNCN的一个约束。
这个时候就可以对A这个压缩率非常高的时候,也可以啊,这个拿到一个非常高的一个啊,这个就输出图像吧,大家有兴趣的话可以看一下,就是北大张健老师的,有一个叫east net,那个就是用神经网络呃。
就是一步一步的一个神经网络来一个几,定定了几部的一个小神经网络来去呃,恢复了这么一个压缩感知的问题,这个在呃这个video的compression里面,就比较有大的一个优势吧。
DCC就是一个deep network嘛,就是一个小神经网络,但这个时候让我们来分析一下,我们通过概率的一个方式来分析一下,我们这个东西到底是打球解,这个inverse problem到底是咋回事。
我们还是回到这个image formation model,就是我们这个B就等于一个ax加上一个noise,Image formation,乘一个来tt max加上一个noise。
我们这个整个的一个中文,poverty的一个观测是什么呢,我们这个我们已知我们这个大概噪声啊,噪声的一个幅度是西格玛,然后呢,我们在这个X的情况下观测到B的一个概率,实际上就是我们每个像素点。
然后在一个像素xi观测到B的bi的一个,当然他要这个时候要符合的一刻哎,随着B减AX为中心的一个分布,当然我们这个整个乘起来之后,就符合了一个E的一个负B减ax平方除以二。
西格玛方对这个是呃它造成水平决定的,然后呢我们就这个前面的系数咱就不管了,这个咱要优化啊,后面这么这么一项对吧,我们把这个通过一个呃白定理把这个拆开是吧,我们把这个我们要实际上要是观测。
这个是观测已知X观测B,但实际上我们要观测到的是B啊,我们要求这个X啊,也是在这个噪声sigma的一个情况下,我们把这个用by定理去把这个拆开,然后PBX西格玛px over pb。
这个时候我们就可以把这个拆开,当然这里面有一部分这个这不这回是长,这个可以把它当成一个常数哈,这个时候这个observation已经,因为我们是已经知道这是啥了,这个就可以当成一个常数不管了。
忽略它的系数,那么只看后面上面这两项和X相关的这两项,这个时候我们是要求一个就是最大,似然估计对吧,就是map的这么一个问题,我们叫maximum,我们要使这个概率达到最大化,要求这个让这个概率最大哈。
但这个时候我们搞点小trick,我们加一个负号,实际上就是求这么一个负的一个最小,但我们知道这个logo本身就这里面有个E嘛,这个这个expectation这个很烦,我就取个logo。
因为log本身不影响它的凸性凹陷,这个解它的优化方程,这个没问题对吧,我们把这个logo本身套在这是吧,然后再加个负号,实际上最后就变成了一个AAX减B,加上一个约束的一个问题。
这个绕来绕去实际上就绕回来了,这说明这个是符合我们的一个概率分布的,一个模型的,就是我们已知观测B在一个啊,noise sigma的一个情况下,我们要求X对吧。
实际上啊最后跟我们本身构建的那个啊image formation,model是一样的对吧,但这个时候,比如说像这个破损问题是咋办呢,我要观测了一个噪声,破松噪声,这个是怎么办呢。
来来再给大家详细的掰扯一下,这个破损噪声啊本身是跟这个信号相关的,对不对,他也不再是一个高斯,刚才我们看到的是一个高斯模型,就直接就是它的一个petition,但是破松本身我们已知一个X。
我们观察到B的时候对吧,它是一个符合泊松的一个随机过程的一个函数,那我们要所有的像素加在一起,这个时候我们把这个每个点的一个破松分布,乘起来对吧,然后就拿到了一个这么A这个真是复杂哈。
这又又是带logo呀,又是带这个E的负X2,又在这个就是就是在这个阶层啊,这个我最后拿了这个很复杂,咋办呢,这个复杂我们就怎么把它一步步抽象拆解,然后拆解到我们可以优化的问题,但我还是老一样的套路哈。
还是用by定理把这个再拆回去,这个B还是已知的,我们最后要求的还是PBXA西格玛,然后X这么一个问题,然后我们这个时候求max lo就求这个呃,它的最大自然估计也是老套路,求个log。
但中间有一些就是这个地方啊,这个这个有一些呃这些常数啊,像这些地方就可以忽略掉了,咱不管,就咱看看后面这个,因为后面这两项哈,我们取个log之后,这两个就可以乘就变成加了对吧,乘就变成加了。
我们就干脆就写到后面去,成就变成加了,然后我们最后我们整个的一个问题啊,就直接,归结为这个求解的这么一个概率图,概率的一个最大四人估计,然后第二项就是本身他带了一个ax那个项,然后再加上啊。
我们最后想要加一个约束,对不对,然后我们把这个我们前面那几项哈,就是ADI这个这边这几项都组合在一起,形成一个K这个好好表示哈,就是写成KX等于Z哈,因为我们最后要求解的时候。
基本上都是要一个contributed,就是一个contribute gra链往里套,我们把这个形式叫reformulate,一下就拿到了一个我们想要的一个,就是增广型的一个拉格朗日,这个拉格朗日啊。
那还是老套路哈,咱把这个再继续套一下,这个我们还是XZU分别的一个update,然后我们这个就看X的时候,X只有这一项诶,更简单对吧,我们把这个一套,然后求一个呃gradient,然后再让等于零。
GRADIA等于零,这个时候就可以很轻松的用一个Ctrl gradient,把这个这一步X的proximal求出来,然后第二步呢我们要求Z的一个proximal,这个时候要前两项哈。
我要minimize这个关于Z的这么一个变量,然后第三步还是老套路,这个update一下这个U就可以了,然后关键就是这个这一步来怎么求呢,我们来怎么样求这个ZI呢,实际上这个求ZR哈。
这个这个这里没写哈。
这个就基本上我们遇到的情况,也就是还是要么是啊加个约束TV的约束了。
这个还是呃soft rice coding,或者是我们一些dony prior,这些都是老套路,往里带都一样的,那我们就可以看到我们的total variation,加上ADM之后。
我们对一个破损噪声的去除。
我们把这个破损噪声的一个概率,通过这个概率模型把它的一个呃问题写出来,再通过ATM去求解它,我们以前哈,这个我们直接用一个max拉力后的一个solution,就是RICHARD的lucy method。
有时也简称叫RL method,大家可以解出来诶这个这个噪声挺大对吧,但是我们通过一个呃一个TV加上一个ak mm,加上一个呃非正非负,好像这个没有负的。
然后就是max a poster solution,最后发现我们可以解到一个比较清晰,然后又比较小噪声的一个图像,但有时候嗯像求解这么一种问题。
也会用一些呃skill gradient projection来去求解它,这个可能会稍微复杂一些,稍微复杂一些,会比ADM或者多一些,这个以前我也上过当,就是以前我们导师wolf on就给我布置作业。
就让我们去一个破损噪声哈,那个时候有没有给任何指示,也没有给一个参考文献,当时就随便找了一个呃,效果看着很好啊,有skill gradient projection method。
然后最后发现这个解起来是挺困难,其实挺困难,当然最后也解出来了,但后来在实现先发ADMM,就发现这个ADM是真干净利索快啊,求解的效果也不错,所以说这个A点M这个工具啊,大家要学好,就总共就两三步。
就很简单。
在刚才给大家举了一个例子,是一个嗯单个的一个像素,然后一个阵列,这个实际上就是一个非常简单的一个,低维度的一个问题哈,二维映射到一个点,但我们很多情况下,我们是要遇到一个高维度的一个,压缩感知的问题。
这个我们遇到了一个high dimensional的一个inverse,Problem,我们这个时候怎么办呢,但我就曾经遇到过一个问题啊,他一个要解决一个高维的压缩感知问题,就是15年还是16年的时候。
就那个时候不知道从哪弄了一台,就是spider camera,就是一个单光子雪崩光电二极管阵列,这个雪崩光电二极管阵列呢,有很多很多比较好的性能啊,比如说它这个响应频秒级的。
当时我手上拿的那台是20匹秒,然后呢,他就可以对单公子的一个,就是它可以对单公子有能力进行响应,然后呢比上我们那种光电倍增管,要PPT或者是调控相机super camera,唉,在一样的情况下。
我又变得又小又便宜,但是当时我们遇到一个什么问题呢,这个speed camera自己分辨率非常低,这个时候我们想到了这个分辨率非常低的时候,这不就是一个压缩等质的问题嘛,对不对。
他这个就是维度会比较高哈,我要减到这个XY分别是一个阵列,然后再加上一个时间轴,T本来成一个二维变成三维,变成四维,就是四维解解个四维的问题啊,当然这个除了这个啊低分辨率的问题。
本身还有一个laf factor的问题,就few factor就采样哈,采样问题就我一个图像,我采用压缩感知,没有办法汇集到一个点,那这个我们就需要用光学的办法来解决它,大家来看一看当时是怎么做的。
但为了把这个图像调制过的一个小,图像的一个小块诶。
完整的捕捉到传感器上,那个时候1617年那会吧,就我们自己做了一个就是演示的微透镜阵列,当时为啥做这个衍射卫生间之列呢,就因为折射的不会做,你知道吧,就当时那个这个是我们没有那个做折射的工艺。
但其实做折射会更简单一些,当时就第一个提出了,当然这应该是全世界第一个提出了,做演示的微透镜金链,那个时候我们就有一台光刻机啊,一台光刻机,然后刻了一个四层的一个mask,来形成的这么一个演示经典。
当然大家看的很刻的很粗糙哈,因为分辨率的问题,这个就这个衍射环都出来了,这些大家可以看到这个这个采样了,这个班ALLAZING的一个班都已经出来了,但这个效果还不错,效果还不错。
这个最后就是加工出来了一个四层光,四层套课,每一层就是二的二次方,四层总共就是二的16次方,但这个理解一下就行了,这个实际上就是一个小透镜,把光汇聚一下,但也做成方的方的这个透镜呢,它的效率会高一些。
我把整个图像这一小块儿的,这个全都收集过来了对吧,然后最后哎这个对齐很麻烦,当时就找这个传感器厂家找了做了一个alan walker,我们就把上下跟那个抽氧传感器对齐,这是第一次对这个spy的传感器。
这有史以来第一次,就他用一个衍射的一个麦克莱瑞,进行它的一个填充率的提升,但也提高了很多光的一个效率哈,但还是很复杂。
套路还是一样,我们要在一个焦平面上,我们相面上放一个binary,一个map来进行调制,但这个时候有一些情况不一样哈,就刚才是一个像素点,现在是我一个XY的一个阵列,然后第二个不一样是什么呢。
我们要捕捉它的时间序列,就是我在一个频秒量级下的一个时间训练,我最后拿到的是一个4G的一个DQ,我在一个4D的一个MANAGMENT下,就是你看我的我的采集是啥呢,第一个维度相机的XY,那这两个维度哈。
前两个维度X相机的XY,第二个维度我们这个DMD的一个随机班,这是第二个维度,第三个维度就是时间轴T的维度不对,这是第四个,总共是XY随机班,然后还有一个时间T4个维度。
我们捕捉到的信号就已经是四个维度了,我们要求解的呢是一个XYT的一个DT的阵列,这个告诉大家怎么去啊,构建这么一个函数来去求它,但这个中间遇到的问题还是非常多的哈,因为这个SPA传感器本身啊。
它是有一些hot pixel的,就是就是坏点哈,他这是当然这个这可能是初期啊,这个这个图像传感器有一些工艺上的缺陷呃,它会有一些坏点,这个没有办法,我们要先把对这个坏点进行去除,然后去传完之后呢。
就然后这个是我拿到了一个原始的,一个就是DMD调制过的图像,然后去除,这是去除之后的一个电BL图像,然后最后,我们还要标定一下,这个,当时还要我们就是每个像素块对一个。
对应一个随机班,就每个像素点对应一个随机班,这一个像素就相当于一个小的single pig cha,但是它会有一个更高的一个维度,像这个celebrate这个问题很麻烦很麻烦。
当时这个我们叫微米级对齐啊,这个还是有故事的,这有故事的,像最早我们我们在cost这个VR computer center,那个地方,当时我们的实验室,我们那个实验室不知道为啥啊,也不是实验室。
整个办公楼办公楼地下都是空调,就我地板都是空的,当时哎呀这年轻啊,我们把这个光学平台啊,就放在那个地上,结果就发现我们这种在这种微米级对齐的时候,这个走过人,这个人走过去蹦一蹦。
这整个对齐全都不能工作了,哎这个时候我这个倒腾这个实验,倒腾了好几个月,就发现这个对齐怎么实验结果老是不出呢是吧,这个仿真早就过了,最后做实验就是做不出来,最后发现这个B在抖,后来就换到了一个实验楼。
我们把这个桌子搬到了实验楼,这个才完成了对齐啊,这个标定做了好几个月,接近大半年,那我们要恢复的是一个高分辨率的,一个XYT的这么一个这个这么一个data cube,但我的MAGIMENT本身。
那我们就把这个每个小块儿,通过一个像素来去捕获对吧,然后每一层也是通过一个像素进行捕获,然后最后观测到的就是一个嗯嗯,4D的一个就是一个managment matrix,就是XYT。
再加上了我一个不一样的一个班的一个调制啊,这一个啊这一个这一个就是二级的一个随机班,调制了整个的一个XYT的一个高维图像是吧,但这个matrix也变得非常庞大啊,这个从F1到FT当时后来是用卷积解决的。
这个太大了,没办法去解这个management,我们拍了一个场景,就是mt,然后这个观测矩阵就是mt乘以NT,这么一个大的一个矩阵啊,我们最后想办法用就是一个for循环。
或者是一个很LUTION来解决掉,这是我们实际的观测值对吧,实际的观测值,啊这个这个同学问一个T,是不是对应好几个DMD的mask,呃实际上你可以这么理解哈,就是这么一个我们要观测的一个XYT啊。
这是我们最后要求解的一个CLEIMAGE,我在一个cube本身受一张mask调制完,然后再来那么一个循环,再调制完,再来一个循环再调制完,你也可以也可以这么说吧,没问题。
对你只是这个怎么组合的一个问题啊,这个dimension的变化这都一样的,最后我们最后搞来搞去,这个套路发现还是一样的,还是一个X减B的问题,只有我的规定,可能可能要在一个三维空间中进行约束。
也是3D的一个TV,但这个嗯没没有太大区别哈,没有什么本质区别,其实我最后我操作的时候,就这个操作这个FX减Y这个data formation的时候,它是一个变成一个高维的了。
那本质上其实解的问题没有任何本质区别,但这个时候我们那个augmented lagrange,可以写成啥呢,就可以通过我们这个引入这么一个,西格玛德尔塔这么一个变量,然后把这个增强它的凸性哈。
增强它的凸性,那最后把这个拉格朗日,然后分别求解这么四四部吧,就是第一步还是这个update这个XK,然后第二步我们把这个omega固定住,然后对这个呃拉格朗日函数进行一个规定。
design求解来update这个X,然后第三步呢我们就通过一个呃就是soft,因为这个时候有TTV的一个约束嘛,就是鬼点的一个约束,这个时候我们就通过一个啊。
这么一个简单的一个soft rinking,soft soft啊,stressful的一个也叫SHRINKY的一个方法吧,也求解这么一个呃l y norm的一个约束问题,第四步哎。
分别对两个就拉格朗日的橙子,就是德尔塔跟西格玛和德尔塔分别去update一下,这个很简单很简单,这个套路跟a dim没有任何区别啊,这个有有时候这个也,这个就说这个时候就叫TVSRA这个。
但实际上跟N上面没有任何区别,都是这么一个套路,这是我们当时做了一个圆形,当时搭这个光路搭的我们真是心力憔悴啊。
心力憔悴,在这个那时候刚来啥也不会,那个时候自己也做了,因为这个光路哈前面套了个透镜,他自己用当时用solid word,现学的画了这么一个啊,这个叫这个这个这叫turn prem。
可以从这个方向进这个方向出,因为我们直接大家要通过,因为这个电B很烦,还有一个12度的角度,你在一个光学平台上对你怎么对你都对不好,对你不大好,调出来一个12度的角度,干脆就搞一个七零部件,既然不见了。
这个组里呢就大家都不会,这个都是新人啊,只能靠自己,只能靠自己,最后先画了一个呃,这个我们这个透镜的一个套筒,然后再对新我们要对这个12度的一个角度哎,对完12度角度。
我们把这个t presume怎怎么变化,最后我们要再留在出口的地方,要给这个要给这个就是日立镜,日立镜留一个就是一个接口,这样好好接到一个更好一些,但像这种过程中,特别害怕我们砸光的出现。
他这个为了搞这个杂光问题呢,我们这自己又搞了一个这个套筒来,直接也是3D打印的,最后接到这个SPD传感器就搞,搞了这么一个封闭式的一个光柱带调制的,当然这个很烦哈。
这个歌手当时那DMD呢就自己就是没有那个线,不够长了,那当然自己当时那个时候闲着没事,就画了一条这种延长了这种线啊,这个高速信号线当时也是现学现画,这也是第一次画嗯,好赖是能能能管用。
这是我们当时做了一些结果,这是比如说我们在一个2D的intensity上,做一个压缩感知,上面是一个64×32的一个分辨率,诶,下面这是我们经过恢复出来的,一个啊高分的一个intensity。
这是一些它在深度上的一个压缩感知的应用,也是XY方向的一个像素的变化,那最后我们也闲着没事儿哈,就记了一下这个光线到底是怎么汇聚的。
因为光在空气中走,你是看得不到的,当时我们就搞了这么一个这么一个。
当时是哎当时可能是比较有钱。
那个时候是搞了这么一个玻璃的一个对,他是就增加了一个拉格朗日的一个橙子,来增强了它的凸性,实际上它是可以啊,比较好的收敛到一个更好的位置,所以说这个ABM结果比这个HQS好。
再回到这个当时可能是比较有钱,当时就拿了这么一个WIFI,就一个field的CD卡的一个wafer,这一片有好几百美金啊,好几百美金,然后把这个喷了一层白漆,搞一点斜斜的角度。
让这个光线可以有这么一个角度汇聚过来,大家可以看到这个光线汇聚的一个过程啊,当时做计算摄影呢。
这个是实验步骤啊,其实就是很很很很困很困很困惑哈。
大家这个经常会遇到各种各样的问题。
当然这个正是因为我们整个计算摄影领域,有这个实验验证,也就是我们比起码比其他领域是可以有一个呃,更强的一个倾向应用的一个点,就是我们已经验证了自己,已经验证了这个东西是work的。
所以说做计算摄影的同学,为什么在市场上啊这么容易找到好工作啊,这么容易被市场认可,也都是嗯需要有一定一定的动手跟计算能力的,培养,各方面的能力啊,今天的课程就到这里,也欢迎大家读一下这些参考文献。
今天就主要给大家分享了一下这个inverse problem,然后以ADMM为目标,我们通过这个single pixel camera为例,同时我们也啊分析了一个他的一个半身的一个。
Perspective model,然后包括像高斯的一个噪声模型,还有一个破损的噪声模型,它的概率啊,表示我求他一个最大三人估计的时候,哎,这个时候他最后我们发现。
跟我们最后要FORMATE一个image model是一样的哈,解的时候就结套路,Ctrl t gradient加上RINKAGE或者deny the power,这个呃大部分问题都是可以求解到。
实际上本质上就是两三步,第一步写拉格朗日,然后通过把那个橙子加上去,然后呢第二步就是求解,求解里面就分三小步,update你的U这个地方就解决了,就非常稳,非常简单哈,当然这个时候既然简单。
就是要求大家写一下,这个要实现一下,写完之后才会有一个更清晰的印象,这个作业的时候会布置给大家好,今天的课程就到这里,同学们还有什么问题吗,好既然同学没有问题,那今天课程就到这里。
16.游戏引擎Gameplay玩法系统:基础AI (Part 1) | GAMES104-现代游戏引擎:从入门到实践 - P1 - GAMES-Webinar - BV19N4y1T7eU
嗨,欢迎大家回到games4104,现代游戏引擎的理论与实践啊,我是王思,那个就是很高兴得了一周边大家见面了,那么首先的话呢就是在今天开课之前的话,跟大家讲一个好消息,我们的社区的T恤终于设计出来了。
那么这个T恤的话,我设计我还是非常喜欢非常喜欢的,因为我觉得这个T恤特别能代表,我们games104课社区的精神啊,我觉得大家到底为什么要学这门课对吧,我们不是为了找一份更好的工作。
也许吧找一份更好的工作,也许是这个我就是喜欢,那其实当时我们在想设计这些衣服的时候,我们想表达什么东西,我觉得我们就是游戏引擎的探索者,Gaimaging explorer,就是说我们只是出于好奇。
我们希望能够在一起去,进入到这个巨大的未知的世界,所以的话呢这些小引擎的那个logo,设计的这个这个T恤设计的非常有意思,比如说啊我们这两款这一款在这一款的话呢,我们在这个我们小引擎的logo上面。
是选了我们很多小引擎的代码,把这些代码放在上面,而且今天我们程序员还很认真的检查了一下,代码有没有写错哈,然后很怕上面写错了代码,然后呢还有另外一款是这样的,上面写的是说我很忙,不要打扰我。
我现在正在构构造世界,I’m building the word,那么我觉得这两件都蛮好看的,很干净,那么大家到底想要哪一件呢,对吧,那我们games104社区的精神是什么,就是永远是大家去挑选。
所以呢,到时候等我们的B站的那个视频上上传之后,我们会让大家那个弹幕你投票对吧,大家那个如果喜欢第一款的就扣个一,喜欢第二款的扣个二,然后呢我们会根据大家最喜投票投票是最高的。
我们去选择哪一款T恤做出来对吧,希望大家都很喜欢我个人,其实我两个都要,但是的话他们告诉我说必须得选一款啊,当然我会保密,我会我会告诉大家,我会选择什么,其实我哪件都很好。
那么就是说这些T恤做好之后的话呢,首先第一个就是说,我们之前参与我们小引擎命名的小伙伴,我们不是选了十个幸运的投票者嘛,那我们会给同学们寄过去,到时候麻烦大家联系我们课程组的小伙伴。
然后我们会把它的地址啊,这个通讯方式留下来,这样的话我们还把这个印好的T恤给他寄过去,另外一个的话呢,就是在我们每个星期的B站的那个视频下面,同学们留言,然后的话呢就是哎记得注意啊。
不要只是在抠这种无效留言,最好要写内容,就是说还是有点信息量的,比如大家有什么问题啊,或者对课程有什么反馈啊,然后呢我们会把这些就是有效的留言筛选出来,然后呢在中间会抽出十个我们的幸运的,咱们的同学。
然后呢也把小T恤送给大家,就以后就争取每一节课吧,我们都能给大家送出时间T恤出去,因为怎么讲呢,咱们经费有限,只能应时间,大家原谅我们,然后但是的话呢,我觉得就是说我们104课程的,每一件T恤的话呢。
都会就是呃怎么说呢,表达我们自己对大家的这个啊想法和意见,那么另外一个的话呢,就是说我们也希望未来能够偶尔,或者是什么时候我们会再做新的T恤,我们一件一件的小T恤呢,积积积攒起来时间久了。
其实已经也能形成我们自己的一个啊,一个小的这样的一个一个就是社区,也有我们一个很多很多自己的纪念品,所以的话呢这个是我今天特别开心的事情,因为我今天看到这些设计图的时候。
我的第一反应就是我也想要一件对吧,所以拜托大家选一件好看的,一定要选中我心目中那件最好看的T恤,然后的话我们一起把它拎出来,但是啊老规矩我是不能干预大家的选择的啊,然后呢第二个的话呢,就是也回答一下。
今天社区里的同学问我的一个问题啊,第一个问题的话,其实同学们在我们的那个学习群里面有满足,满足的问题,然后课程组的小伙伴还是蛮认真的,挑了三个问题,那我在里面给大家做一个我们的回答。
第一个就是有同学问我们说,诶,是不是现在的游戏逻辑,都是用这个component base的这样的一个架构,有没有其他更好的结构,那实际上的话呢就是写游戏逻辑的话,还真其实有很多种写法。
就像我们讲的时候,你硬生生的用C加加语言去写,你甚至用汇编去写也是可以的对吧,你完全用脚本也是可以的,但是你会发现就是说现代游戏引擎的话呢,很多时候大家会不依不谋而合的,都会选择这种组件化的方法。
因为这种组件化的方法呢本身也非常符合,就是说我们人类对这个世界的认知,就是我们看到很多东西,我们都认为这个东西有几大属性,几大特点对吧,每个人有几个不同的身份,比如现在我们很多同学都是什么斜杠青年对吧。
我我我又是一个这个程序员,那我又是一个个人的这个吉他手对吧,我又是一个什么户外摄影爱好者,所以说其实像这种component方法的话呢,它非常符合我们人天然的对世界的认知,然后呢我就可以自由的去组合。
另外一个呢它有一个很大的好处,就是说当我们的CPU,计算能力越来越并行化的时候,因为你把一个物世界的逻辑,拆成一个个组件的时候,它就特别方便并行化的同时执行,所以其实今天最主流的引擎。
无论是这个像虚幻引擎还是unity引擎,包括很多啊,就是3A大厂的引擎,都不约而同的采取了这个component base的方法,所以这件事情的话呢,就是说啊,就是我觉得这是一个行业的大家一个共识。
但是同学们如果有更好的想法,比如说嘿我们的下一代引擎该怎么去做,那我觉得会更好,我反正我个人会非常有兴趣,跟大家一起去学习和探讨这件事情,因为确实每一种结构它都有它的局限性。
computer其实它也有很多他自己的决心局限性吧,比如说它的效率就没有集成在一起的,效率要足够高,对不对,但是的话呢就是说呃,我觉得也许未来10年之后,又有新的结构会成为这个世界。
就游戏引擎结构的主流,那么第二个同学问我的问题是什么呢,就是说哎这个如何实现blue print的多人协同,我当时看到这个问题,就是我的第一反应就是一下子抓脑袋,我说哇这个问题太难了啊。
首先呢这个问题是一个好问题,因为确实蓝图会越做越复杂,然后大家真的想多人协同,但是我记得我在上一节课就讲过蓝图这个方法,就是visual graph的这个方法,有一个最大的问题就是他别说协同了。
连mg都很难,那么就是说因为你几个人做的蓝图,对它进行修改在一起很难merge,因为它是一种图的表达嘛,就像艺术家三个不同艺术家画了三幅画对吧,都说我们画一个,比如说这个这个这个画一个蒙娜丽莎。
然后说能不能把这三个画家到蒙娜丽莎妹,集到一起,形成他们三个人共同的作品,大家想想知识有多难,其实这就是高尔夫很难的一个问题,那么另外一个的话呢,就是其实在我们的现代游戏引擎里面。
很多东西他都是可以协同,举个例子,比如说我们认为像场景编辑,未来协同起来就很容易,但是的话呢就是说对于蓝图来讲的话,协同可能就非常的难,那为什么呢,就是说那个就是说,因为蓝图它跟场景有一个很大的区别。
是场景你把它做好了,放在这,就比如说我在那边放个房子,其他人就能看到一个房子,所以说所见即所得,所以大家协同就是我在做我的工作的时候,我能看到别人的工作,一眼就明白他在做什么对吧,你在那边放了一个房子。
他好那好,如果我觉得没有意见,我会在你的房子里面种棵树,我种树的时候,你我另外一个artist马上就能看到,对不对,那么他就会知道哦,我的房子的这个摆放被认可了,它是一个就是所见即所得的过程。
但是蓝图不一样,就蓝图我在这边拖一个控制节点,我拉出一个信号,其实除了做那个蓝图的人,本人知道是什么作用的话,其实另外一个比如说设计师,他很难知道它是什么意思,所以蓝图你要看到它的效果的话。
不是说看那个图本身知道的,你得执行它,而且呢你还得执行到那个特定的那个,那个那个那那个区域,而且呢还要在特定的环境下,你才能理解那句话是什么,所以的话呢回答回答这个问题的本身的话。
就是呃有我目前的理解是,我还没有想到一个方法,就是包括我在行业里面,好像也没有看到一个方法能够实现蓝图的协同,包括现在很多的这个就是优秀的游戏团队,他们普遍都在解决这个困难,就是说别说这个蓝图的协同啊。
就是一个人做蓝图,我今天做的,明天做的和一个月做的这个版本,时间怎么去管理对吧,好比如张三李四修了个bug要改一下,我怎么知道他改的东西到底是什么,都已经非常的困难,所以说呢。
这是现代蓝图系统一个很难的一个挑战,那么第三个的话呢,同学们问,所以这个问题我的回答就是,目前这个目前我们还没有找到一个好的方法,让蓝图的开发可以协同起来好,那第三个问题也很有意思,他就说诶。
我们能不能给我们的游戏逻辑的1event系统,设置优先级,哎这个想法非常的巧妙,其实设置优先级呢,在我们所有这种消息类型处理上面,都是一个很常用的方法,但是的话呢这个方法比较最常见的是。
比如像网络消息对吧,我一下子用过了很多消息诶,我有些消息要高优先级的处理掉,有些消息我要第一优先级的,这是一个很合理的方法,但是呢这就是目前在我们的游戏,game logic里面的话。
我们一般就是至少我个人的经验是,不要再给消息系统定义定义优先级,为什么呢,因为是这样的,就是说其实啊当我的一个TIK下去,我所有的game object产生了很多的消息,如果我们对它定义为一优先级。
其实就假设了我们这些消息先后处理的顺序,但是原则上来讲的话呢,就是这个就是那个publisher,SUBSCRIPTOR这样的一个pattern的话,当publisher发出消息的时候。
他不应该去假设谁来处理它,它也不应该假设哪些人应该在提前处理它,因为这样一旦假设之后,你的逻辑的耦合度就会非常的高,那么你一开始,如果你定义了这样的一个执行或处理顺序的话,那这样反过来就是说。
当我们把这个那个就是游戏逻辑,把它变得非常的并行化的时候,诶其实你大家都知道吗,就B性化,那个就是比如说我们的用B性化语言,开发一些一些算法的时候,你会发现它最难的地方是说,我并不知道同样一段函数是谁。
在谁之前去执行的,如果我们是单线程的话,大家很清楚的知道,执行完执行完函数A再去执行函数,B再去执行函数C,对不对,如果你想象的是ABC3个不同的event的话,那么一旦你的游戏逻辑开始并行化之后。
其实你并不能知道ABC的event是在那谁在先,谁在后续执行,所以当我们今天在架构,这个就是游戏逻辑系统的时候,我们一般不会假设,或者说我们希望他对这个order是independent。
就是说无论我的所有的event按什么,那个就是顺序执行它的结果必须是一致的,否则的话呢这就是我的系统设计的有问题,但真实的情况呢就是在游戏引擎结构中的,我们很多component是有一个。
有的时候会有个先后关系的,比如说我们假设游戏系统会把动物理系统,先去TIK对吧,然后呢再去TIK动画系统,对不对,然后再去tick什么AI系统,在早期的就是游戏引擎架构的时候。
有的时候我们会假设这样一个顺序,那事实上就会导致,就是比如说你一个物理系统的信号,那个会比动画系统,会比AI系统要略高那么一点点,但是呢在现代引擎架构里面的话呢。
我们也在探讨说这种order是不是都能打乱,因为当你扔给那个,就是就有那么多的核去处理的时候,很多时候你做这样的假设的话呢,是有点危险的,但是呢其实在现代引擎。
有些有些真实的这个practice里面的话呢,我们确实会做这样的事情,比如说我们要等所有的物理跑完了对吧,我们先fork出去很多很多的这个这个这个job,然后全部收回来,然后我们再让动画系统举报。
有的是什么,但是这个我个人认为呢,这还是一个不够彻底的一个B性化架构,所以回到刚才同学问我的问题,就是说你问的系统要不要收优先级,优先级收计算机本身是没有问题的,就是你但是的话呢如果你的整个系统。
它的这个就是它的正确性,是依赖于优先级去保证的话,那它会让你的系统就是它的1event,the publisher和他的subscriber,就是他的处理者之间的耦合关系过强。
这样不方便你的引擎继续扩展和结构,所以呃我们只能假设说,所有的event在下一个tick会全部处理完,但是呢他先后处理的顺序尽量不要做假设,那这样的话这个引擎的结构就相对比较鲁莽了。
这当然这是我个人的一家之言,实际上有可能有一些团队的时间是要加加,那个就是priority,实际上我们最早在设计一般的系统的时候,我们有过这个讨论之后,我们的结论就是还是不要设优先级。
因为一旦设了优先级,你就隐含了大量的假设,OK这其实啊这这这第三个问题,我觉得问的水平还是蛮高的,就是说当我们在设计一个,像游戏引擎这么复杂的一个系统的时候,你会发现你可以做的技术选型特别的多。
但这里面有一个原则是什么呢,就是就是decoupling,就是耦合度越低越好,也就是让系统各个系统的工作尽可能的剥离,为什么呢,因为这是个超级复杂的系统,所以的话呢如果彼此之间都不是黑盒。
就彼此之间都知道对方是怎么处理的话,那么这个系统它为来了可维护性,可扩展性,包括它的鲁棒性,实际上都会受到影响,OK好,那今天的话呢就是我们的社区的互动环节,到到这到这好。
接下来就回到我们今天的课程的这个正文了,就是game play系统的第2part就是这个人工智能诶,大家有没有注意到,今天我们的标题好像有一点点小的变化。
我们在artificial intelligence前面加了一个basic,哎今天这个玄妙在这地方,所以呢接下来我给大家介绍一下,为什么有这个玄妙,首先的话大家跟大家讲一个既是好消息,也是坏消息。
就是说我们在准备人工智能这一节课的时候,我们发现就是说我们写了180页,大家想想就是我们一直我们的口号是什么,Game,104的特点,就是这个课程组跟咱们的同学们比赛,谁更干对吧。
所以最近我们又干出了新高度,就是我们居然写出了180页的课件,干嘛,按照我们传统的经验,就100页的课程,我们要讲两个小时,那么180的课程我们要讲四个小时去了,对不对,那么更重要的一点,当然了。
我们有办法可以把180页的课件,压缩成80页或者100页的课件,但是当我仔细的读完了这个所有的内容之后,我发现唉真的是太有意思了,就是你大家知道,就是说我们前辈讲引擎讲了这么多节课对吧。
比如说我们讲物理,讲渲染,讲这些很多复杂的数学动画,其实啊,这一切都是为做一个有趣的游戏做基础的,那么做游戏引擎,做游戏最有趣的是什么呢,至少我个人是最喜欢的是什么,做这个AI这一部分特别好玩。
特别有意思,特别具有想象力,所以我看了所有的内容之后,我的感觉是什么呢,哇爱不释手,哪一个我都舍不得放掉,所以呢我就没有征得大家的同意,上上次做了一个主张,就是说我决定把AI拆成两节课。
就第一节课呢跟大家讲一些AI基本的东西,比如说如何做寻路,如何做导航,如何做这个CROSSROMULATION,就是群体模拟,如何做,这个就是环境的感知,包括一些基础的决策的算法,那么第二节课。
我想跟大家讲一些比较有意思的东西,就是现代的这个游戏引擎里面,那些一些更复杂的AI系统是怎么架的对吧,基于目标的这个这个这个AI系统啊,基于这个计划的AI系统,那么当然也要讲到。
我们大家特别关心的一个问题,就是说哎我们用人工智能深度学习对吧,能不能做我们游戏的AI这些话题,其实我自己也非常的感兴趣,我也会很喜欢,所以我在这里面我就决定夹带私货。
就是我想把AI这节课好好的准备拆成两节课,所以的话呢这个希望大家这个对我们有不原谅,我们的这个擅作主张对吧,希望求大家这个有这个不杀我们,我们的话呢就是说把这一部分分成两派,两派来讲。
因为确实AI它实际上是游戏性,一个非常核心的一个东西,那么就是说我觉得两节课讲一讲啊,还是里面有很多有意思的东西给大家去分享,所以的话这个是我们课程的安排,所以的话呢我们基本的课程安排可能会这样。
就是说这节课讲AI的基础,因为没有这些基础的话,你后面那些高级的AI系统,它全部是跑不起来的,那么下一节课呢,我跟同学们讲一些比较有意思的,比如说大家想了解一下。
比如说那个以前DeepMind是怎么去打星际争霸的,对不对,我相信同学们很关心这件事情对吧,那我们在下一节课,争取跟同学们讲一讲这件事情,那么这两节课讲完的话呢,我们相当于用整整一个section。
三节课讲完了gap的系统,然后呢在下下周的时候我们缓一周对吧,让让那个就是课程组稍微回点血,然后我们就进入到后面更有意思的,比如像网络啊这些东西了,怎么样,这就是我们课程组的安排。
到时候我们官网上的话也会及时的更新好,那前面这个解释对吧,解释就是掩饰,我已经解释的太多了,反正也就这么决定了,大家那个有砖头啊,有有有什么那个什么鸡蛋,西红柿就可以砸过来了,好那接下来我们言归正传。
就讲我们AI的基础,那么其实AI大家想想我在做一个AI系统对吧,我我要把我的小人做的特别的厉害,但其实啊你做AI的最基础的东西是什么,你要让这个小人在这个世界里面动起来。
大家想想看我们人在这个世界怎么动呢,我们觉得哦我看一看前面有条路,我就可以走对吧,我看见那个有上门挡着,我就过不去了,对不对,对于我们来讲,这一切都是非常的自然和本能,但实际上在游戏的这个虚拟时间里面。
比如说我们学完了所有的物理physics,我们学完了所有的rendering,我可以把这个世界的几何high five全表达出来,我也学完了所有的animation动画,我能让角色动起来。
但是其实呢这里面这个角色他如果想要有智能,他第一件事情是说我要能在世界怎么去运动,就哪些地方我可以去,哪个地方我不可以去,所以呢这就是AI的第一个foundation,就是navigation系统。
就是导航系统,那么navigation系统的话呢,实际上在游戏中是非常重要的,比如这里面我举了一个刺客信条,奥德萨的这个例子对吧,你可以看到就是当你从A点跑到B点,地上出现了一条导航线。
你就可以双手离开键盘,看着他走了,我记得以前我在大概是56年前吧,我们在这个,我有一次我记得我在看CDC的时候,有个老外设计师跟我讲了一个观点,但是让我非常的震撼,他说所有这个在游戏中有自动寻路的游戏。
都是垃圾网友对吧,他是我们的好的游戏,都是开放世界,都是你自己要探索的,然后呢我当时听完之后,我说哦明白了,懂了对吧,但是当我在那做navigation系统的时候,我觉得我必须要按照垃垃圾网格的做法。
就是说我必须要让AI它能够自动寻路对吧,你可以让玩家不自动寻路,但是我的AI总是要自动寻路的,AI是怎么聪明的知道在这个世界里面,我能找到一条路线的呢,这个事情看上去很简单。
但实际上它有很多的底层系统去支撑着的,那么其实讲这个navigation系统的话呢,你去理解它,它实际上有三个步骤,第一个步骤呢其实也是大家最容易忽略的,就是说这个世界我看见了。
并不代表我的AI能理解他,所以对这个世界的话呢,我们需要有一个表达对吧,你是用一个一个一个的小格子表达它,还你还是用一些点线面表达,他其实这个表达非常的重要,因为你只有了有这样一个表达的时候。
你才能够输入一个诶,我现在站在这儿,我要去哪儿,然后呢我在这个表达上面形成我的路径,Pass finding,那么但是这种寻路呢,他很多说理论上能找到最短的距离。
但实际上这个pass finding的话呢,它生成的结果很多时候是我们叫做ZC,就是说哎一折一折的,这里面的话呢接下来又有更复杂的算法了,就是叫past motion,就是把这个路径变得更加的顺滑。
看起来就像我们人,或者是诶一个小动物走的这个样子,所以说其实几乎所有的寻路系统,它都是由这三个部分构成的好,那我们先从这个怎么表达这个世界开始,那么其实啊这个世界的表达,我觉得最简单的一个表达是什么。
就是workable area,就是大家想一想,我我在这个地图上放了很多的NPC对吧,NPC在这个世界哪个地方可以走,那其实呢这个WORKPAI就是可通行的区域,是AI活动的这个场景,就是他的舞台。
所以这个舞台不是说我的美术把一个关卡,他就有了,不是的,实际上是要这个时候不是artist了,是我们的designer说诶,我们认为这个角色可以在哪儿走,那么它最基础的输出是什么呢。
哎比如说物理的碰撞对吧,你总不想看着NPC没事顺着墙,这就直接上去了,对不对对吧,还有就是说哎,但是呢有的时候你不是说遇到一个台阶,他就挡住了,其实有的NPC他可以还可以爬墙对吧。
它是有个climb的高度,比如说一米以下他都能直接越过去,对不对,那这个时候你要设一个可以爬的高度,还有什么呢,就是说有的地方有些小gap对吧,有些小沟其实NPC如果足够聪明,他可以跳过去的。
你想想知识是很复杂,我再讲一个更复杂的情况,就今天课程中我不会去讲知识,比如说在面前有一条沟,这条沟的话呢,它对于一个不行的NPC来讲,可能是不可通过了,但是如果这个NPC他骑了一匹马马可以冲过去的。
大家想一想,这个时候work for area,它是不是针对不同属性的这个那个NPC的话,是完全不一样的对吧,就像我们说一辆车它能通行的去和一个坦克呢,通行的去和一个士兵的通行区域,其实都是不一样的。
所以大家想想,就是如果想做一款好游戏啊,包括你想让AI跑的漂亮,它其实你们的细节特别的多,那么当然了,就WORKAREA,真的完全由这些基础的物理和行为的原则,决定吗,也不是。
比如说其实有的时候设计师会干什么一件事情,我知道我讲到这儿,很多同学们会觉得哦,我知道了,就是那个我最痛恨的叫空气墙对吧,就是我们玩很多游戏的时候,大家最恨的就是我本来想在这个城里面转转的。
结果你做了很多空气墙,包括我自己也很醉过,我自己做游戏的时候,我也被迫做了很多空气墙,我每次每每自己走到空气墙面前,当那行字告诉我说那个地方我过不去的时候,无论你跟我做什么动画表现,我心里面都很不开心。
对是的,没有办法,现在游戏引擎的话,就是至少在过去吧,他以很多时候很难把空间做的足够大,但是大家也看到了,就是现在做游戏最喜欢讲的一个概念,叫什么叫开放世界对吧,我们希望就是下一代的游戏。
再也没有空气墙了,所以说呢这些东西都是我们的一个期许了,但是就是对于寻路系统来讲的话,它首先要把我在这个地图上,所有能去的区域搞清楚,这个区域就是啊workboard area好。
当你知道了所有可以去的区域,那只是你设计师中间那个感觉,但是你没办法告诉计算机,对不对,计算机说啊,这张地图给你了,那哪些地方可以去哪家,哪些地方不可以去呢,计算机你没办法跟他交流对吧。
那计算机没办法跟他交流,我AI就没办法在上面跑,所以这个时候呢就来了,第二件事情就是你到底是用什么样的一种format,去表达这些workable area,那这里面的话呢其实有这个很多经典的做法。
那这后面我会详细的展开讲,比如最近比较经典的像微point network,就是路点的网络图对吧,包括你用网格的方法去表达,你也可以用这个navigation mesh的方法去表达。
那么就是这个寻路网格,寻路网格我待会给大家去讲这个东西是什么,还有什么呢,就是用A空间上的这个析出的八叉树来表达它,每一种方法呢都有它的优势和劣势,所以当我们去做一个引擎的时候,很多时候。
你可能被迫要支持一种以上的寻路算法,哎这个事情是不是发现做引擎就很麻烦对吧,当然你也可以偷懒说,我大引擎我不管这件事,你全部交给游戏开发组来解决,这件事情行不行呢,其实也是可以的对吧。
早期的引擎很多时候就是个渲染器对吧,他把那个机构管管,把渲染换汇出来,然后这里面AI怎么做,比如说如何确保这个NPC不掉到地下去,全是游戏开发组自己的事,这也是可以的,但是呢基本上来讲。
现代游戏引擎这个寻路的这个表达,是他一个标准的服务之一,好那我们接下来就仔细的去讲,就是说我怎么去用什么样一种格式去表达,这个workable area,怎么样对吧,AI首先把这个这个叫什么。
把这个这个地形搞明白对吧,都很麻烦好这第一种做法是什么呢,就是用露点的网络,其实它也很简单,这个方法其实在早期的游戏引擎里面,用的特别多,就是说有设计师说诶我放很多的关键点对吧,比如说这是一个桥梁。
这是门口,这是一个通道的走廊的两端,好在这些关键点中间呢我会插值很多的过路点,这个有一些算法可以自动生成一些过路点,然后呢包括一些就是说啊,我们可以在中间的话呢还加一些。
就是因为你的WORKERROR有的时候它不是一条线,我们可以再往两边再扩散一点点,这样的话我插入了这些点之后,形成一个网络,这个网络之后呢,当我任何一个这个点从起点到终点的时候,他的做法怎么样了。
首先哎从起点找一条这个路网中最近的一条边,我往那走,就是我要先走到这个路网上去,接下来我就顺着路往走,然后呢等我走到离那个终点最近的时候,我就啊终点也是反向投影,然后呢我去找那个点。
这个这个方法很像什么呢,它非常像这个地铁的网络图对吧,大家发现我要用地铁去交通的时候,是我的方法,我的solution是什么,首先所有的地铁站是不是连成一个个点,点和点之间,它会形成一个网络。
但是比如说我要从我的家去我的工作的地方,比如说我要去学校,那是可以直接对着路网去做吗,其实是不可以的,你首先得找到一个最近的地铁站,对不对,然后接下来顺着这个地铁的线路去走对吧,然后呢,等你走到一个。
就是离你那个就是学校最近的地铁站之后,你出来再从地铁站走到你的学校,所以这里面的话就是传统的路网的方法去生成,那么这个方法呢其实啊很古老,非常的经典,它的好处是什么呢,非常好的实现。
我觉得基本上我们所有的这个学过,computer science的同学对吧,有基础编程能力的同学都会写这个代码,然后呢,就是说它呢即使对它的运行效率也非常的高,因为它可以把一个很复杂的地图。
抽象的表象成一个网络结构,但是它其实一个最大的一个缺点,我认为就是你得艺术家,这个我们的设计师得上手,比如说这个地图经常会变化,但每次都得更新那个路网的图,有的时候做不好的时候,那个还经常就会出现。
路网和真实的那个图跟不上去,所以呢在现代的游戏中的话呢,路网的方法已经越来越少见,还有一点就是路网会产生一个问题,就是说你会发现NPC的行为,他老是会往路的中间去走。
就是你本来有很大的这个work for AI,但是呢除非你的路网的点撒的足够密对吧,但是这里面的话不可能靠手工伞,它可能有别的方法去撒,但是这个会让寻路又会变得特别的麻烦。
那么所以的话呢we point,这个network在早期的要求比较低的游戏里面,会经常用得到,但现在游戏中呢用的其实没有那么多了,但是你如果做一个简单的,比如说这个这个回合式战斗战斗游戏啊。
你这种用路网的方法其实也是可以的好,那么第二种方法呢也是非常自然的,就是诶既然路网把空间抽象成一个个点对吧,就相当于你表达一个人,你只是表达了骨头,他的血肉你表达不来了,那怎么办。
我就用一个个的这个这个grade嘛去表达他,很自然的,对不对,那grade的话大家想到最简单的方法怎么样,就是哎画方格嘛,虽然说你其实也可以用传统,但其实用传统的方法去表达,这种就是细密的传狗去表达。
这个可以寻路的方法,其实我现在非常的少,但是呢有的游戏里面他用那个六边形对吧,比如说像那个大家喜欢的文明文明,你们很多时候就用六边形表达寻路,但是呢我我它的底层我不知道怎么写的。
但是实际上六边形表达的时候在那个数据库里,就是在那个计算机存储去了,其实挺麻烦的,因为用那个square存储的时候,用一个array就可以很很容易成来了对吧,你表达六方形的六边形的。
它每次都要调一个一半一个,反正呢你说奇数行,偶数行,奇数行,偶数行,你稍微要做一个小错位的处理,但是这个我相信总是有办法可以处理的,所以的话呢那grade表达呢,它就非常的简单和直接了,对不对。
那我只要把所有的workable area,我用足够细密的小格子把它一盖上,诶,这个时候我其实就就可以在这个格子里面,去寻找我的路线,那么这个grade的方法呢它其实有很多好处。
它一个最大的好处是什么呢,你会发现就是你有任何的环境的物体,它只要做一个,我们叫其实很像render里面的光栅化,对不对,我就变成了一个个的ZAC的格子,然后我就把它可以通行的区域大致表达出来。
这个有个很大的好处是什么呢,就是我这个世界假设发生了改变,我去更新这个grade,其实非常的简单对吧,大家看一下,这里面我增加了一个障碍物诶,只是寻路网格瞬间就可以发生来了一个变化。
所以这是国内的一个非常大的好处,就是说它可以动态的更新,也非常好实现,而且呢非常好,第八个,其其实在我最早写游戏的时候,我每次都很倾向于把这个寻路用grade去写,太好debug了。
打印出来时间可以看得一清二楚,比如说可通行的用绿色,不可同频的用黄色,对吧啊,用红色,然后呢我走到哪儿,把那个格子标来了,一条线一条就连起来了,特别好debug,但是呢GRA的方法呢。
它其实也有它的问题,第一个问题呢,就是说它的存储空间是比较浪费的,那么包括呢因为它的格子呢,如果你想表达的世界比较密的话,你的格子的尺寸是不是比较小,比如说你很纠结。
说我这个寻路到底是一米乘一米的格子呢,还是这个这个2米乘2米的格子,还是半米乘半米的格子,如果你要表达方圆几十平方公里的地方的话,你会发现那个格子的数量,它本身就已经达到了上千万。
那么这个上千万的这个格子数据啊,你在这里面进行任何的操作,比如说你觉得你是往下走了一格,但实际上如果是上千万个格子的话,大家想想看它在内存中啊,其实是跳了很大的一步。
我记得我在前面讲RANDO时间跟大家讲过texture,对吧,为什么texture如果很大,它的访问效率就很低,不是说大了之后这个数据访问被困难了,实际上是因为一个我们在计算机里存储数据。
是一个线性的存储的,所以呢你如果是个二维的一个map的话,我往下走一格的时候,有可能在内存中跳了很大的一步,所以这个时候会让我们的各种cash很容易miss掉,所以grade的哈做寻路的时候。
无论是数据的访问,包括那么多格子去寻路对吧,一一个一个的寻路,它其实效率是比较低的,那么还有一个问题是什么呢,就是用great的方法,我很难去表达这种层叠结构,比如说举个例子,像死亡搁浅里面对吧。
那个诶你突然架了一座桥,那么桥下面你其实也可以走过去了,对不对,那桥上面你也可以走,那你用grade他就没办法表达,因为你知道这个地方可通行,但是你并不知道你是从桥上走过去的,还是从桥下走过去的。
所以这个时候呢就引入了大名鼎鼎的这个navigation,Mesh,就是寻路网格,这个navigation mesh呢实际上是一个啊,在现代游戏引擎中的话,一比最普遍的一个寻路的方法。
这个可以说是个标配吧,就是说啊基本上我们知道的很多的游戏,就是有一大部分嘛,其实用的就是这个方法,这个方法呢其实想起来非常的简单,就是说我把整个地图上所有可通行的区域。
用一个一个的polygon把它连起来,有的时候用STRANGO,有的时候是用一个那个就是突凸的多边形,把它表达出来,这样的话我在这一个个的凸的多边形中,走来走去,我就能完成寻路。
因为多边形对这个区域的覆盖啊,它不是一个点状或线状的覆盖,刚才讲的v point的方法是它的问题是什么,它对所有可通行的区域,是用一个点状或者现状的表达,对不对。
但是的话呢就是啊navigation mesh的话呢,它是一个面覆盖,所以这上面AI体的行为就可以更自由,比如说我一个一个小兵看见敌人过来了,他可以左右前后这个闪现啊,绕绕后啊,可以呀。
但如果你在微point时间,你会发现那个小兵就像机器人,就这样,哎哎哎哎就在往前就直走了,所以你可以是mesh的话,大家一简单一看,而且它还能解决刚才我讲的那个规则的方法,有很大的问题是什么。
就是说哎我如果这个就是啊,上面有两层结构的话,我只要在上面构建一层拓扑的,这个这个这个就是说连接过去的网,那个网格就可以了,当然了,这里面一旦有这种层叠结构的话呢,在几何学上有个说法,这叫非流行吧。
就当是流行manifold这个词就更有意思了,就是当时在我们寻路网格,大家注意它不是流行,它首先不是封闭的,第二个就是它有那种形状的结构对吧,大家如果学过几何学,就知道那里面讲流行的定义好。
那么其实navigation mesh的话呢,它真的是非常的直观,比如举个例子啊,这里面我看到一个地形是这样,中间你看到有几个凸起的高台,哎我们用navigation mesh生成算法。
它就能生成这些彩色的可通讯区,你会发现就是说上面那些高台,它好像就过不去了,这个其实就是它的很很妙的一个地方,那么negation mesh呢有一个小小的细节,就是说他可以是三角形。
也可以是个polygon对吧,因为就是polygon的话呢,实际上比三角形更省,因为你会发现很多可通行区域啊,它是一个类似一个矩形的区域,那你用三角形表达的时候,有的时候会拉出那种很长很细的三角形。
所以polygon是个好东西,但是呢polygon有个要求,就是当你寻路完之后,它会形成一个polygon的一个corridor,就是一个那个叫多边形走廊,那么这里面每个POLYON呢。
其实我们一个要求就是说它必须是凸的,那为什么是秃的呢,其实下面这个图就看得非常清楚,如果我们这个navigation mesh用的是凹多边形的话,那有可能就是说哎我认为这个A点到B点走过。
学习穿过这个破烂,但实际上它可能穿在这个可通讯区域,之外的地方,因为凹凸变形没有这样的一个属性,其实凸多边形还有一个好处是什么呢,就是你不是形成了一个多边形的走廊吗,大家看这个图上。
其实当我当我知道说这个人从起点到终点,他经过以下的1233号,三个的这个多边形的话,那么每个多边形时间它有且只有一个共享的边,那个边是什么呢,我们一般叫做portal。
就是说这个POR这个词中文怎么翻译呢,叫过道通道对吧,大家知道那个著名的游戏叫portal,对不对,也就相当于是说你从polygon1到polo2的时候,你有一个唯一的PO可以通过。
那么它不会出现第二个,如果凹的话,我就不能保证这件事情了对吧,那么到第三个也是一样的,所以说这样的话其实会让我们很多的寻路算法,包括后面我们会提到就是这个路径,这个smooth就是绕路径。
光光顺化的算法,都有很多地方可以利用到这个属性,所以这也是个很有意思的一个细节好,那么其实呢就是navigation mesh的话呢,其实是一个非常好的,现在也是比较成熟的一个方法。
那么它可以支持在3D的这样一个workbook,的一个space中间可以overlap,而且呢,因为他对很多可通行地区的表达效率很高,因为大家想象一下,比如说我有一个直直的走廊,比如说长100米。
宽50米,如果你用navigation grade,你用great去表达的话,你就算是这个1米1个精度,你想想看我们要用几万个grade呃,呃几千个grade表达它。
但是呢如果你有navigation mesh的话,很可能就是一个polygon,就把它全表达了,所以这样你寻路的速度就会快很多对吧,而且的话呢就是说呃他有很多的这个灵活性。
但是呢navigation mesh其实有两个我觉得比较有意思的,这个可以说是他的一个小小的短板吧,第一个是什么呢,就是说其实navigation mesh的生成是非常复杂的,今天在这节课上的。
我会简单的给大家讲一下,这个东西是怎么生成的,为什么我会跟大家讲这个算法,实际上现在有很多开源的库可以做这件事情,但是我还是希望大家知道它的基本原理,因为真实的在游戏过程中。
你的navision mesh其实你会就是它的生成,你会有很多的干涉的,就是你要确保它能够符合你的游戏的意图,所以的话呢很多时候就是开源库生成的结果,你要进行一些修改,你甚至要调整你们的参数。
所以我觉得你需要知道一些原理,才能让引擎变得更加的自由,那么第二个它的难点是什么呢,这个其实是怎么说呢,这个真的是给他找麻烦,就是说大家想想看,无论刚才我讲的v point的方法。
还是这个grader的方法,还是这个navigation mesh的方法,它都有个缺点是说假设我是一只小鸟雀,在天上飞,或者我是一架直升飞机的AI,是不是完全不能表达了对吧,大家想想看。
就是说比如说我做一个空战游戏对吧,我做一个比如说可以导航的自动机器人的游戏,那可以飞的这个无人机的机器人游戏,其实navigation这个前辈讲的三种方法,它都不能表达,这个当然不能怪它了。
因为你生成的就是一个二维空间的,一个可以通行的区域,你可以想象成我只能做蚂蚁,在贴着地走的情况对吧,那如果你可以飞怎么办呢,哎这里面只是简单提一下,就是说其实呢用这个就是sparse的这个OCTREE。
Walks the space,就是说我对空间进行八叉树的划分,把所有可通行的区域用一个八输出结构,就是八叉树的结构,大家知道就对空间不停的进行这种一分八,一分八,对不对。
但是呢如果这个区域整个都是连通的,那我就用一个很大的这个一个一个WORKO去表达,那如果那个地方是它的边界的话,那我就不断地对它进行细分对吧,大家想象下,这是一个非常简单的一个八叉树结构。
其实我是能够表达一个3D空间的一个课程序,所以今天如果我们要做一个,比如说那个太空空战游戏,或者说一个你操纵的无人机,在一个大峡谷或者大隧道里面,我去这个进行空战模拟的话,包括我的AI有很多行为的话。
其实那个时候你对空间的表达,可能需要用这个技术,比如这里面取的是著名的eve的,这个这个1V1对吧,eve的这个案例,那么义务里面很著名的很多太空的空战的话,它就需要这样的一个表达,当然当然对啊。
这应该不是义务的主体,但是意思就是说,很多时候你对这种空战游戏的话,我们可能需要这样的一个表达,OK但是呢这个space walks treat的话呢,实际上它的算法并没有那么复杂。
就是你就不停的去求教,然后呢如果发现哎,在这个walker里面有其他的这个ACLUDE,那你就对他进行进一步细分,直到它要么就没有了,要么就是它的误差度,小于你给的一个阈值就可以了。
但是这个它的这个这个算法的,一个很大的问题是什么呢,就是说他的这个就是存储的,其实是挺废的,那么另外一个的话呢,就是说啊在上面寻路的话其实也挺麻烦的,OK在这里我们就不展开了,好。
那么其实当我们对这个世界啊,有了一个表达之后,下一步是什么呢,诶我没在上面寻路了,那就你们刚才讲了,说我们又有grade的表达对吧,我们又有这个v point的表达。
我们又有这个navigation mesh的表达,但是呢其实大家会发现一件事情,就是说无论你用什么一种方法,这三种表达都有共同的点。
其实他无论是navigation manimesh里面的每一个POLYG,还是grade里面每个grade的点,你把它的中心点放在这,你会发现它和它周边的连接关系,其实会形成一个graph。
所谓的寻路呢其实就是在graph上去找一条路径,就是我知道了一个起点,我知道了一个终点,然后我知道他每个连接关系我要走多远对吧,比如说navigation mesh里面。
我从a polygon走到BPOLC,加装简单一点都都是从他们的中心去走,那我其实可以知道它的距离对吧,那好那这样的话我们事实上就可以算出来,说这个他们的那个路上的这个距离是多少。
那所谓的寻路问题是不是就解决两个问题,第一个问题就是说我要找到一条可通达的道路,让我从起点能够走到终点对吧,这是一个must的一个问题,就必须是准确的告诉我yes和NO,那么第二个问题是什么呢。
我要尽可能的少走弯路,少走回头路,对不对,我要尽可能的就是找到一条相对近的路,那为什么这里我要讲一个词叫尽可能呢,哎这个就是游戏设计或者游戏引擎,一个很有意思的一个点,就是说在数学上。
我们很多时候会追求一个叫最优解对吧,但是呢在游戏的这样一个交互事件里面,很多时候我们希望它的行为看上去正常,那么这个时候也许一个看似合理的解,就可以被接受,这就是我后面会讲到它的具体的算法的时候。
会会讲到这个点,那么首先的话这个问题啊,他就是个标在一个标准的叫做这个就是双向的,就是没有方向性的,这样的一个有环的这样一个图上面,我怎么去寻找这个一个寻路问题,这个问题实际上是一个非常的这个研究的。
非常通透的一个问题,这里面的话呢就要这个以,它本质上是个搜索问题,那么我们在学数据结构的时候,我们学搜索的话呢,其实有两大这个门派对吧,一个叫做深度搜索,对不对,深度搜索就是找到一个时间点,一头扎下去。
直到我扎到这个底了,发现我还没有到我的目的地,怎么办,我退回来一层层再往上一层倒着地归来退,就是要深度优先,我们以前我记得我在大学学数据结构的时候,会告诉我说深度硬件是什么,是要用时间换空间。
那接下来另外一个跟他这个对应的什么,就是广度优先对吧,就是我就不停的扩散开来,把我的每一个时间点全变了,让时间点再往他的孙子节点去便利,这样的话我用空间换时间,但其实我觉得这里面也是有问题的。
好像我没觉得时间一定要长,但是呢广度搜索呢,它可以就是说啊,感觉上能够尽快的找到那个最优解,所以的话呢就是,所以说其实整个搜索的核心就是两个算法,但是这两个算法能不能解决,刚才我提出的那个问题呢。
其实是可以解决的,但这两个方法都有一个共同的特点,就是他们其实是比较费,在很多时候我们会找出很多没有必要的路径,而且的话呢,就是说我们要要找那个最优路径的时候,其实广度优先算法是可以保证的对吧。
但是的话呢深度优先算法,因为它可以把所有的路径全找了一遍,那么深度优先的话呢,其实你先找到了一条通路,并不代表你能走过去,如果你整个便利一遍的话呢,你才能够比较受很多所有可行的通路,中间最短的这条路。
那这个就是说所有的available,就是说所有符合我要求的路径的比较,本身无论是深度优先算法还是广度优先算法,本身都是比较废的,所以这个时候呢,就引入了这个大名鼎鼎的这个迪杰斯塔算法,对吧。
这个这个老哥的还是非常厉害的,是我们的算法大神吧,就是我都不知道他自己怎么会想到,这样一个算法,就是说哎对于这样的一个就是A点到B点,最短路径的方法,他提出了一个便利方法,是非常有意思的。
就是说他首先把所有从A点出发的,这个其他的零点,都就是到那个就是A点的距离都设成无穷大,然后呢这个算法讲起来就比较比较细了,但今天我在这里不展开它的核心的想法,我给大家复述一遍。
就是说他先把所有的顶点看成是没有访问过的,然后呢从起点开始把他所有的邻居全访问一遍,然后呢跟邻居看,就是这个邻居如果以前被人访问过,他会告诉那个上面会存储,说哎我当前知道从A点到那个我的这个。
比如说我们到C点的话,我的目前找到的最短的距离是多少,他如果只要有人反问,他肯定就不是无穷大,对不对,那么他就比较说我从我新的这个点出发的话,到你的距离是不会变得更短一点,如果是更短好。
那我就把你那个路径只更新一下,然后呢把你的这个previous引擎的上节点指向我,然后呢,这个时候我的这个点我做完所有的扩张之后,我现在在所有的没有被访问过的点里面,去找一个这个距离值最小的那个点。
再从那个点开始去访问所有的点,这样的话以此反复循环反复循环,直到我把这个destination那个点给他做了一次,这个一直到那个destiny那个点,destiny点也要做一次这个explore。
然后我就知道了,就是从十几点到终点之间的最短距离,这个算法其实很有意思的,就是我我今天在这个备课的时候呃,我自己看这个伪代码是非常好理解的,但是我在每一次尝试去讲它的时候,我就发现讲起来很抽象。
但是这个算法呢你仔细看这个,我给大家看一段视频啊,这个视频可能会讲的更清楚,就比如这时候你从A点出发,然后呢,他把他邻居DC点和F点都比较了一下距离,然后他这个时候发现的最小的,这个没有访问的点是F点。
它是二,我再从F点出发,我去把所有的我能访问的凝聚点全访问一遍,这个时候呢我就会把我的A0聚点G点对吧,零基点这个B点,你看B点其实是终点,但因为B点的时候没有被访问过,所以呢B点还不能够认为。
这个这条路径是最短的路径,然后呢我再比较下C点,C点也没有问题,这时候再比较一下,就是所有这些剩下的点中的话,距离最近的是C好,从C点再出发,然后呢我再去找我C点周边的点,你会发现哎我就知道到地点。
目前我能找到最短的距离是7~1点,我会发现从C过去一会儿是更近的,我就呃我就会说我就意识到,就是说其实从C到E是E跟进的一条路,这时候我就把E调成四了对吧,这时候我其实找到了一条路径,就是到E来讲的话。
最近的是四,然后呢这个时候我发现距离最近的是这个E,然后啊然后我从E出发的话,我发现B这时候可以更新到六,好,没问题好,这时候我的终点呢其实是B,我从B点再去一看的话。
发现这个距离其实已经没有什么再扩张了,这个时候我就知道我既找到了我的终点,同时呢这个算法它可以保证,就是说你找到的距离它是最短的距离,但这个算法呢它有一个小小的一个注意点。
就是说我们每一次去访问的时候啊,我们存的都只是他到这个,就是说起点我们知道的最短的距离,其实你并不知道是来自于哪一个方向的,这个距离,这个路径是最短的。
所以呢你要存一个东西叫previous vertex,就是说我这个时候我F上记录的,我最小到起点的距离是二,但是呢我的前导节点是A对吧,那么E我到A的最短距离呢是四,但是呢我的前导节点是是C对吧。
你顺着这个就这个这个这个有向那个无,应该是个有向无环图吧,那么你其实就能规划出整个路径去来了,这里面是这个算法的一个小小的细节,其实这个算法的话并不难,大家对着这个动画。
其实很快就能理解到他的这个这个方法,甚至你自己可以写一段代码就可以实现了,这是我们在学图论的时候对吧,学计算机数据结构的时候,一个最经典的一个寻找最短距离的算法,这个方法好不好好呀。
而且他肯定是比盲目的深度优先算法,和广东学习算法的话效率更高一点,但是诶这里面有一个有意思的问题就来了,就是说对于我们的游戏这个case里面的话,我们并不需要找到那个理论上的最优解,有的时候。
甚至你会觉得那个最优解看上去不真实,比如说我把你放到一个城市里面,我给你一个起点,给你一个终点,你觉得我们人会像计算机一样,很精准的算出他所有的作业路径吗,其实我们人做不到的对吧。
我们只能是说按照大致那个方向去走,这个时候呢,我们其实脑子中一直运转的是什么呢,是个启发式算法对吧,就大家如果以前在学这个数据结构的时候,会经常知道这种叫启发式算法的用处,那么其实这里面的话呢。
就引入了大名鼎鼎的A星算法对吧,那么A星算法呢它的特点是什么呢,它其实啊,它就是基本上是基于这个低阶的算法去做的,但是呢它引入了一个启发函数,就是说当我走到一个点的时候。
我不仅考虑我过去花掉了多少的这个路径成本,我还猜一下说从我现在这个位置到我的目标点,这个还要走多少距离,但因为你那边,你从你现在出发到你的这个destination,其实这个路径你并没有搜索过。
所以你你你算不出来,对不对,怎么办,你是猜诶,这里面这个启发函数就非常重要了,再举个就是,所以说所以说呢刚才我们在第一时段上网里面,我不是算了一个cost吗。
那实际上这个cost就是以前是用那个经典算法,算出来的那个实际距离之后,再加上一个启发的距离,所以这两个距离合在一起就形成了每一个点,就是比如说我现在找到了,比如说啊有六个到七个。
我们的顶点还没有被export过,那么当然它的值呢都不是无穷大,那我会优先选择那个,就是现在已经走了距离,再加上我的这个我估计要走的距离,这个更这个就夹在一起,最近的那个点由从他那个点开始优先搜索。
而且呢它A星还有一个特点是说,我只要走到了目的点,哥们儿我就不干了,不会像那个,就是就是不会像那个那个就是低速大,传统算法是说诶我虽然explore到了我的目的点,但是呢我只要啊。
我虽然已经reach到了这个目的点,但是我只要那个点没有被这个explore,那我就还是要继续搜索这个算法,它就完美的在这中间找了个平衡点,那么其实它非常的简单和直觉,举个例子。
比如说在一个网格状的这样的一个世界里,比如像曼哈顿的地图这种这种情况下,那启发式算法的话,那就用最简单的这个棋盘格的距离对吧,从我现在走到这一点的XY对吧,到我的目标点的XY时间,算X1减X2的绝对值。
加Y1减Y2的绝对值就是它的距离,这个距离一定是理论上最短的距离了,不会跟进了,对不对,那么如果你是一个navigation mesh呢,这个问题就比较复杂了。
因为在navigation mesh里面的话,我想是虚寻路,出来了一个叫polygon的走廊的时候,这个大家去想一想,其实我现在找到了,说我要从这个我要从A点到B点对吧。
我这个起始点终止点我已经走过这些POLA,但我的路径到底是怎么通过呢,大家最直接的做法是什么呀,我会把他的那个所有polygon的终点,中心点连起来,对不对,但是呢大家会意识到一件事情,就是说。
当你把所有polygon的中心点连起来的时候,你会出现一个情况,就是它的连接线可能会超出那个破灵感的距离,所以在工业界里面大家会普遍用什么呢,用polygon和polygon之间的那个棉公共边。
就是刚才我讲的那个什么portal,我把所有POR的终点诶连接起来,形成了一条路径,那这个路径连到一起的距离是什么呢,诶就是我当前的这个我的成本是多少,好,那接下来说我当前到了这个POLYGA。
我到了这个点了,那我从这个点出发,我再到那个目的点有多远呢,大家想想,如果刚才说grade都要去算一个XY的偏移对吧,那在这个这个navigation mesh上我怎么算的诶。
这个时候就有一个很流氓的算法了对吧,首先给它取了一个非常高大上的名字,这时候他的启发算法就是一个欧拉距离,就是我假设你能直线过去,大家发现这这是不是很不讲道理对吧,你看刚才我举的两个例子。
是你搜索到这两个端点,然后呢你算那个它的H的时候,你直接用两点之间的连线了,对这个算法是很粗暴,但是呢确实因为在navigation match里面,如果没有阻碍物的话。
你真的就是走直线就可以直接走过去了,所以呢因为对未来的事情我并不知道,所以我只能做这样的一个假设,因为它本身就是个启发式的算法,但是呢他确实会引导你尽可能的去避免,比如像这个图上的例子对吧。
上面那个点和下面那个点很明显的,就是下面那个点他已经走过的路径,再加上我启发算的那个路径的话,他应该会进一点,对不对,所以说你会优先的去走,从下面那个点去出发,去往外去搜索,大家凭直觉去感觉一下。
是不是这个A星算法收敛的速度会更快,所以这也是为什么,就是在这个就是现在的这个游戏中间啊,A星算法是一个最经典的算法,就是无论你是用grade,你还是用navigation mesh。
你还是用其他的表达对吧,包括微point的表达,其实A星算法都是大家最普遍采用的一个算法,那么这里面的话呢,就是有一个,我们用direction Mac做了一个小小的例子。
大家可以看到就是说诶我从起点出发的时候,我会尝试所有的neighbor的这样的一个POLA,然后呢我去找那条感觉方向最正的,然后我再继续往前捅,那这样的话其实你就会很快的逼近你的几点。
而且在游戏的这个场景中啊,很多时候A星它真的几乎让你没有展开,没有迭代的一根线,直接就冲向目的地去了,因为在游戏中很多时候你从起点特别是AI吧,很多时候从A点到B点的时候呢,它们中间没有障碍物的。
大家想想看,有障碍的情况其实不多的对吧,所以的话呢,这个时候A星算法的效率,会远远的超过那些经典的算法,这也是让大家理解,就是说游戏引擎它在做算法的时候。
他永远是在效率和结构时间找到一个balance好,那么其实呢这里面讲一个一个很有意思的,一个别人的经验承担这个这个经验,说实话啊,我自己用下来,我感觉是我用的经典的算法都还差不多,但是呢有人分析过。
他说呢就是说如果A星算法,你假设对这个就是啊heuristic,就是对未来这个距离的估计,估计的过低的时候呢,你的这个算法的迭代速度的,会这个就是说比较慢,但是呢你很更倾向于找到它的最短路径。
如果你对未来的这个古迹呢趋向保守,就说我总是要走很远的路的话呢,这时候迭代的速度会比较快一点,但是呢他的这个就是说,你不太容易找到最短的路径,但这个结论说句实话。
我没有看到任何的scientific的证明,所以说呢这个地方只给大家做个分享,这也是就是说,也就是说,其实大家在我们知识的游戏引擎时间中的话,我觉得用刚才我讲的那两个方法基本是够用的。
也能得到你想要的结果好,所以这是寻路的最核心的机制,想到这儿的话,大家基本上就形成一个闭环了对吧,我知道怎么去构建一个great v point的那个network。
或者是一个navigation max,然后呢我用A星算法,我基本上就能解决,我在这个课程一开始抛出的问题,就是我一个小机器人,在这个世界里面到底该怎么动,我是知道了诶,这里面的话呢我就这个再讲。
他的下一步就是你觉得你找到了一条路径了,对不对,这个机器人动起来的时候会让你特别在great上,这个机器人给你的感觉是什么,就是ZIGZAC,就是这样呃呃呃就是就是大家想想看,如果他沿着七哥走。
是不是这样的,那么如果沿着这个navigation match的中线走呢,它是不是好一点的,会稍微好一点,但是大家看这时候行车的路线,你是不会觉得有点怪,对不对,明明我们人如果要从A点。
从那个起始点到终点的时候,我们看到那个corner,我们会贴着那个那个拐角转过去,对不对,但是你会发现那个机器人很笨,他会往前走那么一点点,突然转了一个将近90度的弯,再转上去。
这个时候你觉得他是不是很像机器人,所以这个时候呢就要引入到我们的寻路的,第三个也是很重要的一趴,叫什么呢,叫pass mooth,就是让那个机器人用起来走起来,好像是它就像一个弦一样,我把它绷紧。
就是把很多无效的运动尽可能的把它给踢掉,那么这个算法pass moon怎么做呢,其实无论是grade还是这个negation match,其实他用的方法都有个很经典的算法,就是那个final算法。
final算法,final这个词是什么意思啊,叫烟囱算法对吧,这个这个名字非常有意思,很形象,但是算法其实本身比较复杂,有很多的细节,那我这边呢跟大家去讲一个,就是它最简基础的原理是什么。
就是说其实呢你当你在你的起点的时候,你看像你下一个连接的那个这个那个POLYGA,或者是grade都没问题,你们肯定有一条公共的那个portal那个边,那你用那个公共的边呢,你就形成了一个扇形区域。
对不对,好,你就去看说诶,我下一个polygon,是不是完全的在我的上行区域的包围里面,如果是yes的话,说明什么问题,说明就是说从我这个点出发呀,我一定要找一条捷径去逼近我下一个坡头,否则的话呢。
如果我沿着这个边走,或者沿中间走这个路径是不是最优的,这个时候呢你就开始去看,说诶我我这个port之后呢,因为你的polly can corrido,就是那个多边形走廊里面它是有顺序的嘛。
1234567对吧,所以PTO其实有顺序的,你现在看PO1啊,我形成了一个扇形,对不对,如果port1形成的扇形,把这个POLLY感二全包括进去了,那好我就看port2,那坡道的话呢。
你就左右的尽可能贴近它,就这里面的数学上描述起来比较复杂,但仔细给大家从这个图上看出来,就是说我把这个扇形的区域变得更窄了,所以就意味着什么呢,就是从我的这个点出发的话。
他一定是走那个上行这个阴影区域的一条路径,肯定是最最直接的,对不对,好,我们再去比较说,通过这个pola portal2去收缩了这个烟囱之后,这个新的这个扇形的烟囱,它能不能覆盖我的POLYGM3。
那么这个时候你显然会发现诶,好像我已经覆盖不了了,那这时候就说明什么呢,说明你已经要拐弯了,因为你如果不用拐弯的话,那你可以直接再往下走好,这个时候我们就要在这个两个夹片中,选一条边去贴近。
那这个方法其实也很简单,就是说我们去看这个pol3的话,到底在我这个烟囱的左侧还是右侧,它注意这里面,因为你是凸凸变形,它只可能在你的左侧右侧,比如像这个case里面,它在我的这个左侧。
所以你会发现我沿着绿色的边走,它的距离是最近的大,所以这个算法大家如果看算法描述,会非常的复杂,但是呢用几何学算法去理解它的话,就会比较直观一点,其实呢这个时候你就可以从新的点出发,继续往下走。
那直到呢你这个时候,比如说你现在当前的这个点,你又形成了一个新的这个漏斗,这个烟囱你发现终点就在你的烟囱里面的话,这个时候说明什么呢,说明你已经不用care,你这个时候还要经过多少个polygon对吧。
你可以直接在你当前的这个拐角点,连一条直线到那个终点去,那你这条路径就是你能找到的最优路径,所以大家看在这个例子里面的话,你要经过多少个polygon对吧,122 3456789十。
将近十几个POLYG,但是你会发现当我经过了pass moon之后,我的整个路线只拐了一次弯,我就可以直奔我的终点,所以这就是final算法的一个很精髓的东西对吧,很有意思,这个写起来的话呢。
实际上啊不像我讲的这么简单,是特别容易写出各种各样的bug的,我再给你们讲一个比较有意思的东西,是这样的,其实我们在给大家画这个,final算法的示意图的时候,很多时候我们画的是个二维的对吧。
大家看得很清很清楚,对不对,但是大家记得我们讲navigation mesh,一个很大的好处是什么呢,诶它能表达三维空间的top结构对吧,那么所以呢这个算法。
当你真的在游戏引擎的这个DEBUSHMASH用的时候,你是需要作为一点小小的优化和改进的,因为那个这个时候,这些三角就之前navigation的polygon的话,它们实际上是不共面的,对不对。
那这个具体怎么展开,怎么预算的话,同学们如果有兴趣的话,可以去研究,但是呢基本这个大的方向,这个大的方法没有什么太大问题,它实际上我们是要把那个东西给拍平的,那这个具体具体算法的话,同学们可以推演。
大家可以先从2D的写起来,这个算法其实蛮有意思的,就是你写完之后啊,你一步步的假设你会简单的open jdx,把它VISU来了,你感觉就会非常有意思,而且很像你的小机器人走一步。
然后呢你面前有个小雷达射线去扫描,发现我左边有个墙,有个拐角,右边有个拐角好,我开始选择贴一边去拐,然后最后我寻寻出了我这条路,所以虽然我今天给大家讲的是,一些非常数学的一些东西啊。
但其实它已经越来越隐隐的逼近于,我们要这个讲的这个就是我们的这个游戏里面,NPC的一个智能行为,因为整个智能行为的基础,其实是基于这些数学的表达好,那接下来呢就跟大家讲一点点高能的东西了,对吧。
我们其实从rendering结束之后,是我我的印象中,好像没有讲过特别复杂的数学问题哦,好像在讲物理的时候,讲了一些比较复杂的数学问题,那这个地方呢会讲一点点的,这个比较复杂的问题。
就是说我给你做了一张地图,我们做了一张地图,它的navigation mesh到底是怎么生成的对吧,比如说像我们的游戏团队,就今天我讲的这节课的话,其实我觉得不只是对于我们的这个就是游戏的。
这个就是程序写游戏引擎的这个技术的同学,其实对于很多策划同学都是很有帮助的,那么就是大家会很奇怪,就是为什么我们很多游戏里面有很多的bug对吧,比如说你经常可以看到一些机器人,一些NPC或者一些bot。
我们叫bot就是智能体傻在那了,其实因为navigation的mesh生成这件事情,其实是很复杂的,因为当我们的艺术家在构建这个世界的时候,他看的是美,看到了很多的这个视觉啊,转转角对吧。
但是交给计算机的时候,我怎么样呢,生成这个navigation match,那么对于一个小的关卡,当然我们可以说我们的地策划,自己去拉这个网格对吧,其实在最早期的引擎的时候。
真的会让设计师在美术的帮帮助下,在那个3D max maya里面,把这个寻路网给拉出来了,但是呢,现代游戏引擎基本上都是用自动化方法生成,那么最著名那个开源库叫什么叫recast,这是一个啊。
我如果没记错,是个MIT的一个同学自己写的,这个这个库其实在行业里还挺有名的,那么它的基础是怎么工作呢,为什么,其实它分成这么几步,第一步呢,他把整个世界先vocalization变成体式化了。
然后这个时候呢,你整个世界变成了,像一个MINECRAFT这样的世界的时候,我的预算才开始变得可以进行下去,那这个时候呢它的第二步是什么呢,就是我要在这上面标记出我的NPC。
或者我的bot能够通行的区域,就像这个图上面的,你会发现我把空把这个世界提速化之后,那些蓝色的WORKO就表示我可以workable error,那这个workboard error怎么算出来了。
其实就像我们前面讲的原理,就是说哎它相邻的那个worker,时间的这个垂直的距离不要相差过大,比如说你向上向上爬坡的时候,它有个叫maximum slope,就是说你的这个坡度不能太大。
大家还记得我们在讲物理的时候,讲那个control的时候讲过,就是说当我们控制自己的角色爬坡的时候,它有一个叫最大的一个爬坡角度对吧,有的时候你会设置成60度,有的时间设置成45度对吧。
其实各种角度你都可以设置,但是呢这个maximum slope又会在寻路中有作用,因为寻路中我要知道哪些地方是峭壁,我上不去,哪些地方,中间它又断开来了,那这样的话。
我们把这些所有的WORKPA是给给它标记出来,而这个时候的话呢哎有个很有意思的算法了,就是我们会把这些所有的WORKBOARD的这些WORKO找到,所有的age worko,什么叫age worko呢。
就是说它不是上下左右都有地方可以到达,它中间只要有一个邻居,我是不可到达的,那好,那我就认为这个H那个H是不是很容易就DK,这其实叫age detection对吧。
我把很多都在边缘的这些vol给给识别出来了,然后呢,我在这里面的每个WORKO去生成一个什么呢,生成一个叫distance field,distance field这个概念,我在前面好像讲过好多次对吧。
这个其实是在现代游戏引擎里面,非常重要的一个概念,无论是做rendering对吧,你做这个啊这个这个这个animation,包括做这个particle对吧,包括今天我们讲了这个做这个这个AI寻路。
其实包括一些AI的行为,这个东西都是非常有用的一个概念,他这个想法很简单,就是我在里面每一个WORKO,去找离他最近的那个A级WORKO,就是最最近的边,那么你会发现就是说越靠近中心区域的话。
那个地方它的距离是不是越大对吧,那好我们呢就用一个很简单的一个洪水算法,就是我把这些所有的local的这个距离,最远的点找到,以它为种子,就有点像我们之前讲的warning算法一样的,往外扩散。
这样扩散扩散,大家在一起交叠的时候,是不是就完成了对所有worker area的一个空间,的一个婉儿,你划分了对吧,这个算法其实你会发现,就是说所有的算法它其实都是通的诶,这个时候呢你就可以得到了。
这样的一个对空间的划分,大家看来了,这个是不是很很很那个很自然,但实际上他好呢,这里面其实还有一个小小的细节,就是说当我发现我这个在扩散的时候,诶,如果他的有一个WALKO和我的那个WORKO。
时间的话呢,它出现了flipping,什么意思呢,就是说啊我转着转着转着,我自己转到自己的下面去的时候,这个时候我要对它进行打断,其实这里面还有很多更细节的一些处理啊,但简单来讲的话呢。
我们会尽可能的把这个空间分成一块一块的,看起来像凸的这个区域,然后呢,我们基于它生成一个个的POLYGTO的POLA,但这个从这一个个的这个就WORKO的区域,变成这个POLYON了。
其实这个算法是比较复杂的,我本来我们课程中准备了,后来想想哎呀,这节我们的104的课程,核心不是讲那种硬核的数学算法,所以我们就不展开,但是同学们知道,就是说如果这个东西写起来的话,会稍微有点小麻烦。
但是呢很感谢有开源库,帮我们把这个问题给解决掉,所以说大家可以看到就是说我们给你一个地图,我们很人眼,可以很快的在几秒钟之内找到,所有的这个就是workable area,并且我们可以快速的就是把这些。
用polygon的方法把它大概表达出来,但是交给计算机做的时候,这里面有很多部算法一步步的做,而且每一步啊它其实都很可能出错,所以其实大家在做这个navigation mesh的时候。
很多引擎它是作为一个标准操作,但是呢我们一般都会讲,就是说你还是要提供一个工具,让设计师能够修复它,能够万一里面有些错误,但这里面有个更有意思的一个问题是什么呢,就是说哎当设计师修好了一个寻路之后。
当美术这时候又说哎,我对地图进行了一个小小的更新,你要把这个整个navigation mesh要重新生成一遍,对不对,那好了呀,那你上次那个修复就整个彻底失效了。
这其实也是navigation mesh一个很大的一个挑战,就是说他的每一次generate他没有办法继承,他只能够就是说重新生成一遍,那这个时候如果人上手的任何一次,这个修复的话都会无效。
所以呢这也是这个方法,它的一个很大的一个周边,OK好,那你其实这个这个方法,其实navigation mesh是一个非常有用的方法,可以大家可以认为就是说,在现在的很多游戏里面,这就是个标配。
而且这个方法为什么还好用,它还有一点,就是说你不是生成了一个个的三角形吗,其实我可以对这个region进行一些更多的控制,比如说我可以把不同地表材质的这个region,可以区分开,比如这是个草地对吧。
这是一条河流,或者这是一个沙地,那我可以在上面加上一些,polygon的一个flag,就是说我可以打上标签,这样的话走上去我可以让它产生不同的生效,不同的这个粒子效果,甚至我寻路的时候。
我可以选择什么优先,比如说啊,我假设这个水陆两栖的这样的一个一个,一个一个生物对吧,那我可以直接沿着河就直接过去了,走直线,假设呢我只能在路上走的生物,我寻路的时候,我可以把那些所有表达河道的。
就或者浅河滩的那个区域,把它直接变那个区域变成不可不可通行,所以说呢其实在真实的游戏里面的话,一般设计师都会去标记很多的polon flag,但这个flag的话其实就比较复杂了。
很多时候我们不会直接让设计师,在生成的navigation mesh上标,我们一般会让它在地图上标,标好之后呢,我们会把它映射到这个navigation mesh上去,那么还有一个用处是什么呢。
还有一个很有趣的特点,就是说当我给你一个,比如说这个几平方公里大的区域,我们生成了navigation mesh,对不对,那其实在游戏中啊,很多时候场景的寻路会发生动态的变化,比如说有些墙体的倒塌。
比如说有一些大型的这个车辆挡住了去路对吧,那所以说我就要对这个寻路进行更新了,那大家想想看我的游戏在跑对吧,我把整个刚才那么一整套复杂的算法再算一遍,那是不是特别的费。
而这个时候呢其实有一个方法叫tie,什么意思,就是说我们把空间分成一个个的,比如说64米乘64米,或者是128米乘128米,一个个的快,我的寻路呢是在这个快一面去生成,这样的话。
当有一个物体他要去改变寻路的时候,我们只需要把这个太重算一遍,但这个讲起来很简单,实际上它处理起来有很多很细节的东西,比如说我在这个套里面生成的navigation的这个那个。
mesh和另外一个tan AI mesh的话,它在边缘上的顶点是要对齐的,所以在那个很多的这样一个,就是看这个就是说生成的vacation match的库里面的话,他都要处理这件事情。
但这件事情的话呢其实真的非常有用,就是说对于很多现在的3A游戏来讲的话,它的场景的互动性是要求很高的,比如像这里面这个这个就是死亡搁浅里面对吧,那当我这样111个巨型车放在那的时候。
其实很多AI的行为就要发生变化,为什么他的寻路那个地方就要被更新掉对吧,大家看看,好像你平时在游戏中学的一个,很简单的一件事情,真的到我们的引擎这一侧的时候,就会变得非常的复杂对吧。
刚才那套算法如果有同学不怕高能的话,也可以自己去写一遍,我我的判断,反正就是说你的第一遍写出来,一般是很难直接work的。
要调很久,那么最后一个呢navigation mesh呢,还有一个很重要的细节,大家也要注意,就是说前面我们讲的所有的方法,它都是自动算法,它实际上不能表达游戏里面真实的复杂性,举个例子。
比如说啊在游戏里面我们有很多地方对吧,我们专门设置了一个攀爬点,可以让NPC可以去爬的,或者说我们设置那种叫teleport的点,比如像死亡搁浅里面对吧,我们找到了那个电线杆对吧,爬上去了哇。
顺着电线杆一滑很爽,对不对,很刺激,但这个的话呢你用自动化的navigation Mac生成算法,它是它是没办法处理的,这个时候就需要人上手,可能在一些POLYON里面。
我们建立一些就是手动的连接点和连接线,这就会让整个寻路变得更加的复杂,这就是啊,所以的话呢,我这里面为什么跟大家讲这样的东西,就是说当我们做一个实战型的一个,游戏引擎的时候。
我们很可能不能直接用它最简单的,最基础的算法,我们的系统一定要支持这种开发性,这样才能适应一个真实的结构,我们叫做商业级游戏的复杂度,这就是我们一直在讲,就是什么叫游戏引擎。
就是说呃你必须要能支撑一个商业级的游戏,你呢而且还相对比较复杂的游戏啊,这个时候这个引擎才是基本上是能work的,就是基本体系是完备的,所以讲到这的话,我们的navigation基本上就讲清楚了。
大家发现没有,你看从我们开始讲到现在,差不多讲了将近有啊,50分钟左右,就是讲到这儿,我们的小机器人,还只是知道在这个世界是怎么行动啊,那具体我我的目的是什么对吧,为什么要这么行动,现在还没有讲。
16.游戏引擎Gameplay玩法系统:基础AI (Part 2) | GAMES104-现代游戏引擎:从入门到实践 - P1 - GAMES-Webinar - BV1r34y1J7Sg
那这个巡目讲完之后,接下来就讲叫steering,Steering,这个东西很难呃,我他他表达的是什么概念呢,我觉得讲这个词很抽象,其实他讲的实际中是非常的简单,就是我们刚才在寻路中。
我们不是找到了一条路径吗,在路径好像一般来讲,我们能找到最优的那个路径,对不对,但是说如果今天我给大家一辆车的时候,他真的能够严格的按照,我们的自由路径去行走吗,其实是不可能的,对不对。
大家知道车它有一个加速度减速度,它有个最大加速度,最大减速对吧,它不能瞬移,第二个是什么呢,它有个转弯半径,它不能瞬间的改变它的角度,所以我们在路径寻路上找到了一条直线。
但是这个车如果车头朝另外一个方向的话,它其实是沿着一条弧线走过来的,诶这个地方就跟大家讲一个很有意思的问题了,就同学们有没有发现我们在玩很多游戏的时候,你会发现那个就是有些NPC,特别是载具类的。
这个NPC的话会卡死在一些点,其实这种卡死很多时候都是这个原因导致,就是我寻不寻的,也是严格的在WORKBAREA,但是呢有的时候因为我要加入这种诶,更加符合物理实际的这样一个STERY系统的时候。
他可能就走到了一个以前没有规划的区域,那个区域如果很不幸啊,比如说我的这个比如说物理模拟的某一帧tick,它不长大了那么一点点,他就呆在了一个那个unworkable area之后。
接下来所有的曲目对他来讲就失效了,有的时候那个经常你会发现有些角,比如说一个一个角色,它被卡死在一个corner里面,他在里面就会反复的抖动,来回转来回转,但不管怎么转。
它的steering都转不过来对吧,但现在游戏中这种问题已经越来越少了,但是呢其实在就是五到10年前的游戏中,大家经常可以用这种方法去玩弄这个NPC好,那么这个steering的话呢,实际上啊很直觉。
它实际上呢,基本上我们把它归类归类成几个几大行为,一个是second fly,就是说哎追或者是逃对吧,第二个是什么呢,叫做velocity alignment。
就是说我我把velocity那个那个那个matching,就是我要把速度对齐,这个C开的福利,大家很好理解对吧,就是我有个目标点,我去追他,或者说有敌人追,我要总要逃,总是背着他逃跑,对不对。
这个好像很直觉对吧,那么第二种,这个就是说为什么叫velocity,这个matching的这件事情很重要的,大家发现没有,就是如果我一个东西,它是一个加速和减速的过程的话,我知道我的起始点。
我知道我的目标目标点,但我知道我从知识点出发的时候,我是加速速度越来越快,越来越快,但是我到目标点的时候是什么,我得去减速,而且呢当我减速到零的时候,最好你恰好停在了你的目标点。
大家想想这件事情在数学上简单嘛,其实数学上你是要处理一下的对吧,这个这个地方如果是一条直线还可以,但如果是一个弧线的运动呢,是不是更复杂,这个东西特别让我想起,以前我我我以前在看那个航天的时候嘛。
就是那时候我才懂一个道理,比如说今天我们要发射一个,比如说我们的火星探测器发射到火星上,不是说你的火箭足够大足够猛,速度足够快,我把这个发射器送上去就了事了,其实大家知道。
就是当一个就是飞行器到了火星之后,他最难的一件事情是什么呢,那个时候我我我上面已经没有多少燃料了,这个时候我还要浪费我宝贵的燃料,开始减速,要把我的速度变得跟火星的那个绕的那个太阳,公转的速度差不多。
当然我我绕着火星自转对吧,我还要保证一定的这个这个不掉下来那个速度,所以这件事情其实是非常非常的难,其实这里面的计算机很复杂,但是它非常像我们的velocity matching的问题。
就是说你把这个点送到之后,你还要确保你到了目的地的时候,你的速度几乎跟它几乎是一样的,但这时候我的目标点是静止的,对不对,如果我的目标点是动的呢,对不对,他自身就在动,那我要追上他的时候。
哎最后我的速度跟他要一致,大家想想这个问题是不是有点意思了,这个就是最后一个就是我的alignment,就是我保证我的这个朝向一致,这也是很正常的对吧,比大家想象一个鱼群对不对。
头鱼在那边有其他的鱼都会自觉地跟他并行,比如说我一个飞机编队出去的时候,诶,大家会自然而然就形成一个同向的那个编队,那我一开始的速度可能是不一样的,方向不一样的,但我要跟它AI到一起。
所以说其实steering行为的话呢,这三种行为我们认为是最基本的一个行为,那具体给大家讲一下,就是说c can复利,其实呢它要解决的问题,就是说当我的目标点在那边,无论是静止或者动的时候。
我会不断地根据我自己的位置,根据我的目标的位置,然后呢确确定我的加速,让我要么就追着那个目标去,要么我就远离那个目标,那这个这个东西讲起来其实也并不难,但大家可以想到就是我在游戏引擎里面。
我一个tick,一个tick就tick,你也能得到你想要的这个行为,但这里面second的福利呢其实也有些变种了,比如说这个我们经常讲的pursuit追踪,是一个行为对吧,还有一个是什么呢,叫巡逻。
巡逻的话呢,我永远在我前面找一个地方,让他去随机一下,包括这里面呢,我觉得最有意思的行为,是在一个叫速度场或一个方向场里面,一个物体的运动,就是说假设我在空间能够形成一个方向上。
方向上叫什么叫victor fw,我把任何一个质点放进去之后,它会沿着方向盘一直在动,诶这个东西其实非常的有意思,其实大家去看一些这种大型的这种群体的运动,模拟的时候,他很多时候是用这种方向场来做的。
所以这其实也是一个second fleet的一个变种,所以这个地方比较简单,所以我就不展开讲了,那么另外一个呢就比较有意思的,就是velocity mesh。
那实际上的话呢他其实是有一个target的那个速度,我的目标点的速度对吧,我还有什么呢,我的这个我什么时候去那个matching它,我要追上他,这个时候呢,我要反向的用一个一点点积分的方法,反向算出来。
我这时候加速了什么,因为这个时候我会根据我目标点,和我的相对速度决定,我靠近它的时候,我要反向去减速,这里面如果目标点它是静止的,或者是匀速直线运动的话呢,这个计算其实是比较简单的,大家想想看。
这不就是呃,二分之1T平方等于对方的那个点距离对吧,我就可以开个根号,我能算出来我的加速度是多少,基本上我能保证我能稳稳的停在那个点,但是的话如果对方也是在动,而且是一个非直线的,在动的时候。
其实这个we lost the machine的话就非常难了,但其实我自己认为啊,这是你可以假设对方是沿着直线运动的,然后算出一个结果,然后你TK完下一帧的时候,根据它的变化,我再去重新算一个加速度。
其实你会形成那个结果呢,基本也能符合你的需求,为什么呢,因为真实的人在世界上行走,比如说你要你的朋友在前面一通乱走,然后呢你试图跟他去AI,跟他matching在一起的时候,你也不会一上来就全部算出来。
你也算不出来,对不对,你根据你的经验,根据他现在的速度,你大概感觉自己一个一个速度该怎么走对吧,然后的话呢你走了大概比如说半秒钟,一秒钟之后发现他有变化了,你身体你会下意识的。
你的小脑会告诉你有相应的变化,只是我们人的这种计算全是在电光火石之间,但是呢有一点我们非常确信,就是说人类不会算一个复杂的计分方程,我们只是在做一个就是一个小步长的计算,然后呢根据下一个步长来的时候。
情况发生的变化,我们会调整而已,所以呢用这种就是叫分布式的这种算法的话呢,你也能得到一个你想要的一个,基本看上去非常自然的这么一个结果,因为我们是在做AI嘛,所以我们只是希望AI体的行为。
看上去像像真实一样,那么最后一个呢其实更简单的就是alignment,Alignment,这里的细节是什么呢,刚才我们讲它的速度对吧,是一个叫加速到最大速度,然后呢快接近目标点的时候。
或者跟着目标点的速度逐渐一致的时候啊,那我就开始进行减速了,其实online的地方的话,你要做的真实的话,实际上也是就是角速度方面,也有一个叫加速和减速的过程,这样就证明这个案例的时候。
我把方向跟它对齐的时候,一开始我要角速度,角加速度对吧,快到我的目标角度的时候,我要有一个角减速度,这个细节其实非常的重要,为什么,就这也是就是我待会我想的就是。
其实啊steering这个系统是一个大牛,在很早就提出来了,这个大牛是后面做那个就是swarm,就是封这个这个这个群体系统,群体行为系统的这个这个开开山鼻祖,那么这个东西为什么这么重要呢。
其实我在我早期在写这些,这个AI行为系统的时候,我们也很容易犯这个错误,如果大家没有做这个角色,align的这个角加速度和角减速的过程的话,你会发现你的AI的行为是什么,就是这样,它会突然N嗯。
就像那个机器人瞬间的转向,当当当,就是你感觉他很不自然对吧,因为我们做的一切的行为都让它自然了,所以说其实你如果发现这个问题的时候,你会发现哦,肯定是align这个steering算法我也没写好。
我没有把它记做足够的这个角加速,角减速的处理对吧,所以这也是一个非常有趣的一个东西,基本上的话STERING算法的话比大家想象的要简单,其实这三类基本上囊括了我们steering的重点。
但是呢为什么这个同学们单独拎出来讲呢,因为你会发现啊,如果一个人他没有受过系统的游戏,引擎的训练的时候,他很容易在引擎架构中,把这个系统给给那个漏掉,就是他做完了寻路对吧。
但做完了比如说AI的决策系统对吧,然后呢让这个东西就比如环境感知系统,这个角色技能动起来,但是呢你会发现他做出来的这个游戏,或者是角色看上去都很僵硬,就像机器人一样。
因为他没有加入一个让它更符合人自然行为的,这个steering系统,特别是你在做载具的时候,做这个人类体型为的时候,做生物的时候,这个其实很重要,比如说你模拟一个快速奔跑的一个,这个生物的效果的话。
它的惯性对吧,它的最大加速度和它的最大的这个转向速度,其实都是他一个约束条件,基本上这些呢就是我们的series系统,那在这个地方呢我们要荡开一笔,就是说这个系统它的一个大客户是什么呢。
就是这个群体系统,就是或者叫做授权系统啊,Crowd simulation,就是说我有很多的东西在一起运动的时候,你该怎么去处理,大家会觉得这件事情好像AI,我们今天不是讲AI吗。
为什么上来就讲crowd呢,因为实际上现在AI在很多时候,在游戏中处理的情景,我们是要处理这个群体运动的东西,举个例子,比如说我们做一个三游戏,比如说我一个我的主角走到一个广场对吧。
这个广场上你会发现什么,有很多鸽子在地上,你走过去的人的鸽子会啪一下受惊飞起来,那这个鸽子飞起来的行为呢,其实它就是crow的行为,那我们去做我们的环境,大家看到现在所有的游戏里面。
我们都会把这个城市会越做越丰富对吧,很多人走来走去,对不对,你可以在一些好的这个demo里面,你可以看到成千成百上千的NPC走起来,而这些人呢走来走去的时候,他其实不是一个一个的个体。
他是用一个crowd系统去做出来的,那么这种群体性的行为,在很早以前就以为大牛对吧,RENO的这个这个大牛他就开始研究了,其实他也是提出了stein系统的这个开山鼻祖,他就说其实对你这种群体性的行为。
一般我们有三种处理方法,一种是用微观的方法去解决,就是用一个从底向上,就是我定义每一个群体的行为,他们合到一起就实现了我的目标,还有一种方法呢是马就是宏观的,就是我宏观上定义一种大的运动趋势。
你们所有人呢按照我的趋势运动,你的结果也是好的,但第三种就是hybrid,就两种方法都有,就是说既有宏观的控制,又有个体的行为,那这个的话呢就是说能解决一些更特殊的情况。
那我首先跟大家讲一下这个微观算法,微观微观算法其实呢比大家想象的要简单,实际上就像一个鱼群一样的,他的行为是什么呢,或者我们人吧,其实他无非就这么几个行为,第一我看我离一个人太近了对吧。
我要我们之间要产生一个斥力对吧,我们要彼此推开了。
我们不要撞到一起,那么呢如果呢我发现我离人群太远了,特别像鱼群的时候,他会说嗯,我好像也不能太那个离群了,那我要加上一个引力,大家发现这个事情很有意思吧,这个我们在以前在学那个弹簧的时候。
是不是很像一个弹簧,就是压缩的时候产生视力,拉远的时候产生吸引力对吧,其实就是个弹簧,然后呢,诶还有更复杂的就是你们既然都头朝那个方向,我也希望跟你一起同样的头朝一个方向。
其实你用这么一个简单的这个规则啊,你去把很多的AI体放进去之后,他呢就能形成我们想要的这种鱼群的效果,比如说像这里面的这个案例对吧,潜水艇过来的时候,诶我可以看到这鱼群形成这样一个行为。
OK其实这个呢就是一个非常简单的基于规则的,这个就是这个这个就是那个群体crowd simulation,那么宏观的算法的话呢,其实就更害了我,他说因为微观的算法呢,它好处是规则很简单对吧。
但是它产出的结果确实是不可控的,他也不太受人的影响,他自己你他很适合表演出一大群的东西,自己走来走去很随机,但是比如对于一些更有规则体系上,比如说这个demo就是虚幻五的那个max AI的demo对吧。
那我要展示一下在城市的街道里面,他很多的行人走来走去,我希望因为人在大街上是不会乱走的,对不对,我不能像鱼群一样,就是看见人就撞开,就让开,然后呢随机找方向走,那这个行为就很奇怪,那他是怎么做的呢。
诶他首先会定义一个,比如说我的一个一个一个一个一个lab对吧,我的各种land的意思就是说各种人行道,或者是那个就是高速上的一条车道,对我定义很多的车道。
然后呢我把这个区域分成很多zone graph对吧,然后呢我生成了很多人的形体的时候,他会在这个空间的沿着这个line line去行走,它其实是一套规则体系,那这样的话呢。
我会让整个AI的行为会更加的可控,但这里面还有很多其他的处理的,比如说我怎么样避免就是一个一个个体,它会在line之间快速的切换,比如说它本来是往左边走的,他走了,因为避让一个别人,他要让开一点点。
结果呢他一下子切到了,就是逆向的那个烂对吧,那就很奇怪了,对不对,他要保持一致性,但是呢我在十字路口,我要让他有有有这个就是分叉的选择,那这个烂之间的话就是怎么去连接,怎么去分叉。
它有一套简单的规则体系,但这些都是宏观控制了,然后这时候我把几千上万个A技能,放上去的时候,它其实是follow了我这个line去进行行走,那么这种宏观方法的话,其实在现代游戏引擎中用的还是非常多的。
特别是我们的表达这个人类行为的时候,用这个系统会比较多,因为人类的行为真的不像我们叫做无头的苍蝇,那样对吧,他们其实还是隐隐的按照一套规则去走的,那么像这个就是这两种方法结合的方法呢。
就是这个microscope吧,那这个其实很简单,他的当时这个提出来,其实我认为也是一个特殊情况,就是他解决,就是说我把这种蜂群或者这种群体,分成一个个小族群,让小族群呢它受一个大的这个指向。
比如说我要往左走,我要从那个那个到对方的出生点对吧,我们有一个目标,但是呢每个小个体的自己行为的话呢,还是follow那个rule base的方法生成我们的结果,这个这个里面最典型的一个应用是什么呢。
其实就是我们以前玩的那种RTS游戏,比如说大家玩过星际对吧,玩过帝国时代那么经典的游戏,那么你圈出一圈小兵的时候,你会指定一个目标点,说诶我要打那个点,对不对,在这个过程中。
你会发现那些小兵他都往那个方向去走,因为它有一条烂控制它,但是每个小兵的移动的话呢,他又是自主决策的,就是基于他自身的入,比如说他看起来债务,每个小兵是自主决策,我怎么去让他的,而不是整体去决策。
所以呢这也是一个群体的一个行为,那么这里面的话呢,其实要跟他讲一个很有意思的东西,就是说其实在座所有的虚拟行为,包括STERING系统里面,我们一个很重要的东西,就是我们要避免去碰撞对吧。
我们不希望这些小的crowd会overlap在一起,这样看上去非常的不真实,那怎么样去避免碰撞的一个最简单的方法,就给它加力嘛,这个力怎么加,其实很简单,就是说前面讲过了吗,两个东西靠得太近。
我就给它产生斥力,对不对,远了我产生吸引力,只要有斥力在的话呢,很多时候你是不会撞到一起了,但是呢我们一般force space的方法呢,其实还有一个很重要的应用,就是说假设我给你一个环境。
这里面其实有很多障碍物对吧,大家会说我会用寻路,但是你假设有几千个东西,你同时寻路的话,那个速度是不是会非常的费啊,大家还记得刚才我讲的寻路算法吗,你要去做这种这个A星算法对吧,每一个小个体都算一遍。
这个太废了,对不对,然后呢,我要再对他这个这个这个再进行这个pass moon,想想那个烟囱算法是不是也很费诶,这个时候其实有个非常廉价的方法。
就是说我对所有的障碍物加一个就是distant field,就是一个距离,想怎么样,这个地方又来了distant field对吧,当我的每个个体,离那个离那个某个账户最近的时候,越来越近的时候。
这个distance field的值就会变得越来越小,我产生的这个反向的斥力就会越来越大,这个时候呢你给一个大致的方向啊,这个群体会能模拟那种真实的A人类的,这种移动的行为,所以这件事情呢。
在这个模拟这种人类的这个行为,其实非常的有用,这个东西我给大家讲一个游戏之外的意义嘛,就是啊我们经常讲游戏引擎不是在很多地方,这个不只是游戏嘛,比如在数字孪生里面有个很重要的用是什么呢。
就是我们设计好了一座城市,或者一个巨大的一个比如电影院对吧,一个办公场所,办公楼,我们要模拟说,当这个电影院或者办公楼发生火灾的时候,人群该怎么去逃跑,那么其实这里面的很多逃亡的模拟。
就是用这种force space的方法,就是模拟人群的应用,而且这个模拟的结果呢跟知识的情况下,真的很接近,因为我们人在那种慌乱的情况下的寻目,真的就是看见墙我就不会撞他对吧。
看见人我会让一让我尽可能往那个方向去走,所以能形成这样的一个结果,那这个算法呢其实它很粗暴了,这里面的话我要简单的讲一下,一个很高级的算法,叫叫那个就是那个velocity based model。
就是基于这个那个就是基于速度对,就是速度的叫那个velocity obstacle,就是速度障碍法的算法生成的这样的一个碰撞,检测,这个方法其实呢我本来我们准备了十几页。
但是呢最后我觉得还是不要展开了它,它其实核心的想法是什么呢,就是说哎当我两个物体在运动的时候,我看到别人走过的物体,它会在我的速度域上形成一个障碍,然后呢我要调整我的速度,尽量避开这个障碍。
那么它的方法呢,其实VO的方法其实想法非常的简单,就是说我看到一个东西对吧,那如果我判断我在沿着当前的速度,我就会给你形成速度,你和和你形成一个RBO,就是速度障碍产生这个碰撞,那我就赶快调整我的速度。
比如往左边调一点,那么RVO呢就real spect的那个呃,那个voice of st奥斯特的这个方法的话呢,实际上就是不仅我让你也得让这样,我们两个相互是公平的,我们一起就让一下。
那这样的话呢就是说其实最简单的做法,就比如说这个时候A发现我要撞上B了,A决定往左拐,B呢这时候意识到说诶你也要撞上我了,那我也亡往应该是往往左拐,这样的话我们两个对吧,行人车辆都靠左行啊。
靠左行还是靠远,应该是靠靠左行,这样我们彼此就完美的让开了,但是这个方法呢其实讲起来是不是很简单对吧,但实际实现起来很麻烦,然后呢这个方法它会出一个问题,就是说当这个我们参与的个体不止两个的时候。
刚才我们讲的你要处理蜂群对吧,那我们就是A对B的,必然可能会对A对C的必然产生冲突了,就是说你虽然让开了B,但是你可能和C撞到一起了,所以这个时候的话呢,他要做一个整体的优化。
这时候就提出了一个著名的叫ORVO,就是optimal,这个方法讲起来就非常复杂了,就是他把空间上的就是这个所有的速度,假设一个时间里面形成了一个就是速度空间的,一个像羽毛球形状的一个一个一个锥形区。
那一个区域的话呢,每个人的区域之间,他用那个明可复合求了他们一个和,然后求它里面一个最小的一个,对大家所有最公平的一个时机点,这个听上去是不是很数学,说实话这个地方的数学还是蛮深的。
但是呢我要跟大家讲的一件事情,就是说,其实在我们在做引擎的早期的时候,我们把全套的RVO和OO的算法,我们都实现了一遍,但今天我们会发现一个很有意思的事情,就是说RO算法首先在实现上非常复杂。
很容易做错,第二件事情呢他处理的不好,计算复杂度其实非常的高,但是呢他确实可以在理论上找到一个最优解,就像刚才那个一上来给大家看的那个视频中,这些小球的运动就是给大家看一下啊,就是这个小球的运动。
就是说如果你有这样一个围成一圈的小球,他怎么穿过彼此到各自的目的地,这个优化问题其实是蛮难的,那么就是用OOOOO算法的话呢,基本上能得到一个理想解,但是的话呢其实在真实的游戏引擎中。
至少我自己现在的实践,我会更推荐用force space的方法,非常简单,非常好调,但是呢它的结果没有这么漂亮,没有这么elegant对吧,所以的话呢就大家在实现自己的游戏心的时候。
你们可以根据自己的选,根据自己的那个判断,那比如说在现在的商业引擎里面的话呢,一般这两种方法都会提供,那大家会觉得很奇怪了,为什么这个东西会变成一个引擎的标配呢,因为在引擎里面。
我们真实的支撑游戏产品的时候,我们一般都会有大量的NPC个体,就是你的AI系统会有大量NPC,然后呢这个NPC在运动的时候,我们希望他们有一些群体的行为的合理性,同时的话呢我们又不希望他们彼此产生碰撞。
但他会说我不希望那个就是说产生碰撞的时候,那我们是不是会就是说哎用AI来解决呢,其实AI是能解决,但是AI解决这个问题效率是非常非常低的,所以我们需要一个专门的叫clean avoidance。
就是碰撞避免系统,来帮助我们AI来解决这个问题,大家看有点意思了吧,就是讲到这个地方已经讲了将近一个半小时了,我们还没有讲到决策算法,因为这些东西其实是决策的一个基础好,那这个我们就不卖关子了。
同学们还在讲,我还我还想学AI,对不对,那我接下来给大家讲一个,就是当我们去做AI的最后一个这个基础,就是他的AI的foundation叫做对环境的感知叫森林,那么SENORING的话呢。
其实我觉得有我自己喜欢交流词叫perception,就是说感知世界,就是我们人在做所有决策的时候,其实它的基础是什么,要根据我看到的东西,我听到的东西,包括我对自己的感觉,比如说我现在感觉很好。
感觉良好对吧,或者感觉不好,那么基于这所有的信息,我才能做出我的行为,我的决策,所以今天我们在所有的游戏引擎里面,看到这AIT的所有的行为,他最重要的一个就是说决策的依据,就是说而且这个依据是动态的。
就是对世界的感知perception,那么perception的内容呢,它会分为就是由内到外,累的话就是我自己的状态,比如说我的血量对吧,我现在的速度对不对,我是我的手上的武器里面还有没有子弹对吧。
这些东西,那么还有什么呢,还有就是说环境,比如说这个A点到B点到底能不能到达对吧,比如附近有没有一些有意思的点,还有就是说敌人在哪里,有没有敌人对吧,所以这就是很重要的这个AI决策的依据。
perception系统不像大家想的那么简单,就是说它实际上的话就是啊,是需要我们整个game play系统,去在设计上去支持好这件事情,那么perception的话,首先的话呢我要能拿到我自己的信息。
对不对,我的血量,我的这个装那个armor的这个状态,那么其实呢这个对自己的信息的访问是非常快,非常廉价的,但是大家还记得我们在前面,game play统讲了一件事情是什么懒,比如说像那个蓝图对吧。
像visual scription这个系统,它最重要的就是我能读取很多环境的变量,其实这里面有很大的一块变量,就是我自己的状态,那么这个东西为什么他要能支撑论文,因为你没有这个东西的话。
其实我后面所有的AI决策都是随机的,它是没有任何基于这个环境的依据的,那么另外一个的话呢,我们还这个要获取的是什么,就是空间的信息,那空间的信息呢,第一种是静态的空间信息,这件事情非常的重要。
大家一定要理清楚,比如说我可以是那个navigation,可以这个巡逻的这个我可以动的这个区域,就刚才我讲work by area,对不对,还有是什么呢,就是我的战术地图这件事,这个概念的话。
他前面没有介绍过,就是说其实在一个真实的游戏的关卡里面,比如说我形成生成了所有的这个通路区域啊,实际上对AI决策来讲是不够的,举个例子,比如说我的整个这一个,比如说一条河中间有座桥。
河桥的两岸都是开阔地,那么它可通行的区域是不是很大,那今天如果我们去深设计一个AI,这个AI后来要来去这个跟你去对抗的时候,我们的人是不会天然就会意识到说,那个连接河两岸的那个桥的两头。
是一个重要的战术点,对不对,那个所以呢这个时候呢在AI系统里面,我们就会要求设计师形成一个叫text map,你要手动标记那些地方,是一个更具有战术价值的点,那么AI在做决策的时候。
他会优先去控制和那个区域,优先消灭那些虚拟的敌人,那么这个txt map的话当然真的展开比较复杂了,就是说它存的数据的维度是非常多,另外一个的话呢就是在这个游戏的世界里面。
我们的设计师会放放很多可以交互的东西,比如说我们一般叫smart object,比如说那个地方有个梯子,AI就知道到,那我会把这个梯子支起来对吧,或者说那个地方有一座墙,那个墙是可以被打破的。
那我AI就知道说我可以,如果我想去达到某个战斗,结果我可以用我身上的某个武器把那个墙打破,给我的给人类玩家提供一条通路,对不对,那么还有什么呢,还有就是说啊我们有一些就是说在游戏中。
其实周边那个txt map也重要,就是我会标记说有些地方是你可以掩护的,掩护点对吧,就大家最著名,大家记得以前那个就是最早我印象最深,就是最早做的很好的,让我非常impressive的一个游戏。
就是战争机器,因为那个时候我们在做FPS的时候,AI都是跳来跳去,跳来跳去,战争机器里的AI都会躲在树洞后面,拿着枪这样去去打别人对吧,然后你会觉得哇这个AI看上去很智能。
因为我们人类很多时候进入到一个战场的时候,我会首先去找掩体对吧,所以说其实在游戏地图上,有很多分散的这个special的information,空间上的这些信息,这些信息都是由我们的设计师预先构建好。
那么接下来呢还有很多动态的信息,这个信息的话呢实际上也是AI的foundation,那么这里面有一个很著名的一个,也是一个很重要的概念,叫做influence map,就是这个影响力图或者叫热力图。
或者叫叫叫什么因子图吧,就是说其实我们的AI,在对于战场态势进行感知的时候,他的基于的就是这个influence map,举个例子,比如说当那边有很多敌人,当敌人越来越多的时候。
我们可能会把那个地方的敌人比较危险系数,那个维度会逐渐的提的很高,然后呢这样的话,当我的AI如果发现自己哎我残血了对吧,他就会下意识的让自己避开,那怎么知道我要避开那个区域呢。
它其实是要query这个inference math,说我要去的目标点,比如从A点到B点,但这个地方的话,有一个就是威胁系数很高的区域,我会想办法绕开它,如果威胁威胁,那是中等,我可能选择消灭它。
如果位置很低的话,我就选择直接闯过去,其实这个东西的话是对那个AI,在整个战场的行为影响是非常大的,另外一个的话是什么呢,就是这个诶动态的寻路,前面讲的就是为什么寻路要动态。
因为当游戏里面的这些就是其他的玩家也好,或者是其他的AI好,产生了一些行为之后,或者游戏里发生了一些重要的event,一些事件的时候,那么就是说你会发现就是说我的AI的行为,决策就会发生变化。
那么我的AI对这个世界的动态的感知,也非常重要,否则的话你会发现AI,他永远是按照一个套路去打对吧,它不会根据我们真实在这个世界里发生的行为,有变化,那么包括呢就是我的视野,就是在这个世界里面的话呢。
其实这个视野也会发生很多的变化,所以其实啊就是动态的空间信息,也是他的感知,也是我们AI运算的一个基础,所以这几个概念我今天因为时间的关系,我没有展开,但是大家会发现讲到这儿的时候。
其实呢引擎会提供一部分的功能,但是呢很多时候是根据你不同的游戏,有游戏开发者自己来设计,但是引擎这边的话呢是要提供一个开放的接口,就是当我们在设计AI角色系统的时候。
它一定要有一个通用的方法访问这些数据,比如说前面讲到的哎,我的各种自我的变量或者环境的变量对吧,包括你其实在根据game play,我会更新很多的这种influence map。
一般来讲infant map是非常复杂的,它有很多的维度,就是对这个世界的状态进行表达,那么我们在这个AI系统里面对吧,要提供一个通用的接口包,让我们的决策树能够访问这些数据,所以这个地方的话呢。
也是各个游戏团队的一个功力,就是你的AI做的好,做的坏,这个地方的设计是很重要的,但是呢引擎测也要提供你的功力,就是说你的这个接口扩展性要足够强好,那么其实呢另外一个很重要的动态的空间信息。
就是这个游戏里面的物体对吧,那最简单的就是说我现在站在这儿,我得看到说有多少人在我面前,我要感知到这些人存在,同时的话呢我在判断哪些人是我的敌人,因为我只有知道我的敌人有没有,敌人在哪里。
敌人跟我的距离的远近,还有什么呢,哎敌人对我的威胁度,他拿了什么武器,是不是我打不过他对吧,所以这个时候你要干嘛呢,你要找到那些物体的id,你要判断他是不是可见了,甚至你需要知道他是不是看得见你对吧。
包括你最后一次这个探测他用的什么方法呀,这些东西都会影响到我AI的决策,其实这个东西为什么叫CSIN呢,就是对世界的感知呢,因为你会发现,我们这个为AI系统构建了这个,基础感知体系的话。
它在这一趴的话非常像我们人类对世界的感知,那么这里面的话呢就就这里给大家一定要强调,就是在我们做做AI的时候,经常会犯一个错误,就是有两种错误,第一种错误呢,我们会认为我们的AIT整个世界是开图的。
就是没有战争迷雾的,比如说大家作为一个RTSS的AI的时候,最早的时候做的时候就会有一个假设,就是地图上发生的一切我都知道,比如说大家最早以前玩那个啊,比如说像那个啊cs的这个AI bot的时候。
早期做的比较傻的时候,那个bot他是全图全开的,就是机器人永远知道你藏在哪儿,这会让你很绝望,你做所有的战术的躲藏动作,对他来讲都是无效的,人家就直直勾勾的冲着你扔雷,冲着你打枪,对不对。
这种AI你们觉得好玩吗,不好玩对不对,那么我们希望的AI像什么AI像人一样,那人对世界的感知就比较有趣了,第一个是视觉,很自然,对不对,我们可以机器人,它比如说一个bot,它背对你的时候。
你居然会觉得我可以从背后偷袭他,对不对,为什么,因为机器人他的朝前他是有视野的,但是呢我是不是可以冲过去偷袭他呢,哎这件事又不行,为什么呢,因为他还有听觉对吧,就是说它的听觉是360度的。
那么这个听觉呢又是一个很重要的感觉,如果讲的更更加精细一点,就是说你这个听觉的范围,还不是一个完整的语言,为什么你隔着一些障碍物的时候,可能声音有一个阻碍的效果对吧,比如说你躲在草丛里面。
你的声音传播的就会受影响,所以当我们想做一个非常好玩的一个,战术游戏的这个AI的时候,实际上你会让AI模拟这个很像人类的行为,但这里面讲的更夸张点说,我们要模拟一下AI对嗅觉的感知呢,理论上是可以的。
但是呢好像现在游戏中用的这个很少,但是说不定将来以后有个什么游戏,就是里面有一个嗅觉特别灵敏,比如说一只狗对吧,他可以听到很轻的声音,能够顺着你留下的味道一直闻下去。
那么这个里面也会做这样一个无感的东西,所以说这个其实啊对世界的SENSELLING,是我们的AI系统的一个foundation,这也是一个啊非常重要的游戏统,那当在在做的330的时候呢。
你做最基本的算法其实并不难,但是比较容易出问题的一个地方,就是在引擎里面比较容易出的地方是什么呢,就是说如果我们给每一个bot都在,每一帧都在做这个世界的perception的话。
perception本身会成为世界的性能瓶颈哈,所以这个事情是当我们在做引擎的时候,其实这个地方我们一般会开放两件事情,第一个就是说开放给这个游戏产品,你会选择不同的情景下,你对世界340的精度。
你340的范围,这样的话,能够把这个计算机的这个成本压到最低,另外一个的话呢,也就是说,我们实际上会尽量的把一些对世界的感知,如果临近的那个bot的话,我们会共享,这样避免就是说对吧。
一群小也就一群boss,在那时候大家每个人都算一遍,那这个世界就会变得非常的麻烦,但是这里面就是一些工程上的细节了,但是的话呢这个340系统的foundation。
也是游戏引擎系统做AI的一个重要的基础,讲到这一趴的话呢,我们就可以终于进入了,也是我们今天课程的最后一排,就大家会发现就是我们讲AI的话,实际上我的课程百分之啊,可以说是70%到80的部分。
并没有讲大家最想听的,如何让电脑这个让那个bot变得聪明,大家只听到一大堆的基础系统,但是就像我前面讲的,就是如果没有这些foundation的话,其实AI是不能做任何事情的。
所以这就是AI的foundation好,那接下来这一趴的话呢,我们就会讲一些经典的decision making算法好,其实角色算法呢是AI算法的核心,就是前面讲的所有东西都是它的基础,那么决策算法呢。
最基础的最最常见的一个东西是什么呢,就是这个有线状态机对吧,我相信这个是大家的一个老老朋友了,因为在那个就是我们讲动画呀,讲前面的很多系统的时候都讲到这个概念,那么今天重点会跟大家讲的是一个叫行为术。
行为术也是一个AI最核心的一个体系,那其实在现代游戏中,还有很多其他的这个角色算法,比如说这个HTN对吧,就是那个层次这个任务,这个网络还有那个就是GP,就是那个就是基于目标的目标驱动的。
这样的一个就是行为的计划系统,就是go orange的那个action planning系统,就是GP,还有什么呢,蒙特卡罗那个那个那个搜索数,这也是非常经典的一个架构。
包括大家很关心的这个deep learning,基于现在人工智能这些深度学习的方法,我们能不能做AI,但是呢今天这节课,我想这这些算法在我的理解中,但是这些里面完全是我的一家之言啊。
我会认为就是说像状态机也好,像行为术也好,它更像是一种forward planning,就是说就是从前往后,慢慢就是这个我叫做脚踩西瓜皮,走到哪是哪了,这种决策过程,那么像后面要提到,比如像HTM。
像这个GP,像这个就是蒙特卡罗的方法,像那个deep ne,更像是一个就是以目标为驱动的一个反向算法,就是因为我要完成一个目标,所以我要做以下几个动作,所以这些算法会更有意思一点。
所以呢我今天就卖个关子,就是说我把这些最有趣的东西,放到我们的下一节课,而今天呢我先把最经典的前项的这样的一个,决策算法给大家讲清楚,那么首先的话呢让大家见识,来来再温习一下我们的老朋友叫状态机。
其实状态机非常的简单,也是非常符合人类的直觉,就是我们认为就是整个一个AI的行为,不就是在状态一状态二之间来回切换嘛,对不对,当条件满足的时候,我就从状态机切到张张二,或者说条件满足的时候。
我就做一个动作,把我自己从状态一变成状态二对吧,中间有个叫什么transition,transition里面的话,它有个condition,当condition满足的时候,我就做一个动作。
把我的状态切换掉,那我这个时候进入进入到新的状态的时候,我在判断我有其他的,有多少个这个这个这个这个肯定是,只要中间任何一个满足,我就从从这个状态再去transition到下一个状态。
那这里面举个很简单的例子,就是比如说像吃豆人对吧,我们要写一个吃豆人的这个AIT行为怎么写诶,大家发现没有,用状态机其实非常的好写,他的逻辑是这样的,就是我呢先乱走一气对吧,这个时候呢。
如果我发现我前面有一个ghost出来的时候,我怎么办,哎我这样做,我其实有两条通路,第一条通路是这样的,就是说如果我发现我这个时候就是普通状态,我干嘛,我该怎么办,赶快逃命啊,逃跑对不对。
但这个时候如果发现其实我已经吃了药丸,现在进入无敌状态的时候,哎我倒过来去追那个ghost,直到我这个状态结束对吧,那么这个时候如果我在闲晃的过程中,我发现有药丸的话,那我该怎么办,直接上去吃啊对吧。
所以其实用一个简单的三个状态,用六条边,我其实就可以做出一个最简单的吃豆人AI,而且我猜测大概率这个吃豆人这个游戏,PARKMAN的话,他的AI最早其实真的是这么写的,换,因为我以前玩过这个游戏。
我觉得他的AI是非常predictable,我觉得我们在学那个就是啊计算机那个编程,包括AI的时候,我们第一个作业,我经常记得做AI的作业有这么几个,第一个就是啊写一个吃豆人,AI跟要战胜电脑对吧。
还有一个作业是什么呢,就是挖雷,你要自动写个AI算法挖雷,我不知道,同学们,如果有同学们在大学里学computer science,如果学到那AI的时候,最开始的时候很可能会被布置这两个作业。
很有意思的好,那这个算法呢其实是非常符合人的直觉,也很经典,但这个CINEMACHINE呢,它其实有个很大的一个问题,什么问题呢,就是说,当我们真的要做一个商业级的游戏的时候。
你会发现这个角色的state是非常非常的多,然后呢你每加一个state的时候,你都要去考虑说,它和其他的那些state是个什么关系,你一开始只有七八个十几个的时候,你好好管理,但是想象一下。
如果你有几十个上百个state的时候,那些像一团麻花一样的连线,你是不是能够真的搞得清楚怎么连对吧,但是呢你又没有办法,因为你我我们state最复杂的什么叫叫transition。
就是说当我发生了一个事情,当一个条件满足的时候,我要迅速的迁到另外一个点去对吧,这就是我因为我要让这个网络非常的reactive,就是反应速度非常的快,那么这里面有没有办法解决呢。
其实有的就是那个hierarchy finished in the machine,就是就是层次有限,状态机,就是说我把状态机呢分成很多层,然后呢每一个状态机之间它有一个接口对吧。
然后他跟别人都是用有效的接口去跟别人联系,内部维持一个相对可控复杂的状态,这样的好处是什么呢,就是增加了我们状态机的可读性,但是呢它的坏处就是说不好意思,在各个这个这个这个子状态数。
那个子状态机里面加很多的非线对吧,我们希望它的出口总是那么几个,所以呢有的时候当然有些情况,比如说我在这个大的这个这个这个这一块里面,如果发生这些情况,我想迅速地跳到另外一个那个那个紫图啊。
子状态机里面的某个节点的时候,你除非跳飞线,否则的话呢这个过程其实是比较慢比较慢,所以的话呢虽然说就是hierarchical的,这个FSM的话呢,能不分解决状态机不好维护的问题。
但实际上它的反应速度其实是有点问题的,那其实呢这些方法是最古老的,大概是我如果我没记错,大概在15年前那个时候啊,很多时候AI系统还真的是用这个方法去做的,但是呢哎这个差不多在halo20代。
黑龙20代,如果我没记错,应该是两千两千零几年,应该2003年还是04年,玩的很早以前了,有一个方法横空出世了对吧,Be ai tree,因为这个方法我是很有感情的,因为我以前做的就是halo。
然后当时我在写自己的引擎的时候,当我写到AI这一块的时候,我就打电话问我以前的同事,我说诶状态这个比瑞REE到底是怎么写的,它的要点是什么对吧,真的是就是包着跨洋的电话粥。
把这个BEAIREE给他整个湿透了,那么其实行为术这个东西,我相信很多同学们如果对游戏引擎关心的话,都听说过这个大名对吧,那行为术它到底为什么有道理呢,我在before我再去讲行为术这个很细节的时候。
我会我会讲一个就是我自己很high level的一个认知,这也是我们课程组的小伙伴,在这一周里面我们准备的时候,我们总结出来的,我们认为其实状态机呢,它是对这整个这个AI的逻辑的一个抽象。
他确实我们也可以认为整个世界里面,我们AI的行为就是在状态里面来回切换,但是它并不符合人的真实的世界的思考范围,它不是一个像一团乱麻一样的跳来跳去,而是说我先看看情况嗯,我现在这个当我在这个时候遇到了。
没事可干的时候对吧,我遇到了一个,我看看我前面有没有敌人,如果我前面有敌人怎么办,我要判断一下我的状态,我的状态假设是无敌的,那我就去追他,如果我假设我的状态是这个没有Buff的,我马上去掏。
你发现没有,当我用语言描述这句话的时候,是不是像一棵棵的树在展开,对不对,其实behavior tree它的设计最核心的一个逻辑,就是他把我们对一个人类的行为的pattern,用状态之间的这种跳转飞线。
变成了一个就是一个树状的分支,其实比REE或者叫decision tree吧,这个概念在决策学很早就提出来了,就如果大家真的去研究这一套方法论的,这个历史的时候,其实在啊如果没记错。
大家如果学NBA学商务决策的时候,大家可能会学到一个概念叫decision tree,就大家发现,就是说在一些复杂的商业决策的时候,大家之间说诶你为什么做这个角色,你能把你角色的依据输出吗。
很多时候大家想一下,比较简单的方法是说我写我的条件12345,所以我的结论是什么,其实这个东西是不科学的,因为这个决策过程是不可以分为什么,条件123你就能得出你这个结论呢,那么在一个更加专业化的。
就比如说啊打个比方,比如说军队对吧,我们要有一套作战的,这个我们叫做sop标准作战条例,那么这里面的话呢,其实你在战场上遇到的情况非常的复杂,我如果是这个总参谋部,我想定下来一套作战标准。
要求我每一个连排营的指挥官都要这样去,做的时候,他其实就要用一套形式化的这个决策语言,把它表语,把它表达出来,所以人类研发了一个叫decision税的东西,叫决策树的东西。
其实就是用这样的一个形式化的分支逻辑,把你该做的动作,各种情况下做的动作把你表达出来了,那么其实呢behavior tree的设计,就是我以前在学习的时候。
我认为他的祖宗其实是decision tree,就是决策树好,那么行为术的话呢,你真的去理解它,其实也没有大家想象的那么复杂,那么它的node第一种的话呢就是execution node。
就是执行的node,那么执行node你可以认为它全是业界点,那么执行他能做的动作有哪,有哪几种呢,第一种很简单,我做个条件判断,比如说哎,我现在是不是这个有这个ghost在逼近我对吧。
或者说我现在是不是在一个powerful的状态里面,对吧,这就是一个判断,在刚才那个书里面,我们会在这边去判断一下,说诶ghost是不是靠近我了,如果ghost靠近我,那我接下来就做一些的动作,对不对。
还有就是比如说我发现ghost靠近我了,我问我现在是不是在powerful的状态,如果是yes的话,这是干嘛,我就要做一个动作叫cheese ghost对吧。
所以呢接下来还有个什么叫做action node,就是我做一个动作,那么action,但这里面要注意一个细节,就是说我们遇到connote的话,我会瞬间做做成这个判断,我很快就会完成这个判断。
但是呢当你做一个动作的时候,比如说我追那个构思的时候,大家想想看我能TIK到这一帧的时候,我就说我要追你好,下一帧的时候我就追到了,不会吧,我这个时候是不是要调用我前面讲的那个。
什么navigation系统,我首先要在我和他之间寻个路,对不对,我要去追他,第二个呢我还调用我前面的什么呢,steering系统对吧,要确保我能够这个这个始终正确的,朝着他的方向去加速减速,去逼近它。
对不对,那么其实呢这里面我刚才讲的就是ghost,是不是靠近我,就是is close close close的话,实际上是用了什么呢,前面我讲的那个CSIN或者perception系统。
帮你告诉你说这个ghost是不是在靠近你,所以呢比AI try的两个业界两种类型的业绩点,一种是条件节点,一种什么呢,是动作节点,但动作节点这里面大家一定要注意一件事情,就是说他是有三种状态。
就是说动作成功了对吧,success动作还有什么情况呢,叫失败了,Failed,对不对,FID也很重要,血色素在行为树里面试失败,也是一个很重要的状态,那么第三种是什么呢。
诶我们叫做这个i’m running,哥们儿,我正在忙呢,不要打扰我对吧,所以这个是行为树的第一个基础性节点,就是可被执行的node,那么第二种呢就是控制它flow的,其实那个就是行为树的话呢。
是一个非常经典的一个就是系统,所以你会发现它的图示都已经标准化了,比如说它能控制它flow呢,有这么四种啊,有三种,其实最经典的其实就三种,第一种是什么呢,叫sequencer,就是顺序执行。
你看到的这个箭头,它的意思是什么呢,就是当我走到这个条件的时候,我要依次把我的次数全部执行一遍,诶这个很有意思吧,就顺序执行,那么这个时候如果他此时中间有些人返回,在running的时候。
那那个sequence的节点,别人问他,你现在状态怎么样呢,哎我still running对吧,那么这个sequencer他有这个有什么好处呢,它就会让策划,而让设计师非常容易的定义一个计划。
就比如说我们从这个就是说,我们要做一件事情的话,那我其实是会依次去做的对吧,待会我会举个更详细的一个例子,那么第二种呢叫selector,selector是什么意思呢。
就是根据条件我去尝试我所有的这个子节点,那么我会优先执行在前面那个节点,那这个意思是什么呢,就是说这个前面的最前面的几点,只要执行成功了,我这selection就成功,就相当于是说我到了一个状态的时候。
我有三种选择,但三种选择优先级是不一样的,比如说一个士兵,他遇到一个问题,他有三个选择,第一个是消灭敌人对吧,第二个是什么呢,是那个就地这个这个掩护保护自己,第三个什么逃跑。
那按照一个士兵的AIT的行为,假设我们讲一个勇敢的士兵的话,他是不是优先的,是要去攻击别人对吧,就是消灭敌人,其次呢是保保存自己,最后才是逃跑,那其实selector这个决策的时候是有就有优先级。
那他根据什么决策呢,这里面你可以加入一些判断,比如说我的血量够不够对吧,敌人的武器是不是够强对吧,这些因子在我的书上都可以进行描述,那么第三种呢叫parla parlor呢,就是并行的去执行一件事情。
那这里面的话呢,就是说这也是行为树一个很重要的特点,就是说行为树不同于状态机,状态机,你在任何时候你只能在一个状态,但是行为树呢它很强悍的地方,也是适合真实事件的情况,就是我们一个AIT。
一个bot可以同时处理很多件事情,我待会会详细的展开,那这里面还有一种control notes,就是我们的decorator,好,我们依次来讲,首先讲什么叫sequencer sequency呢。
它实际上就是要求我所有的子节点,依次的去执行它,然后呢当我每一次去问他的时候,我的子节点中间有任何一个人在running,你我我正在跑运动啊,我正在正在运行,那我这个你问我这个sequence的时候。
我就是我,我现在下面肯定有我的一个任务,就我一个任务没完成,就怎么去理解这个SEQUENCI,我觉得它就是一个最简单的一个task in系统,就这里面举一个更简单的例子,比如像这里面你看一个AI。
它会他要完成一个剧情动作的时候,他首先干嘛,他要给你开门开门,这个动作讲起来很简单,其实比较复杂,你要拆解成第一,我要首先我面前有没有门,对吧,我要做个判断,意思多look at locker。
就是门现在是不是锁上,有没有门,门锁上了,第二步是什么,诶,我其实这里面才写的还不够,想更相信的是应该是我走到这个门前,第三步是什么呢,我要unlock这个do,把这个多解锁完,这个任务完成之后呢。
我接下来干嘛呢,我要open the door,把这个do打开来,最后一个是什么呢,诶我要首先身先士卒冲冲进去,你会发现没有,就是说如果我们用behavior tree去写的话。
用一个sequencer,它其实是可以非常naturally的去表达,这样的一个planning的东西,所以sequence是在BHTREE里面,是个非常重要的基础控制节点,但这里面要注意一个细节。
就是说一个sequence节点它最后往上面去反应,就是说它也是三个状态,刚才我讲过第一个啊,我失败了,第二个是什么,我成功了,第三个是我正在running running,好理解。
我在这中间任何一个过程,因为比如说一个NPC他去解开一个门,他去推开一个门,他走进去它都不是瞬间完成的,对不对,所以这个时候呢,它实际上都是有一个就是持续时间,在这个过程中呢。
只要这些过程没有fail掉,我们都认为你是成功的,但是呢什么时候会fail掉呢,就是这中间的这个这些这些子节点中,只要有任何一个节点反应说我fail掉的话,那这个sequence就非要的。
比如说我一上来发现这个门已经开了对吧,install lock的这个东西返回来force,那我接下来所有功能全部不要执行了,那我这个six or直接就跳回去了,你有没有发现这个如果大家想象一下。
如果我要去写一个状态机的时候,我依次串了这四个节点对吧,依次去四个state去执行,但是这个state里面,每一次它产生的结果状态主要是force的时候,我是不是要连条飞线回去。
到他那个force的处理地方,那你这时候看这个拓扑结构图啊,它其实就比较乱,但是你用一个sequence的节点的话,你会发现他非常的自然和符合人的直觉,那么当我所有的子的子的这个指数。
执行返回都是成功的时候,这个时候哎,我就说我这个sequencer顺便成功的完成了,那如果你成功完成之后,那上面的AI也会发生一些其他的变化,好这是sequencer控制节点,很好理解。
第二个呢就selector selector,就像我前面讲过的,就是说在这些子节点中的话呢,我们就是说找一个他的这个排放的顺序,你第一个任务完成不了,没关系,我就是第二个对吧,第二个任务完成不了。
我就是第三个,但是只要第三个如果完成得了,那好我就是第三个钻下去,只要第三个任务完成之后,我基本上很多大部分selector的话,只要完成一个我就弹回去,但是有些select说我要至少完成两个。
但这个其实呢有的时候是不一定的,他们很多时候在这里面的话,我给你们讲一个selector的例子,比如说这里面那个白色的,大家注意看这里那个白色的就是一个小智能体,大家去理解一下,他现在第一步的话。
它会判断,就是说哎,我首先判断在我的眼前有没有敌人对吧,如果没有敌人,我做我做一件什么事情呢,我如果有敌人,我做这件事情就是attack,所以我作为一个士兵,勇敢的士兵,我的最高优先级是什么,是打别人。
消灭敌人对吧,但如果这个时候这个select的时候,第一个任务失败掉了,就是我没有发现敌人,或者是我attack失败了,怎么办,我就进入了我第二个节点是什么呢,就是我就开始来回巡逻。
我就来回走来回走的时候,我这就问问一个问题,就是说我眼前有没有看见敌人,如果敌人是已经看见了,这个时候呢敌人并不哦,不好意思,第一个条件是我的我的可攻击范围里面,有没有敌人对吧。
如果攻击范围内有敌人的话,因为我是个近战英雄嘛对吧,那我就马上打他,但是呢,假设这个条件就是在我的晋升没有定点的时候,我就看过第二个条件,就是说我是不是能看见敌人,如果我能看见敌人。
那我就进入了我的第二个陷阱,什么呢,我就去追他,我就chase他对吧,那么嘿如果前两个条件都满足,我就进入我的第三个状态是什么呢,我就去patrol去循环,你们去看那个小白人就很有意思。
就是他一开始自己来回走对吧,来回走哎,当那个玩家靠的足够近的时候,大家仔细看啊,哎他这时候就打他人,玩家开始逃,玩家逃难,如果因为玩家的速度比他快嘛,当玩家逃出了他的视野的时候。
他这时候selector就没事干了,他就进入了他的第三个状态叫patrol,其实这个案例是非常干净和经典的,一个讲select的案例,大家想象一下,反正我是想象过。
就是如果这套逻辑让我不用behavior tree,用状态机去写的话,我觉得那个那个状态机做出来的话,其实就算我做对了,它的可读性是很差的,大家仔细想,包括前面我们跟大家讲动画的时候对吧。
大家看到那个状态机其实就算一个简单的人呢,走跑跳这个idol的状态的变化,那个状态机就已经很复杂了,那么当我们去定义一个人的行为的时候,其实状态机表达的时候非常的不直观,这个时候大家就能很强烈地感觉到。
为什么就行为树这个模型,它其实真的很好理解,但是大家仔细读这个案例,就到时候我们会把这个课程作业给大家,就大家仔细的去看这个案例的时候,你们就能感受到,就是说笔记TRE,它在人类的阅读角度上。
是非常的符合我们的认知的好,那最后一个control呢就是说我们叫做PARALA,叫B性化节点,这个事情很有意思诶,不是说状态机要求我们很多时候都在一个状态,为什么这个时候我们能让这个行为树。
同时执行两棵子树呢,这就会让我的执行变得非常的混乱,对不对,但这里面也是行为树一个非常重要的一个啊,一个一个一个是我觉得是个很天才的设计,就是说我们观察人类的行为啊,其实人类的行为很多时候它是多线程的。
什么意思呢,就是说我们在很多时候,我们的AIT的行为它并不是单一的,他可能是从A点一直走到B点,我在赶路对吧,赶到门口,但我的枪可能会不停的左右循环,如果这个时候我发现有个敌人的时候对吧。
我可能用枪开枪,但是呢我的任务是要从A点左边,所以其实我同时在直线两件事情,比如说我现在AI进入的状态是什么,就是说我要赶快去支援我的队友,队友在那个地方,但是呢我要沿途如果发现了敌人。
我要开枪去射击对吧,就是要保护好自己,大家想想这是不是我们人的正常的反应,对不对,所以其实这就是behavior tree,它非常符合人类的这个认知,就是说知道我们人其实是一个多线程的,这个物种。
但其实所有的bot也是这个逻辑,所以PARALA就这么理解,所以这里面呢就引入了这个,这就是我们的行为术吧,就是大概可以总结一下,这几个几几类节点的这个分布,这里面我就不展开了,因为前面讲的比较细。
但是呢这里面我要讲一个很重要的一个点,就是说行为术,他在每一个tick就这里面讲的是引擎的,同学们要注意了,就每个tick的时候,你怎么去TIK,在前面我们讲到的动画树也好,讲的很多的树也那个蓝图也好。
它它其实更像状态机,就是它总是会停在一个状态对吧,下一次TK来的时候,我会从当前这个状态开始继续往后去找个个的,就是condition,说这个我的边,我的连出去的边的。
肯定是只要满足了我的状态机发生切换,对不对,那么这个效率是很高的,但是行为树这个时候他的问题就来了,就是行为术,你要去taking他的时候,要像人一样,他每一次都会从根节点重新开始去TIK。
因为这个时候当你现在在做一个任务A的时候,在做一个action a的时候,假设这个时候出现了一个新的情况,你action a其实没有做完,比如像刚才那个那个,就是那个啊小吃豆人的案例,比如说你那个小人。
你现在还在巡逻,但是下一帧的时候,你突然发现你面前出现了一个敌人对吧,你这个时候应该要把那个巡逻的那个action要打断,不是把它做完,因为那个那个任务可能是无限循环的对吧。
那个action是无限循环的,你要把它打断,然后呢进入到我行为树的下一个分支,有的时候那是相当的像polo的话,他有的时候是什么,会同时执行两个行为树啊,两两个行为次数。
所以说行为术大家始终牢记一个一个关键点,就是说啊其实两个点,第一点就是说每一次tick它的时候,你要从根节点重新去TIK它,否则你肯定会写出很多AI的bug,第二件事情是什么呢。
就是说行为术同时在running的节点,可以可能不止一个,可能它激活激活的节点有很多个,同时在激活,只要符合你的规范,当然了,我们会看到有些游戏引擎,它对这个事情做了一个优化,因为这个电话产生一个问题。
就是说使得我们行为术每一次契合的时候,成本非常的高,你得反复的对吧,你想想如果这个这个AIT的行为很大,是一个很大的数,你结果每一次每一帧的时候要从头开始tick,所以呢像有些引擎里面他做了个优化。
就是说你还是从当前这些激活的节点去去tick,但是他做了一些event,就是当这些event发生的时候,有一些此树会直接被激活,甚至是直接整棵树被激活重新替换一遍,但这个呢。
其实我认为是对传统的行为树做了一个优化,这个优化本身我认为也是非常合理的,但是他这边产生的一个小问题,就是说设计师呢他必须对这个东西很清楚,否则的话他如果没有做这些激活的event的话。
这个行为树的行为可能就会出错,你会发现你的bot在很傻的在做同一件事,你明明发现诶,这个比如说这个你在这个bot在前面前面砍人,这个时候主将已经发出指令了,说你要跟我来。
但是那个那个那个那个bot还在傻乎砍,因为他要把那个人砍完,这个任务完成,就把那个敌人砍死,任务完成之后,他才跟着主角跑,但是我们人类的行为认知是说,虽然你现在砍人了,对不对。
但是你听到你的这个呃主将发出指令说,跟我走的时候,他应该是尽快的这个把刀收起来,撒腿就跟着你跑对吧,所以其实他那个行为是要被打断的,而且他前面一整套角色的话,要从前面开始跑起来。
所以的话呢这是行为树的一个在ticking的时候,很重要的一个点,OK好,那么其实呢行为术其实传经典的定义啊,其实五类节点基本上就够了,但是呢这里面要跟大家讲一下,就行为术其实后面大家都做了很多的优化。
刚才我讲的就是在效能上大家有优化,就是说可以用event去驱动部分,但其实还有一个重要的东西叫decorator戴AI,他很多人做什么呢,他就是说加上一些我们很常用的东西。
比如说我希望这个action还会loop对吧,我希望这个action呢稍微等一下,不要马上就发生对吧,我希望呢这个action呢能够修改一些环境变量,这些这些杂七杂八的零碎的东西的话。
我们用一个decorator能解决,它,就是一个DEXTER,你挂上去之后,你可以做各种几类不同的动作,这里面给大家举个简单的例子,比如说我们希望一个一个人在巡逻,一会儿是A点,一块是B点对吧。
那大家想想这个东西很简单,是一个sequencer对吧,A到B,但是呢我们希望它到每个目标点之后呢,它能够先停个一秒,那如果用传统的这个行为术去做的话,我们要加四个节点C课程,比如说等一秒。
然后呢移动到A点好呢,到了A点之后,再等一秒移动到B点,我以此循环,对不对,哎,但是如果用DECORA就很简单,我在这个物的A的B前面我加一个小小的修饰,说兄弟仅等一秒我们再来,就决定往前走。
所以其实DECORA很多时候就是,它并不是一个什么新的这个数据结构,它只是让我们的这个行为树的话呢,读起来更加的简洁,包括行为术后来很多的优化,比如说我们给每个行为加一个precondition对吧。
就是A其实会把很多的不必要的那个条件,分支给它省掉,就相当于这个分支,我的下去是你的precondition满足了,我就我就直接往下走,如果不满足的话,我直接返回一个false。
这样这个数看上去更简洁了好,所以呢就是说哦我我这边已经提前讲到了,实际上这个地方讲的就是precondition的概念,就是说如果引入了一个precondition的话,刚才像那棵比较复杂的树。
就是那个啊吃豆人的这个案例嘛,你就会变得这么简单,就是说哎我一个selector,就是说我面前有没有看见那个ghost对吧,如果ghost完全没有看见,直接跳到后面,那个就是说我这个就是啊去找药丸去。
如果这个时候我看到这个ghost的话,判断第一我现在是不是在proper状态,如果是的话,我就去追ghost,如果我不在post,我就直接逃跑,你看看刚才那么一个补起来还是有点那么小,费劲的那棵树。
现在变成了它的可靠性是不是高了很多,所以我们在讲什么叫行为树,这个这个这个数据结构它的核心是什么,就是说在数学上,我个人认为呢啊他没有什么特别的,这个就是特别的地方,但是他最了不起的一个地方。
就是说他真的把设计师设计这个AIT的行为,变成一个人类好去理解好去维护的东西了,所以说这个这个地方实际上有很多,很巧妙的设计思维,另外一个,我觉得它的设计美学上有个很美的地方。
就是说他只用了这种五类节点对吧,就能够几乎把我们想到所有的这个AIT的行为,都能给它定义出来,其实你们会发现我们人类在做很多事情的时候,它就是一个分支决策,分支决策的行为,其实大家如果有机会学那个战略。
比如说啊企业经营啊,或者是军事的那个部队的军事训练上,也是一种叫军事决策的话,其实他们用的也是一种决策树模型,其实跟我们的行为术模型是还蛮像的好,那么在行为树里面的话,还有一个很重要的概念。
就是那个BLACKWORD,就是个黑板,什么意思呢,就是说其实这个在我们的这个前面讲的,game play系统里面已经隐隐的提到,就是说其实在整个游戏跑的过程中间,它会产生很多的环境变量。
就是说当我做了一个行为,我可能会修改这个环境变化,比如说我下一个行为我是攻击了别人,那这个别人到底有没有被杀死呢,如果我现在返回,我是一个这个我现在还在running,但是呢我想告诉别人说这个是敌人。
我其实已经杀死了,那我下一次AI角色的时候,可能根据说我刚刚杀死一个敌人,我这是干嘛,我要做一个庆祝动作对吧,那我心中动作播完之后,我要把我杀死的那个状态给清洗掉。
那这在这个分支数的两个不同的分支的时候,我们怎么去交换信息呢,哎这时候我们会做个黑板,这个黑板的话呢,其实非常像我们之前讲的event系统一样,就是说他用一个IDE,用一个名字和他各种多态的数据去表达。
然后行为树的不同节点,他去消费这些数据,所以呢这是这个就是你行为术,我们认为最后一个building block基本上讲到这儿的话,同学们想想看是不是已经有点急不可耐了。
我终于学完了navigation对吧,我终于学完了这个寻路,那个那个pass finding的寻路,我学完了这个就是说对环境的感知,我学完了steering,然后呢。
终于老师教我了这个这个这个要么用状态机,要么用行为术,其实这个时候实话实说,你可以作为一个简单的很好玩的游戏了,包括为什么我对讲AI讲的这么热心啊,就是因为我觉得这个地方是真的是很好玩,真的是非常好玩。
就是如果大家不想写那么复杂的渲染对吧,那么复杂的这个动画系统,那么复杂的物理系统,还有游戏逻辑体系,光就AI这一块写起来是非常游戏,而且你可以根据自己的想象,构建各种各样的游戏,那么所以说呢呢。
行为术是一个非常好的一个系统了,就像我们前面讲了,它的好处特别多,就是特别符合人类的认知,而且呢很好维护,另外一个很大的好处,就是它DEPAR也很方便,因为它真的是就是可以你观察一个AI的行为。
我可以直接告诉你说哪些节点被激活了,这里面我展示的这个虚幻的行为术,的一个案例吗,就是他会这个有的行为术,比如说它是从左向右的对吧,有的行为术呢是从上向下,这都不重要,这都是细节,重要的是它基本的逻辑。
是符合这样的一个结构的,那么但是行为术的一个它其实也有一个缺点,这个缺点的话呢就是这样,我们前面讲到的就是说啊,他每一次tick如果不做优化的话,都从根节点开始,那这个东西的话呢其实是这个比较废的。
好行为术是不是完美的呢,包括为什么我们AI讲完这么多了,我们还要再讲下一节课呢,这里面就是我也算留一个,就是这个最last word吧,就是给大家讲一句,为什么我们要讲。
下节课给大家讲一个非常high level的东西,其实大家有没有发现啊,无论是状态机也好,行为术也好,有一种叫什么呢,叫做神挡杀神,佛挡杀佛,就是我遇到这个情况,所以我就怎么怎么干。
我干到一半又发生了新情况,所以我就又怎么怎么干,其实这种感觉就像什么呢,就像是我是个robot对吧,其实我没有思想,没有灵魂,我只是遇到情况,主人告诉我说我该怎么处理,我就怎么处理。
所以它的好处是什么呢,确定性非常高,但是它的坏处是什么呢,There’s no purpose,就是没有目的,但是我们人类在思考所有的问题在说什么,我们是有目标的对吧,那么所以呢在下一节课。
我们我们会跟大家讲的一系列的,其他的这个前沿的决策算法,包括就是基于深度学习的一些方法,其实这里面有一个跟这个算法,它有很大的一个转变是什么呢,就是说他会把我们现在所有可以做的动作,变成就是一个选项。
然后呢我根据世界的状态,我要形成我的什么呢,我的目标,我要我的目标,比如说我的目标是要做一个优秀的战士,所以我要杀足够多的敌人,那所以呢我的规划就是要在战场上,寻找到敌人最密集的地方。
其实我要打得过对吧,这个时候我要保护好自己,把敌人消灭,大家听一下,这个东西是不是更像我们人的人去思考,所以你会发现就是当AI研究到这一步的时候,我们会发现传统的无论是状态机也好,还是行为树算法。
它更像像像我刚才讲他是个forward的方法,就是脚踩西瓜皮,走到一步是一步推导的方derive算法,但是的话呢其实我们人类在做很多动作的时候,我们是个逆向的过程,因为我有个目标对吧,我为了考上大学。
所以呢我要好好学习,我要我好好学习呢,我就必须要学完数理化对吧,数学要从最低级的高级的开始,中间我要做习题,我要做很多动作,所以这里面就有一个go目标和planning的问题。
所以这就是我们下节课要讲的一些前沿的AI,算法的话,他的设计思想,所以这里面就会有一些搜索,就会有一些planning,就是规划的问题对吧,包括我们要用一些深度学习的算法,能解决我们部分的东西。
所以下节课的话呢,我个人还是非常的期待,我觉得非常的有意思好,那今天讲到这儿的话,我今天已经讲了哇,我又创了个新纪录,今天我们讲了两个小时,差不多15分钟,那今天我们课程的主题到此结束,到此结束。
那再次呢就是这个这些的话,是我们今天的那个所有的参考论文,那同学们都会发现,就是说哎,这一节课的参考论文的数量是比较多的,确实是基本上每一个算法,大家的话呢如果想深度了解的话。
你会发现这里面有很多的文章,大家可以去读,这就是游戏引擎的魅力,就是说它确实是一个很深的一个东西,那再送我们要感谢一下我们的课程组,其实我们课程的小伙伴已经绝对不止这些同学,对吧。
大家这段时间你想我们能写出180页的PPT,这简直是这个我我觉得我们的概率还是很大的,那么好,那最后的话呢就进入我们的QA环节了,同学们有什么问题,好的OK好,我看到了啊,第一个问题来了。
就是说啊行为术和if else有什么区别,这个问题问的太太厉害了,就是说就行为术和我们在写高级语言的,这个if else的这个分支有什么区别,其实呢一大家如果去理解这个if else的话呢。
其实它就是一个最简单的行为术对吧,它实际上你可以认为它完全是由这个,就是叫selector构成的一个数,就是我往下走,if条件满足了,我就做什么动作,如果不满足,我就选择下一个,对吧好,我就往下钻。
每一次都这样,你可以认为if else是行为术的一个特殊情况,但是就像我刚才讲的,就是如果你用if else的话呢,你很难去写一个planning,当然你可以说衣服这个条件满足了,我。
请一一次执行下面的动作,这个动作过程中间我说衣服发生了什么事,这个时候我们该怎么办,诶这个时候大家如果学习高级语言,有一个指令叫做其实不是高级语言,汇编里面就有这个指令叫什么呢,叫go to对吧。
或者叫jump,其实行为术它的设计,如果你真的强行的用if else去模拟的话,你会发现你的代码中有很多的衣服,什么条件满足了我,go to或者降不到一个什么地方,所以这就讲我就讲语言的美妙的地方。
就是说他在数学上大家都是等价的,但是的话呢行为树的这个表达,会更符合人类的这种规划感,人类的这种就是说啊就这种条件来了之后,我会各种反射或者中断的那种感觉,所以说啊这是我理解啊。
就行为术和衣服else的区别好,希望这个回答让同学们满意啊,然后第二个就是说同学们问我的是说是不是AI,怎么去这个就是sensing,就是从环境里提取数据,其实这个问题如果从很专业的角度上讲,就是说呃。
当我们做一个真实的游戏,或者游戏引擎的这个AI系统,其实对环境的perception,这件事情并不像大家想的那么简单,其实他会他有两个大的挑战,第一个挑战呢就在我的课程中已经提到。
就是说来自于环境的数据真的是异构的,多种多样,有的时候你读的是一个变量对吧,有的时候你读的是一个数值,有的时候你读的是一个true false,有的时候呢你读的是一个空间上的坐标点。
有的时候你读的是一串数组,比如说我当前所有可见的敌人是不是一串数组,对不对,就几人的id,哎有的时候你读的是什么呢,是一张图,比如说我读的读进来的一张infant map。
我要对这个图还进行query对吧,所以这些异构数据怎么在我的AI体系里面,用一套统一的语法去表达和获取,其实这对于你的引擎设计是有一点点挑战,但是其实在前面大家如果听到我们讲反射呀。
去讲到这个event系统的这个payload设计啊,其实隐隐能感觉到,其实用这套其实我们是能解决的,那么第二个是什么呢,就是第二个阶段,就是说其实AIT在获取环境的数据的时候。
实际上的话效率比他想象的要低很多很多,为什么,比如说我们做一群能战斗的AI at,它真的在一个几百个就是战场里面,有几百个AIT在一起的时候,我眼睛看过去,其实每一个AI at它都能看到至少几十个敌人。
对不对,这个数量其实还是比较保守的,那我有几百个AIT看到几十个敌人的时候,这时候你发现没有,我要读上万个G的数据到我的AI里面去,然后呢这AI呢我不仅是只是读他的id啊,我还读什么呢。
读他的tm id,就是说他跟我是敌是友啊,第二个就是说诶他现在是什么状态呀对吧,他现在是不是有武器啊对吧,包括那个他现在离我的距离远近啊,我现在能够得着它,比如说他现在是不是残血还是我残血啊,对不对。
你会发现就是当我真的做一个AI决策的时候,我要不大量的复合数据,那这些数据的从系统中query,实际上是一个很废的事情,所以说我们在做AI行为的时候,我们很多时候是要对世界的这个数据的。
perception的话,是要做指令的规划和处理的,所以的话呢这是我认为就是AI读指令的,这个行为的话,两个设计上的一个难点,就是也供大家去参考,那第三个问题我看有什么问题啊。
如何处理垂直零界面的novation mesh的生成,哎呀我的天呐,这个就是很难的一个问题了,首先这个垂直零界面他讲的,如果你讲的是这样的一个一个直倒角的话。
呃按照那个比如说一些经典的navigation,match的算法的话呢,他要么就直接越过去了,比如说你的垂直面不是特别高,他就直接越过去,认为你是可以直接通行的,如果是特别高的话呢。
他会认为这个地方就断掉了对吧,他就在下面形成了一个navigation mesh,上面也会形成一个navigation mesh,如果大家讲的是这个问题,那么就所以说的话呢。
其实反正我们自己用这个navigation mesh,生成算法的时候,一般到种垂直零界面,都是我们觉得最痛苦的一个问题,那么当然如果比如说啊就像我讲的,就是我们有的时候会处理一些像攀爬的问题。
在引擎里面这个地方我们一般的做法是什么呢,就是说在引擎里会提供一套工具,让这个设计师能够标记一些可攀爬的点,这些点我会标把标记它的端点,就是说你在无论你在下面还是在上面,你都可以知道到这我就可以攀爬。
当然到这儿的话,整个寻路的行为,包括这个人物的动作,它都会发生变化,比如说你在平地上走,那你的这个比如说NPC它是正常走的背,但一到那个地方变成攀爬的时候,它可能要把武器背到背上对吧。
这个手开始开始抓着墙,开始爬,爬到顶上的时候,还要做一个诶翻身上墙的这么一个动作,那你上去是一组,对不对,下来还是一组,而且呢这些数据就是说垂直面的零界面的话,如果我是用这种方法生成的话呢。
我们一般会把这个数据不要存在navigation mesh里面,我直接存在这个word space的坐标里面,这样的话呢当我的环境,比如说其他人去改的时候。
因为他每一次生成的vacation mesh都会不一样,所以这些信息是没办法保留的,所以我只能把它存在世界坐标系里面对吧,然后这样每一次NEC版生成之后,我那个地方再给他补一下。
要么用NAVI把它连上去,要么我加一些,就刚才我讲的那个就是特殊的这个江湖point,把它跳过去,所以这个地方的话呢是一个就是真实在实战中,大家用的gsh mesh的话,就会遇到很多很恶心的case。
这些其实也是很做游戏引擎很挑战的一件事,就是玩家玩的时候天天会骂,说你的AI有很多的bug,但是我们真的在做系统设计,做这个底层技术的团队,就知道你想把它做到符合大家的期望,其实有很多的技术挑战,好的。
那今天的话呢就是说呃我先回答了这么多问题,因为今天时间确实有点长了,那再次谢谢大家,就是说能够那个就是那个听完我们这么长的课,那下一节课呢我们会讲高级的AI的一些技术,那么争取的话呢。
我们用三节课的时间,把整个玩法系统和AI系统给大家讲透好。