Я строю систему индивидуальной движения для своего проекта (своего рода основанная на физике структура движения).
Все отлично работает, за исключением одной раздражающей проблемы с гирозовой стабилизацией.
Видео демонстрация проблемы < /p>
Вот настройка иерархия: < /p>
Wheel -> Gyro -> Turner -> Body -> Head< /code> < /p>
Визуальное представление иерархии:
< /p>
Колесо является корневым компонентом, и это коренное соединение, и это Drives wepints /indants wealsment whefant while -whealcment while -whealcment while -whiponts while -whefants /indignmess /indignmess /> < /p>
. /> Гиропейт применяет пружинную стабилизацию, чтобы сохранить выровненную игрока с гравитационной или поверхностной нормой, если он достаточно быстрый (для выполнения таких трюков, как петли смерти и т. Д.) [b] Идеально < /strong>-он колеблется немного вперед и назад (как пружина), что является то, что я хочу < /strong> < /p>
, но когда я езжу на рампу, вращающуюся вокруг оси z (например, примерно на 20 градусов в сторону),
, гипов, начинает раскачивать сторону, он также должен добавить его, как и он, как и он, он также должен добавить, как он должен быть вроде. стабилизируется в оси высот (вперед /назад /задний наклон), а не начинайте скручиваться в сторону. < /p>
Похоже, что стабилизация каким -то образом смешивается в рысках, но я не могу точно определить, где. < /p>
Соответствующий код < /h1>
Здесь. компонент)
функция, которая преобразует вектор в ротатор (честно говоря, я использовал Chatgpt для этого, потому что мой не работает): [/b]
FRotator UFG_MovementComponent::MakeAlignedRotator(const FVector &FSurfaceNormal, const FRotator &CurrentRotation)
{
FRotator Rot = CurrentRotation;
Rot.Roll = FMath::GridSnap(Rot.Roll, RotMinStep);
Rot.Pitch = FMath::GridSnap(Rot.Pitch, RotMinStep);
Rot.Yaw = FMath::GridSnap(Rot.Yaw, RotMinStep);
FVector N = FSurfaceNormal;
FVector Fwd = UKismetMathLibrary::GetForwardVector(Rot);
FVector Up = UKismetMathLibrary::GetUpVector(Rot);
// Calculate the axis and angle between the current up and normal vectors
FVector Axis = FVector::CrossProduct(Up, N);
float AxisLen = Axis.Size();
FVector NewFwd;
if (AxisLen < KINDA_SMALL_NUMBER)
{
// If the vectors are almost parallel (0° or 180°)
if (FVector::DotProduct(Up, N) < 0.0f)
{
// If it's a 180° angle, reverse the forward vector
NewFwd = -Fwd;
}
else
{
// If the vectors are the same, leave the forward vector as is
NewFwd = Fwd;
}
}
else
{
Axis /= AxisLen;
float Angle = FMath::Acos(FMath::Clamp(FVector::DotProduct(Up, N), -1.0f, 1.0f));
FQuat Q(Axis, Angle);
NewFwd = Q.RotateVector(Fwd);
}
// Project the new forward vector onto the plane perpendicular to the normal vector (to ensure no component along the normal vector)
NewFwd = (NewFwd - N * FVector::DotProduct(NewFwd, N)).GetSafeNormal();
// Combine the matrix and convert it to a rotation
FRotationMatrix RotM = UKismetMathLibrary::MakeRotFromXZ(NewFwd, N);
FRotator NewRot = RotM.Rotator();
NewRot.Roll = FMath::GridSnap(NewRot.Roll, RotMinStep);
NewRot.Pitch = FMath::GridSnap(NewRot.Pitch, RotMinStep);
NewRot.Yaw = FMath::GridSnap(NewRot.Yaw, RotMinStep);
return NewRot;
}
функция, которая выравнивает колесо и направление на поверхность:
void UFG_MovementComponent::AlignToSurface(float DeltaTime)
{
// If the wheel or gyro does not exist, or if the direction is not set, exit
if (!Wheel || !Gyro || !Direction)
return;
// Check if the wheel is currently on the ground
if (!GetGroundStatus(TraceLength))
return;
// Get the surface normal
FVector NewSurfaceNormal = GetSurfaceNormal(NormalTraceLength, SurfaceNormal);
if (NewSurfaceNormal == SurfaceNormal)
return;
SurfaceNormal = NewSurfaceNormal;
// Get the current rotation of the wheel
FRotator CurrentRot = Wheel->GetComponentRotation();
FRotator GyroRot = Gyro->GetComponentRotation();
FRotator CurrentDirectionRot = Direction->GetComponentRotation();
// Calculate the target rotation based on the surface normal and current rotation
FRotator TargetRot = MakeAlignedRotator(SurfaceNormal, CurrentRot);
// Apply the target rotation to the wheel
Wheel->SetWorldRotation(TargetRot);
Gyro->SetWorldRotation(GyroRot);
FRotator TargetDirectionRot = MakeAlignedRotator(SurfaceNormal, CurrentDirectionRot);
Direction->SetWorldRotation(TargetDirectionRot);
}
Функция, выравнивающая колесо и направление по нормали поверхности
void UFG_MovementComponent::AlignToSurface(float DeltaTime)
{
// If the wheel or gyro does not exist, or if the direction is not set, exit
if (!Wheel || !Gyro || !Direction)
return;
// Check if the wheel is currently on the ground
if (!GetGroundStatus(TraceLength))
return;
// Get the surface normal
FVector NewSurfaceNormal = GetSurfaceNormal(NormalTraceLength, SurfaceNormal);
if (NewSurfaceNormal == SurfaceNormal)
return;
SurfaceNormal = NewSurfaceNormal;
// Get the current rotation of the wheel
FRotator CurrentRot = Wheel-\>GetComponentRotation();
FRotator GyroRot = Gyro-\>GetComponentRotation();
FRotator CurrentDirectionRot = Direction-\>GetComponentRotation();
// Calculate the target rotation based on the surface normal and current rotation
FRotator TargetRot = MakeAlignedRotator(SurfaceNormal, CurrentRot);
// Apply the target rotation to the wheel
Wheel-\>SetWorldRotation(TargetRot);
Gyro-\>SetWorldRotation(GyroRot);
FRotator TargetDirectionRot = MakeAlignedRotator(SurfaceNormal, CurrentDirectionRot);
Direction-\>SetWorldRotation(TargetDirectionRot);
}
Тогда есть главное, что, я полагаю, вызывает эту проблему
void UFG_MovementComponent::ApplyGyroStabilization(float DeltaTime)
{
// Check if Gyro component is valid
if (!Gyro)
return;
// Get the current rotation of the Wheel component
FRotator WheelRot = Wheel->GetComponentRotation();
// Get the current rotation of the Gyro component
FRotator GyroRot = Gyro->GetComponentRotation();
// Get the direction of the wheel drive force
FRotator DirectionRot = MakeAlignedRotator(SurfaceNormal, WheelRot);
// Calculate the gravity aligned rotation
FRotator GravityRot = MakeAlignedRotator(-GravityDirection, WheelRot);
GEngine->AddOnScreenDebugMessage(1, 1.f, FColor::Red, FString::Printf(TEXT("GravityRot: %f, %f, %f"), GravityRot.Roll, GravityRot.Pitch, GravityRot.Yaw));
GEngine->AddOnScreenDebugMessage(2, 1.f, FColor::Green, FString::Printf(TEXT("GravityRot: %f, %f, %f"), WheelRot.Roll, WheelRot.Pitch, WheelRot.Yaw));
// Calculate the alignment coefficient based on the wheel drive force
float AlignCoeff = FMath::GetMappedRangeValueClamped(
FVector2D(0.f, 3000.f),
FVector2D(1.f, 1.f),
FMath::Abs(WheelDriveForce)); // YOU CAN IGNORE THIS, IT JUST CALCULATE HOW, BASED ON SPEED IT WILL ALIGN TO SURFACE RATHER THAN GRAVITY
// Calculate the target rotation based on the alignment coefficient
FRotator TargetRot = UKismetMathLibrary::RLerp(
GravityRot,
DirectionRot,
AlignCoeff, true); // AND THIS ALSO
// Calculate the new rotation of the Gyro component
FRotator NewRot;
if (bGrounded)
{
NewRot = CalculateSpringRotation(DeltaTime, GyroRot, TargetRot, GyroSpringVelocity, GyroStiffness, GyroDamping);
}
else
{
NewRot = CalculateSpringRotation(DeltaTime, GyroRot, GyroRot, GyroSpringVelocity, GyroStiffness, GyroDamping);
}
// Apply the new rotation to the Gyro component
Gyro->SetWorldRotation(NewRot);
}
< /code>
Функция, которая разжигает вращение, используя скорость гниения (также изменяет переменную скорости, из-за & (да, я знаю, основные вещи, но на всякий случай)) < /p>
FRotator UFG_MovementComponent::CalculateSpringRotation(float DeltaTime, FRotator CurrentRotation, FRotator TargetRotation, FRotator &CurrentVelocity, float SpringStiffness, float SpringDamping)
{
FRotator SpringOffset = (TargetRotation - CurrentRotation).GetNormalized();
FRotator SpringAccel = SpringOffset * SpringStiffness - CurrentVelocity * SpringDamping;
CurrentVelocity += SpringAccel * FMath::Clamp(DeltaTime, 0.005f, 0.033f);
FRotator NewRot = CurrentRotation + CurrentVelocity * DeltaTime;
return NewRot;
}
Контекст:
Пользовательское движение, полностью C ++ (без символов, компонент). wheel выравнивается в surfacenormal , а gyro применяет устойчивую пружину. Колесо не соответствует направлению, потому что, когда я пытался управлять колесом в качестве корневого компонента, это вызвало проблемы репликации. Я планирую сделать этот многопользовательский, поэтому мне пришлось разделить их, хотя мне интересно, есть ли надежный способ управлять корневым колесом, не нарушая репликацию. Sparsy, Codeium помогла с DocStrings, хотя должен быть читаемым. Ось (вперед/задний наклон) относительно направления прямого направления колеса (компонент направления, а не корень), похожая на пружину, пытающуюся сохранить равновесие.>
Я строю систему индивидуальной движения для своего проекта (своего рода основанная на физике структура движения). Все отлично работает, за исключением одной раздражающей проблемы с гирозовой стабилизацией. Видео демонстрация проблемы < /p> Вот настройка иерархия: < /p> [code]Wheel -> Direction[/code] [code]Wheel -> Gyro -> Turner -> Body -> Head< /code> < /p> Визуальное представление иерархии:
< /p> Колесо является корневым компонентом, и это коренное соединение, и это Drives wepints /indants wealsment whefant while -whealcment while -whealcment while -whiponts while -whefants /indignmess /indignmess /> < /p> . /> Гиропейт применяет пружинную стабилизацию, чтобы сохранить выровненную игрока с гравитационной или поверхностной нормой, если он достаточно быстрый (для выполнения таких трюков, как петли смерти и т. Д.) [b] Идеально < /strong>-он колеблется немного вперед и назад (как пружина), что является то, что я хочу < /strong> < /p> , но когда я езжу на рампу, вращающуюся вокруг оси z (например, примерно на 20 градусов в сторону), , гипов, начинает раскачивать сторону, он также должен добавить его, как и он, как и он, он также должен добавить, как он должен быть вроде. стабилизируется в оси высот (вперед /назад /задний наклон), а не начинайте скручиваться в сторону. < /p> Похоже, что стабилизация каким -то образом смешивается в рысках, но я не могу точно определить, где. < /p> Соответствующий код < /h1> Здесь. компонент) функция, которая преобразует вектор в ротатор (честно говоря, я использовал Chatgpt для этого, потому что мой не работает): [/b] FRotator UFG_MovementComponent::MakeAlignedRotator(const FVector &FSurfaceNormal, const FRotator &CurrentRotation) { FRotator Rot = CurrentRotation; Rot.Roll = FMath::GridSnap(Rot.Roll, RotMinStep); Rot.Pitch = FMath::GridSnap(Rot.Pitch, RotMinStep); Rot.Yaw = FMath::GridSnap(Rot.Yaw, RotMinStep); FVector N = FSurfaceNormal; FVector Fwd = UKismetMathLibrary::GetForwardVector(Rot); FVector Up = UKismetMathLibrary::GetUpVector(Rot);
// Calculate the axis and angle between the current up and normal vectors FVector Axis = FVector::CrossProduct(Up, N); float AxisLen = Axis.Size();
FVector NewFwd;
if (AxisLen < KINDA_SMALL_NUMBER) { // If the vectors are almost parallel (0° or 180°) if (FVector::DotProduct(Up, N) < 0.0f) { // If it's a 180° angle, reverse the forward vector NewFwd = -Fwd; } else { // If the vectors are the same, leave the forward vector as is NewFwd = Fwd; } } else { Axis /= AxisLen; float Angle = FMath::Acos(FMath::Clamp(FVector::DotProduct(Up, N), -1.0f, 1.0f)); FQuat Q(Axis, Angle); NewFwd = Q.RotateVector(Fwd); }
// Project the new forward vector onto the plane perpendicular to the normal vector (to ensure no component along the normal vector) NewFwd = (NewFwd - N * FVector::DotProduct(NewFwd, N)).GetSafeNormal();
// Combine the matrix and convert it to a rotation FRotationMatrix RotM = UKismetMathLibrary::MakeRotFromXZ(NewFwd, N); FRotator NewRot = RotM.Rotator(); NewRot.Roll = FMath::GridSnap(NewRot.Roll, RotMinStep); NewRot.Pitch = FMath::GridSnap(NewRot.Pitch, RotMinStep); NewRot.Yaw = FMath::GridSnap(NewRot.Yaw, RotMinStep); return NewRot;
} [/code] [b] функция, которая выравнивает колесо и направление на поверхность: [/b] [code]void UFG_MovementComponent::AlignToSurface(float DeltaTime) { // If the wheel or gyro does not exist, or if the direction is not set, exit if (!Wheel || !Gyro || !Direction) return;
// Check if the wheel is currently on the ground if (!GetGroundStatus(TraceLength)) return;
// Get the surface normal FVector NewSurfaceNormal = GetSurfaceNormal(NormalTraceLength, SurfaceNormal);
if (NewSurfaceNormal == SurfaceNormal) return;
SurfaceNormal = NewSurfaceNormal;
// Get the current rotation of the wheel FRotator CurrentRot = Wheel->GetComponentRotation(); FRotator GyroRot = Gyro->GetComponentRotation(); FRotator CurrentDirectionRot = Direction->GetComponentRotation();
// Calculate the target rotation based on the surface normal and current rotation FRotator TargetRot = MakeAlignedRotator(SurfaceNormal, CurrentRot); // Apply the target rotation to the wheel Wheel->SetWorldRotation(TargetRot); Gyro->SetWorldRotation(GyroRot);
} [/code] [b]Функция, выравнивающая колесо и направление по нормали поверхности[/b] [code]void UFG_MovementComponent::AlignToSurface(float DeltaTime) { // If the wheel or gyro does not exist, or if the direction is not set, exit if (!Wheel || !Gyro || !Direction) return;
// Check if the wheel is currently on the ground if (!GetGroundStatus(TraceLength)) return;
// Get the surface normal FVector NewSurfaceNormal = GetSurfaceNormal(NormalTraceLength, SurfaceNormal);
if (NewSurfaceNormal == SurfaceNormal) return;
SurfaceNormal = NewSurfaceNormal;
// Get the current rotation of the wheel FRotator CurrentRot = Wheel-\>GetComponentRotation(); FRotator GyroRot = Gyro-\>GetComponentRotation(); FRotator CurrentDirectionRot = Direction-\>GetComponentRotation();
// Calculate the target rotation based on the surface normal and current rotation FRotator TargetRot = MakeAlignedRotator(SurfaceNormal, CurrentRot); // Apply the target rotation to the wheel Wheel-\>SetWorldRotation(TargetRot); Gyro-\>SetWorldRotation(GyroRot);
FRotator TargetDirectionRot = MakeAlignedRotator(SurfaceNormal, CurrentDirectionRot); Direction-\>SetWorldRotation(TargetDirectionRot); } [/code] [b] Тогда есть главное, что, я полагаю, вызывает эту проблему [/b] [code]void UFG_MovementComponent::ApplyGyroStabilization(float DeltaTime) { // Check if Gyro component is valid if (!Gyro) return;
// Get the current rotation of the Wheel component FRotator WheelRot = Wheel->GetComponentRotation();
// Get the current rotation of the Gyro component FRotator GyroRot = Gyro->GetComponentRotation();
// Get the direction of the wheel drive force FRotator DirectionRot = MakeAlignedRotator(SurfaceNormal, WheelRot);
GEngine->AddOnScreenDebugMessage(1, 1.f, FColor::Red, FString::Printf(TEXT("GravityRot: %f, %f, %f"), GravityRot.Roll, GravityRot.Pitch, GravityRot.Yaw)); GEngine->AddOnScreenDebugMessage(2, 1.f, FColor::Green, FString::Printf(TEXT("GravityRot: %f, %f, %f"), WheelRot.Roll, WheelRot.Pitch, WheelRot.Yaw)); // Calculate the alignment coefficient based on the wheel drive force float AlignCoeff = FMath::GetMappedRangeValueClamped( FVector2D(0.f, 3000.f), FVector2D(1.f, 1.f), FMath::Abs(WheelDriveForce)); // YOU CAN IGNORE THIS, IT JUST CALCULATE HOW, BASED ON SPEED IT WILL ALIGN TO SURFACE RATHER THAN GRAVITY
// Calculate the target rotation based on the alignment coefficient FRotator TargetRot = UKismetMathLibrary::RLerp( GravityRot, DirectionRot, AlignCoeff, true); // AND THIS ALSO
// Calculate the new rotation of the Gyro component FRotator NewRot;
return NewRot; } [/code] Контекст: Пользовательское движение, полностью C ++ (без символов, компонент). [b] wheel [/b] выравнивается в [b] surfacenormal [/b], а [b] gyro [/b] применяет устойчивую пружину. Колесо не соответствует направлению, потому что, когда я пытался управлять колесом в качестве корневого компонента, это вызвало проблемы репликации. Я планирую сделать этот многопользовательский, поэтому мне пришлось разделить их, хотя мне интересно, есть ли надежный способ управлять корневым колесом, не нарушая репликацию. Sparsy, Codeium помогла с DocStrings, хотя должен быть читаемым. Ось (вперед/задний наклон) относительно направления прямого направления колеса (компонент направления, а не корень), похожая на пружину, пытающуюся сохранить равновесие.>