test(camera): -cameratest harness for the spring-arm collision, result inconclusive
0.2.3 is about camera clipping, and nothing had ever exercised the spring arm's collision test. This adds a harness: find the nearest wall from the spawn, stand the pawn 70 cm off it facing away, wait for the boom, then report the arm length, whether the boom ends inside geometry, and a back-trace from camera to head. Inconclusive, and the setup is the suspect rather than the engine: every run reports the arm at its resting 340 cm, but BOTH traces come back clear, so nothing was between the pawn and the boom at all. The placement is wrong, not necessarily the collision. Three wrong turns recorded in TODO.md because each cost a run: the probe accepted the floor as a wall, then faced the pawn into the wall by negating the impact normal (which points away from it), then set actor rotation only - which a possessed pawn discards in favour of control rotation. Next: look at the placement screenshot before trusting the measurement.
This commit is contained in:
1 parent
d8b44f1e57
commit
a6701aa3d1
3 files changed
+140
-2
No files matched your search
@@ -15,6 +15,8 @@
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#include "Camera/PlayerCameraManager.h"
|
||||
#include "UnrealClient.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Misc/CommandLine.h"
|
||||
@@ -333,6 +335,7 @@ void AGigLineCharacter::BeginPlay()
|
||||
}
|
||||
|
||||
bWalkForever = FParse::Param(FCommandLine::Get(), TEXT("walk"));
|
||||
bCameraTest = FParse::Param(FCommandLine::Get(), TEXT("cameratest"));
|
||||
|
||||
// `-perf` starts the overlay on: the performance capture and any manual frame
|
||||
// check should not depend on someone pressing F3 first. The built demo has
|
||||
@@ -346,6 +349,120 @@ void AGigLineCharacter::BeginPlay()
|
||||
}
|
||||
}
|
||||
|
||||
float AGigLineCharacter::GetThirdPersonArmLength() const
|
||||
{
|
||||
return ThirdPersonArm ? ThirdPersonArm->TargetArmLength : 0.f;
|
||||
}
|
||||
|
||||
FVector AGigLineCharacter::GetThirdPersonCameraLocation() const
|
||||
{
|
||||
return ThirdPersonCamera ? ThirdPersonCamera->GetComponentLocation() : GetActorLocation();
|
||||
}
|
||||
|
||||
void AGigLineCharacter::UpdateCameraCollisionTest(float DeltaSeconds)
|
||||
{
|
||||
// A dedicated harness rather than a step inside -qaselftest: the QA timeline is
|
||||
// tightly timed and an added teleport perturbed the reset check by 105 cm.
|
||||
CameraTestElapsed += DeltaSeconds;
|
||||
if (CameraTestPlaced && CameraTestElapsed < 2.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!CameraTestPlaced)
|
||||
{
|
||||
CameraTestPlaced = true;
|
||||
UWorld* World = GetWorld();
|
||||
bool bFound = false;
|
||||
if (World)
|
||||
{
|
||||
// Search outward for the nearest blocking wall and take the closest hit.
|
||||
float BestDist = TNumericLimits<float>::Max();
|
||||
FHitResult Best;
|
||||
for (int32 i = 0; i < 16; ++i)
|
||||
{
|
||||
const FVector Dir = FRotator(0.f, i * 22.5f, 0.f).Vector();
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Q(TEXT("GigLineCameraTest"), false, this);
|
||||
if (World->LineTraceSingleByChannel(Hit, GetActorLocation(),
|
||||
GetActorLocation() + Dir * 6000.f, ECC_Visibility, Q))
|
||||
{
|
||||
// A wall, not the ground: the first run picked a floor hit and
|
||||
// rotated the pawn to face straight down, so the boom pointed at
|
||||
// open sky and the arm never had anything to collide with.
|
||||
if (FMath::Abs(Hit.ImpactNormal.Z) > 0.5f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Hit.Distance < BestDist)
|
||||
{
|
||||
BestDist = Hit.Distance;
|
||||
Best = Hit;
|
||||
bFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bFound)
|
||||
{
|
||||
// The impact normal points OUT of the wall into open space, so standing
|
||||
// along it and facing along it puts the wall behind us. The first two
|
||||
// attempts used its inverse and faced the pawn into the wall, which left
|
||||
// the boom pointing at open sky and the arm with nothing to collide with.
|
||||
SetActorLocation(Best.ImpactPoint + Best.ImpactNormal * 70.f);
|
||||
LastWallNormal = Best.ImpactNormal;
|
||||
const FRotator Facing = Best.ImpactNormal.Rotation();
|
||||
SetActorRotation(Facing);
|
||||
if (APlayerController* PC = Cast<APlayerController>(GetController()))
|
||||
{
|
||||
PC->SetControlRotation(FRotator(0.f, Facing.Yaw, 0.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bThirdPerson)
|
||||
{
|
||||
ToggleCamera();
|
||||
}
|
||||
CameraTestPlaced = true;
|
||||
UE_LOG(LogTemp, Warning,
|
||||
TEXT("GigLine CAMTEST placed wall=%d fwd=(%.2f,%.2f) normal=(%.2f,%.2f) collisionTest=%d"),
|
||||
bFound ? 1 : 0, GetActorForwardVector().X, GetActorForwardVector().Y,
|
||||
LastWallNormal.X, LastWallNormal.Y,
|
||||
ThirdPersonArm && ThirdPersonArm->bDoCollisionTest ? 1 : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
// Measure the spring arm itself, not the camera manager: the arm is what carries
|
||||
// the collision test, and its arm length is the value that must shorten.
|
||||
const float Rest = ThirdPersonBoomLength;
|
||||
const float Arm = GetThirdPersonArmLength();
|
||||
const FVector CameraLoc = GetThirdPersonCameraLocation();
|
||||
const float Shortened = Rest - Arm;
|
||||
const FVector Head = GetActorLocation() + FVector(0.f, 0.f, 60.f);
|
||||
const float Dist = FVector::Dist(Head, CameraLoc);
|
||||
FHitResult Hit;
|
||||
FCollisionQueryParams Q(TEXT("GigLineCameraClear"), false, this);
|
||||
const bool bBlocked = World && CameraLoc.SizeSquared() > 1.0f
|
||||
&& World->LineTraceSingleByChannel(Hit, Head, CameraLoc, ECC_Visibility, Q);
|
||||
// Second trace, from the camera back toward the pawn: if this one IS blocked while
|
||||
// the arm length is still at rest, the spring arm's collision test is not running.
|
||||
FHitResult BackHit;
|
||||
FCollisionQueryParams QBack(TEXT("GigLineCameraBack"), false, this);
|
||||
const bool bBackBlocked = World && CameraLoc.SizeSquared() > 1.0f
|
||||
&& World->LineTraceSingleByChannel(BackHit, CameraLoc, Head, ECC_Visibility, QBack);
|
||||
const bool bClear = !bBlocked || Hit.Distance > Dist - 25.f;
|
||||
const bool bShortened = Shortened > 30.f;
|
||||
UE_LOG(LogTemp, Warning,
|
||||
TEXT("GigLine CAMTEST arm %.0f of %.0f cm (shortened %.0f), cam %.0f cm from head, "
|
||||
"blocked=%d -> %s"),
|
||||
Arm, Rest, Shortened, Dist, bBlocked ? 1 : 0,
|
||||
(bClear || bShortened) ? TEXT("PASS") : TEXT("FAIL"));
|
||||
UE_LOG(LogTemp, Warning,
|
||||
TEXT("GigLine CAMTEST back-trace blocked=%d dist=%.0f (arm rest %.0f)"),
|
||||
bBackBlocked ? 1 : 0, bBackBlocked ? BackHit.Distance : -1.f, Rest);
|
||||
FScreenshotRequest::RequestScreenshot(TEXT("review-demo-camera-collision"), true, false);
|
||||
bCameraTest = false;
|
||||
}
|
||||
|
||||
void AGigLineCharacter::ToggleDebugOverlay()
|
||||
{
|
||||
static IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug"));
|
||||
@@ -852,6 +969,11 @@ void AGigLineCharacter::Tick(float DeltaSeconds)
|
||||
Super::Tick(DeltaSeconds);
|
||||
// Speed-driven animation state: correct whether the player walked, was pushed,
|
||||
// or fell, and independent of which input produced the movement.
|
||||
if (bCameraTest)
|
||||
{
|
||||
UpdateCameraCollisionTest(GetWorld() ? GetWorld()->GetDeltaSeconds() : 0.f);
|
||||
}
|
||||
|
||||
WalkForever();
|
||||
UpdateBodyAnimation();
|
||||
|
||||
|
||||
@@ -200,6 +200,18 @@ protected:
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> ToggleDebugAction;
|
||||
|
||||
/** Current boom length and camera position of the third-person rig. */
|
||||
float GetThirdPersonArmLength() const;
|
||||
FVector GetThirdPersonCameraLocation() const;
|
||||
|
||||
/** `-cameratest`: back the pawn against a wall and check the boom does not clip. */
|
||||
void UpdateCameraCollisionTest(float DeltaSeconds);
|
||||
|
||||
FVector LastWallNormal = FVector::ZeroVector;
|
||||
bool bCameraTest = false;
|
||||
bool CameraTestPlaced = false;
|
||||
float CameraTestElapsed = 0.0f;
|
||||
|
||||
/** Flips the gigline.Debug console variable that the HUD reads. */
|
||||
void ToggleDebugOverlay();
|
||||
|
||||
|
||||
Reference in new issue
Block a user