gameplay: walkable demo map, reset and pause

Tools/ue_build_demo_map.py builds /Game/Hollywood/DemoMap: sun, sky, the hero
facade on the building line, six site meshes imported with collision, and a
player start on the north sidewalk. Tools/ue_import_site.py imports the site
FBXs; terrain, road and sidewalks use complex-as-simple so a simple hull cannot
ramp over the kerbs. Art/blender/site/export_site_fbx.py exports one FBX per
object so each can carry its own collision.

Reset (R) and pause (Escape) join the character and the input context: reset
restores the spawn transform and clears momentum, pause is a real game pause that
releases the cursor. DefaultInput.ini captures the mouse on launch.

Three placement defects found by measuring the imported actors rather than
trusting the plan: the spawn was on the wrong sidewalk because the FBX flip
inverts north, a mirror I added to the exporter double-corrected an already
correct sidewalk, and the street meshes only carry part of the north walk - the
rest is terrain.
This commit is contained in:
zwitschi committed 2026-11-07 13:17:24 +01:00
1 parent c9278378ee
commit cf392a02f7
30 files changed
+519 -9

No files matched your search

@@ -7,6 +7,7 @@
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/PlayerController.h"
#include "InputAction.h"
#include "GameFramework/PlayerController.h"
#include "InputMappingContext.h"
AGigLineCharacter::AGigLineCharacter()
@@ -57,6 +58,8 @@ void AGigLineCharacter::LoadInputAssets()
StrafeLeftAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_StrafeLeft.IA_StrafeLeft")));
LookAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Look.IA_Look")));
JumpAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Jump.IA_Jump")));
ResetAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Reset.IA_Reset")));
PauseAction = Cast<UInputAction>(LoadSoft(TEXT("/Game/Input/IA_Pause.IA_Pause")));
if (!MappingContext || !MoveAction || !MoveBackAction || !StrafeAction || !StrafeLeftAction
|| !LookAction)
@@ -77,6 +80,9 @@ void AGigLineCharacter::BeginPlay()
LoadInputAssets();
StartLocation = GetActorLocation();
StartRotation = GetActorRotation();
// Push the mapping context for this local player.
if (MappingContext)
{
@@ -124,6 +130,14 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo
{
Input->BindAction(LookAction, ETriggerEvent::Triggered, this, &AGigLineCharacter::Look);
}
if (ResetAction)
{
Input->BindAction(ResetAction, ETriggerEvent::Started, this, &AGigLineCharacter::ResetToStart);
}
if (PauseAction)
{
Input->BindAction(PauseAction, ETriggerEvent::Started, this, &AGigLineCharacter::TogglePause);
}
if (JumpAction)
{
Input->BindAction(JumpAction, ETriggerEvent::Started, this, &AGigLineCharacter::StartJump);
@@ -199,6 +213,51 @@ void AGigLineCharacter::Look(const FInputActionValue& Value)
AddControllerPitchInput(Axis.Y * LookSensitivity * (bInvertLookY ? -1.0f : 1.0f));
}
void AGigLineCharacter::ResetToStart()
{
// Reset is a level-restart in miniature: put the body back where it spawned,
// clear momentum so a fall does not carry over, and hand control back to the
// player if the pause menu was open.
if (UWorld* World = GetWorld())
{
SetActorLocationAndRotation(StartLocation, StartRotation, false, nullptr, ETeleportType::TeleportPhysics);
GetCharacterMovement()->StopMovementImmediately();
if (AController* Controller = GetController())
{
Controller->SetControlRotation(StartRotation);
}
UE_LOG(LogTemp, Log, TEXT("GigLine: reset to start"));
}
}
void AGigLineCharacter::TogglePause()
{
// Pause is a game-pause, not just an input mute: the world stops so physics and
// timers do not keep running behind the menu.
UWorld* World = GetWorld();
if (!World)
{
return;
}
bPaused = !bPaused;
if (APlayerController* PC = Cast<APlayerController>(GetController()))
{
PC->SetPause(bPaused);
PC->bShowMouseCursor = bPaused;
// The two input-mode structs are unrelated types, so branch rather than
// using a conditional expression.
if (bPaused)
{
PC->SetInputMode(FInputModeUIOnly());
}
else
{
PC->SetInputMode(FInputModeGameOnly());
}
}
UE_LOG(LogTemp, Log, TEXT("GigLine: paused=%s"), bPaused ? TEXT("true") : TEXT("false"));
}
void AGigLineCharacter::StartJump()
{
Jump();