fix(yaw): proxy replica o yaw do CORPO do dono (orient-to-movement), nao a mira
[YAW-BODY-ORIENT] commit conjunto cliente + servidor. Sintoma: ao apertar D o proxy virava ~45 graus de uma vez, em vez de virar igual ao player local (suave, so um pouco), e parado seguia o mouse. Raiz: a fonte de verdade do yaw estava errada. O servidor derivava simpleYawDeg de atan2(velocidade) INSTANTANEO ao mover (snap pra direcao do movimento) e da mira (ControlRotation) ao parar -- nenhum dos dois e o yaw do corpo do dono. Correcao ponta-a-ponta: - Cliente: envia GetActorRotation().Yaw (yaw do CORPO; o CMC ja orienta pra direcao do movimento a 500 deg/s e bUseControllerRotationYaw=false faz o MOUSE nao girar o corpo) no lugar de GetControlRotation().Yaw. - Servidor: usa o yaw recebido DIRETO em simpleYawDeg (remove atan2 instantaneo + mira-no-idle). - Proxy: replica Snapshot.YawDeg interpolado shortest-path (remove a derivacao por Atan2 da velocidade). Resultado: o proxy vira identico ao corpo do dono e o mouse nao gira o corpo. Futuro (aim/combate/Motion Matching): adicionar um campo aimYawDeg dedicado em vez de reusar este. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -640,12 +640,19 @@ void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
|
||||
++InputSequence;
|
||||
const int32 ClientTimeMs = static_cast<int32>(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<float>(GetController()->GetControlRotation().Yaw)
|
||||
: static_cast<float>(GetActorRotation().Yaw));
|
||||
// YAW DO CORPO (nao da mira): GetActorRotation().Yaw reflete o orient-to-movement
|
||||
// do CMC (bOrientRotationToMovement=true, RotationRate=(0,500,0)) -> o corpo vira
|
||||
// pra direcao do MOVIMENTO (WASD), suave a 500 deg/s, e o MOUSE nao gira o corpo
|
||||
// (bUseControllerRotationYaw=false). O servidor usa este yaw direto e o proxy nos
|
||||
// outros clientes o replica -> o proxy vira IDENTICO ao corpo do dono.
|
||||
//
|
||||
// (Historico bug 2026-06-13: antes mandava GetControlRotation().Yaw -- a MIRA --
|
||||
// e o servidor fazia atan2(vel) INSTANTANEO quando movia (proxy "virava demais"
|
||||
// 45 graus) e usava a mira quando parado (proxy seguia o mouse). ADR 0040: o
|
||||
// servidor NAO rotaciona mais o input por este yaw (ADR 0041 manda pos+vel
|
||||
// mundiais), entao trocar a mira pelo corpo e' seguro. Quando precisar da MIRA
|
||||
// real (aim/combate), adicionar um campo aimYawDeg dedicado -- nao reusar este.)
|
||||
const float BodyYawDeg = static_cast<float>(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
|
||||
@@ -662,7 +669,7 @@ void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
bPendingJumpReleased,
|
||||
InputSequence,
|
||||
ClientTimeMs,
|
||||
ViewYawDeg,
|
||||
BodyYawDeg,
|
||||
PosCm,
|
||||
FVector2D(Vel.X, Vel.Y),
|
||||
bIsFalling,
|
||||
@@ -689,7 +696,7 @@ void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
bPendingJumpReleased,
|
||||
InputSequence,
|
||||
ClientTimeMs,
|
||||
ViewYawDeg,
|
||||
BodyYawDeg,
|
||||
PosCm,
|
||||
FVector2D(Vel.X, Vel.Y),
|
||||
bIsFalling,
|
||||
|
||||
@@ -157,12 +157,32 @@ void AZeusPlayerProxy::Tick(const float DeltaSeconds)
|
||||
NewVel = FMath::Lerp(A.VelCmS, B.VelCmS, InterpAlpha);
|
||||
}
|
||||
|
||||
// Yaw deriva da velocidade interpolada: locomotion mantem face na direcao
|
||||
// do movimento; em idle preserva o yaw atual para nao "snappar".
|
||||
// ORIENTACAO DO CORPO = REPLICA do yaw autoritativo do servidor (Snapshot.YawDeg),
|
||||
// interpolado shortest-path entre os MESMOS A/B da posicao. Esse yaw agora carrega
|
||||
// o yaw do CORPO do dono (orient-to-movement do CMC: vira pra direcao do MOVIMENTO
|
||||
// /WASD, suave a 500 deg/s; parado mantem; o MOUSE nao gira o corpo) -- o cliente
|
||||
// dono manda GetActorRotation().Yaw e o servidor repassa direto. Por isso o proxy
|
||||
// so' REPLICA: vira identico ao corpo do dono, sem re-derivar nada.
|
||||
//
|
||||
// NAO derivamos de Atan2(velocidade): isso era a fonte do bug 2026-06-13 ("virava
|
||||
// demais 45 graus") -- a direcao da velocidade salta, o yaw do corpo nao. A
|
||||
// suavizacao correta ja' aconteceu no CMC do dono; aqui so' interpolamos amostras.
|
||||
//
|
||||
// FUTURO -- Motion Matching/aim: quando "a mira dita a locomocao" (strafe/aim
|
||||
// offset), o servidor passara a mandar um aimYawDeg separado e o AnimBP usara a
|
||||
// velocidade relativa a este yaw pra escolher fwd/back/left/right. Por ORA, 1 yaw.
|
||||
FRotator NewRot = GetActorRotation();
|
||||
if (FVector(NewVel.X, NewVel.Y, 0.0f).SizeSquared() > 1.0f)
|
||||
if (IdxB == INDEX_NONE)
|
||||
{
|
||||
NewRot.Yaw = FMath::RadiansToDegrees(FMath::Atan2(NewVel.Y, NewVel.X));
|
||||
NewRot.Yaw = SnapshotBuffer.Last().YawDeg; // extrapolando: segura o ultimo yaw
|
||||
}
|
||||
else
|
||||
{
|
||||
const float YawA = SnapshotBuffer[IdxB - 1].YawDeg;
|
||||
const float YawB = SnapshotBuffer[IdxB].YawDeg;
|
||||
// FindDeltaAngleDegrees -> menor arco (-180..180); evita girar "o lado longo".
|
||||
const float DeltaYaw = FMath::FindDeltaAngleDegrees(YawA, YawB);
|
||||
NewRot.Yaw = YawA + DeltaYaw * InterpAlpha;
|
||||
}
|
||||
NewRot.Pitch = 0.0f;
|
||||
NewRot.Roll = 0.0f;
|
||||
@@ -303,6 +323,7 @@ void AZeusPlayerProxy::ApplyEntitySnapshot(const FZeusEntitySnapshot& Snapshot)
|
||||
FZeusProxySnapshot S;
|
||||
S.PosCm = Snapshot.PositionCm;
|
||||
S.VelCmS = Snapshot.VelocityCmS;
|
||||
S.YawDeg = Snapshot.YawDeg; // yaw do CORPO do dono (orient-to-movement; mouse nao gira)
|
||||
S.bGrounded = Snapshot.bGrounded;
|
||||
S.ServerTimeMs = Snapshot.ServerTimeMs;
|
||||
|
||||
|
||||
@@ -42,6 +42,15 @@ struct FZeusProxySnapshot
|
||||
UPROPERTY()
|
||||
FVector VelCmS = FVector::ZeroVector;
|
||||
|
||||
/** Yaw autoritativo do servidor (graus) = o MESMO yaw que o player LOCAL aplicou
|
||||
* ao virar. O proxy replica EXATAMENTE a viradinha do local (apertou D = mesma
|
||||
* virada nos dois). Sem armazenar isto, o proxy derivava o yaw da velocidade
|
||||
* (Atan2) e "virava totalmente pra direcao do movimento" (bug 2026-06-13).
|
||||
* NAO confundir com "mira dita a locomocao" (strafe / Motion Matching) -- isso
|
||||
* esta' DESABILITADO por ora (ver AVISO em ZeusPlayerProxy.cpp::Tick). */
|
||||
UPROPERTY()
|
||||
float YawDeg = 0.0f;
|
||||
|
||||
UPROPERTY()
|
||||
bool bGrounded = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user