2026-10-13
UMG中有关PlayerState初始化操作,需要等待PlayerState复制完毕
多人模式下,UMG中进行PlayerState相关的初始化操作(例如:获取PlayerState上的某个组件),由于客户端(UMG只存在于客户端)的PlayerState是否从服务端复制到客户端,这是一个异步过程。因此在UMG的Construct或OnInitialized事件内不一定能获取到PlayerState,需要确定PlayerState复制完成了才能进行初始化。
可以通过在PlayerController的InitPlayerState()(适用Standalone和Listen Server主机)和OnRep_PlayerState()(适用纯客户端)内广播回调或事件,伪代码如下:
void APlayerController::InitPlayerState()
{
Super::InitPlayerState();
//Standalone、Listen Server主机初始化调用
NotifyLocalPlayerStateReady();
}
void APlayerController::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
//纯客户端等到PlayerState同步完毕后,调用
NotifyLocalPlayerStateReady();
}
//通知本地客户端PlayerState就绪
void APlayerController::NotifyLocalPlayerStateReady()
{
if(!IsLocalController())
{
return;
}
// ...
//执行回调
OnLocalPlayerStateAvailable.Broadcast();
//广播全局事件
UGameplayMessageSubsystem::Get(this).BroadcastMessage();
}
对于Listen Server模式,如果不想每次都判断是Host还是Client,直接走先监听再执行的固定流程。
2026-10-12
UMG内部,优先使用GetOwningPlayer
UMG内部如果当前本地玩家的相关的Actor或组件(如PlayerController、PlayerState),优先通过GetOwningPlayer()获取。
用GetPlayerController(0)不保证一定获取到的是本地主玩家Controller,其他GetPlayerXXX(0)类函数也一样。
可在APlayerController::PawnLeavingGame()处理里玩家掉线
在《Steal An Egg》这个项目如果玩家在偷蛋逃跑的过程中掉线,需要丢下正持有的蛋。
丢蛋在应该在服务器做(DS),所以选择重载APlayerController::PawnLeavingGame()并指向蛋的掉落逻辑。
而且在执行APlayerController::PawnLeavingGame()时Pawn还未销毁,刚好可以根据Pawn的位置计算掉落点。
GameMode::Logout()中的CleanupLeavingPlayers()虽然也适合做掉线逻辑,但对于依赖角色位置的丢蛋操作,时机偏晚了。
2026-10-10
是否使用UFCUNTION宏速查表
| 使用方式 | 是否需要 UFUNCTION |
|---|---|
AddDynamic |
需要 |
BindDynamic |
需要 |
BindUFunction |
需要 |
| BlueprintCallable/Pure/Event | 需要 |
| Server/Client/NetMulticast RPC | 需要 |
| 自己声明的 RepNotify | 需要 |
AddUObject |
不需要 |
CreateUObject |
不需要 |
AddLambda |
不需要 |
| 普通成员函数直接调用 | 不需要 |
| 普通虚函数 override | 不需要 |
| Enhanced Input 成员函数绑定 | 不需要 |
Development无法触发断点,换成DebugGame才行
DebugGame 和 Development 的主要区别是:游戏 C++ 代码是否优化,以及调试体验与运行性能的取舍。
| 配置 | 游戏代码 | 引擎代码 | 适用场景 |
|---|---|---|---|
DebugGame |
通常关闭优化,方便调试 | 优化构建 | 查游戏逻辑、打断点、看变量 |
Development |
开启大部分优化 | 优化构建 | 日常开发、运行和性能观察 |
Debug |
调试构建 | 调试构建 | 深入调试引擎内部,开销更大 |
Development 也能打断点,并不是没有调试信息。但编译器优化可能会:
- 把函数内联,导致调用栈与源码结构不同;
- 删除或合并某些语句,断点自动移动;
- 消除局部变量,调试器显示“变量已优化掉”;
- 调整指令顺序,单步执行看起来跳行。
DebugGame更容易按源码逐行执行,也更容易查看局部变量,代价是游戏代码运行得更慢
2026-10-9
问题1
蓝图中,用持有AbilitySystemComponent的PlayerState作为Target,通过Wait Gameplay Tag Add To Actor监听State.Carrying.StolenEgg加入事件,Added触发RefreshHUDState()。
RefreshHUDState()函数内通过HasMatchingGameplayTag()判断玩家是否拥有State.Carrying.StolenEgg时,应以AbilitySystemComponent对象为Target。之前用PlayerState作Target,始终返回false。
UGameplayMessageSubsystem::Get
EndAbility中使用UGameplayMessageSubsystem::Get(this)广播事件时,先判断UGameplayMessageSubsystem::HasInstance(this)是否有效。
20260928
Error: Using CommonUI without a CommonGameViewportClient derived game viewport client.
完整错误:LogUIActionRouter: Error: Using CommonUI without a CommonGameViewportClient derived game viewport client. CommonUI Input routing will not function correctly. To disable this warning set CommonUI.Debug.CheckGameViewportClientValid=0 under [SystemSettings] in your project’s DefaultEngine.ini
如果项目使用了CommonUI,那么需要指定GameViewportClientClassName为CommonUI的CommonGameViewportClient,否则会影响以下功能:
CommonActivatableWidget输入路由。- UI输入优先级和输入阻断。
- Back/返回操作。
- 手柄焦点与导航。
- Common Action的绑定和分发。
- 多个激活界面之间的输入切换。
- 游戏输入与UI输入之间的协调。
在DefaultEngine.ini中添加:
[/Script/Engine.Engine] GameViewportClientClassName=/Script/CommonUI.CommonGameViewportClient
然后完整关闭并重启编辑器。

Discussion
留言讨论
发表留言时,评论服务会记录必要的 IP 与 User-Agent 信息,用于安全审核和防止滥用。