From cd8de0f906e4835dfaee70ef258b4e7d32bb9164 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 15 May 2026 10:44:02 -0700 Subject: [PATCH] Implement camera perspective toggle --- AGRARIAN_DEVELOPMENT_ROADMAP.md | 4 +- Content/Input/Actions/IA_ToggleCamera.uasset | Bin 0 -> 1190 bytes Content/Input/IMC_Default.uasset | Bin 129 -> 9455 bytes .../Blueprints/BP_ThirdPersonCharacter.uasset | Bin 130 -> 51566 bytes Scripts/setup_camera_toggle_input.py | 88 ++++++++++++++++++ Scripts/verify_camera_toggle_input.py | 44 +++++++++ Source/AgrarianGame/AgrarianGameCharacter.cpp | 47 +++++++++- Source/AgrarianGame/AgrarianGameCharacter.h | 27 +++++- 8 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 Content/Input/Actions/IA_ToggleCamera.uasset create mode 100644 Scripts/setup_camera_toggle_input.py create mode 100644 Scripts/verify_camera_toggle_input.py diff --git a/AGRARIAN_DEVELOPMENT_ROADMAP.md b/AGRARIAN_DEVELOPMENT_ROADMAP.md index 436485c..2042922 100644 --- a/AGRARIAN_DEVELOPMENT_ROADMAP.md +++ b/AGRARIAN_DEVELOPMENT_ROADMAP.md @@ -369,7 +369,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe - [x] Create player controller. - [x] Create camera setup. - [x] Decide first-person, third-person, or hybrid camera. Decision: hybrid camera with third person as default and optional first-person toggle. -- [ ] Implement first/third-person camera toggle. +- [x] Implement first/third-person camera toggle. - [x] Implement movement. - [ ] Implement sprinting. - [ ] Define real-world baseline walking speed. @@ -1465,4 +1465,4 @@ Earliest incomplete foundation items: Immediate next item: -- [ ] Implement first/third-person camera toggle. +- [ ] Implement sprinting. diff --git a/Content/Input/Actions/IA_ToggleCamera.uasset b/Content/Input/Actions/IA_ToggleCamera.uasset new file mode 100644 index 0000000000000000000000000000000000000000..0f2f30de35131d41acf9a28ea78553764b94b3dd GIT binary patch literal 1190 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 literal 129 zcmWN?%MrpL5CG77s-OYGe__)NuprE+WL(U_>h)dTRo^`3OZ2r)KBT;lb$c}4|MySc z%X~^cYmt}U<{-g684rgJ7%b}OovAu0K%Zj9rc^{*kpd}z6e*VmE|blHjI9wACIXYm MZ9KkJMDTk10hlc&>;M1& diff --git a/Content/ThirdPerson/Blueprints/BP_ThirdPersonCharacter.uasset b/Content/ThirdPerson/Blueprints/BP_ThirdPersonCharacter.uasset index ebc0850358975269e1410e41520e6a5e0088d0e7..bdd5c225759b3b50e3d1bd62c2016e2916b760dd 100644 GIT binary patch literal 51566 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 NMlt)Br8m|`@dN6SCr {key_name}") + return + + key = unreal.Key() + key.set_editor_property("key_name", key_name) + + mapping_data = context.get_editor_property("default_key_mappings") + mappings = list(mapping_data.get_editor_property("mappings")) + new_mapping = unreal.EnhancedActionKeyMapping() + new_mapping.set_editor_property("action", action) + new_mapping.set_editor_property("key", key) + mappings.append(new_mapping) + mapping_data.set_editor_property("mappings", mappings) + context.set_editor_property("default_key_mappings", mapping_data) + + unreal.log(f"Added mapping: {action.get_name()} -> {key_name}") + + +def main(): + toggle_action = create_input_action("/Game/Input/Actions/IA_ToggleCamera") + set_boolean_value_type(toggle_action) + + context = load("/Game/Input/IMC_Default") + map_key(context, toggle_action, "V") + map_key(context, toggle_action, "Gamepad_RightThumbstick") + unreal.EditorAssetLibrary.save_loaded_asset(context) + + character_bp = load("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter") + character_cdo = unreal.get_default_object(character_bp.generated_class()) + character_cdo.set_editor_property("ToggleCameraAction", toggle_action) + unreal.EditorAssetLibrary.save_loaded_asset(character_bp) + + unreal.log("Agrarian camera toggle input setup complete.") + + +main() diff --git a/Scripts/verify_camera_toggle_input.py b/Scripts/verify_camera_toggle_input.py new file mode 100644 index 0000000..aa6e4b4 --- /dev/null +++ b/Scripts/verify_camera_toggle_input.py @@ -0,0 +1,44 @@ +import unreal + + +def load(path): + asset = unreal.EditorAssetLibrary.load_asset(path) + if not asset: + raise RuntimeError(f"Could not load {path}") + return asset + + +def mapping_found(context, action, key_name): + mapping_data = context.get_editor_property("default_key_mappings") + for mapping in list(mapping_data.get_editor_property("mappings")): + mapping_key = mapping.get_editor_property("key") + if ( + mapping.get_editor_property("action") == action + and str(mapping_key.get_editor_property("key_name")) == key_name + ): + return True + return False + + +def main(): + action = load("/Game/Input/Actions/IA_ToggleCamera") + context = load("/Game/Input/IMC_Default") + character_bp = load("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter") + character_cdo = unreal.get_default_object(character_bp.generated_class()) + + missing = [] + for key_name in ["V", "Gamepad_RightThumbstick"]: + if not mapping_found(context, action, key_name): + missing.append(f"missing mapping {key_name}") + + assigned_action = character_cdo.get_editor_property("ToggleCameraAction") + if assigned_action != action: + missing.append("BP_ThirdPersonCharacter ToggleCameraAction is not IA_ToggleCamera") + + if missing: + raise RuntimeError("Camera toggle input verification failed: " + "; ".join(missing)) + + unreal.log("Agrarian camera toggle input verification complete.") + + +main() diff --git a/Source/AgrarianGame/AgrarianGameCharacter.cpp b/Source/AgrarianGame/AgrarianGameCharacter.cpp index 835b6cc..1d423a8 100644 --- a/Source/AgrarianGame/AgrarianGameCharacter.cpp +++ b/Source/AgrarianGame/AgrarianGameCharacter.cpp @@ -43,7 +43,7 @@ AAgrarianGameCharacter::AAgrarianGameCharacter() // Create a camera boom (pulls in towards the player if there is a collision) CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom")); CameraBoom->SetupAttachment(RootComponent); - CameraBoom->TargetArmLength = 400.0f; + CameraBoom->TargetArmLength = ThirdPersonCameraDistance; CameraBoom->bUsePawnControlRotation = true; // Create a follow camera @@ -80,6 +80,11 @@ void AAgrarianGameCharacter::SetupPlayerInputComponent(UInputComponent* PlayerIn { EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &AAgrarianGameCharacter::Interact); } + + if (ToggleCameraAction) + { + EnhancedInputComponent->BindAction(ToggleCameraAction, ETriggerEvent::Started, this, &AAgrarianGameCharacter::ToggleCameraPerspective); + } } else { @@ -110,6 +115,46 @@ void AAgrarianGameCharacter::Interact() TryInteract(); } +void AAgrarianGameCharacter::ToggleCameraPerspective() +{ + SetFirstPersonCamera(!bFirstPersonCamera); +} + +void AAgrarianGameCharacter::SetFirstPersonCamera(bool bEnableFirstPerson) +{ + bFirstPersonCamera = bEnableFirstPerson; + + if (!CameraBoom || !FollowCamera) + { + return; + } + + if (bFirstPersonCamera) + { + CameraBoom->TargetArmLength = 0.0f; + CameraBoom->SocketOffset = FirstPersonCameraOffset; + CameraBoom->bDoCollisionTest = false; + FollowCamera->bUsePawnControlRotation = false; + + if (GetMesh()) + { + GetMesh()->SetOwnerNoSee(true); + } + } + else + { + CameraBoom->TargetArmLength = ThirdPersonCameraDistance; + CameraBoom->SocketOffset = FVector::ZeroVector; + CameraBoom->bDoCollisionTest = true; + FollowCamera->bUsePawnControlRotation = false; + + if (GetMesh()) + { + GetMesh()->SetOwnerNoSee(false); + } + } +} + void AAgrarianGameCharacter::DoMove(float Right, float Forward) { if (GetController() != nullptr) diff --git a/Source/AgrarianGame/AgrarianGameCharacter.h b/Source/AgrarianGame/AgrarianGameCharacter.h index 84105c5..346d3cb 100644 --- a/Source/AgrarianGame/AgrarianGameCharacter.h +++ b/Source/AgrarianGame/AgrarianGameCharacter.h @@ -73,10 +73,26 @@ protected: UPROPERTY(EditAnywhere, Category="Input") UInputAction* InteractAction; + /** Toggle between third-person and first-person camera views. */ + UPROPERTY(EditAnywhere, Category="Input") + UInputAction* ToggleCameraAction; + /** How far the player can interact with MVP objects. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Interaction", meta = (ClampMin = "100")) float InteractionDistance = 450.0f; + /** Third-person spring arm distance used when returning from first person. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Camera", meta = (ClampMin = "0")) + float ThirdPersonCameraDistance = 400.0f; + + /** Local first-person camera offset relative to the capsule root. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Agrarian|Camera") + FVector FirstPersonCameraOffset = FVector(0.0f, 0.0f, 72.0f); + + /** True when this local character is using the optional first-person view. */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Agrarian|Camera", meta = (AllowPrivateAccess = "true")) + bool bFirstPersonCamera = false; + public: /** Constructor */ @@ -98,6 +114,12 @@ protected: /** Called for interaction input */ void Interact(); + /** Called for camera perspective toggle input */ + void ToggleCameraPerspective(); + + /** Applies local camera presentation state. */ + void SetFirstPersonCamera(bool bEnableFirstPerson); + public: /** Handles move inputs from either controls or UI interfaces */ @@ -120,6 +142,10 @@ public: UFUNCTION(BlueprintCallable, Category="Agrarian|Interaction") virtual void TryInteract(); + /** Returns true when this local character is using first-person camera presentation. */ + UFUNCTION(BlueprintPure, Category="Agrarian|Camera") + bool IsFirstPersonCamera() const { return bFirstPersonCamera; } + /** Server-authoritative interaction entry point. */ UFUNCTION(Server, Reliable) void ServerInteract(AActor* TargetActor); @@ -144,4 +170,3 @@ public: /** Returns BuildingPlacementComponent subobject **/ FORCEINLINE UAgrarianBuildingPlacementComponent* GetBuildingPlacementComponent() const { return BuildingPlacementComponent; } }; -