﻿1
00:00:04,370 --> 00:00:05,660
‫你好, 欢迎｡

2
00:00:05,660 --> 00:00:13,910
‫在这堂课中, 我们将了解在Unreal中编译C++文件的主要步骤｡

3
00:00:13,910 --> 00:00:16,070
‫让我们深入了解一下它们是如何工作的｡

4
00:00:16,850 --> 00:00:26,750
‫所以我们很快就要开始使用外部的代码了, 从其他文件中, 我们需要学习C++中是如何工作的｡

5
00:00:26,750 --> 00:00:31,220
‫要做到这一点, 我们需要绕一个小圈子,

6
00:00:31,220 --> 00:00:35,270
‫更详细地了解当我们将C++代码编译成二进制代码时会发生什么｡

7
00:00:35,270 --> 00:00:38,690
‫下面是使用的编译步骤｡

8
00:00:38,690 --> 00:00:44,780
‫我们从你的代码､ C++文件､ 头文件开始, 所有这些都在不同的文本文件中｡

9
00:00:44,780 --> 00:00:49,070
‫这些被输入到一个名为虚幻标题工具的工具中｡

10
00:00:49,070 --> 00:00:58,760
‫现在, 这个工具负责获取所有不真实的特定语法片段, 并使用它们生成更多的C++代码｡

11
00:00:58,760 --> 00:01:05,570
‫让我们看一些例子, 我们在哪里得到了这个不真实的特定语法,

12
00:01:05,570 --> 00:01:15,380
‫这个大写字母的新类是不真实的特定语法, 这里是Crypt Reader API, 那里是生成的主体｡

13
00:01:15,410 --> 00:01:21,830
‫基本上, 这里的情况是, 虚幻头工具会看到你代码中的这些不同的语法片段, 然后说, 好吧,

14
00:01:21,830 --> 00:01:24,500
‫我需要做我的工作｡

15
00:01:24,500 --> 00:01:27,020
‫我得进去, 我得加一堆东西｡

16
00:01:27,050 --> 00:01:34,670
‫它会用一堆代码来替换你生成的身体, 让我们在使用Unreal时更轻松｡

17
00:01:35,090 --> 00:01:38,300
‫它还可以处理U属性和新函数｡

18
00:01:38,300 --> 00:01:43,580
‫它添加了它所需要的所有东西, 这样它就可以与蓝图世界进行交流｡

19
00:01:43,580 --> 00:01:45,020
‫这是第一步｡

20
00:01:45,020 --> 00:01:49,250
‫它将所有不真实的特定内容转换为实际的C++｡

21
00:01:49,490 --> 00:01:57,260
‫一旦我们得到了实际的C++, 我们就可以将其输入到标准的C++编译过程中, 这需要三个步骤｡

22
00:01:57,260 --> 00:02:07,610
‫第一个是处理预处理步骤, 这样它实际上可以触及任何C++语法, 并且实际上只处理以散列开头的行｡

23
00:02:07,610 --> 00:02:10,850
‫我们有一些预处理指令｡

24
00:02:10,850 --> 00:02:13,400
‫这些由预处理器处理｡

25
00:02:14,150 --> 00:02:16,820
‫下一步是我们实际上所称的编译器｡

26
00:02:16,850 --> 00:02:24,680
‫它把一段代码转换成可执行的二进制代码, 但它并没有完全完成这一步, 因为它只对单个的C++,

27
00:02:24,680 --> 00:02:31,790
‫也就是所谓的翻译单元, 基本上是C++文件进行转换｡

28
00:02:31,790 --> 00:02:37,910
‫所以它把一个C++文件单独翻译成二进制文件, 每个单独的C++文件.

29
00:02:37,910 --> 00:02:44,360
‫第三步是连接器, 它将所有这些连接在一起, 并输出一个可执行文件,

30
00:02:44,360 --> 00:02:47,090
‫我们实际上可以运行一个可执行文件｡

31
00:02:47,180 --> 00:02:53,780
‫因此, 让我们通过一个示例文件更详细地了解一下这些标准编译步骤｡

32
00:02:53,840 --> 00:02:55,970
‫想象一下我们有一个博弈｡

33
00:02:56,150 --> 00:03:02,420
‫这是一个非常非常简单的博弈, 因为它所做的就是把一和一加在一起, 但是要把一和一加在一起就必须使用一个函数,

34
00:03:02,420 --> 00:03:08,540
‫这个函数在数学中的另一个文件中声明｡

35
00:03:08,780 --> 00:03:10,700
‫H所以数学证明｡

36
00:03:10,700 --> 00:03:14,090
‫AD, 但它实际上并没有定义它的作用｡

37
00:03:14,090 --> 00:03:16,010
‫所以这是在数学上完成的｡

38
00:03:16,190 --> 00:03:19,520
‫你可以看到这是一个非常简单的实现｡

39
00:03:20,000 --> 00:03:22,700
‫现在, 在预处理器步骤中会发生什么？

40
00:03:22,700 --> 00:03:28,610
‫就像我说的, 它处理任何预处理器指令, 这些指令都是以散列开始的｡

41
00:03:28,610 --> 00:03:34,970
‫哈希函数包含了数学运算, 也就是说把数学运算中的所有内容都复制到这里.

42
00:03:34,970 --> 00:03:40,700
‫它把这两个文件组合在一起, 创建一个新的输出文件, 称为Game

43
00:03:40,700 --> 00:03:48,050
‫I, 基本上是games include, 你可以看到它包含了Master

44
00:03:48,050 --> 00:03:52,730
‫H的定义, 它取代了哈希预处理器指令｡

45
00:03:52,730 --> 00:04:03,180
‫现在博弈II已经有了ADD运算的定义, 然后我们在数学DHCP上运行预处理器｡

46
00:04:03,200 --> 00:04:07,760
‫但是因为它没有任何预处理器指令, 所以基本上没有什么变化｡

47
00:04:08,060 --> 00:04:12,890
‫下一步是编译,

48
00:04:12,890 --> 00:04:21,350
‫这可能是最重要也是最复杂的一步, 因为它所做的是将每个文件中的C++转换为二进制, 但它不是完整的二进制,

49
00:04:21,350 --> 00:04:30,620
‫因为例如, 我的游戏实际上并不知道如何实现AD, 它只知道add函数的形式｡

50
00:04:30,620 --> 00:04:34,940
‫所以这需要在以后填写, 由链接器填写.

51
00:04:34,940 --> 00:04:41,150
‫因此, 链接器将获取为游戏obj文件创建的二进制文件和为主文件创建的二进制文件,

52
00:04:41,150 --> 00:04:44,810
‫并将它们放在一起创建一个游戏｡

53
00:04:45,020 --> 00:04:52,010
‫XY结合了两者的二进制代码, 最终是一个完整的可执行文件, 我们可以运行它,

54
00:04:52,010 --> 00:04:55,280
‫因为它有它所需要的一切的定义｡

55
00:04:55,280 --> 00:04:58,730
‫现在, 为什么我们有这么复杂的过程？

56
00:04:58,730 --> 00:05:03,860
‫这里的想法是, 它加快了我们的编译时间, 因为如果我只做了一个改变｡

57
00:05:04,310 --> 00:05:06,470
‫到游戏DHCP｡

58
00:05:06,710 --> 00:05:10,340
‫我实际上并不需要运行整个编译过程｡

59
00:05:10,340 --> 00:05:16,730
‫我所要做的就是运行涉及游戏DHCP的位,

60
00:05:16,760 --> 00:05:23,660
‫所以我只在CP上运行预处理器, 然后我只在游戏上运行编译器, 这样我就可以重用之前的数学对象文件｡

61
00:05:23,660 --> 00:05:26,960
‫我不需要重新编译它, 因为它没有任何变化｡

62
00:05:26,960 --> 00:05:33,590
‫类似地, 如果我只修改math. CP, 我就不必重新编译游戏obj文件,

63
00:05:33,590 --> 00:05:37,640
‫这就是为什么这是一个更有效的过程, 也是为什么我们首先要这样做的原因｡

64
00:05:37,640 --> 00:05:46,280
‫我想做一个小实验, 试着找出为什么我们要移动一个H和一个CP文件？

65
00:05:46,280 --> 00:05:47,750
‫这是什么原因呢？

66
00:05:47,750 --> 00:05:51,770
‫为什么我们不能只编译C++文件而不包括move？

67
00:05:51,770 --> 00:05:56,660
‫所以我们要做的实验是注释掉散列包含行｡

68
00:05:56,660 --> 00:06:02,000
‫所以用双正斜杠在那一行前面加上注释,

69
00:06:02,000 --> 00:06:08,060
‫这基本上会让它被预处理程序剥离掉, 而不是用文件的内容替换它｡

70
00:06:08,510 --> 00:06:15,050
‫然后我们将重新编译, 我希望你们看一下出现的错误消息, 看看它说了什么,

71
00:06:15,050 --> 00:06:21,110
‫看看你们是否能理解我们使用这个动作的目的｡

72
00:06:21,260 --> 00:06:23,450
‫暂停视频, 开始体验｡

73
00:06:26,590 --> 00:06:27,030
‫好吧, 我会的

74
00:06:27,040 --> 00:06:28,000
‫欢迎回来｡

75
00:06:28,030 --> 00:06:33,870
‫所以我们在这一行的开头加上一个双斜杠, 这基本上使它无效｡

76
00:06:33,880 --> 00:06:40,450
‫然后, 我们将执行一个控制alt f 11, 我们将查看实时编码的输出｡

77
00:06:40,450 --> 00:06:44,680
‫所以, 我们在很早的时候就有了一些错误｡

78
00:06:44,680 --> 00:06:51,160
‫它的意思是你的移动器不是一个类或者命名空间, 下面还有一些其他的东西｡

79
00:06:51,160 --> 00:06:58,360
‫这说明主要技术组件尚未声明super不是一个类或namespace get owner是一个未知标识符｡

80
00:06:58,360 --> 00:07:05,920
‫基本上这就是说你在这里讨论了你的移动者, 但是你没有在任何地方定义它, 你没有在任何地方声明它｡

81
00:07:05,920 --> 00:07:07,240
‫所以我也不知道｡

82
00:07:07,240 --> 00:07:11,560
‫C++编译器说, 我不知道这是什么｡

83
00:07:11,560 --> 00:07:18,250
‫这就是为什么我们是哈希的, 包括移动者H, 因为移动者H声明了类.

84
00:07:18,250 --> 00:07:25,780
‫它说U移动器是一个类, 它从你的行为中派生出来, 一个组件, 它有这些方法, 等等.

85
00:07:25,780 --> 00:07:28,480
‫它是在宣告成为那个阶级意味着什么｡

86
00:07:28,480 --> 00:07:32,440
‫因此, 如果没有该声明, C++编译器将无法继续｡

87
00:07:32,440 --> 00:07:36,160
‫这就是为什么我们需要包括头文件｡

88
00:07:36,760 --> 00:07:37,240
‫好东西｡

89
00:07:37,240 --> 00:07:47,890
‫所以希望你现在能更好地理解C++的编译过程, 特别是在Unreal中的C++编译｡

90
00:07:47,920 --> 00:07:49,810
‫我们下节课再见｡

