﻿1
00:00:04,300 --> 00:00:05,230
‫你好, 欢迎｡

2
00:00:05,230 --> 00:00:12,160
‫在这节课中, 我们将讨论在行为树中的选择, 比如让一个敌人朝我跑来,

3
00:00:12,160 --> 00:00:20,500
‫如果他看不见我, 他会跑到我最后的位置, 基本上做两个行为, 一个追逐,

4
00:00:20,500 --> 00:00:24,250
‫然后是调查.

5
00:00:24,250 --> 00:00:26,080
‫让我们深入了解一下如何实现这一目标｡

6
00:00:26,080 --> 00:00:27,370
‫使用行为树｡

7
00:00:28,470 --> 00:00:35,520
‫因此, 虽然这种巡逻行为非常有启发性, 但它并不是我们真正想要实现的行为｡

8
00:00:35,700 --> 00:00:39,300
‫我们更希望得到的是类似以下的东西｡

9
00:00:39,300 --> 00:00:44,820
‫当我们能看到球员的时候, 我们就会尽可能地靠近他们｡

10
00:00:45,240 --> 00:00:52,670
‫当玩家消失在我们的视线之外时, 我们会尝试移动到玩家最后一次出现的位置去寻找他们｡

11
00:00:52,680 --> 00:01:00,390
‫而一旦我们用尽了这个选择, 也许我们会回到一个不同的地方, 或者我们只是暂时呆在那里｡

12
00:01:00,840 --> 00:01:02,430
‫那么, 我们如何才能做到这一点呢？

13
00:01:02,460 --> 00:01:05,910
‫好吧, 目前, 我们还没有办法通过一系列的行动来实现这一点｡

14
00:01:05,910 --> 00:01:09,240
‫我们需要根据现有的数据做出选择｡

15
00:01:09,240 --> 00:01:15,270
‫我们需要在是否能看到玩家, 是否能看到玩家, 是否有最后一个已知的玩家位置,

16
00:01:15,270 --> 00:01:18,680
‫诸如此类的事情之间做出选择｡

17
00:01:18,720 --> 00:01:24,060
‫因此, 要做到这一点, 我们需要在行为树中添加一种新类型的节点, 称为选择器节点｡

18
00:01:24,060 --> 00:01:29,280
‫如果我们右键单击并查看复合图, 其中一个选项是选择器｡

19
00:01:29,430 --> 00:01:33,330
‫现在, 选择器（而不是序列）将这样运行｡

20
00:01:33,360 --> 00:01:40,740
‫如果在选择器下面有一堆任务, 它将一直运行, 直到第一个任务成功｡

21
00:01:40,740 --> 00:01:45,420
‫基本上, 它期望所有的人都失败, 直到其中一人成功｡

22
00:01:45,660 --> 00:01:48,210
‫而现在的顺序正好相反｡

23
00:01:48,210 --> 00:01:50,460
‫它运行所有这些服务器, 直到其中一个失败｡

24
00:01:50,640 --> 00:01:52,820
‫为什么这有用？

25
00:01:52,830 --> 00:01:56,640
‫为什么选择器运行其中一个而仍然有一个成功是有用的？

26
00:01:56,640 --> 00:02:01,140
‫因为基本上我们希望它选择第一个可行的选项｡

27
00:02:01,830 --> 00:02:08,970
‫现在, 我们要做的不是在选择器下运行任务, 而是在选择器下运行序列｡

28
00:02:08,970 --> 00:02:12,360
‫所以在根级别, 我们要做一个选择器节点｡

29
00:02:12,570 --> 00:02:17,160
‫然后在这下面, 我们把这个序列移到一边, 创建其他序列｡

30
00:02:17,160 --> 00:02:23,220
‫这里有一个序列,

31
00:02:23,220 --> 00:02:35,280
‫我们可以把这个序列从being序列重命名为Chase, 然后我们可以在这里添加另一个序列, 这将是沿着调查的路线｡

32
00:02:35,990 --> 00:02:41,600
‫这基本上是我们的两种行为, 我们希望能够在其中进行选择｡

33
00:02:42,140 --> 00:02:49,210
‫为了让它们失败, 我们可以在节点上添加一些装饰器｡

34
00:02:49,220 --> 00:02:55,130
‫如果你右键点击一个节点, 然后添加装饰器, 你可以看到这里有一堆选项,

35
00:02:55,370 --> 00:02:59,500
‫它们基本上改变了这个节点的工作方式｡

36
00:02:59,510 --> 00:03:00,920
‫然后他们可以通过多种方式做到这一点｡

