JamesB 发表于 2022-8-22 06:20

虚幻4渲染编程(入门引擎篇)【第一卷:初识虚幻引擎】

参考:
1、引擎安装

关于论坛讨论,可以遇到问题后到论坛查找一下:
打开laucher创建一个新的空白项目。选择一下的设置:

http://pic4.zhimg.com/v2-33eb73c2d44ac9424dd2910c576d4763_r.jpg
注意这里的项目名称,他说好像会影响在VS中的一些标识符。会根据名字创建相应的.cpp和.h。和他一样就得了。
创建项目的时候,会同时创建VS文件,里面有所有的Cpp代码。我们就在里面写代码。


右上角鼠标浮上去就会有相关的引擎版本信息。
同时VS现在会加载出来。
配合鼠标键盘来操作场景视角。
2、视口

配合移动缩放选择组件


和下面的实现连贯和不连贯的移动。

http://pic4.zhimg.com/v2-d0ba995385520d57eb92c2db4a30f74b_r.jpg
我们还可以按End键,让物体贴到它下面的面上。
3、编辑器概述


http://pic1.zhimg.com/v2-5f24af6bb74ea3b45dd0ad144344dcd0_r.jpg
最左上角的时标签栏,用于多个项目打开的时候。然后是菜单栏,右边帽子是一个官方给的介绍的工具,还有一个介绍项目相关信息的。

http://pic1.zhimg.com/v2-f51975f1b5592b8459a86ab58472875c_r.jpg
左边时modes栏,里面都是可以访问的各种资源,可以直接拽到场景中。
上边是工具栏。重要的是play按钮。他的下拉框:

http://pic1.zhimg.com/v2-45e321f63749a04518c27dabc5ef813c_r.jpg
可以有不同的运行模式。


然后右下角view option选择:


可以看到


对于世界大纲:

http://pic3.zhimg.com/v2-04e5fa3f3191fe5d448202435c848eb2_r.jpg
我们可以在物体对应或者寻找物体的时候双击。
我们可以F聚焦某一个物体,然后按住alt键来从周围各个视角来看该物体。
detail 面板:会显示选中物体的各种属性信息。

http://pic2.zhimg.com/v2-6b1d1c58f2583b33c05513c96c1b18cd_r.jpg
可以在这里面修改位置等信息,然后回车就生效了。
4、关卡蓝图


http://pic1.zhimg.com/v2-5f046e2d9fa21074b04b5b58b94c3390_r.jpg
然后点击左上角标签,拖拽就能让他独立处于一个浮动窗口。

http://pic1.zhimg.com/v2-44aeb19c053370bff226887a8b5a21f4_r.jpg

http://pic4.zhimg.com/v2-fd82413144cfe1a8df5ea5c1cea6498b_r.jpg
一般给他拽到上边去。

http://pic2.zhimg.com/v2-356ec8ba0b984762a3f6f164bc887fa1_r.jpg
通过搜索创建第一个node。这个就相当于游戏开始了。

http://pic3.zhimg.com/v2-0c1c8ddbaeccda89072a03321f4f55ce_r.jpg
创建一个print函数节点,然后编译并save

http://pic2.zhimg.com/v2-355619e1158247f266a59c0d5a017f31_r.jpg
这时候我们运行游戏就可以:

http://pic1.zhimg.com/v2-fa6c8f9053e2f46404017b965ea9a284_r.jpg
同时,我们点击下拉框:

http://pic3.zhimg.com/v2-fe878f8f9e434810e7511816935b9496_r.jpg
可以进行更多的设置。颜色和持续的时间。
还有一个节点:event tick这个是每一帧都触发的事件。

http://pic4.zhimg.com/v2-f25b4cbae9787d33def2a5f3684be653_r.jpg
然后运行的结果:

http://pic1.zhimg.com/v2-72b7135c546c2d73c885396114166c14_r.jpg
这个每一帧都会输出一个。

http://pic2.zhimg.com/v2-2d35c040f68735a6c109c70a31eaa2a9_r.jpg
这个是帧数的显示。
5、创建蓝图


http://pic4.zhimg.com/v2-b5d590d3757025a9d49d4c45341eadc7_r.jpg
在content右键就可以创建自己的蓝图,

http://pic3.zhimg.com/v2-bc21a310e4cbd22428084f34c6f8032a_r.jpg
这里会让你选择父类。创建后双击打开。
如果创建一个Actor的,那么就会有一个球,代表这里有一个actor,但是这个球只是给我们看的,实际运行起来并不存在。

http://pic4.zhimg.com/v2-c49b08ae243b335836b4ea441f4c29a3_r.jpg
然后添加一个static mesh

http://pic2.zhimg.com/v2-b262aa18f0025149718e74be8a445351_r.jpg
然后选择我们的mesh,右边会有detail panel:

http://pic3.zhimg.com/v2-8d59e2a4ddf224c60655461d166add66_r.jpg
其中有一个设置:

http://pic4.zhimg.com/v2-7957818d77cbef3e7805645b14d47903_r.jpg
stati mesh一般会有这三个选择,这三个和光照有关系,如果写的是static,表示告诉引擎物体不会移动,那么引擎就会帮我们做bake shadow,这种阴影就是不会动的,不会实时更新的。
stationary是用于光源的,表示光源不会移动,但是可以调整亮度等信息。
然后我们在static mesh设置rock模型,编译保存之后,我们可以吧我们的蓝图直接拽到场景中,

http://pic4.zhimg.com/v2-89a2d177ec89787d92d9c21205c5d923_r.jpg
另外:

