﻿1
00:00:04,380 --> 00:00:06,150
‫你好, 欢迎回来｡

2
00:00:06,240 --> 00:00:12,920
‫现在我们有了运动, 但我们没有旋转顶部, 我们称之为炮塔.

3
00:00:12,930 --> 00:00:16,290
‫所以我们希望能够用鼠标来实现｡

4
00:00:16,320 --> 00:00:18,800
‫现在, 看看这里发生了什么｡

5
00:00:18,810 --> 00:00:23,300
‫当我在世界上移动我的鼠标光标时, 我得到了这个红色的球体｡

6
00:00:23,310 --> 00:00:27,060
‫这是我在3D空间中绘制的调试球体｡

7
00:00:27,300 --> 00:00:33,600
‫现在请注意, 当我靠墙向上移动时, 球体也在墙的表面上移动｡

8
00:00:33,600 --> 00:00:40,410
‫因此, 无论我的鼠标光标移动到哪里, 我都会感到这种恐惧,

9
00:00:40,410 --> 00:00:43,620
‫这种恐惧与我根据光标位置点击的任何对象相对应｡

10
00:00:43,620 --> 00:00:51,200
‫这对我们来说很有用, 这样我们就可以在太空中找到一个位置, 我们可以把炮塔旋转到这个位置｡

11
00:00:51,210 --> 00:00:59,250
‫现在, 为了实现这一点, 我们需要在控制器上使用一些功能, 因此我们必须访问控制器上的这些功能｡

12
00:00:59,250 --> 00:01:04,140
‫在这段视频中, 我们将进行设置,

13
00:01:04,140 --> 00:01:07,080
‫我们还将讨论这将如何涉及一个称为“选角”的概念｡

14
00:01:07,080 --> 00:01:12,330
‫现在, 我们需要控制器的原因是, 控制器上有一些函数,

15
00:01:12,330 --> 00:01:14,820
‫允许我们获得光标下的命中结果｡

16
00:01:14,820 --> 00:01:16,860
‫现在我们来谈谈鼠标光标｡

17
00:01:16,860 --> 00:01:20,310
‫假设这是我们的屏幕, 这是我们的鼠标光标｡

18
00:01:20,310 --> 00:01:25,530
‫并且光标尖端处的点对应于屏幕上的位置｡

19
00:01:25,530 --> 00:01:31,740
‫现在, 如果我们移动光标, 我们的目标是坦克的炮塔部分,

20
00:01:31,740 --> 00:01:37,290
‫网格的顶部将旋转, 使其面向光标的方向.

21
00:01:37,440 --> 00:01:44,370
‫现在, 如果我们移动光标, 控制器上有一个函数,

22
00:01:44,370 --> 00:01:47,190
‫我们可以使用它来获得与光标对应的位置｡

23
00:01:47,190 --> 00:01:52,830
‫如果我们从摄像机向光标方向画一条直线,

24
00:01:52,830 --> 00:01:59,700
‫并击中世界上的某个对象, 我们可以获得该位置, 并使用该位置设置坦克炮塔的旋转｡

25
00:01:59,730 --> 00:02:00,960
‫让我来告诉你我的意思｡

26
00:02:00,960 --> 00:02:06,420
‫现在, 为了获得与光标对应的命中位置, 我们需要使用控制器｡

27
00:02:06,420 --> 00:02:09,480
‫现在, 让我们简短地讨论一下控制器｡

28
00:02:09,780 --> 00:02:11,880
‫假设我们在世界上有了棋子｡

29
00:02:11,880 --> 00:02:13,950
‫兵将有一个控制器｡

30
00:02:14,130 --> 00:02:18,210
‫这是一个与兵相关联的不可见对象｡

31
00:02:18,210 --> 00:02:24,180
‫现在, 对于我们的兵, 我们的控制器将具有A型玩家控制器, 在游戏开始时,

32
00:02:24,180 --> 00:02:27,720
‫控制器将拥有兵｡

33
00:02:27,750 --> 00:02:32,790
‫这就是为什么我们可以从相机的角度看到属于我们的兵坦克｡

34
00:02:32,790 --> 00:02:34,890
‫现在, 我们可以访问此控制器｡

35
00:02:34,920 --> 00:02:38,970
‫我们的tank类继承了一个名为Get Controller的函数｡

36
00:02:38,970 --> 00:02:41,100
‫它继承自类.

37
00:02:41,100 --> 00:02:46,950
‫所以我们要做的就是控制这个控制器, 并将它存储在一个变量中, 这样我们就可以用它来访问函数,

38
00:02:46,950 --> 00:02:51,270
‫从而得到光标下的命中结果｡

39
00:02:51,270 --> 00:02:53,010
‫所以让我们为这个做好准备｡

40
00:02:53,010 --> 00:02:58,650
‫现在我在坦克课上, 我们需要访问控制器的地方是开始播放｡

41
00:02:58,680 --> 00:03:05,190
‫如果我们在构造函数中进行, 那就太早了, 我们可能还没有一个有效的控制器｡

42
00:03:05,190 --> 00:03:08,910
‫所以我们需要在坦克类中使用开始play函数.

43
00:03:08,910 --> 00:03:15,090
‫现在我们在基本pong类中有了它, 到目前为止, 我们还没有在基本pong类中使用开始play｡

44
00:03:15,270 --> 00:03:18,450
‫所以我想把它移到坦克课上｡

45
00:03:18,450 --> 00:03:25,500
‫我将突出显示开始播放, 包括注释和受保护的部分, 然后将其剪切｡

46
00:03:25,500 --> 00:03:28,710
‫然后回到坦克阶段, 我将把它粘贴起来

47
00:03:28,710 --> 00:03:33,960
‫让我们在这里添加这个受保护的部分, 就在私人部分的上面, 里面有开始播放｡

48
00:03:33,960 --> 00:03:36,270
‫现在我们已经从基本兵中移除了开始游戏｡

49
00:03:36,840 --> 00:03:41,430
‫我们不再有它在这里, 但我们也需要删除它从基地池塘cpp｡

50
00:03:41,460 --> 00:03:42,240
‫在这里｡

51
00:03:42,240 --> 00:03:51,330
‫我们可以在这里删除它, 现在我们在罐H中声明了它, 但我们还需要在罐CPP中定义它｡

52
00:03:51,360 --> 00:03:55,830
‫因此, 我将在setup player input组件下定义该函数｡

53
00:03:55,830 --> 00:04:00,660
‫所以在没有输入参数的情况下, 它将是无效的｡

54
00:04:00,870 --> 00:04:06,840
‫这是函数体, 我们还需要一个tank来预固定函数名｡

55
00:04:06,840 --> 00:04:09,930
‫现在我们知道, 在玩超级大牌时跟注非常重要｡

56
00:04:09,930 --> 00:04:17,010
‫这样, 开始play函数中的任何功能和任何父类仍将被调用｡

57
00:04:17,340 --> 00:04:18,960
‫现在我们开始｡

58
00:04:18,990 --> 00:04:22,560
‫这是我们可以得到我们的控制器, 并将其存储在一个变量中｡

59
00:04:22,560 --> 00:04:25,920
‫让我们回到坦克H, 创建一个变量｡

60
00:04:25,920 --> 00:04:30,750
‫现在这是私有的,

61
00:04:30,750 --> 00:04:38,460
‫我把它放在私有部分的底部, 这是一个播放器控制器指针类型, 我们可以把它叫做播放器控制器引用.

62
00:04:38,460 --> 00:04:40,920
‫这将是我们对播放器控制器的引用｡

63
00:04:40,950 --> 00:04:43,590
‫现在我们讨论的不是C++引用｡

64
00:04:43,590 --> 00:04:48,750
‫这仍然只是一个指针, 但我们将用它来引用播放器控制器.

65
00:04:48,750 --> 00:04:57,600
‫现在我们有了一个类型为a的播放器控制器ref变量, 我们准备好把控制器存储在这个指针里了.

66
00:04:57,600 --> 00:05:02,730
‫让我们回到Tank Dot CP, 开始游戏, 尝试这样做.

67
00:05:02,730 --> 00:05:03,330
‫是这样｡

68
00:05:03,400 --> 00:05:12,100
‫我要做的就是说player, controller, ref等于然后我会尝试调用get controller.

69
00:05:12,880 --> 00:05:17,440
‫现在得到控制器, 就像我提到的, 在类上｡

70
00:05:17,470 --> 00:05:24,070
‫让我们右键单击并转到定义, 注意这个函数返回一个A控制器｡

71
00:05:24,100 --> 00:05:29,020
‫现在我们的变量是类型A的播放器控制器, 而不是控制器｡

72
00:05:29,020 --> 00:05:35,260
‫所以我们在这里要做的是从get controller中获取返回值, 它是一个控制器指针类型,

73
00:05:35,260 --> 00:05:40,270
‫我们要把它存储在一个A播放器的控制器指针中.

74
00:05:40,300 --> 00:05:42,330
‫请注意, 我们在这里看到了红色的曲线｡

75
00:05:42,340 --> 00:05:43,940
‫它表示类型的值｡

76
00:05:43,960 --> 00:05:48,080
‫无法将控制器指标指派给型别的实体｡

77
00:05:48,100 --> 00:05:49,630
‫播放器控制器｡

78
00:05:49,660 --> 00:05:51,210
‫出现错误｡

79
00:05:51,220 --> 00:05:55,390
‫让我们看一下控制器的文档｡

80
00:05:55,420 --> 00:05:56,400
‫你看看

81
00:05:56,410 --> 00:05:58,030
‫这是控制器｡

82
00:05:58,030 --> 00:06:00,460
‫上面写着继承, 等级制度｡

83
00:06:00,460 --> 00:06:05,690
‫在控制器下面, 我们可以看到一个控制器和一个播放器控制器｡

84
00:06:05,710 --> 00:06:10,210
‫这意味着播放器控制器是控制器的子类｡

85
00:06:10,240 --> 00:06:14,500
‫现在, 在C++中, 我们不能有类型的指针｡

86
00:06:14,530 --> 00:06:20,200
‫播放器控制器存储A控制器的地址值｡

87
00:06:20,530 --> 00:06:22,600
‫控制器是父类型｡

88
00:06:22,780 --> 00:06:25,450
‫播放器控制器是子类型｡

89
00:06:25,450 --> 00:06:30,960
‫您无法取得父型别的物件, 并将它储存在子型别的指标中｡

90
00:06:30,970 --> 00:06:32,680
‫那我们怎么解决这个问题

91
00:06:32,680 --> 00:06:34,000
‫这就是我们的问题｡

92
00:06:34,000 --> 00:06:38,630
‫函数get controller返回A控制器的地址｡

93
00:06:38,650 --> 00:06:45,410
‫我们可以把它存储在A控制器指针里, 但不能存储在A播放器指针里.

94
00:06:45,430 --> 00:06:52,000
‫有趣的是, get controller返回的对象实际上是一个播放器控制器｡

95
00:06:52,000 --> 00:06:58,630
‫所以我们有一个A控制器指针形式的地址, 我们知道它存储了一个播放器控制器,

96
00:06:58,630 --> 00:07:06,210
‫但是如果我们有一个A控制器形式的地址, 我们就不能访问我们需要的函数, 因为它存在于一个播放器控制器上｡

97
00:07:06,220 --> 00:07:08,810
‫调用该函数获取光标下的命中结果｡

98
00:07:08,830 --> 00:07:15,250
‫现在, 如果我们有一个类型为A的控制器指针的值, 并且我们试图获取存在于播放器控制器上的函数,

99
00:07:15,250 --> 00:07:18,430
‫我们将失败｡

100
00:07:18,460 --> 00:07:20,020
‫我们将得到一个编译错误｡

101
00:07:20,020 --> 00:07:25,960
‫既然我们知道对象本身是一个播放器控制器, 它就存储在一个A控制器指针中.

102
00:07:25,960 --> 00:07:27,610
‫我们如何解决这个问题？

103
00:07:27,610 --> 00:07:29,980
‫答案是我们必须铸造｡

104
00:07:30,010 --> 00:07:36,100
‫我们知道控制器是父类, 播放器控制器是子类｡

105
00:07:36,100 --> 00:07:41,320
‫我们知道A控制器不能存储在A玩家控制器指针中｡

106
00:07:41,320 --> 00:07:47,530
‫我们知道我们可以反其道而行之, 有一个A控制器指针存储, 一个播放器控制器的对象｡

107
00:07:47,620 --> 00:07:55,120
‫这就是我们调用get控制器时看到的, 它返回一个A控制器指针, 指向一个A播放器控制器.

108
00:07:55,120 --> 00:08:01,810
‫因此, 如果我们有一个类型为A的控制器指针的值, 我们可以使用一个名为cast的特殊函数,

109
00:08:01,810 --> 00:08:05,920
‫将该指针的类型转换为A播放器控制器指针｡

110
00:08:05,920 --> 00:08:15,640
‫因此, 只要对象本身是我们试图转换到的类型, 转换就会将一个指针的类型更改为另一个指针类型｡

111
00:08:15,670 --> 00:08:23,140
‫现在, 如果我们试图强制转换为一个无效类型, 例如, 我们有一个A控制器指针指向一个播放器控制器对象｡

