Make top-down the default camera mode and bind camera toggle to C.

Matches Fantasy Life style gameplay; first-person remains available as an optional mode.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pixachux 2026-07-22 18:55:07 +02:00
parent 0ced5b17ef
commit c0d9450d30
5 changed files with 37 additions and 13 deletions

View File

@ -4,7 +4,7 @@ A life-simulation action RPG remake inspired by Fantasy Life, built in **Unreal
## Vision
- First-person gameplay (primary) with classic top-down camera toggle
- First-person gameplay with classic top-down camera as the default (toggle with C)
- Mouse, keyboard, and gamepad support via Enhanced Input
- Co-op multiplayer (listen server first, dedicated server hosting)
- Optional hardware ray tracing and scalability presets
@ -31,7 +31,7 @@ A life-simulation action RPG remake inspired by Fantasy Life, built in **Unreal
| Jump | Space | A / Cross |
| Interact / Craft | E | X / Square |
| Attack | LMB | RT |
| Toggle camera (FP / Top-Down) | V | Menu |
| Toggle camera (Top-Down / FP) | C | Menu |
| Quick save | I | Y / Triangle |
| Pause | Esc | Start |

View File

@ -32,6 +32,7 @@ AVocationCharacter::AVocationCharacter()
FirstPersonCamera->SetupAttachment(GetCapsuleComponent());
FirstPersonCamera->SetRelativeLocation(FVector(0.f, 0.f, 64.f));
FirstPersonCamera->bUsePawnControlRotation = true;
FirstPersonCamera->SetActive(false);
TopDownSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("TopDownSpringArm"));
TopDownSpringArm->SetupAttachment(RootComponent);
@ -42,7 +43,7 @@ AVocationCharacter::AVocationCharacter()
TopDownSpringArm->bInheritPitch = false;
TopDownSpringArm->bInheritRoll = false;
TopDownSpringArm->bInheritYaw = false;
TopDownSpringArm->SetActive(false);
TopDownSpringArm->SetActive(true);
TopDownCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("TopDownCamera"));
TopDownCamera->SetupAttachment(TopDownSpringArm, USpringArmComponent::SocketName);
@ -54,21 +55,25 @@ AVocationCharacter::AVocationCharacter()
if (USkeletalMeshComponent* MeshComp = GetMesh())
{
MeshComp->SetOnlyOwnerSee(true);
MeshComp->SetupAttachment(FirstPersonCamera);
MeshComp->SetRelativeLocation(FVector(25.f, 0.f, -90.f));
// Visible body for top-down default; FP mode reattaches in ApplyCameraModeVisuals.
MeshComp->SetOnlyOwnerSee(false);
MeshComp->SetOwnerNoSee(false);
MeshComp->SetupAttachment(GetCapsuleComponent());
MeshComp->SetRelativeLocation(FVector(0.f, 0.f, -90.f));
MeshComp->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));
}
if (UCharacterMovementComponent* Movement = GetCharacterMovement())
{
Movement->bOrientRotationToMovement = false;
Movement->bOrientRotationToMovement = true;
Movement->RotationRate = FRotator(0.f, 540.f, 0.f);
Movement->JumpZVelocity = 500.f;
Movement->AirControl = 0.35f;
Movement->MaxWalkSpeed = 500.f;
}
bUseControllerRotationPitch = true;
// Top-down is the default camera mode (Fantasy Life style).
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = true;
bUseControllerRotationRoll = false;
}
@ -304,6 +309,25 @@ void AVocationCharacter::ApplyCameraModeVisuals()
TopDownSpringArm->SetActive(!bFirstPerson);
TopDownCamera->SetActive(!bFirstPerson);
if (USkeletalMeshComponent* MeshComp = GetMesh())
{
if (bFirstPerson)
{
MeshComp->SetOnlyOwnerSee(true);
MeshComp->SetupAttachment(FirstPersonCamera);
MeshComp->SetRelativeLocation(FVector(25.f, 0.f, -90.f));
MeshComp->SetRelativeRotation(FRotator::ZeroRotator);
}
else
{
MeshComp->SetOnlyOwnerSee(false);
MeshComp->SetOwnerNoSee(false);
MeshComp->SetupAttachment(GetCapsuleComponent());
MeshComp->SetRelativeLocation(FVector(0.f, 0.f, -90.f));
MeshComp->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));
}
}
bUseControllerRotationPitch = bFirstPerson;
if (UCharacterMovementComponent* Movement = GetCharacterMovement())
{

View File

@ -76,7 +76,7 @@ void AVocationHUD::DrawMainHUD()
}
else
{
const FString Controls = TEXT("WASD Move | Mouse Look | V Camera | E Interact/Craft | LMB Attack | I Save | Esc Pause");
const FString Controls = TEXT("WASD Move | Mouse Look | C Camera | E Interact/Craft | LMB Attack | I Save | Esc Pause");
Canvas->DrawColor = FColor(200, 200, 200);
Canvas->DrawText(GEngine->GetSmallFont(), Controls, 40.f * Scale, Canvas->ClipY - 60.f * Scale, Scale, Scale);
}

View File

@ -29,7 +29,7 @@ void AVocationPlayerController::BeginPlay()
LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
Subsystem->AddMappingContext(SharedMappingContext, SharedMappingPriority);
Subsystem->AddMappingContext(FirstPersonMappingContext, ModeMappingPriority);
Subsystem->AddMappingContext(TopDownMappingContext, ModeMappingPriority);
}
}
@ -175,7 +175,7 @@ void AVocationPlayerController::CreateRuntimeInputAssets()
SharedMappingContext->MapKey(JumpAction, EKeys::SpaceBar);
SharedMappingContext->MapKey(InteractAction, EKeys::E);
SharedMappingContext->MapKey(AttackAction, EKeys::LeftMouseButton);
SharedMappingContext->MapKey(ToggleCameraAction, EKeys::V);
SharedMappingContext->MapKey(ToggleCameraAction, EKeys::C);
SharedMappingContext->MapKey(InventoryAction, EKeys::I);
SharedMappingContext->MapKey(PauseAction, EKeys::Escape);
SharedMappingContext->MapKey(JumpAction, EKeys::Gamepad_FaceButton_Bottom);

View File

@ -110,10 +110,10 @@ protected:
void UpdateInteractionPrompt();
UPROPERTY(ReplicatedUsing = OnRep_CameraMode, BlueprintReadOnly, Category = "VocationLife|Camera")
EVocationCameraMode CurrentCameraMode = EVocationCameraMode::FirstPerson;
EVocationCameraMode CurrentCameraMode = EVocationCameraMode::TopDown;
UPROPERTY(ReplicatedUsing = OnRep_CameraMode)
EVocationCameraMode TargetCameraMode = EVocationCameraMode::FirstPerson;
EVocationCameraMode TargetCameraMode = EVocationCameraMode::TopDown;
UPROPERTY()
float CameraBlendAlpha = 1.f;