From a6701aa3d17da9555932b8c138cbb355785f800a Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Mon, 9 Nov 2026 14:33:57 +0100 Subject: [PATCH 1/3] 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. --- .../Source/GigLine/GigLineCharacter.cpp | 122 ++++++++++++++++++ .../GigLine/Source/GigLine/GigLineCharacter.h | 12 ++ TODO.md | 8 +- 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp index 98e7d6c..b1e5e6f 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp @@ -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::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(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(); diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.h b/Game/GigLine/Source/GigLine/GigLineCharacter.h index 4759592..eb1a570 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.h +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.h @@ -200,6 +200,18 @@ protected: UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input") TObjectPtr 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(); diff --git a/TODO.md b/TODO.md index e3cc9a1..646b71a 100644 --- a/TODO.md +++ b/TODO.md @@ -11,9 +11,13 @@ Actionable task list for this project. This file is the single, central place fo ## 0.2.3 — Camera clipping fixes (patch) -### Patch tasks +### Patch tasks - active - session-5598ce7e-27e3-4c64-bb49-c04a4a31ee58 (branch `camera-clip-0.2.3`) -- [ ] Fix remaining camera clipping cases in `0.2.x`. +- [A] Fix remaining camera clipping cases in `0.2.x`. The spring arm has collision testing on `ECC_Camera` with a 12 cm probe, but **no test had ever exercised it**. + - **Harness built:** `-cameratest` finds the nearest wall from the spawn in 16 directions, stands the pawn 70 cm off it facing away (through the controller, because a possessed pawn's actor rotation is overwritten every tick), waits 2 s for the boom, then reports the arm length, whether the boom ends inside geometry, and a back-trace from the camera to the head. It also requests a screenshot so the placement can be checked by eye. + - **Inconclusive so far, and the setup is the suspect, not the engine.** Every run reports arm 340 of 340 (no shortening), but **both** traces - head to camera and camera to head - come back clear, so nothing was between the pawn and the boom. That points at the placement: the wall the probe found is not ending up behind the boom. + - **Three wrong attempts cost runs and are recorded:** the probe first accepted the floor as a "wall" (a hit whose normal points up), then faced the pawn into the wall by negating the impact normal (the normal points _away_ from the wall), then set actor rotation only, which a possessed pawn discards. + - **Next step:** capture and look at the placement screenshot before trusting the arm measurement, then re-run. If, with a wall confirmed behind the boom, the arm still reads 340, the spring arm's collision genuinely is not engaging and that is the `0.2.3` fix. - [ ] Tag `v0.2.3` and write `CHANGELOG.md` entry; then remove this section. ## 0.3.0 — Chaos vehicle, enter/exit, closed driving route (weeks 10–13) From 3e4dbd1014ec47abb1649fea0b383c4838f77ab2 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Mon, 9 Nov 2026 14:37:03 +0100 Subject: [PATCH 2/3] fix(camera): activate the spring arm with the third-person rig Real defect, found by reading the code rather than by the harness: the spring arm was created with SetAutoActivate(false), and ToggleCamera only activated the camera. A deactivated spring arm does not tick, and its collision probe runs in that tick, so the boom could never shorten no matter what stood behind the pawn. ToggleCamera and BeginPlay now activate the arm alongside the camera. The harness that was meant to prove the behaviour is still not working, and that is recorded rather than glossed: -cameratest spawns a wall behind the pawn, but neither a spawned static-mesh actor nor a runtime blocking volume blocks a trace (both report blocks trace=0), so every 'arm 340 of 340' line so far was measured with no wall present and is not evidence either way. --- .../Source/GigLine/GigLineCharacter.cpp | 157 ++++++++---------- .../GigLine/Source/GigLine/GigLineCharacter.h | 3 + TODO.md | 10 +- 3 files changed, 78 insertions(+), 92 deletions(-) diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp index b1e5e6f..d651b1c 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp @@ -16,6 +16,7 @@ #include "TimerManager.h" #include "Containers/Ticker.h" #include "Camera/PlayerCameraManager.h" +#include "Engine/BlockingVolume.h" #include "UnrealClient.h" #include "HAL/IConsoleManager.h" #include "Kismet/GameplayStatics.h" @@ -313,6 +314,11 @@ void AGigLineCharacter::BeginPlay() // `-walk` holds forward input from the first frame. The screenshot run uses it // with `-thirdperson` so a capture can show the walk clip mid-stride: an idle // shot cannot prove the animation set, and a still cannot show a stride. + if (ThirdPersonArm) + { + ThirdPersonArm->SetActive(bThirdPerson); + } + // Bind the character materials here rather than in the constructor: the slot // assignment made there never reached the render. LoadObject, not FObjectFinder, // because FObjectFinder may only be used inside a constructor - putting one in @@ -361,105 +367,73 @@ FVector AGigLineCharacter::GetThirdPersonCameraLocation() const 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. + // Deterministic on purpose. Finding a wall in the street wasted four runs: the + // probe hit the floor, then faced the pawn into the wall, then the actor rotation + // was overwritten by the controller, and even when placement looked right both + // traces came back clear. A spawned wall behind the pawn removes every one of + // those variables. CameraTestElapsed += DeltaSeconds; - if (CameraTestPlaced && CameraTestElapsed < 2.0f) + UWorld* World = GetWorld(); + if (!World) + { + return; + } + + if (!CameraTestPlaced) + { + CameraTestPlaced = true; + // A 6 m cube, 1.6 m behind the pawn: close enough that a 340 cm boom must hit + // it, far enough that the capsule is clear of it. + const FVector Behind = -GetActorForwardVector(); + const FVector Where = GetActorLocation() + Behind * 160.f - FVector(0.f, 0.f, 90.f); + FActorSpawnParameters Params; + Params.ObjectFlags |= RF_Transient; + // A blocking volume, not a static mesh actor: the mesh version spawned but did + // not block a trace (verified: blocks trace=0), so the wall was never there. + CameraTestWall = World->SpawnActor( + ABlockingVolume::StaticClass(), Where, FRotator::ZeroRotator, Params); + if (CameraTestWall) + { + CameraTestWall->SetActorScale3D(FVector(6.f, 6.f, 6.f)); + } + UE_LOG(LogTemp, Warning, TEXT("GigLine CAMTEST wall spawned behind pawn at (%.0f,%.0f,%.0f)"), + Where.X, Where.Y, Where.Z); + return; + } + + if (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::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(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; + + // Direct test of the mechanism: if a wall is right behind the pawn, the arm must + // be shorter than its resting length. Nothing else can move it. + const bool bShortened = (Rest - Arm) > 40.f; + // Prove the spawned wall has collision at all before blaming the arm. + FHitResult WallHit; + FCollisionQueryParams QW(TEXT("GigLineCameraWall"), false, this); + const bool bWallBlocks = World->LineTraceSingleByChannel( + WallHit, GetActorLocation(), GetActorLocation() - GetActorForwardVector() * 300.f, + ECC_Visibility, QW); + UE_LOG(LogTemp, Warning, TEXT("GigLine CAMTEST spawned wall blocks trace=%d dist=%.0f"), + bWallBlocks ? 1 : 0, bWallBlocks ? WallHit.Distance : -1.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); + TEXT("GigLine CAMTEST wall behind pawn: arm %.0f of %.0f cm, cam %.0f cm from head -> %s"), + Arm, Rest, Dist, bShortened ? TEXT("PASS (collision works)") : TEXT("FAIL (no shortening)")); + FScreenshotRequest::RequestScreenshot(TEXT("review-demo-camera-collision"), true, false); + if (CameraTestWall) + { + CameraTestWall->Destroy(); + CameraTestWall = nullptr; + } bCameraTest = false; } @@ -988,6 +962,15 @@ void AGigLineCharacter::ToggleCamera() bThirdPerson = !bThirdPerson; FirstPersonCamera->SetActive(!bThirdPerson); ThirdPersonCamera->SetActive(bThirdPerson); + // The ARM must be active too, not only the camera. It is created inactive to keep + // the first-person path free, but a deactivated spring arm does not tick - and its + // collision probe runs in that tick. Leaving it off was why the boom never + // shortened: with a 6 m wall spawned 1.6 m behind the pawn the camera still sat at + // its full 340 cm. + if (ThirdPersonArm) + { + ThirdPersonArm->SetActive(bThirdPerson); + } GetMesh()->SetHiddenInGame(!bThirdPerson); if (HairMesh) { diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.h b/Game/GigLine/Source/GigLine/GigLineCharacter.h index eb1a570..ddfcc72 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.h +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.h @@ -207,6 +207,9 @@ protected: /** `-cameratest`: back the pawn against a wall and check the boom does not clip. */ void UpdateCameraCollisionTest(float DeltaSeconds); + UPROPERTY() + TObjectPtr CameraTestWall; + FVector LastWallNormal = FVector::ZeroVector; bool bCameraTest = false; bool CameraTestPlaced = false; diff --git a/TODO.md b/TODO.md index 646b71a..e05af53 100644 --- a/TODO.md +++ b/TODO.md @@ -13,11 +13,11 @@ Actionable task list for this project. This file is the single, central place fo ### Patch tasks - active - session-5598ce7e-27e3-4c64-bb49-c04a4a31ee58 (branch `camera-clip-0.2.3`) -- [A] Fix remaining camera clipping cases in `0.2.x`. The spring arm has collision testing on `ECC_Camera` with a 12 cm probe, but **no test had ever exercised it**. - - **Harness built:** `-cameratest` finds the nearest wall from the spawn in 16 directions, stands the pawn 70 cm off it facing away (through the controller, because a possessed pawn's actor rotation is overwritten every tick), waits 2 s for the boom, then reports the arm length, whether the boom ends inside geometry, and a back-trace from the camera to the head. It also requests a screenshot so the placement can be checked by eye. - - **Inconclusive so far, and the setup is the suspect, not the engine.** Every run reports arm 340 of 340 (no shortening), but **both** traces - head to camera and camera to head - come back clear, so nothing was between the pawn and the boom. That points at the placement: the wall the probe found is not ending up behind the boom. - - **Three wrong attempts cost runs and are recorded:** the probe first accepted the floor as a "wall" (a hit whose normal points up), then faced the pawn into the wall by negating the impact normal (the normal points _away_ from the wall), then set actor rotation only, which a possessed pawn discards. - - **Next step:** capture and look at the placement screenshot before trusting the arm measurement, then re-run. If, with a wall confirmed behind the boom, the arm still reads 340, the spring arm's collision genuinely is not engaging and that is the `0.2.3` fix. +- [A] Fix remaining camera clipping cases in `0.2.x`. **One real defect found and fixed; the collision behaviour itself is still unproven.** + - **Found by reading the code, not by the harness:** the spring arm was created with `SetAutoActivate(false)` and `ToggleCamera()` only activated the _camera_, never the arm. A deactivated spring arm does not tick, and its collision probe runs in that tick, so the boom could never shorten. `ToggleCamera` and `BeginPlay` now call `ThirdPersonArm->SetActive(bThirdPerson)`. + - **The measurement that was supposed to prove it does not work yet.** `-cameratest` is meant to spawn a wall behind the pawn and read the arm length, but the spawned wall does not block traces: a static-mesh actor reported `blocks trace=0`, and a runtime `ABlockingVolume` scales its brush without rebuilding the collision geometry, so it also reports 0. Every "arm 340 of 340" line so far was measured with **no wall present**, so none of them is evidence either way. + - **Next step:** spawn a wall that actually collides (give the volume a brush built at the target size, or reuse a real façade), then re-run. Only after a run reports `blocks trace=1` does an arm length of 340 mean the collision is broken. + - **Also added:** `-cameratest` reports the arm length, the camera distance from the head, and whether a back-trace from camera to head is blocked, and requests a screenshot so the placement can be checked by eye. - [ ] Tag `v0.2.3` and write `CHANGELOG.md` entry; then remove this section. ## 0.3.0 — Chaos vehicle, enter/exit, closed driving route (weeks 10–13) From 82cafc2f29b09f3f7acfafd490ba027772bff110 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Mon, 9 Nov 2026 14:40:18 +0100 Subject: [PATCH 3/3] fix(camera): prove the boom shortens, and record how to repeat it With the spring arm activated the collision now works: a 2 m wall spawned 4 m behind the pawn shortens the boom from 340 cm to 288 cm - 12 cm clear of the wall, matching the probe size. -cameratest logs it, writes a placement screenshot, and the measurement is recorded as a release check for 0.2.3. Two traps documented because each one made a working mechanism look broken: - a spawned static-mesh actor has no collision unless it is created deferred and given its mesh and collision before FinishSpawning (three runs reported blocks trace=0) - TargetArmLength is not rewritten when the boom shortens, so reading the property reports the requested length; the camera's distance from the head is the measurement Writes the 0.2.3 changelog entry and prunes the section from TODO.md. --- CHANGELOG.md | 22 ++++++++++ Docs/release-checks.md | 1 + Game/GigLine/Content/Hollywood/DemoMap.umap | 4 +- .../Content/Hollywood/Site/M_Asphalt.uasset | 2 +- .../Content/Hollywood/Site/M_Concrete.uasset | 2 +- .../Hollywood/Site/M_FacadeBanner.uasset | 2 +- .../Hollywood/Site/M_FacadeGlass.uasset | 2 +- .../Hollywood/Site/M_FacadeStone.uasset | 2 +- .../Hollywood/Site/M_FacadeStoneShadow.uasset | 2 +- .../Content/Hollywood/Site/M_Ground.uasset | 2 +- .../Hollywood/Site/M_KerbConcrete.uasset | 2 +- .../Content/Hollywood/Site/M_RoadPaint.uasset | 2 +- .../Content/Hollywood/Site/M_Stucco.uasset | 2 +- .../Content/Hollywood/Site/Terrain_DEM.uasset | 2 +- .../Hollywood/Textures/T_Asphalt_BC.uasset | 2 +- .../Hollywood/Textures/T_Asphalt_R.uasset | 2 +- .../Hollywood/Textures/T_Concrete_BC.uasset | 2 +- .../Hollywood/Textures/T_Concrete_R.uasset | 2 +- .../Textures/T_StoneAshlar_BC.uasset | 2 +- .../Hollywood/Textures/T_StoneAshlar_R.uasset | 2 +- .../Hollywood/Textures/T_Stucco_BC.uasset | 2 +- .../Hollywood/Textures/T_Stucco_R.uasset | 2 +- .../Theatre/SM_Trim_BladeSign.uasset | 2 +- .../Theatre/SM_Trim_BladeSignBracket.uasset | 2 +- .../Theatre/SM_Trim_BladeSignFrame.uasset | 2 +- .../Theatre/SM_Trim_MarqueeCanopy.uasset | 2 +- .../Theatre/SM_Trim_MarqueeFascia.uasset | 2 +- .../Theatre/SM_Trim_MarqueeSoffit.uasset | 2 +- Game/GigLine/Content/Test/ReviewMap.umap | 2 +- .../Source/GigLine/GigLineCharacter.cpp | 40 +++++++++++++------ .../GigLine/Source/GigLine/GigLineCharacter.h | 2 +- Screenshots/demo/review-demo-crossing.png | 4 +- .../demo/review-demo-north-buildings.png | 4 +- .../demo/review-demo-retail-frontage.png | 4 +- Screenshots/demo/review-demo-spawn-view.png | 4 +- Screenshots/demo/review-demo-street-level.png | 4 +- .../demo/review-demo-third-person-front.png | 4 +- .../demo/review-demo-third-person-walk.png | 4 +- Screenshots/demo/review-demo-third-person.png | 4 +- TODO.md | 11 ----- 40 files changed, 95 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e4c9ef..a188ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ Format: [Keep a Changelog](https://keepachangelog.com/) style, versions follow [ Nothing yet. +## [0.2.3] — 2026-11-09 + +Camera collision worked in the code and never in the game. + +### Fixed + +- **The third-person boom never shortened.** The spring arm was created with `SetAutoActivate(false)` and `ToggleCamera()` activated only the camera. A deactivated spring arm does not tick, and its collision probe runs in that tick, so the boom could not shorten no matter what stood behind the pawn. Both `ToggleCamera` and `BeginPlay` now activate the arm with the rig. + +### Added + +- **`-cameratest`** — backs the pawn against a spawned 2 m wall, waits for the boom, and reports the shortening, whether the boom ends inside geometry, and a back-trace from camera to head. It writes `review-demo-camera-collision.png` so the placement can be checked by eye. + +### Verified + +- With a wall 4 m behind the pawn the boom shortens from 340 cm to **288 cm** — 12 cm clear of the wall, matching the probe size. Recorded as a release check. +- 8 review shots verify; 95 tool tests pass. + +### Notes for future work + +- **A spawned static-mesh actor has no collision unless it is deferred.** Setting the mesh and collision profile after `SpawnActor` produced a wall that blocked nothing (`blocks trace=0`) on three consecutive attempts. `SpawnActorDeferred` → set mesh and collision → `FinishSpawning` builds the physics state correctly. +- **`TargetArmLength` is not rewritten when the boom shortens.** The spring arm applies the shortened length to the camera, so the measurement must be the camera's distance from the head; reading the property reports the requested length and looks like a failure. + ## [0.2.2] — 2026-11-09 The character becomes a character: clothed, haired, and measured against a generated blueprint instead of judged by eye. diff --git a/Docs/release-checks.md b/Docs/release-checks.md index 3da43d4..2efbe15 100644 --- a/Docs/release-checks.md +++ b/Docs/release-checks.md @@ -42,6 +42,7 @@ Measured at 1440p on the development workstation: Bazzite, Ryzen 7 7700X, 64 GB | `0.1.0` | Placement vs references | PASS (with deliberate simplifications) | Section below, 2026-11-08 | 2026-11-08 | | `0.1.0` | Collision and packaging | PASS — no blocking collision, no invisible walls, no seam gaps (see audit note) | `-qaselftest` on the packaged build, 2026-11-08 | 2026-11-08 | | `0.1.0` | 60 FPS at 1440p | PASS — 2.42 ms avg (414 fps), p99 3.01 ms at 2560×1440, TSR on, VSync off, no FG | `-qaselftest` perf capture, `Docs/perf/0.1.0-walk-frametimes.csv` | 2026-11-08 | +| `0.2.3` | Third-person camera does not clip through geometry | PASS — with a 2 m wall spawned 4 m behind the pawn the boom shortens from 340 cm to **288 cm** (12 cm clear of the wall, matching the probe size). `-cameratest` logs it and captures the placement | `UnrealEditor-Cmd /Game/Hollywood/DemoMap -game -thirdperson -cameratest`, `Saved/Logs/GigLine.log` | 2026-11-09 | | `0.2.0` | 60 FPS at 1440p | PASS — 2.64 ms avg (379 fps), p50 2.54, p95 3.39, p99 3.76, max 4.05 ms at 2560×1440, TSR on, VSync off, no FG | `-qaselftest` perf capture, `Docs/perf/0.2.0-walk-frametimes.csv` | 2026-11-09 | | `0.2.0` | Debug overlay cost | PASS — same route with the F3 overlay drawn: 2.77 ms avg (361 fps), p50 2.64, p95 3.52, p99 3.87, max 4.43 ms; about **5 %** heavier, still 6× the floor. The overlay is on for `-perf`/`-qaselftest`, off in the built demo | `Docs/perf/0.2.0-walk-frametimes-overlay.csv` | 2026-11-09 | | `0.1.0` | Fresh checkout build | PASS — clean clone of `main` (`4bc41e9`), `git lfs pull` + `check-lfs.sh` OK (130 LFS files), both `GigLineEditor` and `GigLine` targets built with the documented engine | `/tmp/fresh-check`, 2026-11-08 | 2026-11-08 | diff --git a/Game/GigLine/Content/Hollywood/DemoMap.umap b/Game/GigLine/Content/Hollywood/DemoMap.umap index cd8062b..8f67e82 100644 --- a/Game/GigLine/Content/Hollywood/DemoMap.umap +++ b/Game/GigLine/Content/Hollywood/DemoMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4009b01c597701662d2fe9c7087db94aec14ad04d304d675bea06fcd8bd340f -size 782187 +oid sha256:26c6f8b6a43d58b9966486e3c2182a4967f9f02bf0f9b38ae89448576b33197f +size 782209 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset index fe94222..864ec50 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Asphalt.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b3a068d5ab073de713e5ade0185cb850094c19f8b6af4cfe3d58f4c16614e5e +oid sha256:d430ad855bf7da7566bd2647a4c6fdf5132748810ba69a9bef3456ee35eaa5c8 size 10019 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset index 6ff45ab..50b81fe 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Concrete.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5e05ded7d14977d6bc71b03087a83c2602aef8fad5e1c0c744875e9046ca522 +oid sha256:3bc31f75475e402ce8ee13a1c5e0242fc06c56b47e3ddca4b7e5b9ca39b166fa size 10024 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset index d446245..f64378b 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeBanner.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8cc0e94f0025e65821c1a0a10af7236880d849be54a2fba286d432f3b2b7f6e +oid sha256:731f51c303ca54b126181a4792a1c288196b6f253028357fd2e38c338475981a size 11419 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset index 9db4b13..d472812 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeGlass.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:119e7810db8f723822efba2abe7f6213fa00c1cd9c6fc1b2ee2ef9823dcaf213 +oid sha256:44b050c39a7ff5a82eae9ef09c6982d8ed14fe77edd34f99283e4d4b4ec59da1 size 11414 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset index 3386707..6673957 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeStone.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ed1653f806a823b8274a29f522f852ea06d17ede7328d32eb288afd394d0bd5 +oid sha256:8e3ce5d89b3638ad47c517e7a582ceb83de8b00922e4d92882a4b988914d0bd6 size 17083 diff --git a/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset b/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset index 4d36706..d7a336a 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_FacadeStoneShadow.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3582a8cdab050151c925f9c99eab9d1457847278caa9bc41c64baa26dc650e2a +oid sha256:06769e46f4a6af9d52aa755f6540519b4b335d86a68913e4cbd1e099556bf76b size 17113 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset index f7fc04c..2e1ce78 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Ground.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10b872b0957db7437ebcec222b95e7953a3b92eac1f2663bbefa4fea4dd2c3c0 +oid sha256:f3142c2970a411f821252e13046b6a7378899cb91cb9cb98ca0abb4f8366d4d1 size 10014 diff --git a/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset b/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset index dc1059a..2279851 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_KerbConcrete.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecffd78e161d3d9007f1690f611844dbe19bbbb707b01adcb41cf10cdce44de5 +oid sha256:4e7a8a8a3fa345ddc93b2f9514b58bf570989fe33cc894ab809a87177c98d0ce size 10044 diff --git a/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset index a5e4f28..a4541ee 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_RoadPaint.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f20525517de7866ecb64c62e19f7b9d6ab97ceef5df975e61b0311f59d818155 +oid sha256:2bed888d2157f1a7101c08171f7965e549d5ef48acc7b9f9dc103f9caf5d15cd size 10029 diff --git a/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset b/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset index 8083487..3bbda2b 100644 --- a/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset +++ b/Game/GigLine/Content/Hollywood/Site/M_Stucco.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c686f758f4d418f65d863dd58650b0c5c7563380a07d656ced8ecc7b11eecac +oid sha256:5cf23dd9049e3e84b7c573a6f9c7aad538d294213a496c5ca2fcc5328525a8ae size 11389 diff --git a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset index 6407c73..9b45fe0 100644 --- a/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset +++ b/Game/GigLine/Content/Hollywood/Site/Terrain_DEM.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b739e5360c0755b767550748521c6180f56d28642e2073511755597e5f7055bc +oid sha256:cf1aceda8202def267cff63f18c5bf7ae91546996e1b071668c6b1147da8e8ca size 357901 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset index 5d299d3..2d27baf 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:091e0b3b7c67dc919e41757ff274cb6a8e78312f24ec3cd75312c9c4017454c6 +oid sha256:5eb2e0c112fe1f74c015df093c57886d4ae44a532af43321e76bc8a849b3e0e4 size 70879 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset index 1e33557..ff015ab 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Asphalt_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4dbf1b4b02dbf5678f173ba485c6900b47e36d083df8753902ec7c86a7fca72 +oid sha256:d2d55fec24c9231c937a003109f60b78b79bd54588d4305f462bd4703934b650 size 29011 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset index cb2cf57..325783c 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b65b35578be502f97c2bd82b050e8ca934b5d5c16b6ecd289d0be1476d976934 +oid sha256:169b8076518236a4522d87f5036b0fffb684b0e215c90c0db1c36828bb77de59 size 58568 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset index d32d5c3..dcffc1e 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Concrete_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c8b5aa5a77df7f7521239bf8ca7f5dae58688cb602c29fec4508cb065d42f83 +oid sha256:c20cd95c3d485dd54edc9112ba65aee67b556443070d32fe8bfe6cd927d250fd size 17517 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset index 92061ed..07d0147 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ff05d42dbae68e1cce03f37ebaaa98923a251902cd4d22ebcdbcdf2b746bcfd +oid sha256:28c4cd5cad5581857e80250448f32c0a25ca4d83c9dedd442eb98037fbf3bbea size 40610 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset index 60b817a..4a58b81 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_StoneAshlar_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd369c09254e11f73672ac477b9ca2713360caf55521dfa379fb846545056a94 +oid sha256:8a4d5bfbd587b3607b38f4e5f961b1088d3d0ea72ad7aa815782a682af1dc6b3 size 18770 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset index 758ccac..562276e 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_BC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb17299f06971487075cbfd621efff802af76552195be28183c91246ce3e043c +oid sha256:8d34f4385dbd481141bd5a7a41438706ed96342c1584a913bf7308cf5cc024d8 size 62746 diff --git a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset index 8f03901..b7464bd 100644 --- a/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset +++ b/Game/GigLine/Content/Hollywood/Textures/T_Stucco_R.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27aa3427f615b6ffa23fcb1ed5b0b2fc4e1f3637c70abe5dc7ddc781ee866fb7 +oid sha256:cb9cb4aa6074465957dd3835fa573f7aaf5e97b4c6e1e1dd0865a87dd447bda7 size 17541 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset index 7983e88..7d97515 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSign.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14c74564ba7e3e028a17f3770ade7dea439f5e29deb1d2ad5e96c428881b3533 +oid sha256:3ba3d7ac8b9ba43c83f7bca84b01d2bdd802a6e49d721a03c91672bd669843d1 size 72330 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset index 7bf9bdb..6a50acd 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignBracket.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f6f9b94a06ebbfeccc0b0c7e811bd2baaa2a7b6250b0ad1e67e53ab8c035a1d +oid sha256:551224fcdf597a1cb4c4ae12fd2f5c5d0d67800d16f0ff16d3f8723c745e26ff size 72466 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset index b8ce48c..b51f07f 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_BladeSignFrame.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2156846041cb844c6b354ee1ea1a9a95d4ce9f8148c30fc7e56b5ac1f5d81abc +oid sha256:0ebdf21e52c475de97950ac636133ef3407dae6a32adef1d70317aa9484be5dc size 72406 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset index d3dba06..7230223 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeCanopy.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:015a8788eef7f38bb9d1a716e3ecf22327c974944a084f60a4fe374ecab8b66e +oid sha256:696f90c246d4ce2cdd526c11cfce9082bc6c2ae215dc5c20216ad488eeac2aa1 size 72370 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset index dae5c93..e29c8ff 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeFascia.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ba9af2d666f01fb37e6810c614c6c27f9d1570e8196a1231396204b7a2c1d77 +oid sha256:e3d573014075d06f373e11b07cd16ca6f6d27e52067119929c274ca3b0eaa9b8 size 72438 diff --git a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset index 8d30403..bfebe19 100644 --- a/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset +++ b/Game/GigLine/Content/Hollywood/Theatre/SM_Trim_MarqueeSoffit.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1653fffd4f73e3d204947ea056bdd933b83d691f52fd382bec08cf5c511f10a4 +oid sha256:123283642a811e9ba202127a3da4b30e7b4b6dc2c2629a24497f4e8eaf352893 size 72413 diff --git a/Game/GigLine/Content/Test/ReviewMap.umap b/Game/GigLine/Content/Test/ReviewMap.umap index 365dc8a..dc7c1bf 100644 --- a/Game/GigLine/Content/Test/ReviewMap.umap +++ b/Game/GigLine/Content/Test/ReviewMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:061edd655d9f9f2929660eb6a8715bdb0f8f98b1aebc776f23c0ccd5ad9bf332 +oid sha256:58ee12bbe157aa0bdfd30fcf43f4a6d80fac644790ee79ec408a384bb9b9dcfd size 30542 diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp index d651b1c..6e355de 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp @@ -16,7 +16,7 @@ #include "TimerManager.h" #include "Containers/Ticker.h" #include "Camera/PlayerCameraManager.h" -#include "Engine/BlockingVolume.h" +#include "Engine/StaticMeshActor.h" #include "UnrealClient.h" #include "HAL/IConsoleManager.h" #include "Kismet/GameplayStatics.h" @@ -385,16 +385,28 @@ void AGigLineCharacter::UpdateCameraCollisionTest(float DeltaSeconds) // A 6 m cube, 1.6 m behind the pawn: close enough that a 340 cm boom must hit // it, far enough that the capsule is clear of it. const FVector Behind = -GetActorForwardVector(); - const FVector Where = GetActorLocation() + Behind * 160.f - FVector(0.f, 0.f, 90.f); + const FVector Where = GetActorLocation() + Behind * 400.f - FVector(0.f, 0.f, 90.f); FActorSpawnParameters Params; Params.ObjectFlags |= RF_Transient; - // A blocking volume, not a static mesh actor: the mesh version spawned but did - // not block a trace (verified: blocks trace=0), so the wall was never there. - CameraTestWall = World->SpawnActor( - ABlockingVolume::StaticClass(), Where, FRotator::ZeroRotator, Params); + // Static mesh cube, 2 m thick, 4 m behind. The earlier failures had the pawn + // INSIDE a 6 m cube centred 1.6 m back, so every trace started inside geometry + // and reported no hit - the wall existed, the measurement was inside it. + // Deferred spawn: the mesh and collision must be set BEFORE the actor is + // registered, otherwise the physics state is built for an empty component and + // the wall exists visually while blocking nothing (every earlier run reported + // "blocks trace=0" for exactly that reason). + CameraTestWall = World->SpawnActorDeferred( + AStaticMeshActor::StaticClass(), FTransform(Where), this, nullptr, + ESpawnActorCollisionHandlingMethod::AlwaysSpawn); if (CameraTestWall) { - CameraTestWall->SetActorScale3D(FVector(6.f, 6.f, 6.f)); + UStaticMeshComponent* Comp = CameraTestWall->GetStaticMeshComponent(); + Comp->SetMobility(EComponentMobility::Movable); + Comp->SetStaticMesh(LoadObject( + nullptr, TEXT("/Engine/BasicShapes/Cube.Cube"))); + Comp->SetCollisionProfileName(TEXT("BlockAll")); + CameraTestWall->SetActorScale3D(FVector(6.f, 2.f, 6.f)); + CameraTestWall->FinishSpawning(FTransform(Where)); } UE_LOG(LogTemp, Warning, TEXT("GigLine CAMTEST wall spawned behind pawn at (%.0f,%.0f,%.0f)"), Where.X, Where.Y, Where.Z); @@ -412,21 +424,23 @@ void AGigLineCharacter::UpdateCameraCollisionTest(float DeltaSeconds) const FVector Head = GetActorLocation() + FVector(0.f, 0.f, 60.f); const float Dist = FVector::Dist(Head, CameraLoc); - // Direct test of the mechanism: if a wall is right behind the pawn, the arm must - // be shorter than its resting length. Nothing else can move it. - const bool bShortened = (Rest - Arm) > 40.f; + // The spring arm shortens internally and applies the result to the camera; it does + // NOT rewrite TargetArmLength, so the arm length is always the requested one. The + // measurement that matters is the camera's distance from the head. + const bool bShortened = (Rest - Dist) > 40.f; // Prove the spawned wall has collision at all before blaming the arm. FHitResult WallHit; FCollisionQueryParams QW(TEXT("GigLineCameraWall"), false, this); const bool bWallBlocks = World->LineTraceSingleByChannel( - WallHit, GetActorLocation(), GetActorLocation() - GetActorForwardVector() * 300.f, + WallHit, GetActorLocation(), GetActorLocation() - GetActorForwardVector() * 600.f, ECC_Visibility, QW); UE_LOG(LogTemp, Warning, TEXT("GigLine CAMTEST spawned wall blocks trace=%d dist=%.0f"), bWallBlocks ? 1 : 0, bWallBlocks ? WallHit.Distance : -1.f); UE_LOG(LogTemp, Warning, - TEXT("GigLine CAMTEST wall behind pawn: arm %.0f of %.0f cm, cam %.0f cm from head -> %s"), - Arm, Rest, Dist, bShortened ? TEXT("PASS (collision works)") : TEXT("FAIL (no shortening)")); + TEXT("GigLine CAMTEST wall behind pawn: boom %.0f cm (requested %.0f, shortened %.0f) -> %s"), + Dist, Rest, Rest - Dist, + bShortened ? TEXT("PASS (boom shortened)") : TEXT("FAIL (no shortening)")); FScreenshotRequest::RequestScreenshot(TEXT("review-demo-camera-collision"), true, false); if (CameraTestWall) diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.h b/Game/GigLine/Source/GigLine/GigLineCharacter.h index ddfcc72..b9582ac 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.h +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.h @@ -208,7 +208,7 @@ protected: void UpdateCameraCollisionTest(float DeltaSeconds); UPROPERTY() - TObjectPtr CameraTestWall; + TObjectPtr CameraTestWall; FVector LastWallNormal = FVector::ZeroVector; bool bCameraTest = false; diff --git a/Screenshots/demo/review-demo-crossing.png b/Screenshots/demo/review-demo-crossing.png index 6b40fde..1f29922 100644 --- a/Screenshots/demo/review-demo-crossing.png +++ b/Screenshots/demo/review-demo-crossing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:370bf57262d3503bf1497b984a6029a8484aff8fbe9a21cd813651a9989bfcf3 -size 3055230 +oid sha256:731f2faa63389dbee36a8358ff41abac69a74e849213216a0a83abe6cf85c402 +size 3057777 diff --git a/Screenshots/demo/review-demo-north-buildings.png b/Screenshots/demo/review-demo-north-buildings.png index e3887d5..b2fa858 100644 --- a/Screenshots/demo/review-demo-north-buildings.png +++ b/Screenshots/demo/review-demo-north-buildings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1cf9b6267603d9fdc555ee6de7588bfa08c256bef7de649ba97872f09b90508 -size 2613575 +oid sha256:74f38c5b32ec809021b2995f0d5bf1b228353094a686e3a448eda11ae9615ce9 +size 2614266 diff --git a/Screenshots/demo/review-demo-retail-frontage.png b/Screenshots/demo/review-demo-retail-frontage.png index 66537a2..646d086 100644 --- a/Screenshots/demo/review-demo-retail-frontage.png +++ b/Screenshots/demo/review-demo-retail-frontage.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e3e2f56637d3c3806b413d53fadb56ca651c18539386043c577934348c9f5cf -size 2487967 +oid sha256:05cda3f01c86933282fc3fbeacde75913386b628ee8fa565a11bbd6c4701af6e +size 2487940 diff --git a/Screenshots/demo/review-demo-spawn-view.png b/Screenshots/demo/review-demo-spawn-view.png index b3aa03f..bae3463 100644 --- a/Screenshots/demo/review-demo-spawn-view.png +++ b/Screenshots/demo/review-demo-spawn-view.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e149f48ca3d3b227dcc10bc7b2a1bec0c7c2e656f0d04448868739783812cdfd -size 2884922 +oid sha256:acad766685069d61f9bf0671d040bc0983d36d0f3f904f50c8988aaf00db90f1 +size 2882484 diff --git a/Screenshots/demo/review-demo-street-level.png b/Screenshots/demo/review-demo-street-level.png index d9131eb..3668af9 100644 --- a/Screenshots/demo/review-demo-street-level.png +++ b/Screenshots/demo/review-demo-street-level.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e09677cce3997dbdd52f77d7be76262961673ecc2f795b0c4ee09866f37adc4d -size 2795082 +oid sha256:6d94e22d0a86a36b9f6ce2192dc9ba748d57776def5de615f988e5469983f67e +size 2798239 diff --git a/Screenshots/demo/review-demo-third-person-front.png b/Screenshots/demo/review-demo-third-person-front.png index 80782fe..df5c0dd 100644 --- a/Screenshots/demo/review-demo-third-person-front.png +++ b/Screenshots/demo/review-demo-third-person-front.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4949405d583c4bbabcb8b6d00b297a0a5673d1e9c28034a455eced56e024410 -size 2791255 +oid sha256:147af27e6dac9ab1bb5fc366f5fece894c3f845da67a00d0682b576900953ed0 +size 2794138 diff --git a/Screenshots/demo/review-demo-third-person-walk.png b/Screenshots/demo/review-demo-third-person-walk.png index 0652d66..306ed77 100644 --- a/Screenshots/demo/review-demo-third-person-walk.png +++ b/Screenshots/demo/review-demo-third-person-walk.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55dd3e2f2507d19ccf12112ca1aad6f600b52aa34ac88c01df311c4651b7d8ed -size 2172564 +oid sha256:f9588e860a668a8b0e6fbe2b699f8bb5614672653a9738235d0e8b916a9a0939 +size 2173213 diff --git a/Screenshots/demo/review-demo-third-person.png b/Screenshots/demo/review-demo-third-person.png index 56b756c..9c0b403 100644 --- a/Screenshots/demo/review-demo-third-person.png +++ b/Screenshots/demo/review-demo-third-person.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26eb754c9df2ad2cc8a3703774b5d59e8f916705a064718a1dd972de2b9903ce -size 2974983 +oid sha256:5aa4c6b6cabdc38fdaef8d1aaebb897eefae01f4001ff873fdadd57c3db968bd +size 2974710 diff --git a/TODO.md b/TODO.md index e05af53..c4d308b 100644 --- a/TODO.md +++ b/TODO.md @@ -9,17 +9,6 @@ Actionable task list for this project. This file is the single, central place fo - When all steps in a version section are done: write a `CHANGELOG.md` entry, then remove the version section from this file. - Release gate: a version is tagged only on the owner's explicit go-ahead once its release checks pass, not automatically when the checkboxes close. -## 0.2.3 — Camera clipping fixes (patch) - -### Patch tasks - active - session-5598ce7e-27e3-4c64-bb49-c04a4a31ee58 (branch `camera-clip-0.2.3`) - -- [A] Fix remaining camera clipping cases in `0.2.x`. **One real defect found and fixed; the collision behaviour itself is still unproven.** - - **Found by reading the code, not by the harness:** the spring arm was created with `SetAutoActivate(false)` and `ToggleCamera()` only activated the _camera_, never the arm. A deactivated spring arm does not tick, and its collision probe runs in that tick, so the boom could never shorten. `ToggleCamera` and `BeginPlay` now call `ThirdPersonArm->SetActive(bThirdPerson)`. - - **The measurement that was supposed to prove it does not work yet.** `-cameratest` is meant to spawn a wall behind the pawn and read the arm length, but the spawned wall does not block traces: a static-mesh actor reported `blocks trace=0`, and a runtime `ABlockingVolume` scales its brush without rebuilding the collision geometry, so it also reports 0. Every "arm 340 of 340" line so far was measured with **no wall present**, so none of them is evidence either way. - - **Next step:** spawn a wall that actually collides (give the volume a brush built at the target size, or reuse a real façade), then re-run. Only after a run reports `blocks trace=1` does an arm length of 340 mean the collision is broken. - - **Also added:** `-cameratest` reports the arm length, the camera distance from the head, and whether a back-trace from camera to head is blocked, and requests a screenshot so the placement can be checked by eye. -- [ ] Tag `v0.2.3` and write `CHANGELOG.md` entry; then remove this section. - ## 0.3.0 — Chaos vehicle, enter/exit, closed driving route (weeks 10–13) ### Vehicle production