前言:
笔者目前在校本科大三,目标方向是人工智能、计算机视觉。上一个OpenCV学习笔记专栏已完结,在学习完OpenCV后,我继续学习C++,并用纯C++做UE4项目的方式继续提升自己的水平。
梁迪老师的水平非常高,他的课程本来也无需笔记:课程本身即为最好的笔记。但由于我天赋有限,还是边看边记,以防遗忘——知识点太多,步骤太繁杂了。在学习过程中,我也偶有思考,思索为什么某个方法老师要这样做。所以,一是为了记录,二是为了分享,才有了这个专栏。
内容方面,由于我在开启这个专栏时,此项目已经做完很多了。所以,前期的一些大篇幅叙述的知识,可能在后期应用中一带而过。以及,前期的一些知识,后期会重新剖析,并加上我的个人理解。
另外,若有学术交流/学业交流意愿,可以邮件联系1246210283@qq.com,希望一齐进步。
本篇学习内容:
31.注册快捷栏到PlayerState
32.委托的Bind
33.快捷栏切换
31.注册快捷栏到PlayerState
我们不会在Controller下引入Widget文件,也不会在Widget下引入Controller文件。所有的交互用委托实现。
(1)在ShortcutWidget下定义一个委托
//注册快捷栏到PlayerState类的委托
DECLARE_DELEGATE_TwoParams(FRegisterShortCutContainer,TArray<TSharedPtr<ShortcutContainer>>*, TSharedPtr<STextBlock>) //委托名和2个参数
(2)创建委托变量并在cpp的InitializeContainer()中调用
InitializeContainer()中:
//将实例化的结构体注册进PlayerState的容器数组
RegisterShortcutContainer.ExecuteIfBound(&ContainerList, ShortcutInfoTextBlock);
(3)委托函数写在PlayerState中:
void ASlAiPlayerState::RegisterShortcutContainer(TArray<TSharedPtr<ShortcutContainer>>* ContainerList, TSharedPtr<STextBlock> ShortcutInfoTextBlock)
{
for (TArray<TSharedPtr<ShortcutContainer>>::TIterator It(*ContainerList); It; ++It) {
ShortcutContainerList.Add(*It);
}
ShortcutInfoTextAttr.BindUObject(this, &ASlAiPlayerState::GetShortcutInfoText);
//绑定快捷栏信息TextBlock
ShortcutInfoTextBlock->SetText(ShortcutInfoTextAttr);
}
设置ShortcutInfoTextBlock用TAttribute<FText> ShortcutInfoTextAttr
来进行:
FText ASlAiPlayerState::GetShortcutInfoText() const
{
return FText::FromString("hahah");
}
然后将其绑定到这个函数:
在RegisterShortcutContainer()中:
ShortcutInfoTextAttr.BindUObject(this, &ASlAiPlayerState::GetShortcutInfoText);
(4)由于GameHUD中已获取了GameHUDWidget的指针,而ShortcutWidget是写在GameHUDWidget下的,所以实际上GameHUD已经可以获取ShortcutWidget的指针。然而怎么获取PlayerState的指针:
可以用一个较为复杂的方式来获取:
在GameMode下:
创建指向Controller、Character、PlayerState的三个public指针,并用函数添加引用。
随后去GameHUD下获取GameHUD指针,然后在BeginPlay函数中绑定委托:
void ASlAiGameHUD::BeginPlay()
{
Super::BeginPlay();
//为什么不在构造函数中创建委托:因为在构造函数调用时,世界中还没有实例化GameMode、Character、Controller
GM = Cast<ASlAiGameMode>(UGameplayStatics::GetGameMode(GetWorld()));
if (!GM) return;
//确保要调用的组件都已经实现
GM->InitGamePlayModule();
//绑定注册快捷栏容器
GameHUDWidget->ShortcutWidget->RegisterShortcutContainer.BindUObject(GM->SPState, &ASlAiPlayerState::RegisterShortcutContainer);
}
解释为什么不能在ShortcutWidget的构造函数里运行InitializeContainer:因为此时委托还没有绑定上,所以需要在Tick函数里运行,即第一帧运行。
注册快捷栏到PlayerState的目的:滚轮滑动切换物品的时候,就对PlayerState中的ShortcutContainerList进行操控。
整理一遍逻辑:注册快捷栏到PlayerState
(1)首先我们创建了一个委托:
DECLARE_DELEGATE_TwoParams(FRegisterShortCutContainer,TArray<TSharedPtr<ShortcutContainer>>*, TSharedPtr<STextBlock>)
(2)定义了委托变量:
FRegisterShortCutContainer RegisterShortcutContainer;
(3)将实例化的结构体注册进PlayerState的容器数组:
RegisterShortcutContainer.ExecuteIfBound(&ContainerList, ShortcutInfoTextBlock);
(4)在PlayerState定义委托函数,将快捷栏容器添加到了PlayerState下的快捷栏序列TArray<TSharedPtr<ShortcutContainer>> ShortcutContainerList
void ASlAiPlayerState::RegisterShortcutContainer(TArray<TSharedPtr<ShortcutContainer>>* ContainerList, TSharedPtr<STextBlock> ShortcutInfoTextBlock)
{
for (TArray<TSharedPtr<ShortcutContainer>>::TIterator It(*ContainerList); It; ++It) {
ShortcutContainerList.Add(*It);
}
ShortcutInfoTextAttr.BindUObject(this, &ASlAiPlayerState::GetShortcutInfoText);
//绑定快捷栏信息TextBlock
ShortcutInfoTextBlock->SetText(ShortcutInfoTextAttr);
}
(5)在GameHUD获取ShortcutWidget和PlayState,创建委托。
GameHUDWidget->ShortcutWidget->RegisterShortcutContainer.BindUObject(GM->SPState, &ASlAiPlayerState::RegisterShortcutContainer);
32.委托的Bind
BindRaw:普通的C++函数,在SlateWidget下定义的函数就用BindRaw
BindStatic:绑定静态函数
BindUFunction:用UFunction宏定义的函数
BindUObject:绑定比如我们在Controller、PlayerState下写的方法。如果没有用宏定义的话,就用BindUObject
33.快捷栏切换
添加鼠标滚轮事件,随后像之前一样实现事件函数即可。
由于是PlayerState来控制快捷栏容器,所以需要获取快捷栏指针,然后操控快捷栏来切换。
我的理解:PlayerState修改了快捷栏容器内元素的属性后,由于是指针,所以同时Shortcut里的GridPanel指针也被修改。这样就会更新快捷栏选中的图标。然而文字是如何切换的?GetShortcutInfoText这个函数被绑定到了ShortcutInfoTextAttr(在RegisterShortcutContainer函数中),随后ShortcutInfoTextBlock->SetText(ShortcutInfoTextAttr);虽然这个委托函数只会调用一次,但是这就将GetShortcutInfoText这个函数的返回值绑定在了快捷栏的显示文字中。因为:这个ShortcutInfoTextBlock实际上是Shortcut.h中定义的一个指针。这样就巧妙地实现了快捷栏选中物品的切换和快捷栏显示物品文字的切换。
void ASlAiPlayerController::ScrollDownEvent()
{
//如果不允许切换,直接返回
if (!SPCharacter->IsAllowSwitch) return;
//按下左键或者右键时不能切换
if (IsLeftButtonDown || IsRightButtonDown) return;
//告诉状态类切换快捷栏容器
SPState->ChooseShortcut(false);
}
需要注意,在鼠标左右键按下时不能切换物品,所以需要额外创建两个变量,来限制按下左键或者右键时不能切换物品。