前言:
笔者目前在校本科大三,目标方向是人工智能、计算机视觉。上一个OpenCV学习笔记专栏已完结,在学习完OpenCV后,我继续学习C++,并用纯C++做UE4项目的方式继续提升自己的水平。
梁迪老师的水平非常高,他的课程本来也无需笔记:课程本身即为最好的笔记。但由于我天赋有限,还是边看边记,以防遗忘——知识点太多,步骤太繁杂了。在学习过程中,我也偶有思考,思索为什么某个方法老师要这样做。所以,一是为了记录,二是为了分享,才有了这个专栏。
内容方面,由于我在开启这个专栏时,此项目已经做完很多了。所以,前期的一些大篇幅叙述的知识,可能在后期应用中一带而过。以及,前期的一些知识,后期会重新剖析,并加上我的个人理解。
另外,若有学术交流/学业交流意愿,可以邮件联系1246210283@qq.com,希望一齐进步。
本篇学习内容:
43.拾取物品时不显示手上物品
44.生成掉落物
45.掉落物定期检测玩家,并飞向玩家
46.游戏暂停
43.拾取物品时不显示手上物品
实现目标:在拾取物品时,例如拿着苹果,在拾取物品时,苹果应当不显示在人物手上。
(1)在UE4编辑器内的动作蓝图的Pickup动画中,创建两个通知:PickupStart和PickupStop
(2)在PlayerAnim中准备RenderHandObject()函数,实际上是调用Character中的RenderHandObject()函数:
void ASlAiPlayerCharacter::RenderHandObject(bool IsRender)
{
//手上物品是否存在
if (!HandObject->GetChildActor()) return;
//如果有物品
HandObject->GetChildActor()->SetActorHiddenInGame(!IsRender);
}
(3)在动作蓝图中绑定这个事件到PickupStart和PickupStop通知上
44.生成掉落物
(1)新建碰撞配置文件
(2)新建C++类:Actor:FlobObject
(3)新建一个给Flob用的材质:通过修改ObjectTex参数来显示不同的物体
(4)创建根组件和BaseMesh,设置好其碰撞、大小、旋转等参数。
(5)如何通过修改ObjectTex参数来显示不同的物体:在FlobObject中创建两个变量:UTexture* ObjectIconTex;
和UMaterialInstanceDynamic* ObjectIconMatDynamic;
具体做法在RenderTexture()中:
void ASlAiFlobObject::RenderTexture()
{
//ObjectIconMatDynamic导入了我们在UE4编辑器内创建的材质,ObjectIconTex的作用是动态地把不同的材质赋给ObjectIconMatDynamic定义好的参数,从而实现动态修改资源
TSharedPtr<ObjectAttribute> ObjectAttr = *SlAiDataHandle::Get()->ObjectAttrMap.Find(ObjectIndex);
ObjectIconTex = LoadObject<UTexture>(NULL, *ObjectAttr->TexPath); //如果写为本地变量,此函数结束后,栈内销毁这个变量,就会报错
ObjectIconMatDynamic->SetTextureParameterValue(FName("ObjectTex"), ObjectIconTex);
BaseMesh->SetMaterial(0, ObjectIconMatDynamic);
}
45.掉落物定期检测玩家,并飞向玩家
实现目标:掉落物定时检测玩家,检测到玩家就飞向玩家,在接近玩家时销毁。如果固定时间内检测不到玩家,自动销毁。
(1)创建2个定时器,再创建2个用于绑定定时器的函数,再创建一个玩家指针。
(2)在BeginPlay函数中设置定时器
void ASlAiFlobObject::BeginPlay()
{
Super::BeginPlay();
//检测世界是否存在
if (!GetWorld()) return;
//注册检测事件
FTimerDelegate DetectPlayerDele;
DetectPlayerDele.BindUObject(this, &ASlAiFlobObject::DetectPlayer);
//每秒运行一次,循环运行,延迟3秒运行
GetWorld()->GetTimerManager().SetTimer(DetectTimer, DetectPlayerDele, 1.f, true, 3.f);
//注册销毁事件
FTimerDelegate DestroyDele;
DestroyDele.BindUObject(this, &ASlAiFlobObject::DestroyEvent);
GetWorld()->GetTimerManager().SetTimer(DestroyTimer, DestroyDele, 10.f, false);
//初始化玩家指针为空
SPCharacter = NULL;
}
(3)用于检测的定时器是每秒运行一次,循环运行的。在检测到玩家后,就给玩家指针赋值,随后在Tick函数中每帧改变其位置。
//靠近玩家 V表示vector的插值
SetActorLocation(FMath::VInterpTo(GetActorLocation(), SPCharacter->GetActorLocation() + FVector(0.f, 0.f, 40.f) , DeltaTime, 5.f));
(4)另外,用于动态球形检测需要准备多个参数,下面附检测事件代码。
void ASlAiFlobObject::DetectPlayer()
{
//检测世界是否存在
if (!GetWorld()) return;
//保存检测结果
TArray<FOverlapResult> Overlaps;
FCollisionObjectQueryParams ObjectParams;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this); //不检测自己
Params.bTraceAsyncScene = true;
//进行动态检测,检测范围是200,检测成功的话返回true
if (GetWorld()->OverlapMultiByObjectType(Overlaps, GetActorLocation(), FQuat::Identity, ObjectParams, FCollisionShape::MakeSphere(200.f), Params)) {
for (TArray<FOverlapResult>::TIterator It(Overlaps); It; ++It) {
//如果检测到了玩家
if (Cast<ASlAiPlayerCharacter>(It->GetActor())) {
SPCharacter = Cast<ASlAiPlayerCharacter>(It->GetActor());
//如果背包有空间
if (true) {
//停止检测
GetWorld()->GetTimerManager().PauseTimer(DetectTimer);
//停止销毁定时器
GetWorld()->GetTimerManager().PauseTimer(DestroyTimer);
//关闭物理模拟
BoxCollision->SetSimulatePhysics(false);
}
return;
}
}
}
}
46.游戏暂停
(1)准备游戏菜单、背包、聊天室的SlateWidget,在GameHUDWidget中添加对应组件
(2)准备数据结构:一个Game界面分类的枚举。在GameHUDWidget下用一个Map来保存上面创建的三个Widget。
(3)显示游戏UI界面事件由一个委托来控制。这个委托创建在PlayerController下,名为ShowGameUI
(4)游戏中按键改变UI。ESC键会呼出游戏暂停页面,B键打开背包,T键打开聊天室。所以需要在UE4编辑器内创建3个输入事件,然后在PlayerController下创建对应的方法,实现并绑定。
(5)实现ShowGameUI委托函数