37
00:03:00,920 --> 00:03:05,870
‫例如, 我们有一个条件循环, 它允许你在序列中循环所有的动作,

38
00:03:05,870 --> 00:03:08,960
‫只要这个条件是正确的｡

39
00:03:09,110 --> 00:03:15,830
‫我们还有其他的东西, 比如锥形检查, 它是关于能见度和其他一些可爱的东西｡

40
00:03:15,830 --> 00:03:17,990
‫我们只有一个纯循环｡

41
00:03:18,110 --> 00:03:23,450
‫我们有时间限制, 所以我们只能在这个序列中停留一定的时间｡

42
00:03:23,450 --> 00:03:27,200
‫基本上有很多东西可以修改这个序列的行为｡

43
00:03:27,200 --> 00:03:33,320
‫但我们感兴趣的是黑板, 因为这是黑板条件｡

44
00:03:33,320 --> 00:03:39,950
‫正如你可以从下面的文字中看到的, 默认值是这样沿着：这是自我演员集吗？

45
00:03:40,130 --> 00:03:47,000
‫如果没有设置, 那么这个节点和它下面的所有节点都会自动失效, 这意味着我们的选择器会继续选择第二个选项,

46
00:03:47,000 --> 00:03:51,050
‫即调查｡

47
00:03:51,170 --> 00:03:53,840
‫所以在这个例子中, 我们实际上想改变一下｡

48
00:03:53,840 --> 00:03:55,220
‫我们想改变这种运作方式｡

49
00:03:55,220 --> 00:04:02,420
‫所以我们的播放器位置, 我们希望它是这样的, 它只设置当播放器是可见的｡

50
00:04:02,420 --> 00:04:10,310
‫然后我们要在这里再次添加一个新变量, 一个向量, 它将是最后一个已知的球员位置,

51
00:04:10,310 --> 00:04:16,250
‫当球员可见时, 最后一个已知的球员位置是相同的｡

52
00:04:16,400 --> 00:04:24,080
‫但是一旦玩家变得不可见, 玩家的位置就会被取消设置, 最后一个已知的玩家位置就是我们想要去的地方｡

53
00:04:24,590 --> 00:04:30,290
‫我们可以这样做, 进入黑板库条件, 将其更改为查询,

54
00:04:30,290 --> 00:04:35,330
‫设置为Yes, 但我们要设置的是播放器位置.

55
00:04:35,330 --> 00:04:41,960
‫如果玩家的位置已经设定好了, 那么我们就给予Chase, 我们给Chase, 基本上就是移动到,

56
00:04:41,960 --> 00:04:48,020
‫然后我们移动到玩家的位置, 就像这样.

57
00:04:48,020 --> 00:04:51,770
‫所以我们在这个序列下没有做其他的事情｡

58
00:04:51,770 --> 00:04:58,490
‫所以实际上, 你也可以把黑板上的条件直接放到移动中, 但是因为我们可能还想做其他的事情, 比如射击,

59
00:04:58,490 --> 00:05:02,390
‫我现在就把它作为一个序列｡

60
00:05:02,510 --> 00:05:04,070
‫然后我们就开始调查了｡

61
00:05:04,070 --> 00:05:10,490
‫调查基本上也会移动, 但它不是移动到玩家位置,

62
00:05:10,490 --> 00:05:16,250
‫而是移动到最后一个已知玩家位置.

63
00:05:16,490 --> 00:05:23,030
‫你可以看到我们没有条件, 因为如果这个条件不成立的话, 这个就不成立了｡

64
00:05:23,300 --> 00:05:29,240
‫关于这些黑板条件, 需要注意的一点是, 在默认情况下,

65
00:05:29,240 --> 00:05:34,130
‫一旦我们决定开始执行这个节点, 我们就不会重新评估它｡

66
00:05:34,130 --> 00:05:39,830
‫因此, 如果由于某种原因, 我们在移动到那个玩家的位置时看不到那个玩家,

67
00:05:39,830 --> 00:05:41,810
‫我们会继续尝试到达那里｡

68
00:05:41,960 --> 00:05:49,610
‫所以如果我们希望它能够中止这个节点的执行, 那么就有一个选项｡

69
00:05:49,610 --> 00:05:53,780
‫如果你进入流控制, 有通知的观察者, 在它下面有｡

70
00:05:53,780 --> 00:05:56,270
‫观察程序在设置为“无”时中止｡

71
00:05:56,510 --> 00:05:58,700
‫但您可以从几个选项中进行选择｡

72
00:05:59,180 --> 00:06:00,830
‫您可以将其设置为self｡

