Implement camera perspective toggle

This commit is contained in:
2026-05-15 10:44:02 -07:00
parent c5e795ef26
commit cd8de0f906
8 changed files with 206 additions and 4 deletions
+2 -2
View File
@@ -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.
Binary file not shown.
Binary file not shown.
+88
View File
@@ -0,0 +1,88 @@
import unreal
def load(path):
asset = unreal.EditorAssetLibrary.load_asset(path)
if not asset:
raise RuntimeError(f"Could not load {path}")
return asset
def create_input_action(path):
existing = unreal.EditorAssetLibrary.load_asset(path)
if existing:
return existing
template_path = "/Game/Input/Actions/IA_Interact"
template = unreal.EditorAssetLibrary.load_asset(template_path)
if not template:
template_path = "/Game/Input/Actions/IA_Jump"
template = unreal.EditorAssetLibrary.load_asset(template_path)
if not template:
raise RuntimeError("Could not load an input action template")
action = unreal.EditorAssetLibrary.duplicate_asset(template_path, path)
if not action:
raise RuntimeError(f"Could not create {path}")
return action
def set_boolean_value_type(action):
action.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN)
try:
action.set_editor_property("triggers", [])
except Exception as exc:
unreal.log_warning(f"Could not clear camera toggle triggers; keeping template defaults: {exc}")
unreal.EditorAssetLibrary.save_loaded_asset(action)
def mapping_exists(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 map_key(context, action, key_name):
if mapping_exists(context, action, key_name):
unreal.log(f"Mapping already exists: {action.get_name()} -> {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()
+44
View File
@@ -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()
+46 -1
View File
@@ -43,7 +43,7 @@ AAgrarianGameCharacter::AAgrarianGameCharacter()
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(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)
+26 -1
View File
@@ -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; }
};