Implement camera perspective toggle
This commit is contained in:
@@ -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.
Binary file not shown.
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user