73
00:06:00,830 --> 00:06:09,800
‫也就是说如果这个条件突然变为假, 那么停止你正在做的任何事情, 重新计算你的选择器, 看看｡

74
00:06:09,800 --> 00:06:11,780
‫也许我们现在需要做的调查｡

75
00:06:11,780 --> 00:06:18,530
‫注此处的另一个选项的优先级较低, 您可以看到它更改了此处突出显示的内容｡

76
00:06:18,530 --> 00:06:27,050
‫它说如果黑板条件突然成立, 停止我们正在做的任何事情, 开始执行这个追逐条件｡

77
00:06:27,260 --> 00:06:29,840
‫最后一个选择是两者兼顾｡

78
00:06:29,840 --> 00:06:31,160
‫这就是我们想要的｡

79
00:06:31,160 --> 00:06:38,690
‫基本上, 它是说这个基于黑板的条件, 如果它为真, 我们立即开始执行这个序列｡

80
00:06:38,690 --> 00:06:44,150
‫如果它变为false, 我们立即停止执行它, 或者停止执行正在进行的任何其他操作｡

81
00:06:44,150 --> 00:06:51,380
‫我们基本上总是想按照黑板上的条件, 走任何一条路｡

82
00:06:51,380 --> 00:06:56,750
‫因此, 希望观察器中止停止正在执行的操作是有意义的,

83
00:06:56,750 --> 00:06:59,960
‫否则它将等到操作完成后再重新评估｡

84
00:07:00,380 --> 00:07:07,130
‫现在我们要实现我们最后一次知道的播放位置和播放器位置, 因为现在,

85
00:07:07,130 --> 00:07:16,370
‫如果我们转到C++, 给予自己多一点空间, 那么你会看到我们只在开始播放中设置了这个播放器位置｡

86
00:07:16,370 --> 00:07:23,780
‫理想情况下, 我们应该在每次看到兵的时候都这样做, 所以我们可以在这里取消对视线代码的注释.

87
00:07:23,960 --> 00:07:27,470
‫我们可能还想取消对else代码的注释｡

88
00:07:27,470 --> 00:07:32,330
‫但在这个例子中, 我们并不是要清除焦点, 直接停止运动｡

89
00:07:32,330 --> 00:07:35,570
‫我可以继续删除这些内容, 因为｡

90
00:07:35,640 --> 00:07:37,830
‫这不是我们目前的做法｡

91
00:07:38,010 --> 00:07:48,200
‫相反, 如果我删除代码, 我们要做的是在这里设置播放器的位置｡

92
00:07:48,210 --> 00:07:59,820
‫我们还希望在那里设置最后一个已知位置, 在其他地方, 我们希望清除球员的位置｡

93
00:08:00,210 --> 00:08:07,860
‫现在, 我们有一个函数, 它不是向量形式的集合值,

94
00:08:07,860 --> 00:08:17,250
‫如果我们打开黑板组件, 有一个明确的值, 它只取f的名字, 不需要取类型或任何东西,

95
00:08:17,250 --> 00:08:24,090
‫因为我们只是完全擦除变量中的任何内容｡

96
00:08:24,480 --> 00:08:28,140
‫现在, 我为您提供了填写分笔成交点集合的挑战｡

97
00:08:28,140 --> 00:08:35,010
‫这两个变量在视线范围内, 基本上填写if语句的第一位,

98
00:08:35,010 --> 00:08:39,270
‫然后在我们有alt时清除播放器的位置｡

99
00:08:39,270 --> 00:08:42,210
‫所以当我们失去视线时, 我们就再也没有玩家的位置了｡

100
00:08:42,240 --> 00:08:48,510
‫降低世界上敌人的速度, 这样你就可以更容易地摆脱他｡

101
00:08:48,510 --> 00:08:57,150
‫然后检查功能, 检查你是否能躲在角落里, 他是否能去最后一个已知的位置试图找到你｡

102
00:08:57,240 --> 00:08:59,460
‫暂停视频, 试试这个｡

103
00:09:00,490 --> 00:09:00,820
‫好吧, 我会的

104
00:09:00,820 --> 00:09:01,420
‫欢迎回来｡

105
00:09:01,420 --> 00:09:02,560
‫所以我们来试试吧｡

106
00:09:02,560 --> 00:09:04,240
‫首先, 我们要设定播放位置｡

107
00:09:04,240 --> 00:09:07,240
‫我将把它开始播放中移到这里｡

108
00:09:07,330 --> 00:09:12,880
‫所以我们要把参与者的位置设为我们需要把参与者的兵放在这里, 然后再勾一次.

