[code] add character rotation to movement component
parent
01ef4abe50
commit
df3deef577
|
|
@ -10,6 +10,7 @@ import { Cast } from '#root/UE/Cast.ts';
|
||||||
import type { Controller } from '#root/UE/Controller.ts';
|
import type { Controller } from '#root/UE/Controller.ts';
|
||||||
import { EnhancedInputLocalPlayerSubsystem } from '#root/UE/EnhancedInputLocalPlayerSubsystem.ts';
|
import { EnhancedInputLocalPlayerSubsystem } from '#root/UE/EnhancedInputLocalPlayerSubsystem.ts';
|
||||||
import type { Float } from '#root/UE/Float.ts';
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
|
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
||||||
import { Pawn } from '#root/UE/Pawn.ts';
|
import { Pawn } from '#root/UE/Pawn.ts';
|
||||||
import type { PlayerController } from '#root/UE/PlayerController.ts';
|
import type { PlayerController } from '#root/UE/PlayerController.ts';
|
||||||
import { Rotator } from '#root/UE/Rotator.ts';
|
import { Rotator } from '#root/UE/Rotator.ts';
|
||||||
|
|
@ -94,14 +95,43 @@ export class BP_MainCharacter extends Pawn {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process movement input for ground-based movement
|
* Process movement input for ground-based movement
|
||||||
* @param actionValueX - Horizontal movement input value (-1 to 1)
|
* @param ActionValueX - Horizontal movement input value (-1 to 1)
|
||||||
* @param actionValueY - Vertical movement input value (-1 to 1)
|
* @param ActionValueY - Vertical movement input value (-1 to 1)
|
||||||
*/
|
*/
|
||||||
EnhancedInputActionMoveTriggered(
|
EnhancedInputActionMoveTriggered(
|
||||||
actionValueX: Float,
|
ActionValueX: Float,
|
||||||
actionValueY: Float
|
ActionValueY: Float
|
||||||
): void {
|
): void {
|
||||||
this.CurrentMovementInput = new Vector(actionValueY, actionValueX, 0);
|
const CalculateResultMovementInputVector = (
|
||||||
|
rightVector: Vector,
|
||||||
|
forwardVector: Vector,
|
||||||
|
actionValueX: Float,
|
||||||
|
actionValueY: Float
|
||||||
|
): Vector => {
|
||||||
|
const vec1 = new Vector(
|
||||||
|
rightVector.X * actionValueX,
|
||||||
|
rightVector.Y * actionValueX,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const vec2 = new Vector(
|
||||||
|
forwardVector.X * actionValueY,
|
||||||
|
forwardVector.Y * actionValueY,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Vector(vec1.X + vec2.X, vec1.Y + vec2.Y, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.CurrentMovementInput = CalculateResultMovementInputVector(
|
||||||
|
MathLibrary.GetRightVector(
|
||||||
|
this.GetControlRotation().roll,
|
||||||
|
0,
|
||||||
|
this.GetControlRotation().yaw
|
||||||
|
),
|
||||||
|
MathLibrary.GetForwardVector(0, 0, this.GetControlRotation().yaw),
|
||||||
|
ActionValueX,
|
||||||
|
ActionValueY
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -167,7 +197,7 @@ export class BP_MainCharacter extends Pawn {
|
||||||
this.CurrentMovementInput,
|
this.CurrentMovementInput,
|
||||||
DeltaTime
|
DeltaTime
|
||||||
);
|
);
|
||||||
this.ApplyMovementToActor();
|
this.ApplyMovementAndRotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
@ -178,7 +208,9 @@ export class BP_MainCharacter extends Pawn {
|
||||||
* Apply calculated movement velocity to actor position
|
* Apply calculated movement velocity to actor position
|
||||||
* @category Movement Application
|
* @category Movement Application
|
||||||
*/
|
*/
|
||||||
private ApplyMovementToActor(): void {
|
private ApplyMovementAndRotation(): void {
|
||||||
|
this.SetActorRotation(this.MovementComponent.CurrentRotation);
|
||||||
|
|
||||||
const CalculateNewLocation = (
|
const CalculateNewLocation = (
|
||||||
currentLocation: Vector,
|
currentLocation: Vector,
|
||||||
velocity: Vector
|
velocity: Vector
|
||||||
|
|
|
||||||
BIN
Content/Blueprints/BP_MainCharacter.uasset (Stored with Git LFS)
BIN
Content/Blueprints/BP_MainCharacter.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/Debug/Components/AC_DebugHUD.uasset (Stored with Git LFS)
BIN
Content/Debug/Components/AC_DebugHUD.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -8,6 +8,7 @@ import type { S_MovementConstants } from '#root/Movement/Structs/S_MovementConst
|
||||||
import { ActorComponent } from '#root/UE/ActorComponent.ts';
|
import { ActorComponent } from '#root/UE/ActorComponent.ts';
|
||||||
import type { Float } from '#root/UE/Float.ts';
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
||||||
|
import { Rotator } from '#root/UE/Rotator.ts';
|
||||||
import { Vector } from '#root/UE/Vector.ts';
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -132,6 +133,9 @@ export class AC_Movement extends ActorComponent {
|
||||||
if (this.IsInitialized) {
|
if (this.IsInitialized) {
|
||||||
this.InputMagnitude = MathLibrary.VectorLength(InputVector);
|
this.InputMagnitude = MathLibrary.VectorLength(InputVector);
|
||||||
|
|
||||||
|
this.TargetRotation = this.CalculateTargetRotation(InputVector);
|
||||||
|
this.UpdateCharacterRotation(DeltaTime);
|
||||||
|
|
||||||
// Only process movement on walkable surfaces
|
// Only process movement on walkable surfaces
|
||||||
if (this.IsSurfaceWalkable(this.CurrentSurface) && this.IsGrounded) {
|
if (this.IsSurfaceWalkable(this.CurrentSurface) && this.IsGrounded) {
|
||||||
this.ProcessGroundMovement(InputVector, DeltaTime);
|
this.ProcessGroundMovement(InputVector, DeltaTime);
|
||||||
|
|
@ -252,6 +256,93 @@ export class AC_Movement extends ActorComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate target rotation based on movement direction
|
||||||
|
* Determines what rotation character should have based on movement
|
||||||
|
* @param MovementDirection - Camera-relative movement direction from Blueprint
|
||||||
|
* @returns Target rotation for character
|
||||||
|
* @pure true
|
||||||
|
* @category Character Rotation
|
||||||
|
*/
|
||||||
|
public CalculateTargetRotation(MovementDirection: Vector): Rotator {
|
||||||
|
const TargetYaw = (
|
||||||
|
movementDirectionX: Float,
|
||||||
|
movementDirectionY: Float
|
||||||
|
): Float =>
|
||||||
|
MathLibrary.RadiansToDegrees(
|
||||||
|
MathLibrary.Atan2(movementDirectionY, movementDirectionX)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (MathLibrary.VectorLength(MovementDirection) < 0.01) {
|
||||||
|
// No movement, maintain current target rotation
|
||||||
|
return this.TargetRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Character should remain level (pitch = 0, roll = 0, only yaw changes)
|
||||||
|
return new Rotator(
|
||||||
|
0,
|
||||||
|
TargetYaw(MovementDirection.X, MovementDirection.Y),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update character rotation towards target
|
||||||
|
* Uses instant rotation approach similar to Zelda: BotW
|
||||||
|
* @param DeltaTime - Time since last frame
|
||||||
|
* @category Character Rotation
|
||||||
|
*/
|
||||||
|
public UpdateCharacterRotation(DeltaTime: Float): void {
|
||||||
|
if (this.ShouldRotateToMovement) {
|
||||||
|
const ShouldRotate = (): boolean =>
|
||||||
|
this.CurrentSpeed >= this.MinSpeedForRotation;
|
||||||
|
|
||||||
|
if (ShouldRotate()) {
|
||||||
|
let yawDifference = this.CurrentRotation.yaw - this.TargetRotation.yaw;
|
||||||
|
|
||||||
|
if (yawDifference > 180) {
|
||||||
|
yawDifference = yawDifference - 360;
|
||||||
|
} else if (yawDifference < -180) {
|
||||||
|
yawDifference = yawDifference + 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NewYaw = (
|
||||||
|
currentYaw: Float,
|
||||||
|
clampedRotationAmount: Float,
|
||||||
|
coef: Float
|
||||||
|
): Float => currentYaw + clampedRotationAmount * coef;
|
||||||
|
|
||||||
|
const ClampedRotationAmount = (deltaTime: Float): Float =>
|
||||||
|
MathLibrary.Min(
|
||||||
|
this.RotationSpeed * deltaTime,
|
||||||
|
MathLibrary.abs(yawDifference)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.RotationDelta = MathLibrary.abs(yawDifference);
|
||||||
|
|
||||||
|
if (this.RotationDelta > 1) {
|
||||||
|
this.IsCharacterRotating = true;
|
||||||
|
|
||||||
|
this.CurrentRotation = new Rotator(
|
||||||
|
0,
|
||||||
|
NewYaw(
|
||||||
|
this.CurrentRotation.yaw,
|
||||||
|
ClampedRotationAmount(DeltaTime),
|
||||||
|
yawDifference > 0 ? -1 : 1
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.IsCharacterRotating = false;
|
||||||
|
this.CurrentRotation = this.TargetRotation;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.IsCharacterRotating = false;
|
||||||
|
this.RotationDelta = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize movement system with angle conversion
|
* Initialize movement system with angle conversion
|
||||||
* Converts degree thresholds to radians for runtime performance
|
* Converts degree thresholds to radians for runtime performance
|
||||||
|
|
@ -359,4 +450,56 @@ export class AC_Movement extends ActorComponent {
|
||||||
* @category Movement State
|
* @category Movement State
|
||||||
*/
|
*/
|
||||||
public CurrentSpeed: Float = 0.0;
|
public CurrentSpeed: Float = 0.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotation speed in degrees per second
|
||||||
|
* Controls how fast character rotates towards movement direction
|
||||||
|
* @category Character Rotation Config
|
||||||
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
public RotationSpeed: Float = 720.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should character rotate when moving
|
||||||
|
* Allows disabling rotation system if needed
|
||||||
|
* @category Character Rotation Config
|
||||||
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
public ShouldRotateToMovement: boolean = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum movement speed to trigger rotation
|
||||||
|
* Prevents character from rotating during very slow movement
|
||||||
|
* @category Character Rotation Config
|
||||||
|
* @instanceEditable true
|
||||||
|
*/
|
||||||
|
public MinSpeedForRotation: Float = 50.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current character rotation
|
||||||
|
* Used for smooth interpolation to target rotation
|
||||||
|
* @category Character Rotation State
|
||||||
|
*/
|
||||||
|
public CurrentRotation: Rotator = new Rotator(0, 0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target character rotation
|
||||||
|
* Calculated based on movement direction and camera orientation
|
||||||
|
* @category Character Rotation State
|
||||||
|
*/
|
||||||
|
public TargetRotation: Rotator = new Rotator(0, 0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether character is currently rotating
|
||||||
|
* Used for animation and debug purposes
|
||||||
|
* @category Character Rotation State
|
||||||
|
*/
|
||||||
|
public IsCharacterRotating: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Angular difference between current and target rotation
|
||||||
|
* Used for determining rotation completion and debug display
|
||||||
|
* @category Character Rotation State
|
||||||
|
*/
|
||||||
|
public RotationDelta: Float = 0.0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
Content/Movement/Components/AC_Movement.uasset (Stored with Git LFS)
BIN
Content/Movement/Components/AC_Movement.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,6 +1,7 @@
|
||||||
// UE/Actor.ts
|
// UE/Actor.ts
|
||||||
|
|
||||||
import { Name } from '#root/UE/Name.ts';
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { Rotator } from '#root/UE/Rotator.ts';
|
||||||
import { UEObject } from '#root/UE/UEObject.ts';
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
import { Vector } from '#root/UE/Vector.ts';
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
|
|
@ -18,6 +19,14 @@ export class Actor extends UEObject {
|
||||||
// Implementation for setting actor location
|
// Implementation for setting actor location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SetActorRotation(
|
||||||
|
NewRotation: Rotator = new Rotator(),
|
||||||
|
TeleportPhysics: boolean = false
|
||||||
|
): void {
|
||||||
|
console.log(NewRotation, TeleportPhysics);
|
||||||
|
// Implementation for setting actor rotation
|
||||||
|
}
|
||||||
|
|
||||||
public GetActorLocation(): Vector {
|
public GetActorLocation(): Vector {
|
||||||
return new Vector(); // Placeholder implementation
|
return new Vector(); // Placeholder implementation
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,18 @@ class MathLibraryClass extends BlueprintFunctionLibrary {
|
||||||
return (Degrees * Math.PI) / 180;
|
return (Degrees * Math.PI) / 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert radians to degrees
|
||||||
|
* @param Radians - Angle in radians
|
||||||
|
* @returns Angle in degrees
|
||||||
|
* @example
|
||||||
|
* // Convert π/2 radians to degrees
|
||||||
|
* RadiansToDegrees(Math.PI / 2) // returns 90
|
||||||
|
*/
|
||||||
|
public RadiansToDegrees(Radians: Float): Float {
|
||||||
|
return (Radians * 180) / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate sine of angle in radians
|
* Calculate sine of angle in radians
|
||||||
* @param Value - Angle in radians
|
* @param Value - Angle in radians
|
||||||
|
|
@ -62,6 +74,16 @@ class MathLibraryClass extends BlueprintFunctionLibrary {
|
||||||
return Math.acos(Value);
|
return Math.acos(Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate arctangent2 of Y and X
|
||||||
|
* @param Y - Y coordinate
|
||||||
|
* @param X - X coordinate
|
||||||
|
* @returns Angle in radians (-π to π)
|
||||||
|
*/
|
||||||
|
public Atan2(Y: Float, X: Float): Float {
|
||||||
|
return Math.atan2(Y, X);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate dot product of two vectors
|
* Calculate dot product of two vectors
|
||||||
* @param Vector1 - First vector
|
* @param Vector1 - First vector
|
||||||
|
|
@ -207,6 +229,69 @@ class MathLibraryClass extends BlueprintFunctionLibrary {
|
||||||
|
|
||||||
return new Vector(0, 0, 0);
|
return new Vector(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the minimum of two float values
|
||||||
|
* @param A - First value
|
||||||
|
* @param B - Second value
|
||||||
|
* @returns Minimum value
|
||||||
|
* @example
|
||||||
|
* // Minimum of 3 and 5
|
||||||
|
* Min(3, 5) // returns 3
|
||||||
|
*/
|
||||||
|
public Min(A: Float, B: Float): Float {
|
||||||
|
return Math.min(A, B);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get right vector from roll, pitch, yaw angles
|
||||||
|
* @param Roll - Rotation around forward axis in radians
|
||||||
|
* @param Pitch - Rotation around right axis in radians
|
||||||
|
* @param Yaw - Rotation around up axis in radians
|
||||||
|
* @returns Right direction vector
|
||||||
|
* @example
|
||||||
|
* // Right vector for no rotation
|
||||||
|
* GetRightVector(0, 0, 0) // returns Vector(1,0,0)
|
||||||
|
*/
|
||||||
|
public GetRightVector(
|
||||||
|
Roll: Float = 0,
|
||||||
|
Pitch: Float = 0,
|
||||||
|
Yaw: Float = 0
|
||||||
|
): Vector {
|
||||||
|
const CP = this.Cos(Pitch);
|
||||||
|
const SP = this.Sin(Pitch);
|
||||||
|
const CY = this.Cos(Yaw);
|
||||||
|
const SY = this.Sin(Yaw);
|
||||||
|
|
||||||
|
console.log(Roll);
|
||||||
|
|
||||||
|
return new Vector(CP * CY, CP * SY, SP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get forward vector from roll, pitch, yaw angles
|
||||||
|
* @param Roll - Rotation around forward axis in radians
|
||||||
|
* @param Pitch - Rotation around right axis in radians
|
||||||
|
* @param Yaw - Rotation around up axis in radians
|
||||||
|
* @returns Forward direction vector
|
||||||
|
* @example
|
||||||
|
* // Forward vector for no rotation
|
||||||
|
* GetForwardVector(0, 0, 0) // returns Vector(1,0,0)
|
||||||
|
*/
|
||||||
|
public GetForwardVector(
|
||||||
|
Roll: Float = 0,
|
||||||
|
Pitch: Float = 0,
|
||||||
|
Yaw: Float = 0
|
||||||
|
): Vector {
|
||||||
|
const CP = this.Cos(Pitch);
|
||||||
|
const SP = this.Sin(Pitch);
|
||||||
|
const CY = this.Cos(Yaw);
|
||||||
|
const SY = this.Sin(Yaw);
|
||||||
|
|
||||||
|
console.log(Roll);
|
||||||
|
|
||||||
|
return new Vector(CP * CY, CP * SY, SP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { Actor } from '#root/UE/Actor.ts';
|
import { Actor } from '#root/UE/Actor.ts';
|
||||||
import { Controller } from '#root/UE/Controller.ts';
|
import { Controller } from '#root/UE/Controller.ts';
|
||||||
import { Name } from '#root/UE/Name.ts';
|
import { Name } from '#root/UE/Name.ts';
|
||||||
|
import { Rotator } from '#root/UE/Rotator.ts';
|
||||||
import { UEObject } from '#root/UE/UEObject.ts';
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
|
||||||
export class Pawn extends Actor {
|
export class Pawn extends Actor {
|
||||||
|
|
@ -13,4 +14,8 @@ export class Pawn extends Actor {
|
||||||
public GetController(): Controller {
|
public GetController(): Controller {
|
||||||
return new Controller(this);
|
return new Controller(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GetControlRotation(): Rotator {
|
||||||
|
return new Rotator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue