最近在做一个 NPC 的 AI,不想写代码,看看库里还有去年 HB 包里的 NodeCanvas,想着拿出来学习一下,不然不知道又得吃多久的灰了...
首先这种高质量的插件都有相应的官方网站,所以我的第一建议是查询官方手册和解决方案:
https://nodecanvas.paradoxnotion.com
目录
图1-1
在菜单栏 Tools 里看见选项 ParadoxNotion 即导入成功
在下载完成后这个包里是没有 Sample 样例的,是可能比较迷惑的一点,不过不要担心,官方网站是有提供的,我们参照实例解读学习,下载后正常导入 Assets 即可
样例下载地址(下载 Example Scene Unity 2018.4.14+):https://nodecanvas.paradoxnotion.com/downloads/
首先我们在 Project 窗口里找到我们导入的文件夹(如图2-1),观察到有两个文件夹,而 _Common 文件夹是存放一些预制和代码的,其实例部分是在 Scene 文件夹,打开可以发现有三种样例场景,正是对应了 NodeCanvas 三个核心功能模块

图2-1
· BT Scenes(Behavior Tree):行为树,我的理解是做一些 Enemy、NPC 自动化的处理行为
· DT Scenes(Dialogue Tree):对话树
· FSM Scenes(Finite State Machine):有限状态机,能自主操作的对象的状态设置
我先选择 BT Scenes 文件夹中的 Events 场景来举例
打开场景后,观察 Hierarchy 窗口里的物体,有个叫 GameObject 的物体较为特殊,因为它的右方有个小图标,那就是带有 Node Canvas 行为树 Component 的个体,然后观察它的 Inspector 窗口,点击 Behaviour Tree Owner 组件的 EDIT BEHABVIOUR TREE 即可进入 Canvas 编辑器(如图3-1)
图3-1
我们来了解一个重要的东西 —— 节点 Nodes
在 Canvas 面板上空白处,右击或是空格可以调出节点面板,如图3-2 
图3-2
注意:这里三个不同的状态机的节点是不一样的,后面会讲述
BT 节点类型:
Action:事件节点,添加自定义的事件,达成自定义条件后执行,返回一个自定义的 Failure 或 Success 值
Condition:条件节点,添加自定义的条件,达成自定义条件后执行,返回一个 Failure 或 Success 值
Composites:进入到子页面,可以选择一个复合执行路径顺序(方法)
Decorators:进入到子页面,有非常多的条件判断方式、执行方式
SubGraphs:可以创建并连接其他的状态机
因为有如此丰富的节点,一个个在这描述不太现实(可查询手册,更详尽清晰),所以要引出一个重要的学习方法——看注释
如图3-4 ,这里选择 #1 节点,红色框选的内容就是该节点的注释,通过注释我们能了解该节点体内容:做什么事,返回什么值等等(节点类型是图3-2里的其中一个) 
图3-4
在了解每个节点的作用后,我们开始试着理解整个流程
一般是,从上至下,从左至右的顺序查看(Composite 箭头方向),所以先看 #0 ,我们前面讲过一个 Composite 节点类型,这表示子节点运行的方向方式,这里是 Composite 中的 Selector 节点,第一,阅读注释,简述一下:「计算所有子节点,如果所有的子节点返回 Failuer 那么该节点返回 Failure ,如果有一个子节点返回 Success ,则该节点返回 Success」,然后看 #1,这是一个 Condition 类型的节点,里面有一个重要的按钮,Assign Condition Task,通过它可以命令该条件节点分配什么任务,比如这里是检测一个事件,所以在这个示例中我们需要找到该事件的发出者,一般来说要么是单独的脚本挂载在该人物上,又或者是其他的节点发出,该示例就是通过 #7 节点发出的事件,这是一个 Action 节点,不是上面 Condition 判断类的,是直接执行你分配的任务,这里的任务就是延迟一秒发送事件,将 Event Value 设置为 50,然后 #1 节点再将 Event Value 的值赋给设置的全局变量 value,我们再看 #2 ,这是最简单的 Composite 类型的节点,就是从左至右执行并返回子节点结果,#3、#4 就分别是打印 value 值和重新将 value 值设置为 0
整个流程就是,按下空格键(#6),发送名为「My event」的事件(#7),然后接收事件并赋值 value(#1),打印值(#3),更改值(#4)
可能会有一些问题:
Q1:#0 上的 start 什么意思?如何设置?
A1: start 就是整个流程第一个开始运行的节点,右击 Set Start 即可如图3-5(整个流程只能有一个 start)

图3-5 这里随便创建了一个 Composite 类型的节点演示
Q2:#0 中的 Dynamic 什么意思?为何勾选?
A2:将鼠标置于上方,如图3-6 的注释描述,表示「会每帧都计算子节点返回值的类型,如果有一个 Success,那么立即终止其他还在计算的返回类型,并执行该子节点,返回 Success」

图3-6 根据项目需求灵活勾选
Q3:节点旁边的像 #0、#1 这样的没有
A3:如图3-7勾选

图3-7
Q4:什么是全局变量?
A4:如图3-8,就是在黑板可以定义不同类型参数,供给该编辑器 Graph 在任何节点有需要的话可以调用,也可以在菜单栏 Tools -- ParadoxNotion -- Node Canvas -- Create -- Global Scene BlackBorad 创建所有场景都能调用的变量参数(@GlobalBlackboaed 命名后存好为预制,并在其他场景拖出来即可)

图3-8
打开文件夹 DT Scenes 中的 DialogueExample 场景示例
上面提到的,先找那个小图标,也就是空物体 DialogueTree,点击 EDIT DIALOGUE TREE,进入编辑器,我们右击或是空格可以发现,节点不一样了,但没关系,一样先看示例,读注释,首先看第一个 #0 say 节点,意思比较容易理解,就是选择一个 Actor 来执行文本框的对话,因为是 Start,所以他是第一个执行的节点,然后待 #0 说完后,就是 #1 ,完后就是 #2,这是 Branch 节点类的 Multiple Choice 复合对话框,这里要选中 #2 选择左边框内的 Add Choice 才能添加对话,每个对话我们都可以添加任务,其中有个 Available Time 是设置对话持续的时间,拉到 0 可以自动不消失,然后这里添加的是一个 Bool 值的判断,设置了两个全局布尔变量,再往下就是各个支节点,依次往下阅读就是说话然后做 Action 节点直接做指定的任务,这里就是播放动画然后设置两个全局布尔值为 True,以达到下面 #6、#10 Control 类型的 Jump 节点到Multiple Choice 复合对话框,以至于不重复对话
但其实光这个编辑器里的内容是不够组成对话的
上面提到的,我们其实还要去设置 Actor,在 Hierarchy 窗口里有三个人物(Player、John、Doe),随便点击一个查看 Inspecter 窗口里的内容,是需要 Add Component 一个叫 Dialogue Actor 的脚本组件,还有示例中需要的 Animation 组件(控制动画),为需要对话的角色添加了这个脚本后,再去找这个示例是如何触发这个对话的
在 Hierarchy 窗口里有个叫 CLICK ME 的物体,它是添加了 Text Mesh 的,也就是说点击文字 「CLICK ME」就可以触发动画,关键还在于它自身添加了 Click To Start Dialogue 组件,它所需要的 Dialogue Controller 就是上面提到的空物体 Dialogue Tree(其自身有 Dialogue Tree Controller 组件),拖拽上即可
回头如图4-1,依次添加三个 Add Parameter,把上述的三个人物拖进来(其自身有 Dialogue Actor组件),编好名即可返回编辑器,为每个节点把说话的人物选择上即可,如图4-2

图4-1

图4-2
OK,这个示例应该是解读的差不多了,还有几个节点示例没有给出,自己可以研究一下噢
可能有一些问题:
Q1:有其他方法触发对话吗?
A1:当然,上面提到了一个 SubGraphs 节点类型记得吗,利用它就可以连接起 BT 或 FSM了(也可以在 DT 里再套一个 DT ,也就是调用另一个对话)
Q2:我在自己场景弄了一个对话 AI,但显示一个警告,如图4-3,对话 UI 没有显示
A2:
方法一:自己绘制 UI,然后添加组件 Dialogue UGUI(Script),一一将绘制的 UI 拖到相应的位置即可
方法二:利用这个场景的示例,将 Hierarchy 窗口里的 @DialogueUGUI 保存成预制,再拖到自己场景的 Hierarchy 即可(还有 EventSystem,如果自己场景没有,也需要保存成预制然后拖拽到自己场景)

图4-3
留一个 FSM 的示例,大家通过以上方法来自己尝试理解噢
· 添加属于自己的注释
双击想要更改的节点,将图5-1高亮部分改成中文翻译或者其他你想修改的都可以,效果如5-2(谷歌翻译结果)

图5-1

图5-2 「孩子」应该是子节点哈哈
同样的,在每个节点都有可编辑的 Comments 和 Tag,在 Commects 里输入这个节点的作用可以方便记忆,如图5-3

图5-3 节点下方就是示例注释的 Comments
· 菜单栏 Tools 工具
有很多可视化工具也可帮助你的开发过程,如图5-4,例如这个 Graph Explorer,另一种竖式查看根节点看法,鼠标触摸有颜色提示如图5-5

图5-4

图5-5
感谢各位的观看,这也是我学习的一个笔记记录,希望对你有帮助,
如有错误还请指正,
再次感谢!
bilibili:@小冰小零
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t