Files
gig-line/Game/GigLine/Source/GigLine/GigLineShotActor.h
T
zwitschi eeeabe56b1 feat(review): deterministic walking capture, and the defect it exposed
The shot actor can now photograph a moving pawn: a per-shot ShotFollow flag re-places
the camera every frame at a fixed offset in the pawn's frame, and the walk is driven
per shot (SetReviewWalk) with the pawn returned to spawn after a follow shot, so the
fixed-camera shots later in the list still find it in place. With that, the capture
stopped depending on where a 4.5 m/s pawn happened to be.

Two further bugs fixed on the way:
- the engine's screenshot folder is cleared before each run. The collect step copies
  every review-*.png it finds in Saved/, so a shot retired from the camera list kept
  reappearing with byte-identical content from an older run - which is how a walk shot
  that was never captured looked like a captured one.
- UpdateBodyAnimation no longer calls SetAnimation every frame. Re-setting the same
  clip restarts it, which freezes the body on frame 0.

What the deterministic capture then exposed: the walk clip is applied but does not
advance - every walking frame shows the same pose (arms forward, legs together). The
stride shot is retired again rather than shipped, and TODO.md names the next step:
log GetCurrentAssetTime across frames and check the imported PlayRate. The harness
stays wired for when the clip plays.

Shots 6 and 7 were re-rendered after these changes and still pass; 7/7 verify.
2026-11-09 12:11:24 +01:00

86 lines
2.8 KiB
C++

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GigLineShotActor.generated.h"
/**
* Dev-only helper for the visual-review screenshot pipeline.
*
* Placed in the review map by Tools/ue_build_review_map.py. In -game mode it
* walks a fixed list of camera setups, sets the view target to a spawned
* CameraActor for each, requests a screenshot via FScreenshotRequest, and
* quits the game when all shots are written. Lives in test content only;
* the shipped game never references this map.
*/
UCLASS()
class GIGLINE_API AGigLineShotActor : public AActor
{
GENERATED_BODY()
public:
AGigLineShotActor();
virtual void BeginPlay() override;
virtual void Tick(float DeltaSeconds) override;
/** World-space locations for each shot camera. */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FVector> ShotLocations;
/** Rotations for each shot camera (same index as ShotLocations). */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FRotator> ShotRotations;
/** File name fragment for each shot (same index as ShotLocations). */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FString> ShotNames;
/**
* Optional per-shot field of view (degrees), same index as ShotLocations.
* Zero or missing entries keep the engine default (90 deg). Elevation shots
* taken from across the street need a long lens: a level camera 45 m out with
* the default FOV leaves the building small in frame, and tilting it up to fill
* the frame is what breaks the verticals.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<float> ShotFieldOfView;
/** Seconds to let the view target settle before the capture. Resolution comes from the runner's -ResX/-ResY. */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float SettleSeconds = 0.35f;
/**
* Optional per-shot "follow the player" flag, same index as ShotLocations.
*
* A fixed camera cannot photograph a walking pawn: at 4.5 m/s it covers the
* distance between two captures, so three attempts framed it at a different
* distance each run and two missed it entirely. With this flag the camera is
* re-placed every frame at a fixed offset in the pawn's frame, which is what
* makes a stride shot deterministic.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<bool> ShotFollow;
/** Quit the game after the last shot (disable to keep the game open). */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bQuitWhenDone = true;
private:
void NextShot();
/** Re-places the active camera at the follow offset behind the player pawn. */
void UpdateFollowCamera();
UPROPERTY()
TObjectPtr<class APlayerController> PlayerController;
UPROPERTY()
TObjectPtr<class ACameraActor> CurrentCamera;
int32 CurrentShot = 0;
float SettleCountdown = 0.f;
bool bWaitingForSettle = false;
bool bWaitingForScreenshot = false;
};