109
00:09:12,880 --> 00:09:22,000
‫所以我会取消对这行的注释, 然后我们做同样的事情, 用同样的代码来表示我们最后知道的玩家位置.

110
00:09:22,000 --> 00:09:28,060
‫我们来看看黑板, 确保我们复制了它, 这样我们就得到了完全相同的名字,

111
00:09:28,060 --> 00:09:33,160
‫然后我们要清除, 最后一件事就是清除这里的参与者位置.

112
00:09:33,160 --> 00:09:41,980
‫所以这是一个清除值, 它会把这个文本播放器的位置作为我们在最后要清除的值｡

113
00:09:42,280 --> 00:09:46,870
‫我们可以继续进行编译, 看看它是否有效｡

114
00:09:48,640 --> 00:09:50,530
‫井编译是成功的｡

115
00:09:50,530 --> 00:09:55,870
‫我说的另一件让我们的调试更容易的事情是选择敌人,

116
00:09:55,870 --> 00:10:04,360
‫我们可以去改变他们的速度, 通过他们的移动部件, 找到他们的行走和速度｡

117
00:10:04,360 --> 00:10:07,270
‫这里最大步行速度是600｡

118
00:10:07,300 --> 00:10:11,470
‫我们把它降到400, 这样我们就可以轻松摆脱他们｡

119
00:10:11,470 --> 00:10:15,220
‫他们会走得有点慢, 然后继续玩｡

120
00:10:15,220 --> 00:10:16,720
‫他们正慢慢地向我们走来｡

121
00:10:16,720 --> 00:10:18,010
‫我们可以逃走｡

122
00:10:18,040 --> 00:10:22,840
‫我们躲在一个角落里, 他们每次都来找我们｡

123
00:10:22,840 --> 00:10:28,120
‫但如果我们躲在两个角落里, 他们就找不到我们了｡

124
00:10:28,120 --> 00:10:31,210
‫所以我躲在那里, 躲在这里, 躲在这里｡

125
00:10:31,210 --> 00:10:35,350
‫他要去最后一个已知的地点调查, 你看,

126
00:10:35,350 --> 00:10:39,730
‫他只是在拐角处探出头来, 但他被困在那里, 因为他不知道该怎么做｡

127
00:10:39,790 --> 00:10:46,960
‫如果我们去看一下我们的行为树, 把这个放在一边, 把游戏放在另一边,

128
00:10:46,960 --> 00:10:50,110
‫给予自己一点空间｡

129
00:10:50,200 --> 00:10:56,680
‫如果我们看一下行为树, 我们可以看到它有点滑稽, 因为它不能做这两件事｡

130
00:10:56,680 --> 00:11:00,400
‫它试图移动到最后一个已知的位置, 成功了｡

131
00:11:00,400 --> 00:11:01,930
‫所以有点卡住了｡

132
00:11:01,930 --> 00:11:05,980
‫但如果我出现在你的视野里, 你会看到他正朝我走过来｡

133
00:11:05,980 --> 00:11:11,830
‫如果他看不见我, 那么他就会进入调查状态｡

134
00:11:11,830 --> 00:11:12,760
‫所以失去了视力｡

135
00:11:12,760 --> 00:11:14,110
‫他要去调查｡

136
00:11:14,110 --> 00:11:18,130
‫然后他来了, 看到我, 然后他回去追我｡

137
00:11:18,130 --> 00:11:20,800
‫所以这种行为是有效的｡

138
00:11:20,800 --> 00:11:29,710
‫现在我们可以根据黑板上的行为, 在追逐和调查之间做出选择.

139
00:11:29,710 --> 00:11:37,210
‫我们把黑板的名字改成行为, 这样就能看到玩家的问号了.

140
00:11:37,210 --> 00:11:38,890
‫这样就更容易理解了｡

141
00:11:38,890 --> 00:11:43,990
‫现在我们有了这个选择器可以看到玩家, 然后我们应该追逐,

142
00:11:43,990 --> 00:11:49,120
‫否则我们应该调查, 我们可以删除这里的序列, 因为它不再使用｡

143
00:11:49,150 --> 00:12:01,690
‫希望你们现在对如何将行为树连接在一起以创建选择和序列有了更好的理解, 并将大量的任务放在一起以轻松地创建更复杂的行为｡

144
00:12:01,690 --> 00:12:10,510
‫在下一节课中, 我们会学习如何用C++编写自定义任务, 这样我们就可以进行射击动作了｡

145
00:12:10,510 --> 00:12:11,980
‫下一个视频里见｡

