修复游戏角色复活后受伤无红屏告警
2023年11月21日 2024年1月11日
UMG Damage Effect Fix
验证
- 游戏角色使用第一条生命受伤会有红屏告警
- 游戏角色复活后, 再受伤会掉血条, 但没有红屏告警
回顾游戏角色受伤红屏实现
粒子系统 > 游戏角色受伤时添加闪烁红屏
- 健康组件提供委托成员OnChangeHealth, 当生命值发生改变时, 通知客户端
- STUPlayerHUDWidget在Initialize中注册了OnChangeHealth委托
- 需要注意的是, 随着游戏角色死亡, 其健康组件也被销毁: 每次重建游戏角色时, STUPlayerHUDWidget需要在其健康组件再次注册OnChangeHealth委托
解决思路
- 可以通过UUserWidget::GetOwningPlayer获得窗口组件当前PlayerController
- AController::OnNewPawn
- 访问属性为
protected
, 可通过GetOnNewPawnNotifier访问 - 在AController::UnPossess和AController::Possess中通知客户端
- 访问属性为
- 我们可以注册AController::OnNewPawn服务, 在处理函数中检查是否已注册新Pawn的OnChangeHealth, 如果没有, 注册之
整理窗口部件类
- | |
---|---|
STUAboveWidget | 关卡使用的窗口部件: 根据游戏角色存活情况决定显示STUPlayerHUDWidget还是STUSpectatorWidget |
STUSpectatorWidget | 游戏角色死亡后, 使用该窗口部件 |
STUPlayerHUDWidget | 游戏角色存活时, 显示该窗口部件 |
STUGameDataWidget | 于STUPlayerHUDWidget中显示回合信息 |
创建窗口部件类
- | |
---|---|
基类 | UserWidget |
路径 | UI |
名称 | STUAboveWidget |
属性 | Public |
实现STUAboveWidget
屏蔽USTUPlayerHUDWidget::IsPlayerAlive和USTUPlayerHUDWidget::IsPlayerSpectating, 在STUAboveWidget中实现
ShootThemUp: UI/STUPlayerHUDWidget.h
ShootThemUp: UI/STUPlayerHUDWidget.cpp
public
ShootThemUp: UI/STUAboveWidget.h
1UFUNCTION(BlueprintCallable) 2bool IsPlayerAlive() const; 3 4UFUNCTION(BlueprintCallable) 5bool IsPlayerSpectating() const; 6};
ShootThemUp: UI/STUAboveWidget.cpp
1#include "Components/STUHealthComponent.h" 2#include "STUUtils.h" 3 4bool USTUAboveWidget::IsPlayerAlive() const 5{ 6 const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(GetOwningPlayerPawn()); 7 return HealthComponent && !HealthComponent->IsDead(); 8} 9 10bool USTUAboveWidget::IsPlayerSpectating() const 11{ 12 const auto Controller = GetOwningPlayer(); 13 return Controller && Controller->GetStateName() == NAME_Spectating; 14}
修改WBP_AboveHUD
修改基类为STUAboveWidget
修改WBP_PlayerHUD
-
删除函数IsPlayerAlive
-
删除弹药信息文本可见性绑定
修改STUPlayerHUDWidget
添加接口: 注册给定Pawn的OnChangeHealth
将当前Initialize注册逻辑移过来
private
ShootThemUp: UI/STUPlayerHUDWidget.h
1void OnRespawn(APawn *Pawn);
- AController::UnPossess通知客户端时, 传入
nullptr
- AController::Possess通知客户端时, 传入新Pawn
其实可以不用判断是否已注册委托, 此处仅作学习示例
ShootThemUp: UI/STUPlayerHUDWidget.cpp
1void USTUPlayerHUDWidget::OnRespawn(APawn *Pawn) 2{ 3 UE_LOG(LogTemp, Warning, TEXT("STUPlayerHUDWidget is trying to bind OnRespawn, Pawn equals nullptr : %i"), Pawn == nullptr); 4 5 const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(Pawn); 6 if (HealthComponent && !HealthComponent->OnChangeHealth.IsBoundToObject(this)) 7 { 8 UE_LOG(LogTemp, Warning, TEXT("It's the first try")); 9 HealthComponent->OnChangeHealth.AddUObject(this, &USTUPlayerHUDWidget::OnChangeHealth); 10 } 11}
屏蔽原注册逻辑, 改为调用OnRespawn
- 窗口部件的Initialize函数在控制器的Possess函数之后调用, 即控制器首次拥有Pawn时, 控制器提供的OnNewPawn委托还未注册上
可以通过在Initialize和ASTUPlayerController::OnPossess中添加打印验证; 不建议覆写Possess, 而OnPossess在Possess中调用, 因此建议覆写OnPossess - 如果此处不调用OnRespawn, 游戏角色复活前受到伤害无红屏告警
ShootThemUp: UI/STUPlayerHUDWidget.cpp
1// Initialize 2 3/* 4 const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(GetOwningPlayerPawn()); 5 if (HealthComponent) 6 { 7 HealthComponent->OnChangeHealth.AddUObject(this, &USTUPlayerHUDWidget::OnChangeHealth); 8 } 9*/ 10OnRespawn(GetOwningPlayerPawn());
注册AController::OnNewPawn
ShootThemUp: UI/STUPlayerHUDWidget.cpp
1// Initialize 2 3if (GetOwningPlayer()) 4{ 5 GetOwningPlayer()->GetOnNewPawnNotifier().AddUObject(this, &USTUPlayerHUDWidget::OnRespawn); 6}
查看
- 第一个框框中的日志对应在USTUPlayerHUDWidget::Initialize中显式调用OnRespawn
- 第二个框框为AController::UnPossess处通知客户端
- 第三个框框为AController::Possess处通知客户端
单玩家测试受伤红屏方法
-
BP_STUGameModeBase
中设置Players Num
为1 -
在
BP_STUPlayerController
绑定键位添加事件: 按下
1
触发对控制器当前游戏角色造成伤害: 无法验证, 当前受到伤害时判断了阵营; 可以加个
Print String
验证触发 -
在
WBP_PlayerHUD
中的受伤事件里添加打印 -
移除上述测试
说明
-
在STUGameModeBase中调用RestartPlayer, 会触发控制器调用Possess, 即触发控制器委托OnNewPawn通知客户端
动态创建游戏角色 > AGameModeBase::RestartPlayer > AGameModeBase::RestartPlayerAtPlayerStart > AGameModeBase::FinishRestartPlayer -
在 AGameModeBase::RestartPlayerAtPlayerStart 和 AController::OnPossess 中均有调用 AController::SetPawn , 这只是使得可以通过Controller获取Pawn
AController::OnPossess 还设置了Pawn的Controller, 使得可以通过Pawn获取Controller而 AController::Possess 中广播的条件, 使用 AGameModeBase::RestartPlayer 重建游戏角色是满足的