http://pic3.zhimg.com/v2-63af378aae54097bf3e8112194ab8f46_r.jpg

http://pic2.zhimg.com/v2-b4f63dbcc9c25ab42ca29fcfcbfb35c9_r.jpg
这里默认有三个节点。

http://pic1.zhimg.com/v2-0e6371ffd5051dfb15799eba8fdded04_r.jpg
然后:

http://pic2.zhimg.com/v2-7b3b34a82a7c46d0327f1dc13e4b6045_r.jpg
这样每一帧都能够让x移动1。另外编译之前,我们需要改一下设置,改成movable:

http://pic3.zhimg.com/v2-84b3d5272f70ece35bf54dca8ac92242_r.jpg
然后回到编辑器。play就可以看出效果。
6、C++和虚幻4


http://pic4.zhimg.com/v2-a1e13fda6b246b721433a7ae7b451397_r.jpg

http://pic3.zhimg.com/v2-e3680d95db518e6a84559158bb1b1a22_r.jpg

http://pic2.zhimg.com/v2-ea6277acc094438ef56c9ce07c849915_r.jpg

http://pic4.zhimg.com/v2-30499c4074e490d42293a4ca7ac5404b_r.jpg

http://pic4.zhimg.com/v2-e70dc7e4393ba4b4a55ea505170ef1ab_r.jpg
下面是虚幻中的继承关系:

http://pic1.zhimg.com/v2-2a907665691db420190846f6e76ca334_r.jpg

http://pic4.zhimg.com/v2-623e5dcc0ae2dbab527ab3602955cc5f_r.jpg
pawn有额外的特性,比如可以用控制器控制(控制器是一个特殊的Actor类,可以接受用户输入),就意味着可以鼠标等控制移动。
character有专属的运动组件,组件有合适的函数,人的各种动作,跳,跑,等等。
而对于这些东西的实现非常复杂:

http://pic3.zhimg.com/v2-6e0f66079501620550af448046012846_r.jpg

http://pic2.zhimg.com/v2-28a8369cf001c3181746f0d0a7dc62c1_r.jpg

http://pic3.zhimg.com/v2-7abc48ed305c66e993c745dde064cde6_r.jpg

继承关系中出现的

http://pic4.zhimg.com/v2-4c50d493e1ea89352ccc5b6dda074f77_r.jpg

一个自定义类型的变量作为另外一个类的成员

http://pic3.zhimg.com/v2-5116154d9a6236c3ec830a50a1a9553a_r.jpg

虚幻中

http://pic1.zhimg.com/v2-97d81450d96f849907e508ec85c8a6dc_r.jpg

http://pic2.zhimg.com/v2-9eba4f504b05359f72a264ffa7e6acd9_r.jpg
7、虚幻引擎类的创建


http://pic4.zhimg.com/v2-63e21f5c0b314ced1979f60f21150dd3_r.jpg
右键点击就可以创建,同样我们还是可以选择父类:

http://pic1.zhimg.com/v2-8b906c9e6ea0b84239ad8a67ee7bddf0_r.jpg
这里的右上角有一个show all ,然后这里我们选择父类为character。

http://pic1.zhimg.com/v2-cdd5e31cc4f8ec96ca13ae41b43612f4_r.jpg
创建好之后,会在VS中打开:

http://pic4.zhimg.com/v2-5c3241807e0003ef2295630e8dc1f3db_r.jpg
这里类名多了一个A是因为继承于Actor,会自动加上这个A字母。
然后这里的三个函数也都是对于父类的override。
我们可以利用VS的转到定义,一级级的往上找到父类。最后找到UObject,以后有U字母在前边的表示继承于object,如果继承了Actor,那就会变成A。

http://pic3.zhimg.com/v2-85695e7b0166df9ba0c40a3bc433352a_r.jpg
关于cpp文件中这三个函数给的实现,都是Super::什么,意思就是调用父类的同名函数。
关于构造函数

http://pic1.zhimg.com/v2-dedc90062bbbc51a44c7758b31eb9f38_r.jpg
这个真表示tick函数起作用,false表示不起作用,可以节省一些资源。
8、反射和垃圾回收


http://pic4.zhimg.com/v2-d50428d15aa6e70d77a5b172ce1e7dfb_r.jpg
c++本身没有,虚幻有反射系统,负责采集数据,负责合并c++数据和虚幻编辑器系统并显示在蓝图中,负责垃圾回收,

http://pic2.zhimg.com/v2-b83e1bae9098e8ff65ad4bfcee8c8a79_r.jpg
关于垃圾回收:大致就是虚幻引擎会记录对象的引用数,一旦所有引用消失之后,便会进行对象的资源回收。
要想实现这一点,我们需要用到宏。
这里有一个UHT,unreal header tool,他是在编译的时候去识别这些宏的,

http://pic3.zhimg.com/v2-02566a1ec253b568ea187c42c992da1a_r.jpg
当他识别到之后,会为这些宏生成相应的code,这样反射系统就可以发挥作用。
UCLASS放在所有类的顶部,代表反射系统的类。修饰之后该类就会有垃圾回收机制。

http://pic2.zhimg.com/v2-49682aada2f643bdad0ad7bd2d4e2921_r.jpg
同时类里面的变量和函数前面也必须加上相应的宏,才能参与反射系统。

http://pic3.zhimg.com/v2-9d2ca776dfe4bf0af528bbc0347bdfd6_r.jpg
这个头文件包含了所有的

http://pic1.zhimg.com/v2-725a5ae39f183a64044ee5257e886e7c_r.jpg
上边说的那些宏他们可以在后面加个括号传进去参数。
9、UObject


http://pic3.zhimg.com/v2-42bc04ce35f601070465ab3566aa2806_r.jpg
选中show all之后创建一个继承object的类。

http://pic2.zhimg.com/v2-a78d7adbfa43cf72a8ef0b59a5515495_r.jpg
这里的几个头文件,第一个一般object类都会包含它,它包含了object需要使用的大量后台代码。第二个和第一个是差不多的。第三个就是反射系统的,有了它才有垃圾回收,也能够把变量和函数反射至虚幻编辑器中。

http://pic2.zhimg.com/v2-da7af6b694726db6a4fdc143f5329171_r.jpg
这里的三个宏都是为了反射系统进行服务的。
通常来说,我们创建好cpp类之后,我们喜欢创建一个基于该Cpp类的蓝图。

http://pic3.zhimg.com/v2-5e074cfce0d0437dec7accbc687dbd5a_r.jpg
这一点右键就可以实现,但是这里是不行的,

http://pic4.zhimg.com/v2-10184872a6703dd23c3c2140eabc48db_r.jpg
然后我们只需要回到cpp代码加上一个参数就好了:

http://pic3.zhimg.com/v2-7b62cb2aa7799ebc44afcebc16c240ca_r.jpg
然后保存,右键build

http://pic2.zhimg.com/v2-ecc95eed9c246cb02901fe204dbbd7c5_r.jpg

http://pic2.zhimg.com/v2-0b0f3f8331a8335ef83fff0a27783cf1_r.jpg
现在就好了。这是因为我们的反射系统在起作用。
现在就把刚刚的cpp和这里的蓝图联系在一起了。创建的东西就是之前cpp代码的蓝图版本。


他是object,不是actor,我们之前说class继承关系的时候,说过object不能直接拽到level中,

http://pic1.zhimg.com/v2-043e886a59be94959ff699132ee101cc_r.jpg
现在给cpp中加上一些自定义的变量和函数,同时写上构造函数,注意类名需要加上U~
想要在虚幻里面看到这些变量函数,需要加上宏:

http://pic3.zhimg.com/v2-31bccc737aac39ce7072ad0e4218d1f6_r.jpg
但是加了之后只是反射系统弄过去了,但并不能访问,需要访问的话得对宏加上参数,

http://pic4.zhimg.com/v2-e76b84187f821251a56c6c994ff1b5b3_r.jpg
另外还需要加上public,才能真的访问的到。

http://pic3.zhimg.com/v2-d01a593fbbc63de2b091185ffb7b1d5a_r.jpg
同时cpp文件中写上俩函数的定义。执行build

http://pic1.zhimg.com/v2-b3ec41a542de218c06c4e9c24d7460ec_r.jpg
现在回到蓝图之后,我们就可以read and write myfloat。

http://pic3.zhimg.com/v2-ffbf7894431cc4f17fc83ec061aa70d2_r.jpg
10、在蓝图中运用UObject


http://pic3.zhimg.com/v2-c32f7b0acf63f5e4701a66bf5edca55a_r.jpg
这里我们在两个宏里面加上更多的参数。

http://pic1.zhimg.com/v2-4b94c74bda1f7244708fe63b2a69b0e8_r.jpg
这时候就是对于我们的变量进行一个分类。这样可以更好的区分变量和函数。

http://pic4.zhimg.com/v2-eed7e12d2e291a35feb482ab70e799cf_r.jpg
我们还可以设置为ReadOnly。这样让蓝图那里九只能读。
我们可以对我们的函数写一些实现:

http://pic4.zhimg.com/v2-5e545e2821c73089a58eccbda0917cfb_r.jpg

http://pic4.zhimg.com/v2-29a878c8a82ef27ae31c31d5f74b1417_r.jpg
第一个参数表示该日志的类别是临时的,warning表示警告,会输出颜色为黄色。编译之后我们到MyActor:

http://pic1.zhimg.com/v2-a938c4518532f94ef5385d060ece4644_r.jpg
我们只需要在这里添加一个MyObject_BP类型的变量。detail面板可以修改类型:

http://pic1.zhimg.com/v2-5222ef4cd19c4be09122a59fb0f89f48_r.jpg
这里搜索,不带BP的是c++类,而带BP的是蓝图。注意右边还会出来四个选择,我们通常选第一个。
然后把变量拽到视口中,然后选择get就可以得到MyObject类的对象了。

http://pic4.zhimg.com/v2-83ddc69aefafcb2e6b5fd0a5a1d975af_r.jpg
然后一拽就能够访问它的函数了。然后连接到BeginPlay上:

http://pic2.zhimg.com/v2-5cc8236a2f68a904795174f6032909a1_r.jpg
这样游戏一旦开始就会调用这个函数了。

http://pic4.zhimg.com/v2-1cf6fdb64c03572059924e6e7ef4f8df_r.jpg
然后把日志窗口调出来。

http://pic2.zhimg.com/v2-f827d9f40d661fa9217e07e1ae462e21_r.jpg
这时候直接运行会出错。

http://pic3.zhimg.com/v2-7921a25e6d327a3f29ccd3da6d3c33ce_r.jpg
原因就是我们创建的myobject_BP对象只是一个指针,指向该类型的指针,同时你看一下默认值是none,也就相当于这是一个空指针,那么这肯定不能访问相应的函数的。
所以我们需要创建处相应的对象,那么怎么创建呢?在C++中需要相应的new和构造函数,这里可以用一个节点:

http://pic2.zhimg.com/v2-f3415a1dd3ebaf92281b612665589801_r.jpg
这里第一个class是我们需要构造对象的那个类,然后outer这里是在Actor类中写的,就写MyActor就好了,最后的返回值其实就是new的结果,也就是指向构造的对象的指针。

http://pic1.zhimg.com/v2-27e621d4b279a492729e95f43e304774_r.jpg
然后我们用该指针去set我们的变量,set之后就可以调用函数了。

http://pic2.zhimg.com/v2-1344ff1cf715bd2856415d50c830e7c9_r.jpg
我们就可以看到我们设置的log了。
所以捋一捋:我们在c++ class 的地方创建一个c++类,然后用VS打开把代码完善,并设置好反射系统,然后回到UE,我们可以创建出这个c++类对应的蓝图类,然后这个类如果是Object的,不能直接拽到level中,那么我们可以在其他蓝图类中创建该类的对象,来使用该类编写好的函数。然后其他的蓝图类可以拽到level中直接创建一个实例。
11、Actors和Actor组件


http://pic4.zhimg.com/v2-f9a89aeb83c3df6350f0ce2e944e2e2b_r.jpg
这里首先创建一个BluePrints文件夹,把我们的蓝图都放进去,便于查找。

http://pic1.zhimg.com/v2-dffc806ce7c4927912c787cb301277f0_r.jpg
我们现在又俩蓝图,对于My Actor来说我们是直接右键创建的蓝图类,也就是说他并不想MyObject那样有一个对应的C++类。
我们现在想要创建一个带有对应c++类的蓝图Actor。我们要创建一个新的actor,它可以四处浮动,所以我们合理命名为Floater。

http://pic1.zhimg.com/v2-38c2b327b777dfe72c54368cd7e28d90_r.jpg
这里生成的代码和我们之前的MainCharacter是类似的,但是比character少了一个

http://pic2.zhimg.com/v2-019321e42e50f84ae5d9f0dbfbd0d811_r.jpg
这一点我们之前介绍虚幻中这些类的时候说过,character是继承于pawn,而pawn则是来自Actor的,也就是Actor更加基础一些。character功能更多一些,包括鼠标键盘输入等等。
然后我们就可以直接创建想要的蓝图类了,之前我们的Object还需要设置一下Blueprintable,这里并不用了,因为它继承了父类的这一点。(在Actor中)

http://pic2.zhimg.com/v2-8e41fe29057aa282e11df6633b456a21_r.jpg
然后创建相应的蓝图,打开后:

http://pic1.zhimg.com/v2-703721b50845e76e1d38b7fd557ac1e0_r.jpg
我们看一下这里的组件,组件是UObject的特殊类型,专门被用作Actor中的子对象,每一个Actor都有一个DefaultSceneRoot组件,这个组件不可见,不支持可视化,也不能赋给他网格,这个就是那个白色的球,但是它现在显示一个白色的球,只是告诉我们有东西,实际游戏的时候就没了。

http://pic3.zhimg.com/v2-380c8bb5cef36af0e1f9ac17a354adf6_r.jpg
我们把蓝图类拽到场景中,然后创建相应的实例,是可以看到那个球的,但是游戏一旦运行起来就没了。只是给我们一个reference。告诉我们这里有一个Actor。
这个是Scene 组件,他是Actor组件的一种,是一个basic组件,它支持的detail panel有transform等等。
primitive组件更加的具体。他是源于场景组件的,有几何和碰撞等信息。

http://pic2.zhimg.com/v2-0ecc3745fe5bb9f0f8660249189728b1_r.jpg
这里的cube这些组件就是primitive组件,我们创建之后可以有static mesh。

http://pic4.zhimg.com/v2-f76dd8b4a7edb6f252c0445a68242b1f_r.jpg
我们创建cube之后,cube是root的child,会随着场景的transform等变化。

http://pic2.zhimg.com/v2-6efcb49d8dd44fad513731dc93e601d5_r.jpg
然后我们可以把scene root组件给舍弃,用我们创建的cube取而代之。

http://pic1.zhimg.com/v2-af9377ad0dbab516e21abaf03239f3ec_r.jpg
只需要拽着cube到root上边就好了。刚刚我们按End键对齐是按照球的中心和面对齐的,但是现在改了root之后,再对齐就是以立方体的底部进行对齐了。这样体现了场景组件和primitive组件的不同。
当我们现在把cube组件删除,之前的默认的scene组件就又回来了。
我们可以在该位置创建static mesh来使用,也可以在cpp code中创建。


组件是特殊的Object,所以这里我们不要忘记U前缀:

http://pic1.zhimg.com/v2-2c407387b3591b1697ef8b9361d7f778_r.jpg
但是上边我们只是创建了一个指针,并没有实际的组件,我们还需要在构造函数中:

http://pic3.zhimg.com/v2-6bfdee8b97163c814f869e3985d12b3e_r.jpg
这个函数就是用来创建对象的,可以创建任意类型的对象,模板参数告诉它类型即可,然后参数是一个TEXT。

http://pic3.zhimg.com/v2-b40be61983cccc8260cb4fd900f12662_r.jpg
这时候我们就创建出来了。
12、位置矢量


http://pic2.zhimg.com/v2-8ae5f3d7599782904ec46e7cdb3e1c81_r.jpg
这里我们先创建一个新的文件夹,然后导入这个模型,导入的同时UE会帮我们创建一个材质。
这里的

http://pic3.zhimg.com/v2-360daeb8ba673c06a00bf1ce3b7284ae_r.jpg
这个mesh导入之后会同时导入四个材质球。

http://pic3.zhimg.com/v2-1c4c9b71f57c204a39f912723503cb8a_r.jpg
我们按住ctrl 或者 shift都可以实现多选,当我们想要移动的时候,按住shift进行一个移动,会使得相机和物体一起移动。
剩余的就是说了一下关于位置表示。
13、FVector 上

我们可以在detail panel中设置位置变换,同样我们在代码中也可以实现:

http://pic2.zhimg.com/v2-a759c85bf5945c033ae2dc4af55b52d5_r.jpg
这样我们就可以实现,让物体在游戏运行的时候把位置重置到原点。
然后点开了FVector看了一波,这个FVector的第一个字母f是因为和计算有关系,f表示float,计算多是float。
14、FVector 中

这里我们尝试把设置的位置作为类的一个成员,然后成员在面板中是可以编辑的。

http://pic4.zhimg.com/v2-a3f5be533686c18ddd4a73f0d0b7c763_r.jpg
这里我们多加了一个宏的关键字。他的作用就是:EditInstanceOnly表示该属性可通过属性窗口来编辑,但仅能对实例而非原型进行编辑。
面板中剩余的东西就是这个对象的组件的属性,因为组件其实说白了就是组成该对象的成分,所以组件的属性就是该对象的属性。包括mesh 等等的全都是。

http://pic2.zhimg.com/v2-e02c934c444fedaf4ac844924c9f5de9_r.jpg
上边的InstanceOnly怎么体现的呢?其实我们在蓝图中是找不到这个值的,蓝图中如果有了,那就相当于对类本身的值进行修改了,而不是对每一个实例去编辑。
这里还有一个位置的设置,就是当我们把一个instance拽到视口中的时候,它放在哪里,这个位置我们可以给显示出来。

http://pic1.zhimg.com/v2-9af2bd360bf5eada99a33d8c0d37ada8_r.jpg
这里又用到了一个新的关键字,VisibleInstanceOnly : 表示该属性仅在实例的属性窗口中可见,但对原型则不行,并且无法被编辑。其实对于实例可见对于原型不可见这一点还有一个理解方式:就是每一个实例的该值是可以不一样的,原型你也没法显示。
然后这个位置我们只需要在游戏刚开始的时候获取赋值一下就行了,

http://pic2.zhimg.com/v2-8dea8288ede6be73f793b59c4ed6ccf9_r.jpg
我们只需要运行之后按shift + f1就可以把鼠标弄出来,然后用鼠标点击世界大纲中的这些instance,我们就可以看到他们被拽进去的位置。
接下来弄一个bool类型的变量,bool类型的需要在变量名前面加一个b,这个是虚幻的代码习惯,加上就得了。

http://pic1.zhimg.com/v2-bbc4e02de02cefb3a43a2726bdba8b24_r.jpg
这里新的关键词,EditDefaultsOnly,表示该属性可通过属性窗口来编辑,但仅能对原型编辑。

http://pic4.zhimg.com/v2-b22d66491794bc3d6d284e02a962822b_r.jpg
这时候虚幻把我们的bool类型b字母就给自动去掉了。
15、FVector 下


http://pic2.zhimg.com/v2-91b16b27b24026aadd7b538b53ac58dd_r.jpg
16、碰撞


http://pic2.zhimg.com/v2-b68f8ab0ce4c4befac6944f695d0b991_r.jpg
这里的模型本身没有带简单碰撞,我们可以自己去为他添加:

http://pic4.zhimg.com/v2-08ff6da7bcc63e524bf65dd6df57059f_r.jpg
点击后:

http://pic4.zhimg.com/v2-5cddf277351df1ba647d97b1df602f33_r.jpg
我们根据这个组件,是可以对碰撞的盒子进行一个编辑的,包括平移缩放旋转等。

http://pic3.zhimg.com/v2-7e4a6f66e666ee952b760f7b64f164a2_r.jpg
也可以进行remove。还可以尝试其他各种各样的包围盒。

http://pic2.zhimg.com/v2-56f86edb3a7162753640367108817a15_r.jpg
回到蓝图中,这里有一个物理模拟的开关,然后下面的Mass是质量,后面还有是否开启重力,如果关闭物理模拟,开启重力是没用的。
damping那俩是线速度阻尼和角速度阻尼。就是减慢平移速度和转动速度的。

http://pic2.zhimg.com/v2-64ec9fcf0e27e1713f54adfa62d71861_r.jpg
场景中的这种图标没了按一下G就回来了,回来之后再按就消失了。
17、碰撞(续篇)


http://pic1.zhimg.com/v2-5fef8591ca69cf5e75c9182f754d67a4_r.jpg
在蓝图中我们创建一个节点,创建之前,我们要在左边点一下选中这个:


才能搜索到该节点。因为他们需要static mesh,这样选中之后生成的节点会自动帮我们带出来一个static mesh。这个static mesh我们也可以选中上边的,然后拽进去也会出现。

http://pic2.zhimg.com/v2-860ff1c8c5a60c3ba53e1beb633e9b85_r.jpg
我们把力的值设置好之后,我们编译会出现警告,我们一定要弄清楚原因,这里就是静态网格节点有限制,不可以在蓝图中使用。我们需要在cpp中消除这一点限制。

http://pic2.zhimg.com/v2-6098c4c291ef4e2b69d45595b7197679_r.jpg
加上上边这个就好了,其实这就是cpp中限制了蓝图操作,而蓝图中硬操作出现的结果。

http://pic2.zhimg.com/v2-3c91c2e1aaee2c668f10f816c90d3a11_r.jpg
然后我们如果把椅子的物理模拟也加上,就会出现互相碰撞,椅子被撞翻了的情况:

http://pic1.zhimg.com/v2-07b5e098bc87a5b06125bdd920ba2f84_r.jpg
然后我们可以查看这些物体的

http://pic3.zhimg.com/v2-1b3260341254861d380dbf711c8934a2_r.jpg
这时候我们运行,然后shift+f1把鼠标显示出来,按一下键盘上的~键,就可以出现一个cmd:

http://pic3.zhimg.com/v2-ee673700ffc44de0122240ee8b475d82_r.jpg
然后我们输入show collision然后直接回车,就可以看到物体的碰撞检测的盒子了:

http://pic4.zhimg.com/v2-01d53a7a78e7964636e6386abede3847_r.jpg
另外这里如果我们关闭重力:

http://pic1.zhimg.com/v2-264669d8d59f7fb644f5c0d6872ef1c4_r.jpg
就直接像太空人一样飘起来了,如上图的桌子。
18、扫除


http://pic3.zhimg.com/v2-404b51a50a63a26627d2973dcaf2df12_r.jpg
这里的shape,各种的网格形状,如果我们把他们拽到视口中,那么实则是创建了一个带静态网格的Actor。

http://pic1.zhimg.com/v2-e3d1cfe193c0ff9dd9b06042b6512ae0_r.jpg
然后把立方体给缩放成一个墙,关闭物理模拟和力量的节点(需要在实例的面板和默认设置面板都给关了)。

http://pic2.zhimg.com/v2-b6e005a4dc9306d06a84b08552033779_r.jpg
然后勾选一下自动Float的,这时候我们去运行游戏,我们的盒子并不会和墙相撞,而是直接float穿过墙。直接穿透过去。
原因就是我们关闭了物理模拟。

http://pic1.zhimg.com/v2-1a971bc586ca4a9f979c7f87736f487c_r.jpg
然后回到我们的c++代码中,这里的第二个参数是是否sweep,而这个sweep就和我们的碰撞检测相关。
如果我们把刚刚的物理模拟关掉,然后把这里的sweep改成true,这样再运行就会发现盒子不能穿过墙了,

http://pic1.zhimg.com/v2-a35f12706d92ffed2fc0fe84841cbb8c_r.jpg
也就是sweep为true,会组织碰撞发生。

http://pic1.zhimg.com/v2-bf60b929d3a457b98917991a2a6e01a4_r.jpg
第三个参数是一个结构体,他里面有一个成员,首先这个结构体是一个hit result表示hit命中结果,然后这个成员的意思是:该命中结果是否是阻挡碰撞的结果。

http://pic1.zhimg.com/v2-b0170af9be01f67cda3e42fcce4a69dc_r.jpg

http://pic3.zhimg.com/v2-5f5f4e9bc8a41211abcc0f39953edbfe_r.jpg
这里还有一个成员,表示hit的location,这里他的类型是FVector的优化版本,我们可以把他当作FVector使用。

http://pic3.zhimg.com/v2-ecdc6ecc0fb668dba579f682f02483c6_r.jpg
然后我们吧hitLocation给输出一下:

http://pic4.zhimg.com/v2-0ee254c602d35e1d6823d574b529c18f_r.jpg
刚开始没有hit信息,也就是保持默认初始化的状态,全都是0,然后第一次碰撞发生之后就更新hitlocation。

http://pic4.zhimg.com/v2-f8f1196e8835a6bedc5e7c45f59cf5bf_r.jpg
对于每一个物体还有一个collision模块,

http://pic4.zhimg.com/v2-47f99e200c4f006c4d74245b2da33dcf_r.jpg
其中的设置还可以自定义,让我们有很大的自由度。

http://pic1.zhimg.com/v2-c63e4975bde77ab663eb6ee69034a3d8_r.jpg
这里有一个Object Type,这个选项能够决定该对象和其他对象碰撞时的表现。同时她下面的勾选,是对于其他类物体的回应,可以忽略可以重叠,阻挡。

http://pic1.zhimg.com/v2-0a328d22feb613cfc16656cdf6e30ca8_r.jpg
我们的floater,他的object 类型是world dynamic,然后我们修改一下墙的

http://pic4.zhimg.com/v2-8cb566d91141eb07d312c9402da5f8bf_r.jpg
变成自定义的,然后我们设置和world dynamic碰撞为overlap也就是重叠。这样我们运行之后就算sweep为true,但是这里设置的类型是overlap,他也会进行一个穿透了。当然在floater那里修改该physicsBody类型的选择也是一样可行的。
19、局部与世界偏移

把之前的每一帧平移给关掉,然后再beginplay的地方加上:

http://pic3.zhimg.com/v2-5226e177deed3b9790e1ee87d9632a6e_r.jpg
这里我们给我们的物体发生一些旋转,

http://pic4.zhimg.com/v2-5a7141929d4e87445b178053004f95d3_r.jpg
然后通过上边的这个地方,我们就可以对物体身上的平移组件显示的轴进行一个切换,切换世界或者local轴。而我们旋转之后,世界轴和局部轴不再统一,这时候我们可以发现我们刚刚的offset是顺着局部轴进行的。因为他就叫 localoffset。
我们想要顺着世界的轴平移,那就可以换个函数。

http://pic2.zhimg.com/v2-1f375a249186e6a460db6c409643eadd_r.jpg
我们出了平移还有旋转:

