在开发一个多人联机TPS游戏时,出现了一个网络同步导致的AnimNotify重复触发的问题。
本游戏客户端换弹逻辑为:
- 按下R,触发换弹函数OnActionReload.
- OnActionReload调用战斗模块- Combat中的- Reload函数。
- Reload调用- ServerReload,向服务器发出RPC请求。
- 服务器调用HandleReload,播放换弹Montage动画,更新CombatState至Reloading。
- 客户端接受CombatState的同步,执行HandleReload方法,播放换弹Montage动画。
- Montage动画播放至AnimNotify,提示上弹一颗,客户端、服务器执行ShotGunShellReload.
- ShotGunShellReload检查服务器权限,拒绝客户端更新子弹,允许服务器更新子弹。
- 后续换弹逻辑。
问题出现在 “Montage动画播放至AnimNotify,提示上弹一颗,客户端、服务器执行ShotGunShellReload” 这一步,由客户端触发的换弹时,服务器在一次换弹动画触发两次AnimNotify,导致每次换弹换上两颗。
解决思路:
通过Debug定位到AnimNotify多次触发上后,将原属于蓝图编程的AnimNotify触发行为交给CPP,并希望通过记录相应时间和堆栈,确定原因。
原蓝图逻辑:

创建UShotGunReloadAnimNotify类,继承自UAnimNotify.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | // Fill out your copyright notice in the Description page of Project Settings.
 #pragma once
 
 #include "CoreMinimal.h"
 #include "Animation/AnimNotifies/AnimNotify.h"
 #include "ShotGunReloadAnimNotify.generated.h"
 
 /**
 *
 */
 UCLASS()
 class SHOOTGAME_API UShotGunReloadAnimNotify : public UAnimNotify
 {
 GENERATED_BODY()
 public:
 virtual void Notify
 (USkeletalMeshComponent* MeshComp,
 UAnimSequenceBase* Animation,
 const FAnimNotifyEventReference& EventReference)
 override;
 };
 
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | void UShotGunReloadAnimNotify::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,  const FAnimNotifyEventReference& EventReference){
 Super::Notify(MeshComp, Animation);
 
 FString Timestamp = FDateTime::Now().ToString(TEXT("%H:%M:%S.%s"));
 UE_LOG(LogTemp, Log, TEXT("[%s] AnimNotify Triggered"), *Timestamp);
 
 FString Callstack = FFrame::GetScriptCallstack(true);
 UE_LOG(LogTemp, Log, TEXT("Callstack:\n%s"), *Callstack);
 
 if(MeshComp && MeshComp->GetOwner())
 {
 if(APlayerCharacter* PlayerCharacter =
 Cast<APlayerCharacter>(MeshComp->GetOwner());
 PlayerCharacter && PlayerCharacter->GetCombat())
 {
 PlayerCharacter->GetCombat()->ShotGunShellReload();
 }
 }
 }
 
 | 
通过对比试验,确定额外触发的原因。
正常情况下服务器换弹触发一次,输出为:
| 12
 
 | LogTemp: [16:57:16.103] AnimNotify TriggeredLogTemp: Callstack:
 
 | 
异常情况下客户端换弹触发两次,输出为:
| 12
 3
 4
 5
 
 | LogTemp: [17:21:57.748] AnimNotify TriggeredLogTemp: Callstack:
 /Script/Engine.Character.ServerMovePacked
 LogTemp: [17:21:57.784] AnimNotify Triggered
 LogTemp: Callstack:
 
 | 
结果显示是/Script/Engine.Character.ServerMovePacked导致的额外触发。
后续:设置了计时器,避免多时间内快速触发。