# 前言 继承关系:BP_XXX_Base -> BP_Idol_Base -> TsIdolActor -> AVCharacter -> ACharacter 。 主要逻辑位于TsIdolActor中,文件路径为`Script/LiveDirector/Character/TsIdolActor.ts` # AVCharacter 主要实现了`virtual void OnRep_AttachmentReplication() override;`,声明了若干BlueprintNativeEvent: - bool CanSyncRelativeTransform(); - void BeforeAttachToNewParent(); - void AfterAttachToNewParent(); ## OnRep_AttachmentReplication() 注释: >// 动捕模式下,CanSync=false. 各端自行计算Actor Location, client无需使用Server计算结果 // 自由行走模式下, CanSync=true,client需要同步server的transform信息。 同步Attachment行为。在AActor::OnRep_AttachmentReplication()的基础上添加: - 判断CanSync标记,以此来决定是否同步Transform - 未Attach组件=>Attch组件前后添加BeforeAttachToNewParent()、AfterAttachToNewParent() ```c++ auto CanSync = CanSyncRelativeTransform(); //获取Sync标记,具体的逻辑位于TsIdolActor.ts中 if (attachmentReplication.AttachParent) { if (RootComponent) { USceneComponent* AttachParentComponent = (attachmentReplication.AttachComponent ? attachmentReplication.AttachComponent : attachmentReplication.AttachParent->GetRootComponent()); if (AttachParentComponent) { if(CanSync)//增加判断Sync判断,只有在自由行走模式下才会同步Transform。 { RootComponent->SetRelativeLocation_Direct(attachmentReplication.LocationOffset); RootComponent->SetRelativeRotation_Direct(attachmentReplication.RotationOffset); RootComponent->SetRelativeScale3D_Direct(attachmentReplication.RelativeScale3D); } // If we're already attached to the correct Parent and Socket, then the update must be position only. // AttachToComponent would early out in this case. // Note, we ignore the special case for simulated bodies in AttachToComponent as AttachmentReplication shouldn't get updated // if the body is simulated (see AActor::GatherMovement). const bool bAlreadyAttached = (AttachParentComponent == RootComponent->GetAttachParent() && attachmentReplication.AttachSocket == RootComponent->GetAttachSocketName() && AttachParentComponent->GetAttachChildren().Contains(RootComponent)); if (bAlreadyAttached) { // Note, this doesn't match AttachToComponent, but we're assuming it's safe to skip physics (see comment above). if(CanSync) { RootComponent->UpdateComponentToWorld(EUpdateTransformFlags::SkipPhysicsUpdate, ETeleportType::None); } } else { BeforeAttachToNewParent();//增加BlueprintNativeEvent RootComponent->AttachToComponent(AttachParentComponent, FAttachmentTransformRules::KeepRelativeTransform, attachmentReplication.AttachSocket); AfterAttachToNewParent();//增加BlueprintNativeEvent } } } } ``` # TsIdolActor.ts ## VirtualOverrider CanSyncRelativeTransform() - 不需要同步Transform的情况 - AI控制的ACao角色不需要同步。 - 使用TsIdolMovementComponent并且勾选了ManulMovement的情况不需要同步。 - 动画蓝图中使用了**AnimGraphNode_Fullbody**节点,并且bGetMotionData为true的情况不需要同步。 具体代码如下: ```typescript CanSyncRelativeTransform(): boolean { if (Utils.HasTag(this.PropTags, new UE.GameplayTag("Idol.AIACao"))) { return false; } if(this.MovementComp && this.MovementComp.ManulMovement){ return false } var animInstance = this.Mesh.GetAnimInstance() as UE.IdolAnimInstance let fullbodyNode = Reflect.get(animInstance, 'AnimGraphNode_Fullbody') as UE.AnimNode_FullBody return !(fullbodyNode && fullbodyNode.bGetMotionData) } ```