diff --git a/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp b/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp index 2184efb..5644cfa 100644 --- a/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp +++ b/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp @@ -344,13 +344,28 @@ void AZMMOPlayerCharacter::FlushInputAxisToServer(const float DeltaSeconds) ++InputSequence; const int32 ClientTimeMs = static_cast(FPlatformTime::Seconds() * 1000.0); + // ADR 0040: yaw da camara/controller para que o servidor consiga rotar o + // input (referencial-camara) para velocidade mundial. Fallback para o yaw + // do actor se nao houver controller (e.g. tela de loading). + const float ViewYawDeg = (GetController() + ? static_cast(GetController()->GetControlRotation().Yaw) + : static_cast(GetActorRotation().Yaw)); + // ADR 0041: cliente autoritativo da posicao XYZ + velocidade XY. O CMC ja + // integrou o input local antes deste flush, portanto `GetActorLocation` e + // `GetVelocity` reflectem o estado real que o servidor deve replicar para + // os outros clientes (sujeito ao clamp anti-cheat). + const FVector PosCm = GetActorLocation(); + const FVector Vel = GetVelocity(); ZeusNetwork->SendInputAxis( PendingMoveForward, PendingMoveRight, bPendingJumpPressed, bPendingJumpReleased, InputSequence, - ClientTimeMs); + ClientTimeMs, + ViewYawDeg, + PosCm, + FVector2D(Vel.X, Vel.Y)); SendAccumulatorSec = 0.0f; TimeSinceLastSendSec = 0.0f; diff --git a/Source/ZMMO/Game/Entity/ZMMOPlayerProxy.cpp b/Source/ZMMO/Game/Entity/ZMMOPlayerProxy.cpp index 32d0c69..0cf3d59 100644 --- a/Source/ZMMO/Game/Entity/ZMMOPlayerProxy.cpp +++ b/Source/ZMMO/Game/Entity/ZMMOPlayerProxy.cpp @@ -1,7 +1,13 @@ #include "ZMMOPlayerProxy.h" +#include "Animation/AnimInstance.h" #include "Components/CapsuleComponent.h" +#include "Components/SkeletalMeshComponent.h" +#include "Engine/SkeletalMesh.h" +#include "Engine/World.h" #include "GameFramework/CharacterMovementComponent.h" +#include "UObject/ConstructorHelpers.h" +#include "ZMMO.h" AZMMOPlayerProxy::AZMMOPlayerProxy(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -19,8 +25,54 @@ AZMMOPlayerProxy::AZMMOPlayerProxy(const FObjectInitializer& ObjectInitializer) UCharacterMovementComponent* CMC = GetCharacterMovement(); if (CMC) { + // MOVE_Walking (em vez de MOVE_None) e necessario para o AnimBP do Quinn + // transitar entre Idle/Run — a state machine consulta `IsFalling()` / + // `MovementMode` no CMC mesmo quando este nao integra fisica. Mantemos + // `bOrientRotationToMovement=false` e `RotationRate=0` porque a + // orientacao e ditada pelo snapshot do servidor; `bRunPhysicsWithNoController` + // evita que o CMC tente integrar movimento quando nao ha PlayerController. + // Espelha [F:/ZeusV1/ZeusClient ZeusRemoteCharacter:34-38]. CMC->bOrientRotationToMovement = false; - CMC->SetMovementMode(MOVE_None); + CMC->RotationRate = FRotator::ZeroRotator; + CMC->bRunPhysicsWithNoController = false; + CMC->MaxWalkSpeed = 600.0f; + CMC->SetMovementMode(MOVE_Walking); + } + + // Defaults visuais (mesh + AnimBP) em paridade com `AZMMOPlayerCharacter`. + // O `UZMMOWorldSubsystem` instancia `AZMMOPlayerProxy::StaticClass()` + // directamente — sem este bloco o `MeshComponent` ficaria sem + // `SkeletalMesh`/`AnimInstance` e o proxy seria invisivel em PIE. Uma BP + // filha continua opcional (basta atribuir em `RemoteEntityClasses[Player]`). + if (USkeletalMeshComponent* MeshComponent = GetMesh()) + { + static ConstructorHelpers::FObjectFinder QuinnMesh( + TEXT("/Game/Characters/Mannequins/Meshes/SKM_Quinn_Simple.SKM_Quinn_Simple")); + static ConstructorHelpers::FClassFinder QuinnAnimBp( + TEXT("/Game/Characters/Mannequins/Anims/Unarmed/ABP_Unarmed")); + + if (QuinnMesh.Succeeded()) + { + MeshComponent->SetSkeletalMesh(QuinnMesh.Object); + MeshComponent->SetRelativeLocation(FVector(0.0f, 0.0f, -90.0f)); + MeshComponent->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f)); + } + else + { + UE_LOG(LogZMMO, Warning, + TEXT("AZMMOPlayerProxy: SKM_Quinn_Simple nao encontrado em /Game/Characters/Mannequins/Meshes.")); + } + + if (QuinnAnimBp.Succeeded()) + { + MeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint); + MeshComponent->SetAnimInstanceClass(QuinnAnimBp.Class); + } + else + { + UE_LOG(LogZMMO, Warning, + TEXT("AZMMOPlayerProxy: ABP_Unarmed nao encontrado em /Game/Characters/Mannequins/Anims/Unarmed.")); + } } bUseControllerRotationPitch = false; @@ -43,8 +95,31 @@ void AZMMOPlayerProxy::SetZMMOIdentity(const int64 InEntityId, const EZMMOEntity void AZMMOPlayerProxy::ApplyEntitySnapshot(const FZMMOEntitySnapshot& Snapshot) { const FRotator NewRotation(0.0f, Snapshot.YawDeg, 0.0f); - SetActorLocationAndRotation(Snapshot.PositionCm, NewRotation); - LastSnapshotPosition = Snapshot.PositionCm; + + // V0: o servidor envia Z=spawnZ fixo (ADR 0039). ADR 0041 passou a Z a + // vir do cliente do dono (`GetActorLocation().Z`), pelo que o snapshot + // ja contem a Z autoritativa. Mantemos o line trace local como fallback + // gracioso para os casos em que o snapshot Z fica estranho (terreno + // receiver com buracos, drift entre clientes, packet loss). Sem hit, o + // AdjustedPos.Z fica igual a Snapshot.PositionCm.Z. + FVector AdjustedPos = Snapshot.PositionCm; + if (UWorld* World = GetWorld()) + { + const FVector TraceStart = AdjustedPos + FVector(0.0f, 0.0f, 1000.0f); + const FVector TraceEnd = AdjustedPos - FVector(0.0f, 0.0f, 4000.0f); + FHitResult Hit; + FCollisionQueryParams Params(TEXT("ZMMOProxyGround"), false, this); + if (World->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, Params)) + { + const float HalfHeight = GetCapsuleComponent() + ? GetCapsuleComponent()->GetScaledCapsuleHalfHeight() + : 96.0f; + AdjustedPos.Z = Hit.Location.Z + HalfHeight; + } + } + + SetActorLocationAndRotation(AdjustedPos, NewRotation); + LastSnapshotPosition = AdjustedPos; VisualVelocity = Snapshot.VelocityCmS; bHasFirstSnapshot = true; RefreshAnimationDrivers(); diff --git a/ZMMO.uproject b/ZMMO.uproject index a79ad75..d21784d 100644 --- a/ZMMO.uproject +++ b/ZMMO.uproject @@ -1,51 +1,56 @@ { - "FileVersion": 3, - "EngineAssociation": "5.7", - "Category": "", - "Description": "", - "Modules": [ - { - "Name": "ZMMO", - "Type": "Runtime", - "LoadingPhase": "Default" - } - ], - "Plugins": [ - { - "Name": "ModelingToolsEditorMode", - "Enabled": true - }, - { - "Name": "StateTree", - "Enabled": true - }, - { - "Name": "GameplayStateTree", - "Enabled": true - }, - { - "Name": "VisualStudioTools", - "Enabled": true, - "SupportedTargetPlatforms": [ - "Win64" - ] - }, - { - "Name": "ZeusNetwork", - "Enabled": true - }, - { - "Name": "ZeusMapTools", - "Enabled": true, - "TargetAllowList": [ - "Editor" - ] - } - ], - "TargetPlatforms": [], - "AdditionalRootDirectories": [], - "AdditionalPluginDirectories": [ - "../../Server/Plugins/Unreal" - ], - "EpicSampleNameHash": "" + "FileVersion": 3, + "EngineAssociation": "5.7", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "ZMMO", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "ModelingToolsEditorMode", + "Enabled": true + }, + { + "Name": "StateTree", + "Enabled": true + }, + { + "Name": "GameplayStateTree", + "Enabled": true + }, + { + "Name": "VisualStudioTools", + "Enabled": true, + "SupportedTargetPlatforms": [ + "Win64" + ] + }, + { + "Name": "ZeusNetwork", + "Enabled": true + }, + { + "Name": "ZeusMapTools", + "Enabled": true, + "TargetAllowList": [ + "Editor" + ] + }, + { + "Name": "meshy", + "Enabled": false, + "SupportedTargetPlatforms": [ + "Win64", + "Mac" + ] + } + ], + "AdditionalPluginDirectories": [ + "../../Server/Plugins/Unreal" + ] } \ No newline at end of file