[code] deterministic ground movement system
parent
98ce2bb903
commit
01ef4abe50
|
|
@ -92,6 +92,25 @@ export class BP_MainCharacter extends Pawn {
|
||||||
this.CameraComponent.ProcessLookInput(new Vector(0, 0, 0), this.DeltaTime);
|
this.CameraComponent.ProcessLookInput(new Vector(0, 0, 0), this.DeltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process movement input for ground-based movement
|
||||||
|
* @param actionValueX - Horizontal movement input value (-1 to 1)
|
||||||
|
* @param actionValueY - Vertical movement input value (-1 to 1)
|
||||||
|
*/
|
||||||
|
EnhancedInputActionMoveTriggered(
|
||||||
|
actionValueX: Float,
|
||||||
|
actionValueY: Float
|
||||||
|
): void {
|
||||||
|
this.CurrentMovementInput = new Vector(actionValueY, actionValueX, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset movement input when move action is completed
|
||||||
|
*/
|
||||||
|
EnhancedInputActionMoveCompleted(): void {
|
||||||
|
this.CurrentMovementInput = new Vector(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
|
|
@ -143,6 +162,39 @@ export class BP_MainCharacter extends Pawn {
|
||||||
this.CameraComponent.GetCameraRotation().Yaw
|
this.CameraComponent.GetCameraRotation().Yaw
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.MovementComponent.ProcessMovementInput(
|
||||||
|
this.CurrentMovementInput,
|
||||||
|
DeltaTime
|
||||||
|
);
|
||||||
|
this.ApplyMovementToActor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// FUNCTIONS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply calculated movement velocity to actor position
|
||||||
|
* @category Movement Application
|
||||||
|
*/
|
||||||
|
private ApplyMovementToActor(): void {
|
||||||
|
const CalculateNewLocation = (
|
||||||
|
currentLocation: Vector,
|
||||||
|
velocity: Vector
|
||||||
|
): Vector =>
|
||||||
|
new Vector(
|
||||||
|
currentLocation.X + velocity.X * this.DeltaTime,
|
||||||
|
currentLocation.Y + velocity.Y * this.DeltaTime,
|
||||||
|
currentLocation.Z + velocity.Z * this.DeltaTime
|
||||||
|
);
|
||||||
|
|
||||||
|
this.SetActorLocation(
|
||||||
|
CalculateNewLocation(
|
||||||
|
this.GetActorLocation(),
|
||||||
|
this.MovementComponent.CurrentVelocity
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
@ -190,4 +242,9 @@ export class BP_MainCharacter extends Pawn {
|
||||||
* Cached delta time from last tick - used for time-based calculations
|
* Cached delta time from last tick - used for time-based calculations
|
||||||
*/
|
*/
|
||||||
private DeltaTime: Float = 0.0;
|
private DeltaTime: Float = 0.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current movement input vector - updated by input actions
|
||||||
|
*/
|
||||||
|
private CurrentMovementInput: Vector = new Vector(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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/Camera/Tests/FT_CameraSmoothing.uasset (Stored with Git LFS)
BIN
Content/Camera/Tests/FT_CameraSmoothing.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -16,6 +16,7 @@ import { DataTableFunctionLibrary } from '#root/UE/DataTableFunctionLibrary.ts';
|
||||||
import { ESlateVisibility } from '#root/UE/ESlateVisibility.ts';
|
import { ESlateVisibility } from '#root/UE/ESlateVisibility.ts';
|
||||||
import type { Float } from '#root/UE/Float.ts';
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
import type { Integer } from '#root/UE/Integer.ts';
|
import type { Integer } from '#root/UE/Integer.ts';
|
||||||
|
import { StringLibrary } from '#root/UE/StringLibrary.ts';
|
||||||
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
import { SystemLibrary } from '#root/UE/SystemLibrary.ts';
|
||||||
import type { Text } from '#root/UE/Text.ts';
|
import type { Text } from '#root/UE/Text.ts';
|
||||||
import { UEArray } from '#root/UE/UEArray.ts';
|
import { UEArray } from '#root/UE/UEArray.ts';
|
||||||
|
|
@ -276,10 +277,19 @@ export class AC_DebugHUD extends ActorComponent {
|
||||||
PageID: Page.PageID,
|
PageID: Page.PageID,
|
||||||
Title: Page.Title,
|
Title: Page.Title,
|
||||||
Content:
|
Content:
|
||||||
|
// Constants
|
||||||
`Max Speed: ${this.MovementComponent.MovementConstants.MaxSpeed}\n` +
|
`Max Speed: ${this.MovementComponent.MovementConstants.MaxSpeed}\n` +
|
||||||
`Acceleration: ${this.MovementComponent.MovementConstants.Acceleration}\n` +
|
`Acceleration: ${this.MovementComponent.MovementConstants.Acceleration}\n` +
|
||||||
`Friction: ${this.MovementComponent.MovementConstants.Friction}\n` +
|
`Friction: ${this.MovementComponent.MovementConstants.Friction}\n` +
|
||||||
`Gravity: ${this.MovementComponent.MovementConstants.Gravity}`,
|
`Gravity: ${this.MovementComponent.MovementConstants.Gravity}\n` +
|
||||||
|
`\n` + // Разделитель
|
||||||
|
// Current State
|
||||||
|
`Current Velocity: ${StringLibrary.ConvVectorToString(this.MovementComponent.CurrentVelocity)}\n` +
|
||||||
|
`Speed: ${this.MovementComponent.CurrentSpeed}\n` +
|
||||||
|
`Is Grounded: ${this.MovementComponent.IsGrounded}\n` +
|
||||||
|
`Surface Type: ${this.MovementComponent.CurrentSurface}\n` +
|
||||||
|
`Movement State: ${this.MovementComponent.MovementState}\n` +
|
||||||
|
`Input Magnitude: ${this.MovementComponent.InputMagnitude}`,
|
||||||
IsVisible: Page.IsVisible,
|
IsVisible: Page.IsVisible,
|
||||||
UpdateFunction: Page.UpdateFunction,
|
UpdateFunction: Page.UpdateFunction,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
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.
BIN
Content/Debug/Tests/FT_DebugNavigation.uasset (Stored with Git LFS)
BIN
Content/Debug/Tests/FT_DebugNavigation.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/Debug/Tests/FT_DebugPageContentGenerator.uasset (Stored with Git LFS)
BIN
Content/Debug/Tests/FT_DebugPageContentGenerator.uasset (Stored with Git LFS)
Binary file not shown.
BIN
Content/Debug/Tests/FT_DebugSystem.uasset (Stored with Git LFS)
BIN
Content/Debug/Tests/FT_DebugSystem.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -10,6 +10,8 @@ import { FT_DebugNavigation } from '#root/Debug/Tests/FT_DebugNavigation.ts';
|
||||||
import { FT_DebugPageContentGenerator } from '#root/Debug/Tests/FT_DebugPageContentGenerator.ts';
|
import { FT_DebugPageContentGenerator } from '#root/Debug/Tests/FT_DebugPageContentGenerator.ts';
|
||||||
import { FT_DebugSystem } from '#root/Debug/Tests/FT_DebugSystem.ts';
|
import { FT_DebugSystem } from '#root/Debug/Tests/FT_DebugSystem.ts';
|
||||||
import { FT_InputDeviceDetection } from '#root/Input/Tests/FT_InputDeviceDetection.ts';
|
import { FT_InputDeviceDetection } from '#root/Input/Tests/FT_InputDeviceDetection.ts';
|
||||||
|
import { FT_BasicMovement } from '#root/Movement/Tests/FT_BasicMovement.ts';
|
||||||
|
import { FT_DiagonalMovement } from '#root/Movement/Tests/FT_DiagonalMovement.ts';
|
||||||
import { FT_SurfaceClassification } from '#root/Movement/Tests/FT_SurfaceClassification.ts';
|
import { FT_SurfaceClassification } from '#root/Movement/Tests/FT_SurfaceClassification.ts';
|
||||||
import { FT_ToastLimit } from '#root/Toasts/Tests/FT_ToastLimit.ts';
|
import { FT_ToastLimit } from '#root/Toasts/Tests/FT_ToastLimit.ts';
|
||||||
import { FT_ToastsDurationHandling } from '#root/Toasts/Tests/FT_ToastsDurationHandling.ts';
|
import { FT_ToastsDurationHandling } from '#root/Toasts/Tests/FT_ToastsDurationHandling.ts';
|
||||||
|
|
@ -47,9 +49,13 @@ const InputDeviceDetectionTest = new FT_InputDeviceDetection();
|
||||||
InputDeviceDetectionTest.EventStartTest();
|
InputDeviceDetectionTest.EventStartTest();
|
||||||
|
|
||||||
// Movement Tests
|
// Movement Tests
|
||||||
|
const BasicMovementTest = new FT_BasicMovement();
|
||||||
const SurfaceClassificationTest = new FT_SurfaceClassification();
|
const SurfaceClassificationTest = new FT_SurfaceClassification();
|
||||||
|
const DiagonalMovement = new FT_DiagonalMovement();
|
||||||
|
|
||||||
|
BasicMovementTest.EventStartTest();
|
||||||
SurfaceClassificationTest.EventStartTest();
|
SurfaceClassificationTest.EventStartTest();
|
||||||
|
DiagonalMovement.EventStartTest();
|
||||||
|
|
||||||
// Toasts Tests
|
// Toasts Tests
|
||||||
const ToastLimitsTest = new FT_ToastLimit();
|
const ToastLimitsTest = new FT_ToastLimit();
|
||||||
|
|
|
||||||
BIN
Content/Levels/TestLevel.umap (Stored with Git LFS)
BIN
Content/Levels/TestLevel.umap (Stored with Git LFS)
Binary file not shown.
|
|
@ -1,13 +1,14 @@
|
||||||
// Movement/Components/AC_Movement.ts
|
// Movement/Components/AC_Movement.ts
|
||||||
|
|
||||||
import { BFL_Vectors } from '#root/Math/Libraries/BFL_Vectors.ts';
|
import { BFL_Vectors } from '#root/Math/Libraries/BFL_Vectors.ts';
|
||||||
|
import { E_MovementState } from '#root/Movement/Enums/E_MovementState.ts';
|
||||||
import { E_SurfaceType } from '#root/Movement/Enums/E_SurfaceType.ts';
|
import { E_SurfaceType } from '#root/Movement/Enums/E_SurfaceType.ts';
|
||||||
import { S_AngleThresholds } from '#root/Movement/Structs/S_AngleThresholds.ts';
|
import { S_AngleThresholds } from '#root/Movement/Structs/S_AngleThresholds.ts';
|
||||||
import type { S_MovementConstants } from '#root/Movement/Structs/S_MovementConstants.ts';
|
import type { S_MovementConstants } from '#root/Movement/Structs/S_MovementConstants.ts';
|
||||||
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 type { Vector } from '#root/UE/Vector.ts';
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Movement System Component
|
* Movement System Component
|
||||||
|
|
@ -120,6 +121,137 @@ export class AC_Movement extends ActorComponent {
|
||||||
return SurfaceType === E_SurfaceType.None;
|
return SurfaceType === E_SurfaceType.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process movement input from player controller
|
||||||
|
* Normalizes input and calculates target velocity
|
||||||
|
* @param InputVector - Raw input from WASD/gamepad stick
|
||||||
|
* @param DeltaTime - Time since last frame for frame-rate independence
|
||||||
|
* @category Movement Processing
|
||||||
|
*/
|
||||||
|
public ProcessMovementInput(InputVector: Vector, DeltaTime: Float): void {
|
||||||
|
if (this.IsInitialized) {
|
||||||
|
this.InputMagnitude = MathLibrary.VectorLength(InputVector);
|
||||||
|
|
||||||
|
// Only process movement on walkable surfaces
|
||||||
|
if (this.IsSurfaceWalkable(this.CurrentSurface) && this.IsGrounded) {
|
||||||
|
this.ProcessGroundMovement(InputVector, DeltaTime);
|
||||||
|
} else {
|
||||||
|
this.ApplyFriction(DeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always apply gravity
|
||||||
|
this.ApplyGravity(DeltaTime);
|
||||||
|
|
||||||
|
// Update movement state
|
||||||
|
this.UpdateMovementState();
|
||||||
|
|
||||||
|
// Calculate current speed for debug
|
||||||
|
this.UpdateCurrentSpeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process ground-based movement with acceleration and max speed limits
|
||||||
|
* @param InputVector - Normalized input direction
|
||||||
|
* @param DeltaTime - Frame delta time
|
||||||
|
* @category Movement Processing
|
||||||
|
*/
|
||||||
|
private ProcessGroundMovement(InputVector: Vector, DeltaTime: Float): void {
|
||||||
|
if (this.InputMagnitude > 0.01) {
|
||||||
|
const CalculateTargetVelocity = (
|
||||||
|
inputVector: Vector,
|
||||||
|
maxSpeed: Float
|
||||||
|
): Vector =>
|
||||||
|
new Vector(
|
||||||
|
MathLibrary.Normal(inputVector).X * maxSpeed,
|
||||||
|
MathLibrary.Normal(inputVector).Y * maxSpeed,
|
||||||
|
MathLibrary.Normal(inputVector).Z * maxSpeed
|
||||||
|
);
|
||||||
|
|
||||||
|
this.CurrentVelocity = MathLibrary.VInterpTo(
|
||||||
|
new Vector(this.CurrentVelocity.X, this.CurrentVelocity.Y, 0),
|
||||||
|
CalculateTargetVelocity(InputVector, this.MovementConstants.MaxSpeed),
|
||||||
|
DeltaTime,
|
||||||
|
this.MovementConstants.Acceleration
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Apply friction when no input
|
||||||
|
this.ApplyFriction(DeltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply friction to horizontal velocity
|
||||||
|
* @param DeltaTime - Frame delta time
|
||||||
|
* @category Movement Processing
|
||||||
|
*/
|
||||||
|
private ApplyFriction(DeltaTime: Float): void {
|
||||||
|
this.CurrentVelocity = MathLibrary.VInterpTo(
|
||||||
|
new Vector(this.CurrentVelocity.X, this.CurrentVelocity.Y, 0),
|
||||||
|
new Vector(0, 0, this.CurrentVelocity.Z),
|
||||||
|
DeltaTime,
|
||||||
|
this.MovementConstants.Friction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply gravity to vertical velocity
|
||||||
|
* @param DeltaTime - Frame delta time
|
||||||
|
* @category Movement Processing
|
||||||
|
*/
|
||||||
|
private ApplyGravity(DeltaTime: Float): void {
|
||||||
|
if (!this.IsGrounded) {
|
||||||
|
const ApplyGravityForce = (
|
||||||
|
velocityZ: Float,
|
||||||
|
gravity: Float,
|
||||||
|
deltaTime: Float
|
||||||
|
): Float => velocityZ - gravity * deltaTime;
|
||||||
|
|
||||||
|
// Apply gravity when airborne
|
||||||
|
this.CurrentVelocity = new Vector(
|
||||||
|
this.CurrentVelocity.X,
|
||||||
|
this.CurrentVelocity.Y,
|
||||||
|
ApplyGravityForce(
|
||||||
|
this.CurrentVelocity.Z,
|
||||||
|
this.MovementConstants.Gravity,
|
||||||
|
DeltaTime
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Zero out vertical velocity when grounded
|
||||||
|
this.CurrentVelocity = new Vector(
|
||||||
|
this.CurrentVelocity.X,
|
||||||
|
this.CurrentVelocity.Y,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update movement state based on current conditions
|
||||||
|
* @category Movement Processing
|
||||||
|
*/
|
||||||
|
private UpdateMovementState(): void {
|
||||||
|
if (!this.IsGrounded) {
|
||||||
|
this.MovementState = E_MovementState.Airborne;
|
||||||
|
} else if (this.InputMagnitude > 0.01) {
|
||||||
|
this.MovementState = E_MovementState.Walking;
|
||||||
|
} else {
|
||||||
|
this.MovementState = E_MovementState.Idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update current speed for debug display
|
||||||
|
* @category Movement Processing
|
||||||
|
*/
|
||||||
|
private UpdateCurrentSpeed(): void {
|
||||||
|
// Calculate horizontal speed only (ignore vertical component)
|
||||||
|
this.CurrentSpeed = MathLibrary.VectorLength(
|
||||||
|
new Vector(this.CurrentVelocity.X, this.CurrentVelocity.Y, 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
|
||||||
|
|
@ -151,7 +283,7 @@ export class AC_Movement extends ActorComponent {
|
||||||
*/
|
*/
|
||||||
public readonly MovementConstants: S_MovementConstants = {
|
public readonly MovementConstants: S_MovementConstants = {
|
||||||
MaxSpeed: 600.0,
|
MaxSpeed: 600.0,
|
||||||
Acceleration: 2000.0,
|
Acceleration: 10.0,
|
||||||
Friction: 8.0,
|
Friction: 8.0,
|
||||||
Gravity: 980.0,
|
Gravity: 980.0,
|
||||||
};
|
};
|
||||||
|
|
@ -185,4 +317,46 @@ export class AC_Movement extends ActorComponent {
|
||||||
* @category Debug
|
* @category Debug
|
||||||
*/
|
*/
|
||||||
public IsInitialized = false;
|
public IsInitialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current character velocity in world space
|
||||||
|
* Updated every frame by movement calculations
|
||||||
|
* @category Movement State
|
||||||
|
*/
|
||||||
|
public CurrentVelocity: Vector = new Vector(0, 0, 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ground contact state - true when character is on walkable surface
|
||||||
|
* Used for gravity application and movement restrictions
|
||||||
|
* @category Movement State
|
||||||
|
*/
|
||||||
|
public IsGrounded: boolean = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of surface currently under character
|
||||||
|
* Determines available movement options
|
||||||
|
* @category Movement State
|
||||||
|
*/
|
||||||
|
public CurrentSurface: E_SurfaceType = E_SurfaceType.Walkable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current movement state of character
|
||||||
|
* Used for animation and game logic decisions
|
||||||
|
* @category Movement State
|
||||||
|
*/
|
||||||
|
public MovementState: E_MovementState = E_MovementState.Idle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magnitude of current movement input (0-1)
|
||||||
|
* Used for determining if character should be moving
|
||||||
|
* @category Movement State
|
||||||
|
*/
|
||||||
|
public InputMagnitude: Float = 0.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current movement speed (magnitude of horizontal velocity)
|
||||||
|
* Calculated from CurrentVelocity for debug display
|
||||||
|
* @category Movement State
|
||||||
|
*/
|
||||||
|
public CurrentSpeed: 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.
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Movement/Enums/E_MovementState.ts
|
||||||
|
|
||||||
|
export enum E_MovementState {
|
||||||
|
Idle = 'Idle',
|
||||||
|
Walking = 'Walking',
|
||||||
|
Airborne = 'Airborne',
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
### 2.1 Default значения
|
### 2.1 Default значения
|
||||||
- [ ] **MaxSpeed = 600.0** - значение установлено по умолчанию
|
- [ ] **MaxSpeed = 600.0** - значение установлено по умолчанию
|
||||||
- [ ] **Acceleration = 2000.0** - значение установлено по умолчанию
|
- [ ] **Acceleration = 10.0** - значение установлено по умолчанию (уменьшено для плавности)
|
||||||
- [ ] **Friction = 8.0** - значение установлено по умолчанию
|
- [ ] **Friction = 8.0** - значение установлено по умолчанию
|
||||||
- [ ] **Gravity = 980.0** - значение установлено по умолчанию
|
- [ ] **Gravity = 980.0** - значение установлено по умолчанию
|
||||||
|
|
||||||
|
|
@ -32,9 +32,94 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Критерии прохождения
|
## 3. Базовое движение (Этап 7)
|
||||||
- [ ] Все пункты выполнены
|
|
||||||
- [ ] Система инициализируется без ошибок
|
|
||||||
- [ ] Default константы соответствуют спецификации
|
|
||||||
|
|
||||||
**Примечание:** Большая часть Movement System тестируется автотестами (FT_SurfaceClassification с 10 test cases). Ручное тестирование фокусируется на базовой инициализации.
|
### 3.1 Управление клавиатурой
|
||||||
|
- [ ] **W** - персонаж движется вперед (+X направление)
|
||||||
|
- [ ] **S** - персонаж движется назад (-X направление)
|
||||||
|
- [ ] **A** - персонаж движется влево (+Y направление)
|
||||||
|
- [ ] **D** - персонаж движется вправо (-Y направление)
|
||||||
|
- [ ] **Отсутствие input** - персонаж останавливается
|
||||||
|
|
||||||
|
### 3.2 Управление геймпадом
|
||||||
|
- [ ] **Left Stick Up** - движение вперед
|
||||||
|
- [ ] **Left Stick Down** - движение назад
|
||||||
|
- [ ] **Left Stick Left** - движение влево
|
||||||
|
- [ ] **Left Stick Right** - движение вправо
|
||||||
|
- [ ] **Stick в центре** - персонаж останавливается
|
||||||
|
|
||||||
|
### 3.3 Физика движения
|
||||||
|
- [ ] **Плавное ускорение** - персонаж набирает скорость постепенно при нажатии клавиш
|
||||||
|
- [ ] **Плавное торможение** - персонаж останавливается плавно при отпускании клавиш
|
||||||
|
- [ ] **MaxSpeed limit** - скорость не превышает 600.0 units/sec
|
||||||
|
- [ ] **Диагональное движение** - скорость диагонального движения равна прямому (не быстрее)
|
||||||
|
- [ ] **Стабильное поведение** - нет рывков, заиканий или неожиданных ускорений
|
||||||
|
|
||||||
|
### 3.4 Состояния движения
|
||||||
|
- [ ] **Idle state** - MovementState = Idle когда персонаж стоит
|
||||||
|
- [ ] **Walking state** - MovementState = Walking при движении
|
||||||
|
- [ ] **InputMagnitude** - корректно отражает силу input (0-1)
|
||||||
|
- [ ] **CurrentSpeed** - показывает текущую горизонтальную скорость
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Debug HUD Integration
|
||||||
|
|
||||||
|
### 4.1 Movement Constants Page (Page 1)
|
||||||
|
- [ ] **Константы** отображаются корректно:
|
||||||
|
- Max Speed: 600
|
||||||
|
- Acceleration: 10
|
||||||
|
- Friction: 8
|
||||||
|
- Gravity: 980
|
||||||
|
- [ ] **Текущее состояние** отображается:
|
||||||
|
- Current Velocity: X, Y, Z компоненты
|
||||||
|
- Speed: горизонтальная скорость
|
||||||
|
- Is Grounded: Yes (пока всегда true)
|
||||||
|
- Surface Type: Walkable (пока всегда)
|
||||||
|
- Movement State: Idle/Walking
|
||||||
|
- Input Magnitude: 0.00-1.00
|
||||||
|
|
||||||
|
### 4.2 Реальное время обновления
|
||||||
|
- [ ] **Velocity** изменяется в реальном времени при движении
|
||||||
|
- [ ] **Speed** корректно показывает magnitude горизонтальной скорости
|
||||||
|
- [ ] **Movement State** переключается между Idle и Walking
|
||||||
|
- [ ] **Input Magnitude** отражает силу нажатия (особенно на геймпаде)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Автотесты Integration
|
||||||
|
|
||||||
|
### 5.1 FT_BasicMovement
|
||||||
|
- [ ] **Тест проходит** - инициализация, ускорение, торможение, состояния
|
||||||
|
- [ ] **No console errors** при выполнении автотеста
|
||||||
|
|
||||||
|
### 5.2 FT_DiagonalMovement
|
||||||
|
- [ ] **Тест проходит** - диагональное движение не быстрее прямого
|
||||||
|
- [ ] **Input normalization** работает корректно
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Performance
|
||||||
|
|
||||||
|
### 6.1 Производительность
|
||||||
|
- [ ] **Stable 60+ FPS** при активном движении
|
||||||
|
- [ ] **No memory leaks** при длительном использовании
|
||||||
|
- [ ] **Smooth movement** без микро-заиканий
|
||||||
|
|
||||||
|
### 6.2 Отзывчивость
|
||||||
|
- [ ] **Instant response** на нажатие клавиш (нет input lag)
|
||||||
|
- [ ] **Smooth transitions** между состояниями движения
|
||||||
|
- [ ] **Consistent timing** независимо от FPS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Критерии прохождения
|
||||||
|
- [ ] Все основные направления движения работают
|
||||||
|
- [ ] Физика движения плавная и отзывчивая
|
||||||
|
- [ ] MaxSpeed limit соблюдается
|
||||||
|
- [ ] Диагональное движение не дает преимущества в скорости
|
||||||
|
- [ ] Debug HUD показывает корректные данные движения
|
||||||
|
- [ ] Автотесты проходят успешно
|
||||||
|
- [ ] Performance стабильная
|
||||||
|
|
||||||
|
**Примечание:** Этап 7 фокусируется на базовом движении по плоскости. Camera-relative движение и поворот персонажа будут в этапе 8.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
# Система Movement - Техническая Документация
|
# Система Movement - Техническая Документация
|
||||||
|
|
||||||
## Обзор
|
## Обзор
|
||||||
Детерминированная система движения для 3D-платформера с точной классификацией поверхностей и математически предсказуемым поведением. Система обеспечивает основу для всех механик перемещения персонажа в игровом мире.
|
Детерминированная система движения для 3D-платформера с точной классификацией поверхностей и полнофункциональным базовым движением. Система обеспечивает математически предсказуемое поведение как для классификации поверхностей, так и для физики движения персонажа с плавным ускорением и торможением.
|
||||||
|
|
||||||
## Архитектурные принципы
|
## Архитектурные принципы
|
||||||
- **Детерминизм:** Математически предсказуемые результаты для одинаковых входных данных
|
- **Детерминизм:** Математически предсказуемые результаты для одинаковых входных данных
|
||||||
|
|
@ -24,6 +24,10 @@
|
||||||
- `InitializeMovementSystem()` - Инициализация с конвертацией углов
|
- `InitializeMovementSystem()` - Инициализация с конвертацией углов
|
||||||
- `ClassifySurface()` - Определение типа поверхности по normal вектору
|
- `ClassifySurface()` - Определение типа поверхности по normal вектору
|
||||||
- Приватные методы проверки типов (`IsSurfaceWalkable()`, `IsSurfaceSteep()` и др.)
|
- Приватные методы проверки типов (`IsSurfaceWalkable()`, `IsSurfaceSteep()` и др.)
|
||||||
|
- `ProcessMovementInput()` - Обработка input и расчет velocity
|
||||||
|
- `ProcessGroundMovement()` - VInterpTo physics для плавного движения
|
||||||
|
- `ApplyFriction()` - Система торможения через VInterpTo
|
||||||
|
- `ApplyGravity()` - Вертикальная физика для airborne состояний
|
||||||
|
|
||||||
### BFL_Vectors (Blueprint Function Library)
|
### BFL_Vectors (Blueprint Function Library)
|
||||||
**Ответственности:**
|
**Ответственности:**
|
||||||
|
|
@ -98,6 +102,22 @@ interface S_SurfaceTestCase {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Физика движения (Этап 7)
|
||||||
|
|
||||||
|
### VInterpTo Movement System
|
||||||
|
Основная логика движения использует VInterpTo для плавного ускорения и торможения.
|
||||||
|
|
||||||
|
### E_MovementState (Movement States)
|
||||||
|
- Idle: Персонаж стоит на месте
|
||||||
|
- Walking: Движение по земле
|
||||||
|
- Airborne: В воздухе (падение/прыжок)
|
||||||
|
|
||||||
|
### Input Processing Chain
|
||||||
|
Enhanced Input → BP_MainCharacter → AC_Movement → Apply to position
|
||||||
|
|
||||||
|
### Diagonal Movement Prevention
|
||||||
|
Система нормализации input предотвращает diagonal speed boost.
|
||||||
|
|
||||||
## Математическая основа
|
## Математическая основа
|
||||||
|
|
||||||
### Расчет угла поверхности
|
### Расчет угла поверхности
|
||||||
|
|
@ -165,6 +185,12 @@ TestCases: S_SurfaceTestCase[] = [
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### FT_BasicMovement (Movement Physics)
|
||||||
|
Тестирует acceleration, friction, state transitions
|
||||||
|
|
||||||
|
### FT_DiagonalMovement (Input Normalization)
|
||||||
|
Тестирует предотвращение diagonal speed boost
|
||||||
|
|
||||||
### Test Coverage
|
### Test Coverage
|
||||||
- **100% методов:** Все публичные функции покрыты тестами
|
- **100% методов:** Все публичные функции покрыты тестами
|
||||||
- **Boundary testing:** Проверка поведения на границах диапазонов
|
- **Boundary testing:** Проверка поведения на границах диапазонов
|
||||||
|
|
@ -217,7 +243,7 @@ ClassifySurface(SurfaceNormal: Vector, AngleThresholds: S_AngleThresholds): E_Su
|
||||||
```typescript
|
```typescript
|
||||||
readonly MovementConstants: S_MovementConstants = {
|
readonly MovementConstants: S_MovementConstants = {
|
||||||
MaxSpeed: 600.0, // Максимальная скорость персонажа
|
MaxSpeed: 600.0, // Максимальная скорость персонажа
|
||||||
Acceleration: 2000.0, // Ускорение при движении
|
Acceleration: 10.0, // Ускорение при движении
|
||||||
Friction: 8.0, // Коэффициент трения
|
Friction: 8.0, // Коэффициент трения
|
||||||
Gravity: 980.0 // Сила гравитации (UE units/s²)
|
Gravity: 980.0 // Сила гравитации (UE units/s²)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
// Movement/Tests/FT_BasicMovement.ts
|
||||||
|
|
||||||
|
import { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
|
import { E_MovementState } from '#root/Movement/Enums/E_MovementState.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Basic Movement System
|
||||||
|
* Tests fundamental movement mechanics: acceleration, friction, max speed
|
||||||
|
* Validates movement state transitions and input processing
|
||||||
|
*/
|
||||||
|
export class FT_BasicMovement extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates basic movement functionality
|
||||||
|
* Tests initialization, input processing, state management
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
// Initialize movement system
|
||||||
|
this.MovementComponent.InitializeMovementSystem();
|
||||||
|
|
||||||
|
// Test 1: Initialization
|
||||||
|
if (this.MovementComponent.IsInitialized) {
|
||||||
|
// Test 2: Initial state should be Idle
|
||||||
|
if (this.MovementComponent.MovementState === E_MovementState.Idle) {
|
||||||
|
// Test 3: Process forward input and verify acceleration
|
||||||
|
this.MovementComponent.ProcessMovementInput(
|
||||||
|
new Vector(1.0, 0, 0), // Forward input
|
||||||
|
0.016 // 60 FPS delta
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.MovementComponent.CurrentVelocity.X > 0) {
|
||||||
|
// Test 4: Movement state should change to Walking
|
||||||
|
if (
|
||||||
|
(this.MovementComponent.MovementState as string) ===
|
||||||
|
(E_MovementState.Walking as string)
|
||||||
|
) {
|
||||||
|
// Test 5: Process multiple frames to test max speed limit
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
this.MovementComponent.ProcessMovementInput(
|
||||||
|
new Vector(1.0, 0, 0),
|
||||||
|
0.016
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify max speed is not exceeded
|
||||||
|
if (
|
||||||
|
this.MovementComponent.CurrentSpeed <=
|
||||||
|
this.MovementComponent.MovementConstants.MaxSpeed + 1
|
||||||
|
) {
|
||||||
|
// Test 6: Test friction by removing input
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
this.MovementComponent.ProcessMovementInput(
|
||||||
|
new Vector(0, 0, 0), // No input
|
||||||
|
0.016
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify friction slowed down the character
|
||||||
|
if (this.MovementComponent.CurrentSpeed < 50) {
|
||||||
|
// Test 7: Verify state changed back to Idle when stopped
|
||||||
|
if (
|
||||||
|
this.MovementComponent.MovementState === E_MovementState.Idle
|
||||||
|
) {
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Movement state should be Idle when stopped, got ${this.MovementComponent.MovementState as string}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Friction should reduce speed, current speed: ${this.MovementComponent.CurrentSpeed}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Speed ${this.MovementComponent.CurrentSpeed} exceeds MaxSpeed ${this.MovementComponent.MovementConstants.MaxSpeed}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Movement state should be Walking with input, got ${this.MovementComponent.MovementState}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
'Forward input should produce forward velocity'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Initial movement state should be Idle, got ${this.MovementComponent.MovementState}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
'Movement system failed to initialize'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement system component - component under test
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private MovementComponent = new AC_Movement();
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Movement/Tests/FT_DiagonalMovement.ts
|
||||||
|
|
||||||
|
import { AC_Movement } from '#root/Movement/Components/AC_Movement.ts';
|
||||||
|
import { EFunctionalTestResult } from '#root/UE/EFunctionalTestResult.ts';
|
||||||
|
import { FunctionalTest } from '#root/UE/FunctionalTest.ts';
|
||||||
|
import { MathLibrary } from '#root/UE/MathLibrary.ts';
|
||||||
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Test: Diagonal Movement Speed Control
|
||||||
|
* Tests that diagonal movement is not faster than cardinal movement
|
||||||
|
* Validates input normalization prevents diagonal speed boost
|
||||||
|
*/
|
||||||
|
export class FT_DiagonalMovement extends FunctionalTest {
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// GRAPHS
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
// EventGraph
|
||||||
|
// ────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test execution - validates diagonal movement speed control
|
||||||
|
* Tests cardinal vs diagonal movement speeds
|
||||||
|
*/
|
||||||
|
EventStartTest(): void {
|
||||||
|
// Initialize movement system
|
||||||
|
this.MovementComponent.InitializeMovementSystem();
|
||||||
|
|
||||||
|
// Test 1: Cardinal movement (forward only)
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
this.MovementComponent.ProcessMovementInput(
|
||||||
|
new Vector(1.0, 0, 0), // Pure forward
|
||||||
|
0.016
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const cardinalSpeed = this.MovementComponent.CurrentSpeed;
|
||||||
|
|
||||||
|
// Reset velocity
|
||||||
|
this.MovementComponent.CurrentVelocity = new Vector(0, 0, 0);
|
||||||
|
|
||||||
|
// Test 2: Diagonal movement (forward + right)
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
this.MovementComponent.ProcessMovementInput(
|
||||||
|
new Vector(1.0, 1.0, 0), // Diagonal input
|
||||||
|
0.016
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const diagonalSpeed = this.MovementComponent.CurrentSpeed;
|
||||||
|
|
||||||
|
// Test 3: Diagonal should not be faster than cardinal
|
||||||
|
if (diagonalSpeed <= cardinalSpeed + 1) {
|
||||||
|
// Small tolerance for floating point
|
||||||
|
// Test 4: Both speeds should be close to MaxSpeed
|
||||||
|
if (
|
||||||
|
cardinalSpeed >=
|
||||||
|
this.MovementComponent.MovementConstants.MaxSpeed - 50 &&
|
||||||
|
diagonalSpeed >= this.MovementComponent.MovementConstants.MaxSpeed - 50
|
||||||
|
) {
|
||||||
|
// Test 5: Test input normalization directly
|
||||||
|
const rawDiagonalInput = new Vector(1.0, 1.0, 0);
|
||||||
|
const inputMagnitude = MathLibrary.VectorLength(rawDiagonalInput);
|
||||||
|
|
||||||
|
if (inputMagnitude > 1.0) {
|
||||||
|
// This confirms our diagonal input needs normalization
|
||||||
|
this.FinishTest(EFunctionalTestResult.Succeeded);
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Diagonal input magnitude should be > 1.0, got ${inputMagnitude}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Speeds too low: Cardinal=${cardinalSpeed}, Diagonal=${diagonalSpeed}, Expected~${this.MovementComponent.MovementConstants.MaxSpeed}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.FinishTest(
|
||||||
|
EFunctionalTestResult.Failed,
|
||||||
|
`Diagonal speed ${diagonalSpeed} exceeds cardinal speed ${cardinalSpeed}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
// VARIABLES
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement system component - component under test
|
||||||
|
* @category Components
|
||||||
|
*/
|
||||||
|
private MovementComponent = new AC_Movement();
|
||||||
|
}
|
||||||
Binary file not shown.
BIN
Content/Movement/Tests/FT_SurfaceClassification.uasset (Stored with Git LFS)
BIN
Content/Movement/Tests/FT_SurfaceClassification.uasset (Stored with Git LFS)
Binary file not shown.
|
|
@ -2,9 +2,23 @@
|
||||||
|
|
||||||
import { Name } from '#root/UE/Name.ts';
|
import { Name } from '#root/UE/Name.ts';
|
||||||
import { UEObject } from '#root/UE/UEObject.ts';
|
import { UEObject } from '#root/UE/UEObject.ts';
|
||||||
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
export class Actor extends UEObject {
|
export class Actor extends UEObject {
|
||||||
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
constructor(outer: UEObject | null = null, name: Name | string = Name.NONE) {
|
||||||
super(outer, name);
|
super(outer, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SetActorLocation(
|
||||||
|
NewLocation: Vector = new Vector(),
|
||||||
|
Sweep: boolean = false,
|
||||||
|
Teleport: boolean = false
|
||||||
|
): void {
|
||||||
|
console.log(NewLocation, Sweep, Teleport);
|
||||||
|
// Implementation for setting actor location
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetActorLocation(): Vector {
|
||||||
|
return new Vector(); // Placeholder implementation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { BlueprintFunctionLibrary } from '#root/UE/BlueprintFunctionLibrary.ts';
|
||||||
import type { Color } from '#root/UE/Color.ts';
|
import type { Color } from '#root/UE/Color.ts';
|
||||||
import type { Float } from '#root/UE/Float.ts';
|
import type { Float } from '#root/UE/Float.ts';
|
||||||
import { LinearColor } from '#root/UE/LinearColor.ts';
|
import { LinearColor } from '#root/UE/LinearColor.ts';
|
||||||
import type { Vector } from '#root/UE/Vector.ts';
|
import { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* System Library: Core Mathematical Functions
|
* System Library: Core Mathematical Functions
|
||||||
|
|
@ -161,6 +161,52 @@ class MathLibraryClass extends BlueprintFunctionLibrary {
|
||||||
public abs(Value: Float): Float {
|
public abs(Value: Float): Float {
|
||||||
return Math.abs(Value);
|
return Math.abs(Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpolate a vector towards a target vector
|
||||||
|
* @param Current - Current vector
|
||||||
|
* @param Target - Target vector
|
||||||
|
* @param DeltaTime - Time since last update
|
||||||
|
* @param InterpSpeed - Speed of interpolation
|
||||||
|
* @returns New interpolated vector
|
||||||
|
* @example
|
||||||
|
* // Interpolate (0,0,0) towards (10,10,10) over 1 second at speed 5
|
||||||
|
* VInterpTo(new Vector(0,0,0), new Vector(10,10,10), 1, 5) // returns Vector(5,5,5)
|
||||||
|
*/
|
||||||
|
public VInterpTo(
|
||||||
|
Current: Vector = new Vector(0, 0, 0),
|
||||||
|
Target: Vector = new Vector(0, 0, 0),
|
||||||
|
DeltaTime: Float = 0,
|
||||||
|
InterpSpeed: Float = 0
|
||||||
|
): Vector {
|
||||||
|
return new Vector(
|
||||||
|
this.FInterpTo(Current.X, Target.X, DeltaTime, InterpSpeed),
|
||||||
|
this.FInterpTo(Current.Y, Target.Y, DeltaTime, InterpSpeed),
|
||||||
|
this.FInterpTo(Current.Z, Target.Z, DeltaTime, InterpSpeed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a vector to unit length
|
||||||
|
* @param A - Input vector
|
||||||
|
* @param Tolerance - Minimum length to avoid division by zero
|
||||||
|
* @returns Normalized vector (length 1) or zero vector if input is zero
|
||||||
|
* @example
|
||||||
|
* // Normalize vector (3,4,0)
|
||||||
|
* Normalize(new Vector(3,4,0)) // returns Vector(0.6,0.8,0)
|
||||||
|
*/
|
||||||
|
public Normal(
|
||||||
|
A: Vector = new Vector(0, 0, 0),
|
||||||
|
Tolerance: Float = 0.0001
|
||||||
|
): Vector {
|
||||||
|
const length = this.VectorLength(A);
|
||||||
|
|
||||||
|
if (length > Tolerance) {
|
||||||
|
return new Vector(A.X / length, A.Y / length, A.Z / length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Vector(0, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// UE/StringLibrary.ts
|
// UE/StringLibrary.ts
|
||||||
|
|
||||||
import { BlueprintFunctionLibrary } from '#root/UE/BlueprintFunctionLibrary.ts';
|
import { BlueprintFunctionLibrary } from '#root/UE/BlueprintFunctionLibrary.ts';
|
||||||
|
import type { Vector } from '#root/UE/Vector.ts';
|
||||||
|
|
||||||
class StringLibraryClass extends BlueprintFunctionLibrary {
|
class StringLibraryClass extends BlueprintFunctionLibrary {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -13,6 +14,10 @@ class StringLibraryClass extends BlueprintFunctionLibrary {
|
||||||
public Append(A: string, B: string): string {
|
public Append(A: string, B: string): string {
|
||||||
return A + B;
|
return A + B;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConvVectorToString(InVector: Vector): string {
|
||||||
|
return `X=${InVector.X}, Y=${InVector.Y}, Z=${InVector.Z}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StringLibrary = new StringLibraryClass();
|
export const StringLibrary = new StringLibraryClass();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue