﻿1
00:00:04,280 --> 00:00:05,540
‫你好, 欢迎｡

2
00:00:05,540 --> 00:00:09,140
‫在这节课上, 我们的眼睛在世界各地跟随着我们｡

3
00:00:09,140 --> 00:00:11,570
‫等他看不见我的时候, 它就会去调查｡

4
00:00:11,570 --> 00:00:13,730
‫所以很难输｡

5
00:00:13,730 --> 00:00:20,120
‫但是如果我走了太多的弯角, 他就跟不上了, 最终他会失去兴趣,

6
00:00:20,120 --> 00:00:25,160
‫回到他的起点｡

7
00:00:25,370 --> 00:00:31,010
‫这就是我们在这节课上要实现的, 通过完成行为树的任务.

8
00:00:31,010 --> 00:00:32,930
‫让我们来看看它是如何完成的｡

9
00:00:33,550 --> 00:00:38,380
‫所以我们希望我们的任务能够执行, 能够真正做一些事情｡

10
00:00:38,380 --> 00:00:42,400
‫特别是我们希望能够清除黑板上的值, 按照它在锡上说的做｡

11
00:00:42,820 --> 00:00:44,830
‫那么, 我们该如何着手呢？

12
00:00:44,860 --> 00:00:49,210
‫好吧, 为此, 我们需要转到我们的基类, 看看那里实现了什么｡

13
00:00:49,210 --> 00:00:57,880
‫事实上, 我们需要更上一层楼, 正如你从任务黑板库的继承中所看到的｡

14
00:00:57,880 --> 00:01:00,540
‫在此之上, 我们有BT任务节点｡

15
00:01:00,550 --> 00:01:05,320
‫因此, 让我们使用F 12导航到那里的实现｡

16
00:01:05,320 --> 00:01:13,720
‫如果你向上滚动到BT任务节点头文件的顶部, 你可以看到关于这个类的信息｡

17
00:01:13,720 --> 00:01:19,960
‫最重要的是, 这些是我们最有可能要实现的虚拟函数｡

18
00:01:20,260 --> 00:01:22,510
‫第一个是执行任务｡

19
00:01:22,510 --> 00:01:30,760
‫当任务开始执行时调用该函数, 然后我们有一个板任务, 当它决定需要停止执行时, 例如,

20
00:01:30,760 --> 00:01:37,060
‫因为条件变为假, 我们已经设置了该信号｡

21
00:01:37,060 --> 00:01:38,710
‫因此, 它会中止此任务｡

22
00:01:39,040 --> 00:01:43,720
‫Tick task在task执行的所有时间都发生, 但第一个tick不发生｡

23
00:01:43,720 --> 00:01:46,660
‫因此, 第一个tick将在其后调用执行任务｡

24
00:01:46,660 --> 00:01:49,540
‫我们会继续每次调用tick任务｡

25
00:01:50,140 --> 00:01:57,400
‫这是针对那些可能运行时间超过一个滴答的行为, 比如向敌人移动可能需要多个滴答｡

26
00:01:57,400 --> 00:01:59,710
‫所以tick任务允许我们做一些事情｡

27
00:01:59,710 --> 00:02:05,860
‫每一个勾号和打开消息都是针对其他事情, 而不是可能向此任务发送消息的中止｡

28
00:02:05,860 --> 00:02:09,250
‫我们其实不必担心这个特殊的函数｡

29
00:02:09,250 --> 00:02:15,370
‫所以前三个是我们真正感兴趣的, 因为我们的任务只是在执行时清除黑板上的值,

30
00:02:15,370 --> 00:02:21,370
‫然后它会直接完成｡

31
00:02:21,370 --> 00:02:24,100
‫它不需要跨多个分笔成交点执行操作｡

32
00:02:24,100 --> 00:02:30,190
‫我们感兴趣的是执行任务, 因此,

33
00:02:30,190 --> 00:02:37,420
‫如果我们在此头文件中搜索执行任务, 您可以看到完整的签名在此处, 返回类型在此处是此靴子节点结果｡

34
00:02:38,080 --> 00:02:43,660
‫它包含两个元素：所有者组件和节点内存｡

35
00:02:43,720 --> 00:02:48,880
‫它占用节点内存的原因有点复杂,

36
00:02:48,880 --> 00:02:58,930
‫但这里要知道的重要一点是, 一个任务类或任务类实例, 每个黑板上只有一个｡

37
00:02:58,930 --> 00:03:04,960
‫本质上,

38
00:03:04,960 --> 00:03:13,660
‫它们是在所有对象之间共享的, 这就是为什么如果你想保存一些与某个特定行为树中的节点相关的信息, 那么你需要做的就是把它存储在这个内存指针中｡

39
00:03:13,660 --> 00:03:16,210
‫但我们不会做任何与此相关的事情｡

40
00:03:16,210 --> 00:03:18,670
‫需要这样做有点高级｡

41
00:03:18,670 --> 00:03:23,860
‫还有其他的方法可以解决这个问题, 比如允许Unreal为每个行为树创建多个实例,

42
00:03:23,860 --> 00:03:30,430
‫这可能会更简洁, 也可能不会, 但这是一个稍微更有效的方法, 我们不必担心它｡

43
00:03:30,430 --> 00:03:35,620
‫最后, 我们感兴趣的是我们可以得到我们自己的组件, 行为树组件, 从那里我们可以得到其他东西,

44
00:03:35,620 --> 00:03:43,270
‫比如兵, 等等｡ 但这也是另一节课的主题｡

45
00:03:43,270 --> 00:03:48,910
‫所以, 现在有趣和重要的是实现我们的执行任务｡

46
00:03:48,910 --> 00:03:55,450
‫我们将复制签名, 转到空白黑板值H, 让我们看到这个公共注释,

47
00:03:55,450 --> 00:04:00,040
‫我认为它将受到保护, 因为它在默认部分｡

48
00:04:00,040 --> 00:04:04,930
‫因此, 我们将创建一个受保护的部分并将其粘贴到该部分中, 然后继续创建或设置一个覆盖,

49
00:04:04,930 --> 00:04:08,500
‫以确保安全｡

50
00:04:08,500 --> 00:04:14,500
‫然后我们将在C++中创建一个实现, 记住,

51
00:04:14,500 --> 00:04:24,460
‫当我们重写时, 我们总是要调用super, 所以super执行任务, 所有者组件和节点内存被调用到父级｡

52
00:04:24,460 --> 00:04:30,790
‫然后我们要做的是拿着黑板, 这样我们就可以清除这个键了｡

53
00:04:30,790 --> 00:04:32,230
‫这里有两件事很重要｡

54
00:04:32,230 --> 00:04:35,140
‫首先, 我们如何知道选择了哪个Key？

55
00:04:35,140 --> 00:04:42,340
‫好的, 如果我们转到黑板底座H, 你可以看到其中一个方法是获取选定的黑板键｡

56
00:04:42,340 --> 00:04:48,220
‫你可以很容易地在C++中调用这个函数, 得到选定的黑板键, 然后它会返回｡

57
00:04:48,220 --> 00:04:54,730
‫如果我们将鼠标悬停在它上面, 它会返回一个F名称, 即在行为树中选择的黑板键的F名称｡

58
00:04:54,730 --> 00:05:00,280
‫现在, 我们需要做的另一件事是, 实际上, 能够得到与这个行为树相关的黑板｡

59
00:05:00,280 --> 00:05:02,380
‫因此, 我们有我们自己的一个比较｡

60
00:05:02,380 --> 00:05:07,990
‫现在请注意, 这里传递的所有者组件的类型实际上是一个引用, 而不是指针｡

61
00:05:07,990 --> 00:05:09,850
‫所以它不能为空｡

62
00:05:09,850 --> 00:05:10,720
‫那太好了｡

63
00:05:10,720 --> 00:05:15,730
‫但这也意味着我们需要使用点操作符,

64
00:05:15,730 --> 00:05:21,910
‫而不是箭头操作符, 因为我们没有使用指针, 从这里我们可以简单地得到黑板组件｡

65
00:05:21,910 --> 00:05:26,260
‫如果你把鼠标悬停在黑板组件上, 它就显示出来了, 这个是指向黑板组件的指针,

66
00:05:26,260 --> 00:05:29,470
‫所以你需要使用箭头操作符.

67
00:05:29,470 --> 00:05:32,680
‫所以我们已经知道如何清除黑板上的键｡

68
00:05:32,880 --> 00:05:34,590
‫对手们, 这将是你们的挑战｡

69
00:05:34,590 --> 00:05:36,690
‫所以希望挑战不会太复杂｡

70
00:05:36,690 --> 00:05:38,620
‫您要清除选定的键｡

71
00:05:38,800 --> 00:05:41,730
‫你可以查一下我们以前在哪里做过这个｡

72
00:05:42,240 --> 00:05:44,910
‫是否需要任何包含项才能执行此操作？

73
00:05:44,910 --> 00:05:53,550
‫也许黑板需要包括使用在你的行为树中选择的键作为我们想要清除的键｡

74
00:05:53,550 --> 00:05:55,530
‫所以暂停视频, 开始吧｡

75
00:05:55,560 --> 00:05:56,670
‫好的欢迎回来｡

76
00:05:56,670 --> 00:05:58,740
‫所以我们来试试吧｡

77
00:05:58,740 --> 00:06:01,560
‫首先我们有了黑板组件｡

78
00:06:01,560 --> 00:06:03,060
‫让我们在这里使用error运算符｡

79
00:06:03,360 --> 00:06:04,560
‫什么都没发生｡

80
00:06:04,560 --> 00:06:11,910
‫所以我假设我需要, 这里确实有一些红色的曲线, 我需要一个散列, 包括在这里来做行为树,

81
00:06:11,910 --> 00:06:15,960
‫然后是黑板组件｡

82
00:06:15,960 --> 00:06:21,300
‫希望我的自动完成应该工作, 这将使我有明确的｡

83
00:06:21,300 --> 00:06:23,580
‫让我们试着回忆一下｡

84
00:06:23,580 --> 00:06:25,230
‫我们以前在哪里做过这个？

85
00:06:25,230 --> 00:06:31,500
‫如果我们转到射手I, 控制器CP, 看看我们在这里得到了什么, 我们有明确的价值｡

86
00:06:31,500 --> 00:06:32,790
‫这就是我们要找的人

87
00:06:32,790 --> 00:06:35,430
‫让我们试试这个clear值｡

88
00:06:35,430 --> 00:06:36,600
‫它吸收了什么？

89
00:06:36,600 --> 00:06:41,370
‫它接受一个F键或一个f名字, 我们可以得到一个很好的f名字｡

90
00:06:41,370 --> 00:06:48,030
‫我们在哪里说过可以用这个类上的get selected黑板键来得到它呢因为它在父类中｡

91
00:06:48,030 --> 00:06:51,300
‫所以得到选定的黑板键, 它就这么简单｡

92
00:06:51,300 --> 00:06:53,910
‫正在清除所选黑板键的值｡

93
00:06:53,910 --> 00:06:55,590
‫现在, 我们要做的最后一件事｡

94
00:06:55,590 --> 00:06:57,060
‫这不是你的挑战, 所以别担心｡

95
00:06:57,060 --> 00:07:04,680
‫如果你不知道这是我们需要返回, 因为有一个返回值为我们的清除黑板值执行任务｡

96
00:07:04,680 --> 00:07:06,810
‫而且返回值相当有趣｡

97
00:07:06,810 --> 00:07:07,410
‫我们来看看｡

98
00:07:07,410 --> 00:07:12,600
‫让我们用一个F12来跟随它, 看看我们可以从执行测试中返回什么不同的状态｡

99
00:07:12,600 --> 00:07:14,400
‫现在第一个成功了｡

100
00:07:14,400 --> 00:07:14,760
‫好极了｡

101
00:07:14,760 --> 00:07:20,910
‫这基本上向行为树发出信号, 表明该节点已完成执行, 我们可以继续下一个节点｡

102
00:07:20,910 --> 00:07:21,930
‫又失败了｡

103
00:07:21,930 --> 00:07:27,450
‫这是一个已完成的, 所以我们可以继续下一个, 但这次它说它失败了｡

104
00:07:27,450 --> 00:07:34,500
‫因此, 这给了不同的指示, 例如, 作为序列节点或我们选择的节点,

105
00:07:34,500 --> 00:07:36,750
‫关于这些节点实际上应该做什么｡

106
00:07:37,260 --> 00:07:44,850
‫然后我们已经流产了, 这基本上和失败是一样的, 我们已经在进行中了｡

107
00:07:44,850 --> 00:07:51,420
‫如果你从一个执行任务中返回, 它基本上会说你必须一直调用tick, 直到我告诉你, 否则,

108
00:07:51,420 --> 00:07:57,120
‫直到我说, 我已经完成了, 现在我们不会使用它｡

109
00:07:57,120 --> 00:08:03,660
‫但最好记住, 如果您需要创建一个长时间运行的任务, 我们将运行多个分笔成交点｡

110
00:08:03,660 --> 00:08:06,060
‫那我们就得原路返回了｡

111
00:08:06,060 --> 00:08:08,220
‫但正如我所说, 我们要成功｡

112
00:08:08,400 --> 00:08:10,260
‫所以我们进去做吧｡

113
00:08:10,260 --> 00:08:14,490
‫返回EB心跳节点结果冒号, 冒号｡

114
00:08:14,490 --> 00:08:20,550
‫它将是成功的, 因为我们基本上无论如何都成功地清除了这个值｡

115
00:08:20,760 --> 00:08:25,470
‫所以我们可以继续保存所有这些内容, 进入编辑器并点击编译｡

116
00:08:25,470 --> 00:08:31,710
‫完成后, 让我们打开我们的人工智能和行为树｡

117
00:08:31,740 --> 00:08:33,020
‫那么这一拍在哪里？

118
00:08:33,030 --> 00:08:35,490
‫我将它停靠在主窗口中, 让我们来看看｡

119
00:08:35,490 --> 00:08:40,800
‫我们已经得到了一个清空的黑板值, 它现在应该允许我们移动到最后一个已知的位置,

120
00:08:40,800 --> 00:08:47,250
‫清空黑板值, 等待5, 然后我们应该看到调查这个最后一个已知的玩家位置应该是未设置的,

121
00:08:47,250 --> 00:08:52,200
‫所以我们应该回到我们的起始位置｡

122
00:08:52,200 --> 00:08:56,790
‫因此, 让我们看看在任务完全实现后, 此行为现在是否起作用｡

123
00:08:56,800 --> 00:09:00,450
‫所以他来找我, 调查我｡

124
00:09:00,480 --> 00:09:01,800
‫我要去躲起来｡

125
00:09:01,800 --> 00:09:03,000
‫所以我们去躲起来｡

126
00:09:03,150 --> 00:09:04,200
‫他是来调查的｡

127
00:09:04,200 --> 00:09:05,550
‫我们再去躲一躲｡

128
00:09:05,760 --> 00:09:07,290
‫快躲起来｡

129
00:09:07,830 --> 00:09:10,080
‫我们看看能不能在那边找到他｡

130
00:09:10,080 --> 00:09:18,420
‫他现在应该在等着然后他会回到他的起始位置, 不幸的是, 这让他和我相交了.

131
00:09:18,420 --> 00:09:25,350
‫所以让我们看看这是否真的像我们预期的那样工作, 通过给我们的敌人带来一个窗口, 并把它对接在右边,

132
00:09:25,350 --> 00:09:29,820
‫把我们的主窗口放在左边｡

133
00:09:29,820 --> 00:09:37,800
‫有点挤, 但我们可以我们可以凑合然后我们再试着摆脱这家伙一次看看他的行为会发生什么.

134
00:09:37,800 --> 00:09:41,910
‫你可以看到他正在移动到最后一个已知的球员位置｡

135
00:09:42,510 --> 00:09:45,990
‫现在他在等待, 已经清除了值｡

136
00:09:45,990 --> 00:09:54,030
‫然后你可以看到他要移动, 你可以看到装饰器上的这些红色条告诉我们这两个条件都不成立｡

137
00:09:54,030 --> 00:09:55,950
‫所以这两件事他都做不了｡

138
00:09:55,950 --> 00:09:57,840
‫最后, 他移动到了起始位置｡

139
00:09:57,840 --> 00:10:01,170
‫所以现在它在闪烁, 因为基本上它什么也做不了｡

140
00:10:01,320 --> 00:10:09,810
‫基本上, 这节课我们已经了解了如何让任务执行代码,

141
00:10:09,810 --> 00:10:19,890
‫了解了从执行任务返回的不同状态, 如何从任务中获取黑板, 以及如何在黑板任务中获取当前选择的键｡

142
00:10:19,890 --> 00:10:26,310
‫在下一节课中, 我们将学习如何掌握黑板行为树之外的世界,

143
00:10:26,310 --> 00:10:30,450
‫如何掌握行为树任务中的棋子

144
00:10:30,450 --> 00:10:31,080
‫我会看到你在那里｡

