前言:
笔者目前在校本科大三,目标方向是人工智能、计算机视觉。上一个OpenCV学习笔记专栏已完结,在学习完OpenCV后,我继续学习C++,并用纯C++做UE4项目的方式继续提升自己的水平。
梁迪老师的水平非常高,他的课程本来也无需笔记:课程本身即为最好的笔记。但由于我天赋有限,还是边看边记,以防遗忘——知识点太多,步骤太繁杂了。在学习过程中,我也偶有思考,思索为什么某个方法老师要这样做。所以,一是为了记录,二是为了分享,才有了这个专栏。
内容方面,由于我在开启这个专栏时,此项目已经做完很多了。所以,前期的一些大篇幅叙述的知识,可能在后期应用中一带而过。以及,前期的一些知识,后期会重新剖析,并加上我的个人理解。
另外,若有学术交流/学业交流意愿,可以邮件联系1246210283@qq.com,希望一齐进步。
本篇学习内容:
37.交互检测、动作通知
38.资源:树木、石头
39.射线检测物品、拾取物品
37.交互检测、动作通知
在一个动作的进行过程中,我们需要让碰撞检测在动作的某一刻打开,然后再关闭。避免树碰到人的斧子自动掉血这样的问题出现。
我们进行交互检测的碰撞体已在之前设置,绑定了自己创建的ToolProfile碰撞设置。
(1)初始关闭Overlay检测
AffectCollision->bGenerateOverlapEvents = false; //不允许进行交互检测 为了避免树碰到人的斧子就自己掉血
(2)开启和关闭交互检测的函数放在Anim中:
UFUNCTION(BlueprintCallable, Category = "SlAi") // 函数可以在蓝图下被调用
void ChangeDetection(bool IsOpen);//这个函数调用Character下的ChangeHandObjectDetect函数,ChangeHandObjectDetect函数再调用HandObject下的ChangeOverlayDetect函数.
void USlAiPlayerAnim::ChangeDetection(bool IsOpen)
{
if (!SPCharacter) return;
SPCharacter->ChangeHandObjectDetect(IsOpen);
}
void ASlAiHandObject::ChangeOverlayDetect(bool IsOpen)
{
AffectCollision->bGenerateOverlapEvents = IsOpen;
}
void ASlAiPlayerCharacter::ChangeHandObjectDetect(bool IsOpen)
{
//获取手上物品
ASlAiHandObject* HandObjectClass = Cast<ASlAiHandObject>(HandObject->GetChildActor());
if (HandObjectClass) HandObjectClass->ChangeOverlayDetect(IsOpen);
}
注意:HandObject在Character中是UChildActorComponent*类型,真正获取手上物品需要GetChildActor(),就像在character中设置物品到HandObject时是:HandObject->SetChildActorClass(HandObjectClass);
(3)具体在什么时候修改,需要到动作蓝图中进行设置。在Animation中可以添加通知,再在Event Graph中将通知绑定到C++写的函数ChangeDetection上。
38.资源:树木、石头
(1)和读取物品属性类似,先在Types下创建对应的数据结构,然后再JsonHandle写对应的读取函数,再赋值给DataHandle。
(2)创建资源类:基类(基于Actor)和其树木、石头的子类。
(3)和给物品创建组件相同,创建一个根组件和一个静态模型。添加一个RecourcePath和ResourceIndex。
(4)在子类中指定ResourceIndex,添加多个资源路径到ResourcePath,然后通过随机种子来获得随机数,从而实现在世界中添加树木、石头时可以随机添加模型。
39.射线检测物品、拾取物品
我们需要给人物的头部添加一个射线检测,在射线检测到物品时,屏幕顶部的UI会显示出检测出的物品。
所以需要先做UI。
(1)新建一个SlateWidget:SlAiRayInfoWidget
(2)在GameHUDWidget中添加这个Widget
(3)实现RayInfoWidget的基本内容
(4)我们在RayInfoWidget中定义了一个TSharedPtr<STextBlock> RayInfoTextBlock
,然后我们需要让Controller实时修改RayInfoTextBlock
的内容,那么需要用委托来注册。
(5)在RayInfoWidget中定义委托,并创建。
DECLARE_DELEGATE_OneParam(FRegisterRayInfoEvent, TSharedPtr<STextBlock>)
FRegisterRayInfoEvent RegisterRayInfoEvent;
(6)重写帧函数,在第一帧进行初始化
void SSlAiRayInfoWidget::Tick(const FGeometry & AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
//在第一帧进行初始化
if (!IsInitRayInfoEvent) {
RegisterRayInfoEvent.ExecuteIfBound(RayInfoTextBlock);
IsInitRayInfoEvent = true;
}
}
(7)把这个委托绑定到PlayerState下:在PlayerState下创建void RegisterRayInfoEvent(TSharedPtr<STextBlock> RayInfoTextBlock)
函数,创建TAttribute<FText> RayInfoTextAttr
变量,创建FText GetRayInfoText() const
函数(这个函数是绑定到RayInfoTextAttr的),然后实现上述2个函数。
void ASlAiPlayerState::RegisterRayInfoEvent(TSharedPtr<STextBlock> RayInfoTextBlock)
{
RayInfoTextAttr.Bind(this, &ASlAiPlayerState::GetRayInfoText);
//绑定射线检测信息
RayInfoTextBlock->SetText(RayInfoTextAttr);
}
FText ASlAiPlayerState::GetRayInfoText() const
{
return FText::FromString("hahaa");//测试代码
}
(8)在GameHUD下进行绑定:
//绑定注册射线信息文本事件(BeginPlay函数内)
GameHUDWidget->RayInfoWidget->RegisterRayInfoEvent.BindUObject(GM->SPState, &ASlAiPlayerState::RegisterRayInfoEvent);
对比一下之前快捷栏物品文字切换的委托:PlayerState修改了快捷栏容器内元素的属性后,由于是指针,所以同时Shortcut里的GridPanel指针也被修改。这样就会更新快捷栏选中的图标。然而文字是如何切换的?GetShortcutInfoText这个函数被绑定到了ShortcutInfoTextAttr(在RegisterShortcutContainer函数中),随后ShortcutInfoTextBlock->SetText(ShortcutInfoTextAttr);虽然这个委托函数只会调用一次,但是这就将GetShortcutInfoText这个函数的返回值绑定在了快捷栏的显示文字中。因为:这个ShortcutInfoTextBlock实际上是Shortcut.h中定义的一个指针。这样就巧妙地实现了快捷栏选中物品的切换和快捷栏显示物品文字的切换。
所以这种行为的目的就是:将RayInfoTextBlock的实时修改,转为了实现PlayerState内的GetRayInfoText()函数。
GetRayInfoText()函数此时只是测试代码。这一串逻辑的完善将在下一篇文章中讲解。