112
00:08:23,140 --> 00:08:30,820
‫如果我们试图强制转换为一个字符, 则cast函数返回null, 我们说我们的强制转换失败｡

113
00:08:30,820 --> 00:08:33,160
‫所以cast函数看起来像这样｡

114
00:08:33,190 --> 00:08:36,790
‫它是一个模板, 就像创建默认子对象一样｡

115
00:08:36,790 --> 00:08:44,230
‫这个模板函数将适应所选择的类型, 在这个函数中, 我们传入的类型也是我们正在转换的类型｡

116
00:08:44,230 --> 00:08:45,400
‫因此我们传递该类型｡

117
00:08:45,400 --> 00:08:52,570
‫我们想在尖括号内转换两个, 然后把要转换的指针传递到括号内.

118
00:08:52,570 --> 00:08:53,710
‫这里有一个例子｡

119
00:08:53,710 --> 00:08:59,410
‫假设我们要强制转换为播放器控制器和类型为A的控制器类型的指针｡

120
00:08:59,410 --> 00:09:04,240
‫我们将指针传递到括号中, 并在尖括号中传递要转换到的类型｡

121
00:09:04,240 --> 00:09:08,590
‫返回的是a类型的播放器控制器指针的值｡

122
00:09:08,590 --> 00:09:13,720
‫这是函数调用, 我们在尖括号中传递类型,

123
00:09:13,720 --> 00:09:18,550
‫从get控制器返回的控制器指针值可以放在括号中.

124
00:09:18,550 --> 00:09:22,630
‫所以我们需要转换从get控制器返回的值｡

125
00:09:22,630 --> 00:09:24,250
‫这将是你的挑战｡

126
00:09:24,250 --> 00:09:33,550
‫因此在BeginPlay中, 使用controller函数并将该值传递给cast函数, 然后将其转换为播放器控制器｡

127
00:09:33,550 --> 00:09:40,120
‫然后你可以把结果存储在我们的播放器控制器ref指针中, 它是A类型的播放器控制器指针｡

128
00:09:40,120 --> 00:09:42,580
‫暂停视频并执行演员阵容｡

129
00:09:45,530 --> 00:09:45,860
‫好吧, 我会的

130
00:09:45,860 --> 00:09:49,570
‫所以我们几乎已经完成了所有的工作, 我们只需要铸造｡

131
00:09:49,580 --> 00:09:56,000
‫我们要做的就是调用cast函数, 它有尖括号和圆括号｡

132
00:09:56,000 --> 00:09:59,990
‫括号里的是我们要转换的值｡

133
00:09:59,990 --> 00:10:05,060
‫现在get controller返回一个A型控制器的指针｡

134
00:10:05,060 --> 00:10:07,850
‫我把它移到括号里｡

135
00:10:07,850 --> 00:10:12,110
‫我们要转换的类型是播放器控制器｡

136
00:10:14,050 --> 00:10:17,620
‫所以我们再次使用get comptroller返回的值｡

137
00:10:17,620 --> 00:10:21,160
‫这将返回一个审计员指针或地址｡

138
00:10:21,160 --> 00:10:25,000
‫然后我们使用cast将其转换为A播放器控制器｡

139
00:10:25,000 --> 00:10:32,320
‫我们知道, 门控制器返回的对象是存储在A控制器指针中的A播放器控制器对象｡

140
00:10:32,320 --> 00:10:38,500
‫所以在我们转换之后, 返回的是一个a类型的指针, 玩家控制器｡

141
00:10:38,500 --> 00:10:41,410
‫所以我们把它存储在播放器控制器ref中｡

142
00:10:41,410 --> 00:10:46,990
‫既然我们已经以A播放器控制器指针的形式访问了控制器, 我们就可以访问所需的函数,

143
00:10:46,990 --> 00:10:50,830
‫以获取光标下的跟踪命中结果｡

144
00:10:50,830 --> 00:10:55,570
‫因此, 总而言之, 我们使用get controller来获得对控制器的访问权限｡

145
00:10:55,930 --> 00:11:03,670
‫我们还使用cast函数将类型A的控制器指针转换为类型A的播放器控制器指针｡

146
00:11:03,670 --> 00:11:11,290
‫现在我们可以访问所需的函数, 即在A播放器控制器类上的光标下获取命中结果｡

147
00:11:11,290 --> 00:11:14,500
‫因此, 我们将在下一个视频中开始使用此函数｡

148
00:11:14,860 --> 00:11:15,850
‫我很快就会见到你的｡