http://pic1.zhimg.com/v2-57fb5b791b5f2c09b4bc2ca21a813c6c_r.jpg
这里的三个参数表示的pitch yaw roll。


我们现在的形状不容易看出旋转效果,可以给他处理一下:

http://pic4.zhimg.com/v2-03956940e58f5288b661d173db08516b_r.jpg
我们只需要添加一个新的static mesh组件,然后给他放上去我们的轴mesh。

http://pic2.zhimg.com/v2-ea28fbd41a24d2f202178965460623d1_r.jpg
现在就能明显看出旋转之后的效果了。可以看出pitch是抬头低头,yaw是左右扭头。roll是身体左右摇摆。
我们也可以把这个rotation操作放在每一帧中,也可以设置worldrotation。
20、力矩和扭矩

之前我们说过一个AddForce,也就是给物体加一个恒定方向和大小的力,那个带来的结果就是物体会发生平移,而这里有一个类似的力,就是让物体发生旋转的,扭矩。torque。

http://pic4.zhimg.com/v2-826afa425e40ee9c5cf2c5ce26745453_r.jpg
加上这两行,这里视频中出现了一些问题,他的VS不认识StaticMesh对应的类型,也就是缺了一个头文件,然后缺的什么头文件呢?

http://pic2.zhimg.com/v2-b02fb255177e2a66ffa665baabea97b9_r.jpg
可以直接搜索这个名字,然后发现有这么一个头文件:

http://pic2.zhimg.com/v2-9a886e5355b7984015aca112e660edcd_r.jpg
加上就好了,我这里并不需要这个头文件就可以跑起来,可能是版本问题吧~
运行之前需要开启物理模拟才会有效果。
另外一个小经验,就是在测试力这类东西的时候,因为大小可能需要随时调节,所以我们最好加个UPROPERTY,这样可以设置成面板那里直接手动调节了。不需要每次修改code,然后compile了。
类似的我们也在面板上设置一个torque的,

http://pic2.zhimg.com/v2-58cec4b1ee85f09cecb5e17fdf95f4c5_r.jpg

http://pic2.zhimg.com/v2-409b03057e94bc963abb62657dc0ee65_r.jpg
21、随机数值

这里主要是介绍关于虚幻里面的数学函数,有一个FMath,F开头是一个结构,然后他里面有很多的静态函数,我们可以::来直接调用,至于每一个函数的左右,我们可以通过函数名字和一些转到定义的手段来确定,并且可以多做做实验。
我们可以FMath::FRand来生成一个随机值,来作为initialLocation。

http://pic2.zhimg.com/v2-dafbf73fb37f79f43c91705b1193f139_r.jpg
这里你会发现随机值永远是正数,我们想要负数可能还需要两个相减,但是我们还有一个RandRange,这个就可以自己指定范围了。
22、正弦函数

我们利用正弦函数来对物体的浮动方式进行一个控制。

http://pic2.zhimg.com/v2-2606d8ea5c3efd101e7edb9f9fbe90a5_r.jpg
正弦需要随着时间来进行一个计算,我们这里获取时间的方式就是利用每一帧的deltaTime,然后利用一个变量对这些deltaTime进行不断累加。

http://pic3.zhimg.com/v2-d01a5455e085b68a03bf17ba6a76cf1e_r.jpg
正弦函数返回值是-1到1的,如果我们想要实现上下的漂浮,那么我们只需要让我们的xy不变,然后z和sin函数一致进行周期性变化就好了。

http://pic2.zhimg.com/v2-24b943827f3ff7392f8811f5b41a6cdd_r.jpg
这样球就可以实现浮动的效果了。我们可以修改正弦函数的系数,外面的系数可以影响上下浮动的最大幅度,里面加一个系数可以修改浮动的周期。当然也可以尝试最后面加一个上下平移。

http://pic1.zhimg.com/v2-025c062f9c3e52d1ca7a2223e4b5a68c_r.jpg
我们这里可以把三角函数的三个参数给变成成员,然后暴露到蓝图中,这样可以直接在面板中进行一个修改。

http://pic3.zhimg.com/v2-0142d93662b3243392b1600adad79846_r.jpg
这里我们在分类的时候分类一个子类,Wave Parameters,只需要在中间加一个 | 符号就可以实现。

http://pic1.zhimg.com/v2-d453e78c2ce085d6ced0ca77a2c57e38_r.jpg
另外注意,我们每次重现编译代码,都会让面板上的这些参数重置为构造函数中写的值。
改一下名字:A是Amplitude,表示波幅,B叫TimeStretch,表示波左右伸展。

http://pic1.zhimg.com/v2-318f4564968f347796014995f1c236e4_r.jpg
当然它的作用不只是浮动,我们可以这么对xy使用,这样就可以画圆了。

http://pic1.zhimg.com/v2-396f7a6feb186362c7b6bfe25b0ca1c0_r.jpg
我们回顾一下原来的式子,其实这个东西并不是表示的在初始z位置上-1到1的浮动,他是拿的上一次的z去和三角函数做加法,第一次z = 0,然后加了0.1,第二次z = 0.1,z加了0.2,这样第二次相加之后就等于0.3了,这也是为啥我们A=1的时候还能有那么大的幅度。
如果我们是只对最开始的时候的z值加上三角函数,A 等于1 的时候上下浮动基本上是看不出来的。但我们看具体的z值是可以看出确实在上下浮动的。

http://pic3.zhimg.com/v2-e0b36a7c275b6e4e29633cdb85f6088a_r.jpg
另外这里如果在UPROPERTY里面加上一个BlueprintReadOnly,我们再进行编译会报错,这个错误我们只需要把他们放在public下面就好了。

http://pic2.zhimg.com/v2-7a896791b2f43720c2a0f3cdb4e6a7ed_r.jpg
这里其实还有一个,就是加在sin()括号内部的值,这个影响并不大,知道就好了:

http://pic3.zhimg.com/v2-cc713b42f83aa0ee7132b8492bab295a_r.jpg
23、删除类

我们现在知道了如何为类添加组件,添加静态网格组件,
删除蓝图的话,直接按delete

http://pic2.zhimg.com/v2-650131d4c9d0ac29bb1daa92feb5819d_r.jpg
然后所有的instance都会消失,但是对于cpp class直接delete是不行的,而且右键也不能删除。
具体方式:

http://pic3.zhimg.com/v2-0970e47e771f8bce5a1e9b9d27c41e86_r.jpg

http://pic2.zhimg.com/v2-e21237cffb6421df4edc8f9929f58fa1_r.jpg
把这俩文件删除就行了。但是还没完,还得删除一些文件,

http://pic1.zhimg.com/v2-413bbee1352805f42d367cf0f8091f80_r.jpg
这个给删除了。

http://pic2.zhimg.com/v2-6c03c984c761303fa8cbf7cdfed4b5dd_r.jpg
然后右键项目文件,generate那一项。然后重新打开项目,会提示dll确实,点yes重新生成就好了。刚刚删除的Binaries会重新生成,这样我们的cpp类就删除干净了。
flaoter.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Floater.generated.h"

UCLASS()
class FIRSTPROJECT_API AFloater : public AActor
{
        GENERATED_BODY()
       
public:       
        // Sets default values for this actor's properties
        AFloater();

        UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "ActorMeshComponents")
        UStaticMeshComponent* StaticMesh;

        UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "Floater Variable")//实例信息可编辑
        FVector InitialLocation;

        UPROPERTY(VisibleInstanceOnly, BlueprintReadWrite, Category = "Floater Variable")//实例信息可见
        FVector PlacedLocation;

        UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Floater Variable")//原型可见
        FVector WorldOrigin;

        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Floater Variable")//原型,实例均可编辑
        FVector InitialDirection;

        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Floater Variable")//原型,实例均可编辑
        bool bShouldFloat;

        UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Floater Variable")//原型可编辑(默认设置下就是所说的原型)
        bool bInitializeFloaterLocation;

        UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "Floater Variable")
        FVector InitialForce;

        UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "Floater Variable")
        FVector InitialTorque;

private:
        float RuningTime;
public:
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Floater Variable | Wave Parameters")
        float A;
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Floater Variable | Wave Parameters")
        float B;
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Floater Variable | Wave Parameters")
        float C;
        float BaseZLocation;

protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
       
public:       
        // Called every frame
        virtual void Tick(float DeltaTime) override;

};
floater.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "Floater.h"

// Sets default values
AFloater::AFloater()
{
        // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;
        StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CustomStaticMesh"));

        InitialLocation = FVector(0.0f);
        PlacedLocation = FVector(0.0f);
        WorldOrigin = FVector(0.0f);
        InitialDirection = FVector(0.0f);
        bInitializeFloaterLocation = false;
        bShouldFloat = false;
        InitialForce = FVector(2000000.0f, 0.0f, 0.0f);
        InitialTorque = FVector(2000000.0f, 0.0f, 0.0f);
        RuningTime = 0.f;
        A = 1.f;
        B = 1.f;
        C = 0.f;
}

// Called when the game starts or when spawned
void AFloater::BeginPlay()
{
        Super::BeginPlay();


        InitialLocation = FVector(FMath::FRandRange(-1000.f, 1000.f), FMath::FRandRange(-1000.f, 1000.f), FMath::FRandRange(-1000.f, 1000.f));

        PlacedLocation = GetActorLocation();
        if (bInitializeFloaterLocation) {
                SetActorLocation(InitialLocation);
        }
       
        BaseZLocation = PlacedLocation.Z;

       
        //StaticMesh->AddForce(InitialForce);
        //StaticMesh->AddTorque(InitialTorque);

        //FHitResult FHitResult;
        //FVector LocationOffset = FVector(200.0f, 0.0f, 0.0f);
        //AddActorWorldOffset(LocationOffset, true, &FHitResult);
        //FRotator Rotation = FRotator(0.0f, 0.0f, 50.0f);
        //AddActorLocalRotation(Rotation);
}

// Called every frame
void AFloater::Tick(float DeltaTime)
{
        Super::Tick(DeltaTime);

        if (bShouldFloat)
        {

                RuningTime += DeltaTime;
                FVector NewLocation = GetActorLocation();
                NewLocation.Z = BaseZLocation + A * FMath::Sin(B * RuningTime) + C;


                SetActorLocation(NewLocation);


                //FHitResult FHitResult;
                //AddActorLocalOffset(InitialDirection, true, &FHitResult);

                //FVector HitLocation = FHitResult.Location;

                //UE_LOG(LogTemp, Warning, TEXT("Hit Location : X = %f, Y = %f, Z = %f"), HitLocation.X, HitLocation.Y, HitLocation.Z);
        }

        //FRotator Rotation = FRotator(0.0f, 0.0f, 1.0f);
        //AddActorWorldRotation(Rotation);
}
页: [1]
查看完整版本: 虚幻4渲染编程(入门引擎篇)【第一卷:初识虚幻引擎】