第三章 流程控制
第一节 C++和蓝图循环
ForLoop蓝图节点相当于C++中的for循环,ForeLoopWithBreak节点相当于for循环体中加了break语句,当触发某个条件时直接结束。
for()
{
if(xxx)
break;
}
WhileLoop节点相当于C++中的while语句
while()
{
}
第二节 C++和蓝图结构体
UE4可以右键空白处选择蓝图,创建结构体,方便管理数据。C++中如下。
struct MyStruct
{
bool one;
int Two;
};
void main()
{
MyStruct str;
str.one = true;
str.two = 0;
cout<<str.one<<endl;
system("pause");
}
同结构体一样,在UE中同样可以右键空白处,然后选择枚举或者Enumeration来快速创建一个枚举蓝图。之后在具体要用的蓝图中新建一个变量,在变量类型中选择类型为自己刚刚创建的枚举名即可。
enum ETr{
Monster,
Kill,
Hello
};
void main()
{
ETr tr;
//即在几种类型之中选择一种
tr = ETr::Kill;
case Monster:
break;
case Kill:
break;
case Hello:
break;
default:
break;
}
第三节 DoOnceDelay
在流程控制节点中,branch节点相当于if语句。Delay节点相当于Sleep语句。DoOnce节点意思是只执行一次,DoOnce中startclosed选项要是勾上了,则刚开始就不执行,reset用于重置DoOnce。 Gate阀门,可以通过事件来调用。FlipFlow可以控制第一帧走上面的第二帧走下面的,来回往复。MultiGate用于emm,Gate加强版,一次性可以有很多输出,还可以设置从第几个开始,可以设置是否循环是否随机,以及Gate也有的重设。
第四节 SequenceDoOnce
这玩意只要下个信号传过来就会刷新等待时间,所以如果在tick中使用的话,就会无限阻塞。
sequence序列很有用,可以一次同时执行很多事件。
第五节 C++实现蓝图控制
实现Branch
void TureCondition()
{
//这种情况下要实现的功能
}
void FalseCondition()
{
//这种情况下要实现的功能
}
void Branch(bool Condition)
{
if(Condition)
{
TrueCondition();
}
else
{
FalseCondition();
}
}
实现Delay
void DelayCompleted()
{
//要完成的事件逻辑
}
void Delay(float Duration)
{
//sleep函数的单位是ms
Sleep(Duration * 1000);
DelayCompleted();
}
实现DoN
static bool bReset = false;
void Reset()
{
bReset = false;
}
void DoThing(int i)
{
//要执行的逻辑
}
void DoN(int N)
{
if(!bReset)
{
for(int i=0;i<N;i++)
{
DoThing(i);
}
//循环次数完成,关闭循环条件
bReset = true;
}
}
实现DoOnce
static bool bDoOnce = false;
void Reset()
{
bDoOnce = false;
}
void DoOnce(bool StartClosed)
{
if(!bDoOnce)
{
bDoOnce = false;
Completed();
}
}
第四章 事件接口
第一节 封装逻辑蓝图代理
函数中禁止使用delay,会造成线程阻塞,函数要求执行快,执行完就释放临时变量。宏里面可以包含事件,宏里面可以用delay。
代理(发报机、委托)蓝图中绑定和解绑操作如下
C++中差不多这样就行
void print()
{
printf("hello world");
}
void main()
{
print();
system("pause");
}
第二节 Actor事件接口
在蓝图中找到function选项,选择override下拉列表,可看到如下函数功能
Actor生命周期中,有Event Tick、Event BeginPlay、Event Destroyed、Event End Play四个函数。
特别注意,有两个叫ActorEndCursorOver和ActorBeginCursorOver的函数,可以判断场景中鼠标是否和Actor重叠
Event OnEndViewTarget和Event OnBecomeViewTarget函数用于切换视角,一个在切换到某个物体的视角时执行,另一个在离开某个物体的视角时执行。在编辑器窗口中可使用,Vew ACTOR名称 命令来切换观察的物体。
对于伤害函数来说,有RadialDamage和Point Damage、带衰减的半径伤害等形式。
第三节 Character接口
EventOnWalkingOffLedge函数,当人物离开正在行走的表面时就会激活,下面几个参数分别是,离开面的法线(Previous Floor Impact Normal),接触面的法线(Previous Floor Contact Normal),在哪个位置结束的函数(Previous Location),时间增量(Time Delta)。
可以用这个办法切换角色。
第四节 服务端事件接口
GameMode接口:
第五节 UMG接口
获取鼠标位置
这里有个宝藏教程,关于UI中,槽和拖拽的做法,一看就可以用来做背包。我详细纪录一下步骤。
没搞明白。
第五章 蓝图间的访问
第一节 通过实例访问
现在关卡编辑器中,选中要访问的蓝图类,然后在关卡蓝图中右键空白处创建一个对实例蓝图的引用,然后调用TestA事件。
还有一种方法,通过遍历场景中所有的actor,找到选择的TestA类数组,因为场上只有一个TestA,所以选中数组中的第一个元素,调用其TestA事件。
第二节 管理模式访问
创建一个实例蓝图,通过在蓝图中创建或生成另一个蓝图进行管理。
尽量不要用第一节中GetWorld后遍历的方式操作类。
第三节 蓝图接口访问
在C++中通过接口访问的话,举例如下
class Actor
{
public:
}
class IInterface
{
public:
virtual void prinit_f() = 0;
private:
};
//在接口中抽象实现后必须在子类中继承
class A : public IInterface,public Actor
{
public:
virtual void print_f()
{
cout<<"Hello"<<endl;
}
};
void main()
{
IInterface * S_a = new A();
//调用接口
S_a->print_f();
system("pause");
}
在蓝图中这样创建接口
我们知道,在C++中,如果S_a多继承了IInterface接口和Actor类,那么在创建的时候,是无法进行如下操作的。
Actor * S_a = new A();
//调用IInterface的接口
S_a->print_f();
//无法调用,因为S_a是Actor的类
但是在蓝图中可以这样操作,在要继承的类的class setting中添加上面自行创建的蓝图接口后。
然后在侧边栏新增的接口菜单中进入接口的蓝图面板,在其中实现接口的功能。
最后在管理类中,创建接口变量,从生成类的蓝图节点的返回值获得接口的引用。
第四节 UMG访问正确方式
通过获取Controller后获取HUD访问,不能通过UMG直接访问pawn,character。HUD的实例存在于Player Controller。因为在服务端或其他客户端根本不存在你的UMG数据,但是有玩家控制器,所以直接修改UMG是错误的,无法同步的,要通过controller访问。
第六章 组件
第一节 基础组件
静态网格体,骨骼网格体,移动组件,抛射物组件,球形,盒型,粒子组件,没啥特别的。
第二节 渲染组件自定义
有一个缆索组件,英文名是cable,可以模拟一段绳子悬挂的物理效果。可以设置绳子长度,起始点和终止点。末端可以附加到Actor的组件上,也可以附加到插槽上。更改分段数可以改变模拟物理效果的平滑程度。
如果是文本渲染组件的话,叫TextRender组件,如果想显示中文内容,需要使用支持中文的字体,不然会乱码,TextRender本质是使用材质进行贴图。
还有一个重要的组件叫程序化网格体组件(Procedual Mesh),可以用这个组件自定义网格模型,并进行布尔运算,不过都需要自己去写。
如果要画一个面出来,首先要设置四个顶点Vertices,取决于你所用的相对坐标轴。计算机中以三角形绘制一个平面,因此画一个矩形的时候需要画两次。四个点用六次画两个三角形拼出一个矩形。绘制时顺时针或逆时针方向则决定了法线(Normal)的方向,所以画完了要是没看到图像可能是法线的问题,翻转一下就可以解决。
在单独创建一个用于这个Mesh的材质并应用后,即可设置Vertex Color顶点颜色。
也可以借此手动创建一个立体模型。
第三节 渲染组件系列
向量场(VectorField)可以决定粒子是怎么发射的。样条线组件(SplineMesh),可以让模型按想要的样子发生变化。MediaPlane,和Camera组件一起使用,专门给摄像机播放媒体用的。 还比如InstancedStaticMesh,实例化静态网格体组件,可以用来处理渲染同个网格体但是多个实例的情况,可以优化场景渲染的速度。
第四节 AI组件和算法
先往场景里拖一个导航网格体,Nav mesh bounds volume。如果不想要某个地方成为导航网格体的一部分,可以使用Nav Modifier Volume,被覆盖的区域将不会计入导航网格体覆盖范围。如果想要AI从跳跃路线走的话,那就使用Nav Link Proxy,这样AI也会判断是从大路走离目标近还是从某个点在场景中进行跳跃离目标近。
AI通过A*搜寻算法在地图中找到一条最短路径,主要问题是判断路径是否阻塞。在蓝图中添加AIPerception(AI感知组件),与视力和听力有关。PawnNoiseEmitter(Pawn噪点发射器组件)可以发射一种只有AI能听见的信号让AI感知到。NavModifer(导航修改器组件),可以手动标记一段区域的优先级,比如不想要AI优先走某段路但又不想把这条路彻底堵死,可以用这个方式;又比如说可以让AI优先走某条实际上更远的路(通常是大路)。
第七章 数据库搭建
第一节 基于Save增删查改
蓝图类型Save Game本质上是UE的一种本地数据库。SaveGame用GameInstance承载,因为游戏实例不随关卡的变化而变化,只有关掉游戏的时候游戏实例才会释放。
第二节 CSV和Jsob
感觉没啥好讲的,省略一下讲后面的重点吧
第八章 搭建局域网Session
第一节 用户界面菜单、动画、效果的实现
先弄个UMG,也就用户控件。然后随便摆摆控件或者做个按钮动画啥的,我拿我自己的举例子。我先创建了一个WBP_选择联机模式
点击这个按钮可以打开具体的局域网查找界面。对鼠标停留在按钮上,按钮产生悬浮动画感兴趣的可以参考这个蓝图实现。
因为我点击第一个按钮选择局域网模式之后,会播放一个动画来加载出新的用户控件,这个动画跟怎么制作局域网无关,不过纪录在此。
关闭所有UI交互是自己写的一个蓝图函数,目的是关闭在父级用户控件时子级用户控件的交互性。
之后创建了WBP_单机局域网蓝图。
用到了一个垂直框,用来装里面的三个按钮和三个文本框。一个滚动框,后面用于显示创建的房间信息。一个循环动态浏览图示,英文是circular throbber,就是一个一直转圈圈的控件,用于在点击查找房间这种按钮后,显示出来给用户一种程序正在工作的反馈。那么菜单这边就做到这。
第二节 创建会话
上节倒数第二张图中,有个创建服务器按钮。那个按钮就是创建局域网会话按钮,其背后对应的蓝图如下。
这样子就可以在本地局域网上创建一个可供被搜索被加入的在线会话了,至于进入具体的地图后如何操作,那就是关卡蓝图和GameMode的事情了。而上图中开始游戏按钮是用来单人游戏的,点了之后直接跳转到关卡去而不创建会话。
第三节 查找并加入会话
局域网中一台主机创建会话(即一场在线游戏)后,其他的机器就可以寻找并加入这个会话。那么我在查找房间按钮中实现这个查找并加入的功能。蓝图实现如下。
在这个功能中,点击按钮后,首先清除滚动框中的所有子项,即如果之前里面有信息就都给删了。之后设置转圈圈控件的可视性为可视,这样用户就知道程序在工作了。之后寻找会话,这里设置Max Result为10的话,那么程序在局域网中最多只会找到十个可供加入的会话,一般在本地局域网游戏中这肯定是够的。之后蓝图节点将找到的会话存储为数组,我们将其提升为变量,之后循环遍历数组中所有的会话,分别将创建为_WBP_房间条控件,注意两点,每一个会话的信息都被传到了WBP_房间条控件中,另一个则是,WBP_房间条控件是被添加到滚动框中成为WBP_单机局域网中成为一个子项的。
WBP_房间条的实现很简单,布局只需要一个按钮和一个文本就够,连画布面板都不需要。蓝图实现如下。
按钮点击时:
Update事件:
格式化文本是个好东西,里面用{}括住你想要的文本变量后回车,下面就能自动生成你在{}中写的属性。update事件将在本节开头的图中提到,会将按钮添加到滚动框中后刷新按钮上显示的文字,就能告诉用户这个会话中的玩家数量、Ping以及房间名,用户点击这个按钮就能加入会话。
第四节 最后效果
在编辑器中网络模式选择以客户端运行,限于机能所限,这里开启两个会话进行演示。右图为已经点击了创建服务器进入等待大厅的角色,左侧还没进行操作。
左图此时点击查找房间按钮,屏幕中心的滚动框中就会加载按钮,并更新房间信息,此时玩家点击按钮就能直接加入会话。若是一个局域网中同时存在多个会话,这里也会刷新多个按钮出来。
此时两名玩家就加入了同一个会话之中。
到这里,关于搭建局域网会话的内容就结束了。