feat(character): integrate camera system with aiming mode
Character (C++): - Add camera components to character (SpringArm, Camera, CameraManager) * Initialize camera manager in BeginPlay with component references * SpringArm attached to capsule with 60cm vertical offset * Camera attached to SpringArm socket - Add AimingCameraConfig for over-the-shoulder aiming view * Switch to aiming config when RMB/L2 pressed * Return to default config when aim button released - Improve OnThrowInput code clarity * Simplify controller validation flow * Clean up trajectory calculation comments - Add forward declarations for camera classes - Update class documentation to mention camera integration Blueprint: - Remove legacy camera component (AC_Camera) * Camera rotation now handled by C++ TengriCameraComponent * SpringArm interpolation managed by camera config system - Update look input to use native controller input * AddControllerYawInput/PitchInput for FreeLook mode * Skip input in side-scroller mode (camera is fixed) - Remove camera-related variables (moved to C++ config) - Simplify EventTick (camera logic now in C++ component) - Pass DA_CameraAiming config to character constructor Camera now seamlessly transitions between default and aiming modes, working in tandem with strafe movement for precise targeting.main
parent
74996e5e4b
commit
d89e3fb3b3
|
|
@ -1,6 +1,5 @@
|
||||||
// Content/Blueprints/BP_MainCharacter.ts
|
// Content/Blueprints/BP_MainCharacter.ts
|
||||||
|
|
||||||
import { AC_Camera } from '/Content/Camera/AC_Camera.ts';
|
|
||||||
import { AC_DebugHUD } from '/Content/Debug/Components/AC_DebugHUD.ts';
|
import { AC_DebugHUD } from '/Content/Debug/Components/AC_DebugHUD.ts';
|
||||||
import { AC_InputDevice } from '/Content/Input/Components/AC_InputDevice.ts';
|
import { AC_InputDevice } from '/Content/Input/Components/AC_InputDevice.ts';
|
||||||
import { IMC_Default } from '/Content/Input/IMC_Default.ts';
|
import { IMC_Default } from '/Content/Input/IMC_Default.ts';
|
||||||
|
|
@ -11,11 +10,8 @@ import { EnhancedInputLocalPlayerSubsystem } from '/Content/UE/EnhancedInputLoca
|
||||||
import type { Float } from '/Content/UE/Float.ts';
|
import type { Float } from '/Content/UE/Float.ts';
|
||||||
import { MathLibrary } from '/Content/UE/MathLibrary.ts';
|
import { MathLibrary } from '/Content/UE/MathLibrary.ts';
|
||||||
import type { PlayerController } from '/Content/UE/PlayerController.ts';
|
import type { PlayerController } from '/Content/UE/PlayerController.ts';
|
||||||
import { Rotator } from '/Content/UE/Rotator.ts';
|
|
||||||
import { SystemLibrary } from '/Content/UE/SystemLibrary.ts';
|
import { SystemLibrary } from '/Content/UE/SystemLibrary.ts';
|
||||||
import { Vector } from '/Content/UE/Vector.ts';
|
import { Vector } from '/Content/UE/Vector.ts';
|
||||||
import { TengriMovementComponent } from '/Source/TengriPlatformer/Movement/TengriMovementComponent.ts';
|
|
||||||
import { DA_TengriMovementConfig } from '/Content/Movement/DA_TengriMovementConfig.ts';
|
|
||||||
import { TengriCharacter } from '/Source/TengriPlatformer/Character/TengriCharacter.ts';
|
import { TengriCharacter } from '/Source/TengriPlatformer/Character/TengriCharacter.ts';
|
||||||
import { IA_Interact } from '/Content/Input/Actions/IA_Inreract.ts';
|
import { IA_Interact } from '/Content/Input/Actions/IA_Inreract.ts';
|
||||||
import { IA_Throw } from '/Content/Input/Actions/IA_Throw.ts';
|
import { IA_Throw } from '/Content/Input/Actions/IA_Throw.ts';
|
||||||
|
|
@ -23,9 +19,9 @@ import { IA_Aim } from '/Content/Input/Actions/IA_Aim.ts';
|
||||||
import { IMC_ItemHeld } from '/Content/Input/IMC_ItemHeld.ts';
|
import { IMC_ItemHeld } from '/Content/Input/IMC_ItemHeld.ts';
|
||||||
import { CreateWidget } from '/Content/UE/CteateWidget.ts';
|
import { CreateWidget } from '/Content/UE/CteateWidget.ts';
|
||||||
import { WBP_HUD } from '/Content/UI/WBP_HUD.ts';
|
import { WBP_HUD } from '/Content/UI/WBP_HUD.ts';
|
||||||
import { SpringArmComponent } from '/Content/UE/SpringArmComponent.ts';
|
|
||||||
import { LinearColor } from '/Content/UE/LinearColor.ts';
|
|
||||||
import { CustomDefaultSkeletalMesh } from '/Content/BasicShapes/CustomDefaultSkeletalMesh.ts';
|
import { CustomDefaultSkeletalMesh } from '/Content/BasicShapes/CustomDefaultSkeletalMesh.ts';
|
||||||
|
import { ETengriCameraBehavior } from '/Source/TengriPlatformer/Camera/Core/TengriCameraConfig.ts';
|
||||||
|
import { DA_CameraAiming } from '/Content/Camera/DA_CameraAiming.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main Character Blueprint
|
* Main Character Blueprint
|
||||||
|
|
@ -90,17 +86,13 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
actionValueX: Float,
|
actionValueX: Float,
|
||||||
actionValueY: Float
|
actionValueY: Float
|
||||||
): void {
|
): void {
|
||||||
this.CameraComponent.ProcessLookInput(
|
if (
|
||||||
new Vector(actionValueX, actionValueY, 0),
|
this.CameraManager.CurrentConfig.BehaviorType ===
|
||||||
this.DeltaTime
|
ETengriCameraBehavior.FreeLook
|
||||||
);
|
) {
|
||||||
}
|
this.AddControllerYawInput(actionValueX);
|
||||||
|
this.AddControllerPitchInput(actionValueY);
|
||||||
/**
|
}
|
||||||
* Reset look input when look action is completed
|
|
||||||
*/
|
|
||||||
EnhancedInputActionLookCompleted(): void {
|
|
||||||
this.CameraComponent.ProcessLookInput(new Vector(0, 0, 0), this.DeltaTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -132,7 +124,7 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
return new Vector(vec1.X + vec2.X, vec1.Y + vec2.Y, 0);
|
return new Vector(vec1.X + vec2.X, vec1.Y + vec2.Y, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.TengriMovement.SetInputVector(
|
this.MovementComponent.SetInputVector(
|
||||||
CalculateResultMovementInputVector(
|
CalculateResultMovementInputVector(
|
||||||
MathLibrary.GetRightVector(
|
MathLibrary.GetRightVector(
|
||||||
this.GetControlRotation().roll,
|
this.GetControlRotation().roll,
|
||||||
|
|
@ -150,7 +142,7 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
* Reset movement input when move action is completed
|
* Reset movement input when move action is completed
|
||||||
*/
|
*/
|
||||||
EnhancedInputActionMoveCompleted(): void {
|
EnhancedInputActionMoveCompleted(): void {
|
||||||
this.TengriMovement.SetInputVector(new Vector(0, 0, 0));
|
this.MovementComponent.SetInputVector(new Vector(0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
EnhancedInputActionJumpTriggered(): void {
|
EnhancedInputActionJumpTriggered(): void {
|
||||||
|
|
@ -161,24 +153,6 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
this.MovementComponent.SetJumpInput(false);
|
this.MovementComponent.SetJumpInput(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
MovementComponentOnLanded(IsHeavy: boolean): void {
|
|
||||||
if (IsHeavy) {
|
|
||||||
SystemLibrary.PrintString(
|
|
||||||
'Boom! (Heavy)',
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
new LinearColor(1, 0, 0, 1)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
SystemLibrary.PrintString(
|
|
||||||
'Tap (Light)',
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
new LinearColor(0, 1, 0, 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize all systems when character spawns
|
* Initialize all systems when character spawns
|
||||||
* Order: Toast → Debug → Movement (movement last as it may generate debug output)
|
* Order: Toast → Debug → Movement (movement last as it may generate debug output)
|
||||||
|
|
@ -200,11 +174,6 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.CameraComponent.InitializeCameraSystem(
|
|
||||||
this.InputDeviceComponent,
|
|
||||||
this.DebugHUDComponent
|
|
||||||
);
|
|
||||||
|
|
||||||
CreateWidget(WBP_HUD).AddToViewport();
|
CreateWidget(WBP_HUD).AddToViewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,60 +181,21 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
* Update all systems each frame
|
* Update all systems each frame
|
||||||
* Called by Unreal Engine game loop
|
* Called by Unreal Engine game loop
|
||||||
*/
|
*/
|
||||||
EventTick(DeltaTime: Float): void {
|
EventTick(): void {
|
||||||
this.DeltaTime = DeltaTime;
|
|
||||||
|
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.DebugHUDComponent.UpdateHUD(SystemLibrary.GetGameTimeInSeconds());
|
this.DebugHUDComponent.UpdateHUD(SystemLibrary.GetGameTimeInSeconds());
|
||||||
this.ToastSystemComponent.UpdateToastSystem();
|
this.ToastSystemComponent.UpdateToastSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.CameraComponent.UpdateCameraRotation(DeltaTime);
|
|
||||||
|
|
||||||
this.GetController().SetControlRotation(
|
|
||||||
new Rotator(
|
|
||||||
0,
|
|
||||||
this.CameraComponent.GetCameraRotation().Pitch,
|
|
||||||
this.CameraComponent.GetCameraRotation().Yaw
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.ShowDebugInfo) {
|
if (this.ShowDebugInfo) {
|
||||||
this.InputDeviceComponent.UpdateDebugPage();
|
this.InputDeviceComponent.UpdateDebugPage();
|
||||||
this.CameraComponent.UpdateDebugPage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.SpringArm.TargetArmLength = MathLibrary.FInterpTo(
|
|
||||||
this.SpringArm.TargetArmLength,
|
|
||||||
this.bIsAiming ? this.AimArmLength : this.DefaultArmLength,
|
|
||||||
DeltaTime,
|
|
||||||
10.0
|
|
||||||
);
|
|
||||||
|
|
||||||
this.SpringArm.TargetOffset = MathLibrary.VInterpTo(
|
|
||||||
this.SpringArm.TargetOffset,
|
|
||||||
this.bIsAiming ? this.AimSocketOffset : this.DefaultSocketOffset,
|
|
||||||
DeltaTime,
|
|
||||||
10.0
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
// VARIABLES
|
// VARIABLES
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
/**
|
|
||||||
* Camera system component - handles camera rotation and sensitivity
|
|
||||||
* @category Components
|
|
||||||
*/
|
|
||||||
SpringArm = new SpringArmComponent();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Camera system component - handles camera rotation and sensitivity
|
|
||||||
* @category Components
|
|
||||||
*/
|
|
||||||
CameraComponent = new AC_Camera();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input device detection component - manages input device state and detection
|
* Input device detection component - manages input device state and detection
|
||||||
* @category Components
|
* @category Components
|
||||||
|
|
@ -278,10 +208,6 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
*/
|
*/
|
||||||
ToastSystemComponent = new AC_ToastSystem();
|
ToastSystemComponent = new AC_ToastSystem();
|
||||||
|
|
||||||
TengriMovement = new TengriMovementComponent({
|
|
||||||
MovementConfig: DA_TengriMovementConfig,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug HUD system - displays movement parameters and performance metrics
|
* Debug HUD system - displays movement parameters and performance metrics
|
||||||
* @category Components
|
* @category Components
|
||||||
|
|
@ -295,37 +221,8 @@ export class BP_MainCharacter extends TengriCharacter {
|
||||||
*/
|
*/
|
||||||
private ShowDebugInfo: boolean = true;
|
private ShowDebugInfo: boolean = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* @category Camera
|
|
||||||
* @instanceEditable true
|
|
||||||
*/
|
|
||||||
private readonly DefaultArmLength: Float = 400.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @category Camera
|
|
||||||
* @instanceEditable true
|
|
||||||
*/
|
|
||||||
private readonly AimArmLength: Float = 250.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @category Camera
|
|
||||||
* @instanceEditable true
|
|
||||||
*/
|
|
||||||
private readonly DefaultSocketOffset: Vector = new Vector(0.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @category Camera
|
|
||||||
* @instanceEditable true
|
|
||||||
*/
|
|
||||||
private readonly AimSocketOffset: Vector = new Vector(0.0, 100.0, 60.0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cached delta time from last tick - used for time-based calculations
|
|
||||||
*/
|
|
||||||
private DeltaTime: Float = 0.0;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super(new DA_CameraAiming());
|
||||||
|
|
||||||
this.InteractAction = IA_Interact;
|
this.InteractAction = IA_Interact;
|
||||||
this.ThrowAction = IA_Throw;
|
this.ThrowAction = IA_Throw;
|
||||||
|
|
|
||||||
BIN
Content/Blueprints/BP_MainCharacter.uasset (Stored with Git LFS)
BIN
Content/Blueprints/BP_MainCharacter.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,323 +0,0 @@
|
||||||
// Content/Camera/Components/AC_Camera.ts
|
|
||||||
|
|
||||||
import type { AC_DebugHUD } from '/Content/Debug/Components/AC_DebugHUD.ts';
|
|
||||||
import type { AC_InputDevice } from '/Content/Input/Components/AC_InputDevice.ts';
|
|
||||||
import { ActorComponent } from '/Content/UE/ActorComponent.ts';
|
|
||||||
import type { Float } from '/Content/UE/Float.ts';
|
|
||||||
import { MathLibrary } from '/Content/UE/MathLibrary.ts';
|
|
||||||
import { SystemLibrary } from '/Content/UE/SystemLibrary.ts';
|
|
||||||
import { Vector } from '/Content/UE/Vector.ts';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Camera System Component
|
|
||||||
* Deterministic camera control with smooth rotation and device-aware sensitivity
|
|
||||||
* Provides precise control over camera behavior for consistent experience
|
|
||||||
*/
|
|
||||||
export class AC_Camera extends ActorComponent {
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
||||||
// FUNCTIONS
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process look input and update camera rotation
|
|
||||||
* @param InputDelta - Input delta (X = Yaw, Y = Pitch)
|
|
||||||
* @param DeltaTime - Time since last frame
|
|
||||||
* @category Input Processing
|
|
||||||
*/
|
|
||||||
public ProcessLookInput(InputDelta: Vector, DeltaTime: Float): void {
|
|
||||||
if (this.IsInitialized) {
|
|
||||||
const invertMultiplier = this.InvertYAxis ? -1.0 : 1.0;
|
|
||||||
let sensitivity: Float = 0;
|
|
||||||
|
|
||||||
if (SystemLibrary.IsValid(this.InputDeviceComponent)) {
|
|
||||||
sensitivity = this.InputDeviceComponent.IsGamepad()
|
|
||||||
? this.GamepadSensitivity
|
|
||||||
: this.MouseSensitivity;
|
|
||||||
} else {
|
|
||||||
sensitivity = this.MouseSensitivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CalculateTargetPitch = (
|
|
||||||
targetPitch: Float,
|
|
||||||
inputDeltaY: Float,
|
|
||||||
deltaTime: Float
|
|
||||||
): Float =>
|
|
||||||
targetPitch - inputDeltaY * sensitivity * invertMultiplier * deltaTime;
|
|
||||||
|
|
||||||
const CalculateTargetYaw = (
|
|
||||||
targetYaw: Float,
|
|
||||||
inputDeltaX: Float,
|
|
||||||
deltaTime: Float
|
|
||||||
): Float => targetYaw + inputDeltaX * sensitivity * deltaTime;
|
|
||||||
|
|
||||||
this.TargetPitch = MathLibrary.ClampFloat(
|
|
||||||
CalculateTargetPitch(this.TargetPitch, InputDelta.Y, DeltaTime),
|
|
||||||
this.PitchMin,
|
|
||||||
this.PitchMax
|
|
||||||
);
|
|
||||||
|
|
||||||
this.TargetYaw = CalculateTargetYaw(
|
|
||||||
this.TargetYaw,
|
|
||||||
InputDelta.X,
|
|
||||||
DeltaTime
|
|
||||||
);
|
|
||||||
|
|
||||||
this.InputMagnitude = MathLibrary.VectorLength(InputDelta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update camera rotation with smooth interpolation
|
|
||||||
* @param DeltaTime - Time since last frame
|
|
||||||
* @category Camera Updates
|
|
||||||
*/
|
|
||||||
public UpdateCameraRotation(DeltaTime: Float): void {
|
|
||||||
if (this.IsInitialized) {
|
|
||||||
if (this.SmoothingSpeed > 0) {
|
|
||||||
// Smooth interpolation to target rotation
|
|
||||||
this.CurrentPitch = MathLibrary.FInterpTo(
|
|
||||||
this.CurrentPitch,
|
|
||||||
this.TargetPitch,
|
|
||||||
DeltaTime,
|
|
||||||
this.SmoothingSpeed
|
|
||||||
);
|
|
||||||
|
|
||||||
this.CurrentYaw = MathLibrary.FInterpTo(
|
|
||||||
this.CurrentYaw,
|
|
||||||
this.TargetYaw,
|
|
||||||
DeltaTime,
|
|
||||||
this.SmoothingSpeed
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Instant rotation (no smoothing)
|
|
||||||
this.CurrentPitch = this.TargetPitch;
|
|
||||||
this.CurrentYaw = this.TargetYaw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current camera rotation for applying to SpringArm
|
|
||||||
* @returns Current camera rotation values
|
|
||||||
* @category Public Interface
|
|
||||||
* @pure true
|
|
||||||
*/
|
|
||||||
public GetCameraRotation(): { Pitch: Float; Yaw: Float } {
|
|
||||||
return {
|
|
||||||
Pitch: this.CurrentPitch,
|
|
||||||
Yaw: this.CurrentYaw,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if camera is currently rotating
|
|
||||||
* @returns True if there's active rotation input
|
|
||||||
* @category State Queries
|
|
||||||
* @pure true
|
|
||||||
*/
|
|
||||||
public IsCameraRotating(): boolean {
|
|
||||||
return this.InputMagnitude > 0.01;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize camera system with default settings
|
|
||||||
* @category System Setup
|
|
||||||
*/
|
|
||||||
public InitializeCameraSystem(
|
|
||||||
InputDeviceRef: AC_InputDevice,
|
|
||||||
DebugComponentRef: AC_DebugHUD
|
|
||||||
): void {
|
|
||||||
this.InputDeviceComponent = InputDeviceRef;
|
|
||||||
this.DebugHUDComponent = DebugComponentRef;
|
|
||||||
|
|
||||||
this.IsInitialized = true;
|
|
||||||
|
|
||||||
if (SystemLibrary.IsValid(this.DebugHUDComponent)) {
|
|
||||||
this.DebugHUDComponent.AddDebugPage(
|
|
||||||
this.DebugPageID,
|
|
||||||
'Camera System',
|
|
||||||
60
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update debug HUD with current camera info
|
|
||||||
* @category Debug
|
|
||||||
*/
|
|
||||||
public UpdateDebugPage(): void {
|
|
||||||
if (SystemLibrary.IsValid(this.DebugHUDComponent)) {
|
|
||||||
if (
|
|
||||||
this.DebugHUDComponent.ShouldUpdatePage(
|
|
||||||
this.DebugPageID,
|
|
||||||
SystemLibrary.GetGameTimeInSeconds()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.DebugHUDComponent.UpdatePageContent(
|
|
||||||
this.DebugPageID,
|
|
||||||
`Current Device: ${SystemLibrary.IsValid(this.InputDeviceComponent) ? this.InputDeviceComponent.GetCurrentInputDevice() : 'Input Device Component Not Found'}\n` +
|
|
||||||
`Sensitivity: ${SystemLibrary.IsValid(this.InputDeviceComponent) && this.InputDeviceComponent.IsGamepad() ? this.GamepadSensitivity : this.MouseSensitivity}\n` +
|
|
||||||
`Pitch: ${this.GetCameraRotation().Pitch}°\n` +
|
|
||||||
`Yaw: ${this.GetCameraRotation().Yaw}°\n` +
|
|
||||||
`Is Rotating: ${this.IsCameraRotating() ? 'Yes' : 'No'}\n` +
|
|
||||||
`Smoothing: ${this.SmoothingSpeed}\n` +
|
|
||||||
`Invert Y: ${this.InvertYAxis ? 'Yes' : 'No'}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get camera configuration and state data for testing purposes
|
|
||||||
* Provides read-only access to private variables for automated tests
|
|
||||||
* Only includes essential data needed for test validation
|
|
||||||
* @category Debug
|
|
||||||
* @returns Object containing camera settings (sensitivity, pitch limits) for test assertions
|
|
||||||
*/
|
|
||||||
public GetTestData(): {
|
|
||||||
MouseSensitivity: Float;
|
|
||||||
GamepadSensitivity: Float;
|
|
||||||
PitchMin: Float;
|
|
||||||
PitchMax: Float;
|
|
||||||
} {
|
|
||||||
return {
|
|
||||||
MouseSensitivity: this.MouseSensitivity,
|
|
||||||
GamepadSensitivity: this.GamepadSensitivity,
|
|
||||||
PitchMin: this.PitchMin,
|
|
||||||
PitchMax: this.PitchMax,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
||||||
// VARIABLES
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mouse sensitivity multiplier for camera rotation
|
|
||||||
* Higher values result in faster camera movement with mouse input
|
|
||||||
* Typical range: 50.0 (slow) - 200.0 (fast)
|
|
||||||
* @category Camera Config
|
|
||||||
* @instanceEditable true
|
|
||||||
* @default 100.0
|
|
||||||
*/
|
|
||||||
private readonly MouseSensitivity: Float = 100.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gamepad sensitivity multiplier for camera rotation
|
|
||||||
* Higher than mouse sensitivity to compensate for analog stick precision
|
|
||||||
* Typical range: 100.0 (slow) - 300.0 (fast)
|
|
||||||
* @category Camera Config
|
|
||||||
* @instanceEditable true
|
|
||||||
* @default 150.0
|
|
||||||
*/
|
|
||||||
private readonly GamepadSensitivity: Float = 150.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invert vertical axis for camera rotation
|
|
||||||
* When true, pushing up on input rotates camera down and vice versa
|
|
||||||
* Common preference for flight-sim style controls
|
|
||||||
* @category Camera Config
|
|
||||||
* @instanceEditable true
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
private readonly InvertYAxis: boolean = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimum pitch angle in degrees (looking down)
|
|
||||||
* Prevents camera from rotating beyond this angle
|
|
||||||
* Set to -89° to avoid gimbal lock at -90°
|
|
||||||
* @category Camera Config
|
|
||||||
* @instanceEditable true
|
|
||||||
* @default -89.0
|
|
||||||
*/
|
|
||||||
private readonly PitchMin: Float = -89.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum pitch angle in degrees (looking up)
|
|
||||||
* Prevents camera from rotating beyond this angle
|
|
||||||
* Set to +89° to avoid gimbal lock at +90°
|
|
||||||
* @category Camera Config
|
|
||||||
* @instanceEditable true
|
|
||||||
* @default 89.0
|
|
||||||
*/
|
|
||||||
private readonly PitchMax: Float = 89.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Speed of smooth interpolation to target rotation
|
|
||||||
* Higher values make camera more responsive but less smooth
|
|
||||||
* Set to 0 for instant rotation without interpolation
|
|
||||||
* Typical range: 10.0 (smooth) - 30.0 (responsive)
|
|
||||||
* @category Camera Config
|
|
||||||
* @instanceEditable true
|
|
||||||
* @default 20.0
|
|
||||||
*/
|
|
||||||
private readonly SmoothingSpeed: Float = 20.0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current pitch angle for rendering
|
|
||||||
* Smoothly interpolates towards TargetPitch based on SmoothingSpeed
|
|
||||||
* Updated every frame by UpdateCameraRotation()
|
|
||||||
* @category Camera State
|
|
||||||
*/
|
|
||||||
private CurrentPitch: Float = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current yaw angle for rendering
|
|
||||||
* Smoothly interpolates towards TargetYaw based on SmoothingSpeed
|
|
||||||
* Updated every frame by UpdateCameraRotation()
|
|
||||||
* @category Camera State
|
|
||||||
*/
|
|
||||||
private CurrentYaw: Float = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Target pitch angle from player input
|
|
||||||
* Updated by ProcessLookInput() based on input delta
|
|
||||||
* Clamped to PitchMin/PitchMax range
|
|
||||||
* @category Camera State
|
|
||||||
*/
|
|
||||||
private TargetPitch: Float = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Target yaw angle from player input
|
|
||||||
* Updated by ProcessLookInput() based on input delta
|
|
||||||
* No clamping - can rotate freely beyond 360°
|
|
||||||
* @category Camera State
|
|
||||||
*/
|
|
||||||
private TargetYaw: Float = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Magnitude of current input vector
|
|
||||||
* Used by IsCameraRotating() to detect active camera input
|
|
||||||
* Cached to avoid recalculating VectorLength every frame
|
|
||||||
* @category Camera State
|
|
||||||
* @default 0.0
|
|
||||||
*/
|
|
||||||
private InputMagnitude: Float = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* System initialization state flag
|
|
||||||
* @category Camera State
|
|
||||||
*/
|
|
||||||
private IsInitialized: boolean = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to input device component for device detection
|
|
||||||
* Set externally, used for sensitivity adjustments
|
|
||||||
* @category Components
|
|
||||||
*/
|
|
||||||
public InputDeviceComponent: AC_InputDevice | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to debug HUD component for displaying camera info
|
|
||||||
* Optional, used for debugging purposes
|
|
||||||
* @category Components
|
|
||||||
*/
|
|
||||||
public DebugHUDComponent: AC_DebugHUD | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug page identifier for organizing debug output
|
|
||||||
* Used by debug HUD to categorize information
|
|
||||||
* @category Debug
|
|
||||||
*/
|
|
||||||
public readonly DebugPageID: string = 'CameraInfo';
|
|
||||||
}
|
|
||||||
BIN
Content/Camera/AC_Camera.uasset (Stored with Git LFS)
BIN
Content/Camera/AC_Camera.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Content/UE/CameraComponent.ts
|
||||||
|
|
||||||
|
import { SceneComponent } from '/Content/UE/SceneComponent.ts';
|
||||||
|
import type { UEObject } from '/Content/UE/UEObject.ts';
|
||||||
|
|
||||||
|
export class CameraComponent extends SceneComponent {
|
||||||
|
constructor(outer: UEObject | null = null, name: string = 'None') {
|
||||||
|
super(outer, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import { Controller } from '/Content/UE/Controller.ts';
|
||||||
import { Name } from '/Content/UE/Name.ts';
|
import { Name } from '/Content/UE/Name.ts';
|
||||||
import { Rotator } from '/Content/UE/Rotator.ts';
|
import { Rotator } from '/Content/UE/Rotator.ts';
|
||||||
import { UEObject } from '/Content/UE/UEObject.ts';
|
import { UEObject } from '/Content/UE/UEObject.ts';
|
||||||
|
import type { Float } from '/Content/UE/Float.ts';
|
||||||
|
|
||||||
export class Pawn extends Actor {
|
export class Pawn extends Actor {
|
||||||
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
|
|
@ -18,4 +19,12 @@ export class Pawn extends Actor {
|
||||||
public GetControlRotation(): Rotator {
|
public GetControlRotation(): Rotator {
|
||||||
return new Rotator();
|
return new Rotator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AddControllerYawInput(Val: Float): void {
|
||||||
|
console.log(Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddControllerPitchInput(Val: Float): void {
|
||||||
|
console.log(Val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@
|
||||||
#include "Components/CapsuleComponent.h"
|
#include "Components/CapsuleComponent.h"
|
||||||
#include "Components/SkeletalMeshComponent.h"
|
#include "Components/SkeletalMeshComponent.h"
|
||||||
#include "Components/ArrowComponent.h"
|
#include "Components/ArrowComponent.h"
|
||||||
|
#include "GameFramework/SpringArmComponent.h"
|
||||||
|
#include "Camera/CameraComponent.h"
|
||||||
#include "TengriPlatformer/Movement/TengriMovementComponent.h"
|
#include "TengriPlatformer/Movement/TengriMovementComponent.h"
|
||||||
|
#include "TengriPlatformer/Camera/TengriCameraComponent.h"
|
||||||
#include "TengriPlatformer/World/TengriPickupActor.h"
|
#include "TengriPlatformer/World/TengriPickupActor.h"
|
||||||
#include "Kismet/KismetSystemLibrary.h"
|
#include "Kismet/KismetSystemLibrary.h"
|
||||||
|
|
||||||
|
|
@ -54,11 +57,29 @@ ATengriCharacter::ATengriCharacter()
|
||||||
|
|
||||||
// Setup custom movement component
|
// Setup custom movement component
|
||||||
MovementComponent = CreateDefaultSubobject<UTengriMovementComponent>(TEXT("TengriMovement"));
|
MovementComponent = CreateDefaultSubobject<UTengriMovementComponent>(TEXT("TengriMovement"));
|
||||||
|
|
||||||
|
// Setup camera system
|
||||||
|
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
|
||||||
|
SpringArmComp->SetupAttachment(CapsuleComponent);
|
||||||
|
SpringArmComp->bUsePawnControlRotation = true;
|
||||||
|
SpringArmComp->SetRelativeLocation(FVector(0.f, 0.f, 60.f));
|
||||||
|
|
||||||
|
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
|
||||||
|
CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);
|
||||||
|
CameraComp->bUsePawnControlRotation = false;
|
||||||
|
|
||||||
|
CameraManager = CreateDefaultSubobject<UTengriCameraComponent>(TEXT("CameraManager"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATengriCharacter::BeginPlay()
|
void ATengriCharacter::BeginPlay()
|
||||||
{
|
{
|
||||||
Super::BeginPlay();
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
// Initialize camera manager with component references
|
||||||
|
if (CameraManager)
|
||||||
|
{
|
||||||
|
CameraManager->InitializeCamera(SpringArmComp, CameraComp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -178,96 +199,85 @@ void ATengriCharacter::Interact()
|
||||||
|
|
||||||
void ATengriCharacter::OnThrowInput()
|
void ATengriCharacter::OnThrowInput()
|
||||||
{
|
{
|
||||||
if (!HeldItem)
|
if (!HeldItem)
|
||||||
{
|
{
|
||||||
UE_LOG(LogTengriCharacter, Warning,
|
UE_LOG(LogTengriCharacter, Warning,
|
||||||
TEXT("OnThrowInput: No item held"));
|
TEXT("OnThrowInput: No item held"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to forward direction if raycasting fails
|
// Default to forward direction if raycasting fails
|
||||||
FVector ThrowDirection = GetActorForwardVector();
|
FVector ThrowDirection = GetActorForwardVector();
|
||||||
|
|
||||||
// ИСПРАВЛЕНИЕ: Переименовали Controller -> PC, чтобы избежать конфликта имен
|
AController* PC = GetController();
|
||||||
AController* PC = GetController();
|
|
||||||
|
|
||||||
if (UWorld* World = GetWorld(); !PC || !World)
|
if (UWorld* World = GetWorld(); !PC || !World)
|
||||||
{
|
{
|
||||||
UE_LOG(LogTengriCharacter, Warning,
|
UE_LOG(LogTengriCharacter, Warning,
|
||||||
TEXT("OnThrowInput: Invalid controller or world context"));
|
TEXT("OnThrowInput: Invalid controller or world context"));
|
||||||
// Continue with fallback direction
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
// Calculate precise throw trajectory using camera raycast
|
||||||
// ─────────────────────────────────────────────────────────────────
|
FVector CameraLoc;
|
||||||
// PRECISE THROW TRAJECTORY CALCULATION
|
FRotator CameraRot;
|
||||||
// ─────────────────────────────────────────────────────────────────
|
PC->GetPlayerViewPoint(CameraLoc, CameraRot);
|
||||||
|
|
||||||
// Get camera position and rotation
|
const FVector TraceStart = CameraLoc;
|
||||||
FVector CameraLoc;
|
const FVector TraceEnd = CameraLoc + (CameraRot.Vector() * ThrowTraceDistance);
|
||||||
FRotator CameraRot;
|
|
||||||
PC->GetPlayerViewPoint(CameraLoc, CameraRot); // Используем PC вместо Controller
|
|
||||||
|
|
||||||
// Raycast from camera forward
|
FHitResult Hit;
|
||||||
const FVector TraceStart = CameraLoc;
|
FCollisionQueryParams QueryParams;
|
||||||
const FVector TraceEnd = CameraLoc + (CameraRot.Vector() * ThrowTraceDistance);
|
QueryParams.AddIgnoredActor(this);
|
||||||
|
QueryParams.AddIgnoredActor(HeldItem);
|
||||||
|
|
||||||
FHitResult Hit;
|
// Find world target point where camera is looking
|
||||||
FCollisionQueryParams QueryParams;
|
const bool bHit = World->LineTraceSingleByChannel(
|
||||||
QueryParams.AddIgnoredActor(this);
|
Hit,
|
||||||
QueryParams.AddIgnoredActor(HeldItem);
|
TraceStart,
|
||||||
|
TraceEnd,
|
||||||
|
ECC_Visibility,
|
||||||
|
QueryParams
|
||||||
|
);
|
||||||
|
|
||||||
// Find world target point (wall/enemy/floor where camera is looking)
|
const FVector TargetPoint = bHit ? Hit.ImpactPoint : TraceEnd;
|
||||||
const bool bHit = World->LineTraceSingleByChannel(
|
|
||||||
Hit,
|
|
||||||
TraceStart,
|
|
||||||
TraceEnd,
|
|
||||||
ECC_Visibility,
|
|
||||||
QueryParams
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use hit point if found, otherwise use far endpoint
|
// Calculate throw direction from item to target
|
||||||
const FVector TargetPoint = bHit ? Hit.ImpactPoint : TraceEnd;
|
const FVector HandLocation = HeldItem->GetActorLocation();
|
||||||
|
ThrowDirection = (TargetPoint - HandLocation).GetSafeNormal();
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate throw direction FROM item TO target
|
// Add arc elevation for natural parabolic trajectory
|
||||||
const FVector HandLocation = HeldItem->GetActorLocation();
|
ThrowDirection += FVector(0.0f, 0.0f, ThrowArcElevation);
|
||||||
ThrowDirection = (TargetPoint - HandLocation).GetSafeNormal();
|
ThrowDirection.Normalize();
|
||||||
}
|
|
||||||
|
|
||||||
// Add arc elevation for natural parabolic trajectory
|
// Rotate character instantly to face throw direction
|
||||||
ThrowDirection += FVector(0.0f, 0.0f, ThrowArcElevation);
|
FRotator CharacterFaceRot = ThrowDirection.Rotation();
|
||||||
ThrowDirection.Normalize();
|
CharacterFaceRot.Pitch = 0.0f;
|
||||||
|
CharacterFaceRot.Roll = 0.0f;
|
||||||
|
|
||||||
|
if (MovementComponent)
|
||||||
|
{
|
||||||
|
MovementComponent->ForceRotation(CharacterFaceRot);
|
||||||
|
}
|
||||||
|
|
||||||
// Rotate character instantly to face throw direction
|
// Execute throw with calculated trajectory
|
||||||
FRotator CharacterFaceRot = ThrowDirection.Rotation();
|
const FVector Impulse = ThrowDirection * ThrowForce;
|
||||||
CharacterFaceRot.Pitch = 0.0f;
|
HeldItem->OnDropped(Impulse, true);
|
||||||
CharacterFaceRot.Roll = 0.0f;
|
HeldItem = nullptr;
|
||||||
|
|
||||||
if (MovementComponent)
|
|
||||||
{
|
|
||||||
MovementComponent->ForceRotation(CharacterFaceRot);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute throw with calculated trajectory
|
// Disable combat controls
|
||||||
const FVector Impulse = ThrowDirection * ThrowForce;
|
ToggleItemHeldContext(false);
|
||||||
HeldItem->OnDropped(Impulse, true);
|
|
||||||
HeldItem = nullptr;
|
UE_LOG(LogTengriCharacter, Verbose,
|
||||||
|
TEXT("Threw item with force: %.1f cm/s"), ThrowForce);
|
||||||
// Disable combat controls
|
|
||||||
ToggleItemHeldContext(false);
|
|
||||||
|
|
||||||
UE_LOG(LogTengriCharacter, Verbose,
|
|
||||||
TEXT("Threw item with force: %.1f cm/s"), ThrowForce);
|
|
||||||
|
|
||||||
// Note: bIsAiming state will be cleared by OnAimInput when player releases RMB/L2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATengriCharacter::OnAimInput(const FInputActionValue& Value)
|
void ATengriCharacter::OnAimInput(const FInputActionValue& Value)
|
||||||
{
|
{
|
||||||
// Extract boolean state (true = button pressed, false = released)
|
|
||||||
const bool bIsPressed = Value.Get<bool>();
|
const bool bIsPressed = Value.Get<bool>();
|
||||||
|
|
||||||
// Skip if state hasn't changed (optimization)
|
// Skip if state hasn't changed
|
||||||
if (bIsPressed == bIsAiming)
|
if (bIsPressed == bIsAiming)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -279,9 +289,24 @@ void ATengriCharacter::OnAimInput(const FInputActionValue& Value)
|
||||||
if (MovementComponent)
|
if (MovementComponent)
|
||||||
{
|
{
|
||||||
MovementComponent->SetStrafing(bIsAiming);
|
MovementComponent->SetStrafing(bIsAiming);
|
||||||
UE_LOG(LogTengriCharacter, Verbose,
|
|
||||||
TEXT("Aim mode: %s"), bIsAiming ? TEXT("ON") : TEXT("OFF"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch camera configuration for aiming
|
||||||
|
if (CameraManager)
|
||||||
|
{
|
||||||
|
if (bIsAiming && AimingCameraConfig)
|
||||||
|
{
|
||||||
|
CameraManager->SetCameraConfig(AimingCameraConfig);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return to default config (nullptr triggers fallback)
|
||||||
|
CameraManager->SetCameraConfig(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(LogTengriCharacter, Verbose,
|
||||||
|
TEXT("Aim mode: %s"), bIsAiming ? TEXT("ON") : TEXT("OFF"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -290,7 +315,7 @@ void ATengriCharacter::OnAimInput(const FInputActionValue& Value)
|
||||||
|
|
||||||
ATengriPickupActor* ATengriCharacter::FindNearestPickup() const
|
ATengriPickupActor* ATengriCharacter::FindNearestPickup() const
|
||||||
{
|
{
|
||||||
UWorld* World = GetWorld();
|
const UWorld* World = GetWorld();
|
||||||
if (!World)
|
if (!World)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -331,8 +356,8 @@ ATengriPickupActor* ATengriCharacter::FindNearestPickup() const
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float DistSq = FVector::DistSquared(SpherePos, Item->GetActorLocation());
|
if (const float DistSq = FVector::DistSquared(SpherePos, Item->GetActorLocation());
|
||||||
if (DistSq < MinDistSq)
|
DistSq < MinDistSq)
|
||||||
{
|
{
|
||||||
MinDistSq = DistSq;
|
MinDistSq = DistSq;
|
||||||
NearestItem = Item;
|
NearestItem = Item;
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,20 @@
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class UCapsuleComponent;
|
class UCapsuleComponent;
|
||||||
class USkeletalMeshComponent;
|
class USkeletalMeshComponent;
|
||||||
class UTengriMovementComponent;
|
|
||||||
class ATengriPickupActor;
|
|
||||||
class UArrowComponent;
|
class UArrowComponent;
|
||||||
|
class USpringArmComponent;
|
||||||
|
class UCameraComponent;
|
||||||
|
class UTengriMovementComponent;
|
||||||
|
class UTengriCameraComponent;
|
||||||
|
class UTengriCameraConfig;
|
||||||
|
class ATengriPickupActor;
|
||||||
class UInputMappingContext;
|
class UInputMappingContext;
|
||||||
class UInputAction;
|
class UInputAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main player character class with item interaction and throwing mechanics.
|
* Main player character class with item interaction and throwing mechanics.
|
||||||
* Supports dynamic input context switching for item-based actions.
|
* Supports dynamic input context switching for item-based actions.
|
||||||
|
* Features integrated camera system with multiple behavior modes.
|
||||||
*/
|
*/
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class TENGRIPLATFORMER_API ATengriCharacter : public APawn
|
class TENGRIPLATFORMER_API ATengriCharacter : public APawn
|
||||||
|
|
@ -51,6 +56,23 @@ public:
|
||||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||||
TObjectPtr<UTengriMovementComponent> MovementComponent;
|
TObjectPtr<UTengriMovementComponent> MovementComponent;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||||
|
TObjectPtr<USpringArmComponent> SpringArmComp;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||||
|
TObjectPtr<UCameraComponent> CameraComp;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||||
|
TObjectPtr<UTengriCameraComponent> CameraManager;
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// CAMERA CONFIGS
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/** Camera configuration for aiming mode (over-the-shoulder view) */
|
||||||
|
UPROPERTY(EditDefaultsOnly, Category = "Camera|Configs")
|
||||||
|
TObjectPtr<UTengriCameraConfig> AimingCameraConfig;
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// INPUT CONFIG
|
// INPUT CONFIG
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
@ -94,7 +116,7 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle aim input state changes (RMB/L2).
|
* Handle aim input state changes (RMB/L2).
|
||||||
* Toggles strafe mode on MovementComponent for camera-aligned rotation.
|
* Toggles strafe mode and switches camera configuration.
|
||||||
* @param Value - Input action value (true = pressed, false = released)
|
* @param Value - Input action value (true = pressed, false = released)
|
||||||
*/
|
*/
|
||||||
void OnAimInput(const FInputActionValue& Value);
|
void OnAimInput(const FInputActionValue& Value);
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,24 @@ import { ArrowComponent } from '/Content/UE/ArrowComponent.ts';
|
||||||
import { TengriMovementComponent } from '/Source/TengriPlatformer/Movement/TengriMovementComponent.ts';
|
import { TengriMovementComponent } from '/Source/TengriPlatformer/Movement/TengriMovementComponent.ts';
|
||||||
import { InputAction } from '/Content/UE/InputAction.ts';
|
import { InputAction } from '/Content/UE/InputAction.ts';
|
||||||
import { InputMappingContext } from '/Content/UE/InputMappingContext.ts';
|
import { InputMappingContext } from '/Content/UE/InputMappingContext.ts';
|
||||||
|
import { SpringArmComponent } from '/Content/UE/SpringArmComponent.ts';
|
||||||
|
import { CameraComponent } from '/Content/UE/CameraComponent.ts';
|
||||||
|
import { TengriCameraComponent } from '/Source/TengriPlatformer/Camera/TengriCameraComponent.ts';
|
||||||
|
import { TengriCameraConfig } from '/Source/TengriPlatformer/Camera/Core/TengriCameraConfig.ts';
|
||||||
|
|
||||||
export class TengriCharacter extends Pawn {
|
export class TengriCharacter extends Pawn {
|
||||||
constructor() {
|
constructor(AimingCameraConfig: TengriCameraConfig) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.CapsuleComponent = new CapsuleComponent();
|
this.CapsuleComponent = new CapsuleComponent();
|
||||||
this.Mesh = new SkeletalMesh();
|
this.Mesh = new SkeletalMesh();
|
||||||
this.ArrowComponent = new ArrowComponent();
|
this.ArrowComponent = new ArrowComponent();
|
||||||
this.MovementComponent = new TengriMovementComponent();
|
this.MovementComponent = new TengriMovementComponent();
|
||||||
|
this.SpringArmComponent = new SpringArmComponent();
|
||||||
|
this.CameraComponent = new CameraComponent();
|
||||||
|
this.CameraManager = new TengriCameraComponent();
|
||||||
|
|
||||||
|
this.AimingCameraConfig = AimingCameraConfig;
|
||||||
|
|
||||||
this.InteractAction = new InputAction();
|
this.InteractAction = new InputAction();
|
||||||
this.ThrowAction = new InputAction();
|
this.ThrowAction = new InputAction();
|
||||||
|
|
@ -31,6 +40,15 @@ export class TengriCharacter extends Pawn {
|
||||||
public Mesh: SkeletalMesh;
|
public Mesh: SkeletalMesh;
|
||||||
public ArrowComponent: ArrowComponent;
|
public ArrowComponent: ArrowComponent;
|
||||||
public MovementComponent: TengriMovementComponent;
|
public MovementComponent: TengriMovementComponent;
|
||||||
|
public SpringArmComponent: SpringArmComponent;
|
||||||
|
public CameraComponent: CameraComponent;
|
||||||
|
public CameraManager: TengriCameraComponent;
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// CAMERA CONFIGS
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
public AimingCameraConfig: TengriCameraConfig;
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// INPUT CONFIG
|
// INPUT CONFIG
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue