|
最近用虚幻蓝图(之前代码写的多,蓝图用的相对少),也在考虑这个问题。
客观上,作为信息的两种不同表达方式,图和代码各自有各自的特点:
代码一般是连续的序列,清晰而单纯,操作简单,图上拖来拖去的事情,敲几个字符就可以解决。但是,不要忘了,代码中的长分支、长循环、goto一般都是不被建议的,因为会极大降低代码的可读性。
图是图状组织,可以很直观轻松地反映出来模块、数据间的关系,但是如果用图只是单纯做序列,则不如代码那么方便。不使用图形编程,我们也经常需要画流程图、UML图、序列图,这就说明在我们的工作中,图这种信息组织形式是很有帮助的。
所以按照这个感觉,Blockly这种的,私以为只能算作“图形化的序列”,其核心仍然是代码序列。当然也算作一种“图形编程”,但是这种,距离蓝图的Ubergraph的差距就比较远。个人理解,真正能够发挥图形编程优势的,还得是超越序列的东西。
此外,过去我曾经觉得图在描述“非瞬时”的序列上也有优势:
比如“我走到桌子前,拿起一杯水,喝”。
在Coroutine普及之前,需要实现“走到桌子前”,“拿起水”,“喝”三个FSM状态的转换关系,无论是用update+标记还是Listener、回调来实现。
不过,通过 Coroutine,代码实现这种无关系的简单序列也毫无压力:
this.WaitForSequenceEnd("GotoTargetActor", TargetDesk, [&]() {
this.WaitForSequenceEnd("Bring", TargetCup, [&]() {
this.WaitForSequenceEnd("Drink", TargetCup, [&]() {
// All sequence end.
});
});
});
但是,如果这种序列的结构再复杂一些,基于Coroutine的写法也会比较乱了,至少,得考虑把各个Lambda表达式抽出来,以备重用了。
目前主要来用蓝图的UberGraph做FSM的“状态转换”的流程图。
这种图,其实过去本身在纯代码工作的环境下,一般也是需要先在文档里画一张图,然后照着图来写代码或者配置脚本。
目前的方式是:直接在蓝图里通过一些自定义节点和Reroute node来打草稿,尤其是状态机的草稿,然后慢慢往里面加功能。
使用的节点都是概念化的、抽象度比较高的节点,比如“等待所有观测对象进入静止状态”,嗯,对,这是单一的一个节点,有用代码来实现这么个节点的,也有用蓝图包宏的。但是在这种关系图里,只出现抽象度差不多这种程度的节点。
感觉是比“这边置个标记,Tick里根据标记分支”,以及“这边Trigger个Event,那边Listen并处理”的做法要清晰一些,重构的时候也方便一些。
但是目前还没有做到特别复杂的,相比于Coroutine的代码写法而言,主要的好处还是直观,倒不是说代码就完全做不到。
总之就是,时时刻刻把这张图当成是在文档里画流程图,而不是在编程。
刚刚尝试了一段时间,坑也不少,还在继续跳,未来也许会有新的发现。
每个黄色区块是FSM的一个状态,所以可见跨区域的长距离连接,以表述状态转换关系
图中大量使用这种抽象度较高的Coroutine,比起那种“这个地方Trigger,那个地方侦听”要直观很多
总之,就图本身的特点来看,利用纯图编程的工作过程中,最好是可以更多聚焦于“关系”、“时序”而非单纯的“代码序列”。否则发挥不出图的优势,却会面对图操作复杂的劣势。
如何在工作中发挥两种组织方式各自的优势,而不是考虑两者之间的简单替代关系。私以为更加重要。
补充1:
图还有一个地方是比代码要有优势的,就是可以直观显示运行过程。
比如,蓝图调试时,当前正在被执行的连线加粗,这都还只是最初级的。
最具代表性的,是材质图、或者Substance这种节点图,每个节点都可以把当前运行的中间状态直接显示出来。
工作流中如果有这方面需要的,其实也可以考虑加强相应的工作流。比如,基于Noise的地形生成之类的。
找专家来帮着解决问题固然重要,但是,工具能更加简单易用,让更多的人能通过工具来完成自己的目的,对于整个项目而言,也是很有意义的事情吧? |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|