[code] Stage 1 Complete: Surface Classification System
- Implemented deterministic surface classification (Walkable/SteepSlope/Wall/Ceiling) - Added comprehensive test suite (10 automated tests) - Performance optimized with cached angle thresholds in radians - Full documentation and inline comments added - All acceptance criteria metmain
commit
4e1e7be2df
|
|
@ -0,0 +1,24 @@
|
|||
# Unreal Engine file types.
|
||||
*.uasset filter=lfs diff=lfs merge=lfs -text
|
||||
*.umap filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Raw Content file types.
|
||||
*.3ds filter=lfs diff=lfs merge=lfs -text
|
||||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||
*.exr filter=lfs diff=lfs merge=lfs -text
|
||||
*.fbx filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.mov filter=lfs diff=lfs merge=lfs -text
|
||||
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
||||
*.obj filter=lfs diff=lfs merge=lfs -text
|
||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.psd filter=lfs diff=lfs merge=lfs -text
|
||||
*.tga filter=lfs diff=lfs merge=lfs -text
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
*.xcf filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Anything in `/RawContent` dir.
|
||||
/RawContent/**/* filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Ignore all files by default, but scan all directories.
|
||||
*
|
||||
!*/
|
||||
|
||||
# Do not ignore git files in the root of the repo.
|
||||
!/.git*
|
||||
|
||||
# Do not ignore current project's `.uproject`.
|
||||
!/*.uproject
|
||||
|
||||
# Do not ignore source, config, plugins and documentation dirs.
|
||||
!/Source/**
|
||||
!/Config/**
|
||||
!/Plugins/**
|
||||
!/Documentation/**
|
||||
|
||||
# Only allow .uasset, .umap and .ts files from /Content dir.
|
||||
# .uasset and .umap tracked by git-lfs, don't forget to track
|
||||
# other files if adding them here.
|
||||
!/Content/**/*.uasset
|
||||
!/Content/**/*.umap
|
||||
!/Content/**/*.ts
|
||||
|
||||
# Allow any files from /RawContent dir.
|
||||
# Any file in /RawContent dir will be managed by git lfs.
|
||||
!/RawContent/**/*
|
||||
|
||||
# OS/platform generated files.
|
||||
|
||||
# Windows
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Mac OS X
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
._*
|
||||
|
||||
# Linux
|
||||
*~
|
||||
.directory
|
||||
|
||||
# vim
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
|
||||
# Visual Studio
|
||||
.vs
|
||||
|
||||
# IDEA
|
||||
.idea
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
GameDefaultMap=/Engine/Maps/Templates/OpenWorld
|
||||
|
||||
[/Script/Engine.RendererSettings]
|
||||
r.AllowStaticLighting=False
|
||||
|
||||
r.GenerateMeshDistanceFields=True
|
||||
|
||||
r.DynamicGlobalIlluminationMethod=1
|
||||
|
||||
r.ReflectionMethod=1
|
||||
|
||||
r.SkinCache.CompileShaders=True
|
||||
|
||||
r.RayTracing=True
|
||||
|
||||
r.RayTracing.RayTracingProxies.ProjectEnabled=True
|
||||
|
||||
r.Shadow.Virtual.Enable=1
|
||||
|
||||
r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
|
||||
|
||||
r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
|
||||
|
||||
r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
|
||||
|
||||
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
-D3D12TargetedShaderFormats=PCD3D_SM5
|
||||
+D3D12TargetedShaderFormats=PCD3D_SM6
|
||||
-D3D11TargetedShaderFormats=PCD3D_SM5
|
||||
+D3D11TargetedShaderFormats=PCD3D_SM5
|
||||
Compiler=Default
|
||||
AudioSampleRate=48000
|
||||
AudioCallbackBufferFrameSize=1024
|
||||
AudioNumBuffersToEnqueue=1
|
||||
AudioMaxChannels=0
|
||||
AudioNumSourceWorkers=4
|
||||
SpatializationPlugin=
|
||||
SourceDataOverridePlugin=
|
||||
ReverbPlugin=
|
||||
OcclusionPlugin=
|
||||
CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0)
|
||||
CacheSizeKB=65536
|
||||
MaxChunkSizeOverrideKB=0
|
||||
bResampleForDevice=False
|
||||
MaxSampleRate=48000.000000
|
||||
HighSampleRate=32000.000000
|
||||
MedSampleRate=24000.000000
|
||||
LowSampleRate=12000.000000
|
||||
MinSampleRate=8000.000000
|
||||
CompressionQualityModifier=1.000000
|
||||
AutoStreamingThreshold=0.000000
|
||||
SoundCueCookQualityIndex=-1
|
||||
|
||||
[/Script/LinuxTargetPlatform.LinuxTargetSettings]
|
||||
-TargetedRHIs=SF_VULKAN_SM5
|
||||
+TargetedRHIs=SF_VULKAN_SM6
|
||||
|
||||
[/Script/HardwareTargeting.HardwareTargetingSettings]
|
||||
TargetedHardwareClass=Desktop
|
||||
AppliedTargetedHardwareClass=Desktop
|
||||
DefaultGraphicsPerformance=Maximum
|
||||
AppliedDefaultGraphicsPerformance=Maximum
|
||||
|
||||
[/Script/WorldPartitionEditor.WorldPartitionEditorSettings]
|
||||
CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet'
|
||||
|
||||
[/Script/Engine.UserInterfaceSettings]
|
||||
bAuthorizeAutomaticWidgetVariableCreation=False
|
||||
FontDPIPreset=Standard
|
||||
FontDPI=72
|
||||
|
||||
[/Script/Engine.Engine]
|
||||
+ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/TengriPlatformer")
|
||||
+ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/TengriPlatformer")
|
||||
|
||||
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
|
||||
bEnablePlugin=True
|
||||
bAllowNetworkConnection=True
|
||||
SecurityToken=E22FC6A74FF0CDC2EF4851A56AD442B8
|
||||
bIncludeInShipping=False
|
||||
bAllowExternalStartInShipping=False
|
||||
bCompileAFSProject=False
|
||||
bUseCompression=False
|
||||
bLogFiles=False
|
||||
bReportStats=False
|
||||
ConnectionType=USBOnly
|
||||
bUseManualIPAddress=False
|
||||
ManualIPAddress=
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
|
||||
[/Script/CommonUI.CommonUISettings]
|
||||
CommonButtonAcceptKeyHandling=TriggerClick
|
||||
|
||||
[/Script/EngineSettings.GeneralProjectSettings]
|
||||
ProjectID=56CEA3524FAE49EC0DF6D8A5178FEC04
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
[/Script/Engine.InputSettings]
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
bAltEnterTogglesFullscreen=True
|
||||
bF11TogglesFullscreen=True
|
||||
bUseMouseForTouch=False
|
||||
bEnableMouseSmoothing=True
|
||||
bEnableFOVScaling=True
|
||||
bCaptureMouseOnLaunch=True
|
||||
bEnableLegacyInputScales=True
|
||||
bEnableMotionControls=True
|
||||
bFilterInputByPlatformUser=False
|
||||
bShouldFlushPressedKeysOnViewportFocusLost=True
|
||||
bAlwaysShowTouchInterface=False
|
||||
bShowConsoleOnFourFingerTap=True
|
||||
bEnableGestureRecognizer=False
|
||||
bUseAutocorrect=False
|
||||
DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
|
||||
DefaultViewportMouseLockMode=LockOnCapture
|
||||
FOVScale=0.011110
|
||||
DoubleClickTime=0.200000
|
||||
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
|
||||
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
|
||||
DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
|
||||
-ConsoleKeys=Tilde
|
||||
+ConsoleKeys=Tilde
|
||||
|
||||
Binary file not shown.
BIN
Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset (Stored with Git LFS)
Normal file
BIN
Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset (Stored with Git LFS)
Normal file
BIN
Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset (Stored with Git LFS)
Normal file
BIN
Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,13 @@
|
|||
// Content/Movement/Blueprints/BP_MainCharacter.ts
|
||||
|
||||
import {AC_Movement} from "../Components/AC_Movement.js";
|
||||
|
||||
export class BP_MainCharacter {
|
||||
// Components
|
||||
MovementComponent = new AC_Movement();
|
||||
|
||||
// EventGraph
|
||||
EventBeginPlay() {
|
||||
this.MovementComponent.InitializeMovementSystem();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,235 @@
|
|||
// Content/Movement/Components/AC_Movement.ts
|
||||
|
||||
import type {S_MovementConstants} from "../Structs/S_MovementConstants.js";
|
||||
import type {S_AngleThresholds} from "../Structs/S_AngleThresholds.js";
|
||||
import type {Float, Vector} from "../../types.js";
|
||||
import {acos, cos, D2R, Dot, Print, sin} from "../../functions.js";
|
||||
import {E_SurfaceType} from "../Enums/E_SurfaceType.js";
|
||||
import type {S_SurfaceTestCase} from "../Structs/S_SurfaceTestCase.js";
|
||||
|
||||
export class AC_Movement {
|
||||
// === Movement configuration exposed to designers ===
|
||||
// Category: "Movement Config"
|
||||
// Instance editable: true
|
||||
readonly MovementConstants: S_MovementConstants = {
|
||||
MaxSpeed: 600.0,
|
||||
Acceleration: 2000.0,
|
||||
Friction: 8.0,
|
||||
Gravity: 980.0,
|
||||
}
|
||||
|
||||
// Category: "Movement Config"
|
||||
// Instance editable: true
|
||||
readonly AngleThresholdsDegrees: S_AngleThresholds = {
|
||||
Walkable: 50.0, // degrees
|
||||
SteepSlope: 85.0, // degrees
|
||||
Wall: 95.0, // degrees
|
||||
};
|
||||
// =====================================================
|
||||
|
||||
// Category: "Debug"
|
||||
IsInitialized: boolean = false;
|
||||
|
||||
// Category: "Debug"
|
||||
// Instance editable: true
|
||||
ShowDebugInfo: boolean = true;
|
||||
|
||||
// === Runtime cached values for performance (radians) ===
|
||||
// Category: "InternalCache"
|
||||
AngleThresholdsRads: S_AngleThresholds;
|
||||
// =====================================================
|
||||
|
||||
// Category: "Math"
|
||||
// Pure: true
|
||||
/**
|
||||
* Calculate angle between two normalized vectors
|
||||
* @param Vector1 - First normalized vector
|
||||
* @param Vector2 - Second normalized vector
|
||||
* @returns Angle between vectors in radians (0 to π)
|
||||
* @example
|
||||
* // 90° angle between X and Z axes
|
||||
* GetAngleBetweenVectors([1,0,0], [0,0,1]) // returns π/2
|
||||
*/
|
||||
GetAngleBetweenVectors(Vector1: Vector, Vector2: Vector) {
|
||||
return acos(Dot(Vector1, Vector2));
|
||||
}
|
||||
|
||||
// Category: "Math"
|
||||
// Pure: true
|
||||
/**
|
||||
* Calculate angle between surface normal and up vector
|
||||
* @param SurfaceNormal - Normalized surface normal vector
|
||||
* @returns Angle from horizontal plane in radians (0 = flat, π/2 = vertical)
|
||||
* @example
|
||||
* // Flat surface
|
||||
* GetSurfaceAngle([0,0,1]) // returns 0
|
||||
* // Vertical wall
|
||||
* GetSurfaceAngle([1,0,0]) // returns π/2
|
||||
*/
|
||||
GetSurfaceAngle(SurfaceNormal: Vector): Float {
|
||||
return this.GetAngleBetweenVectors(SurfaceNormal, [0.0, 0.0, 1.0]);
|
||||
}
|
||||
|
||||
// Category: "Math"
|
||||
// Pure: true
|
||||
/**
|
||||
* Generate surface normal vector from angle in degrees
|
||||
* @param AngleDegrees - Angle from horizontal in degrees (0-180)
|
||||
* @returns Normalized surface normal vector
|
||||
* @example
|
||||
* // Flat surface (0°)
|
||||
* GenerateNormalFromAngle(0) // returns [0,0,1]
|
||||
* // Vertical wall (90°)
|
||||
* GenerateNormalFromAngle(90) // returns [1,0,0]
|
||||
*/
|
||||
GenerateNormalFromAngle(AngleDegrees: Float): Vector {
|
||||
const AngleRads = D2R(AngleDegrees);
|
||||
return [sin(AngleRads), 0.0, cos(AngleRads)];
|
||||
}
|
||||
|
||||
// Category: "Surface Detection"
|
||||
// Pure: true
|
||||
/**
|
||||
* Classify surface type based on normal vector
|
||||
* @param SurfaceNormal - Normalized surface normal vector
|
||||
* @param AngleThresholds - Angle thresholds in radians
|
||||
* @returns Surface type classification
|
||||
* @example
|
||||
* // Classify flat ground
|
||||
* ClassifySurface([0,0,1], thresholds) // returns E_SurfaceType.Walkable
|
||||
*/
|
||||
ClassifySurface(SurfaceNormal: Vector, AngleThresholds: S_AngleThresholds): E_SurfaceType {
|
||||
const SurfaceAngle = this.GetSurfaceAngle(SurfaceNormal);
|
||||
|
||||
if (SurfaceAngle <= AngleThresholds.Walkable) {
|
||||
return E_SurfaceType.Walkable;
|
||||
} else if (SurfaceAngle <= AngleThresholds.SteepSlope) {
|
||||
return E_SurfaceType.SteepSlope;
|
||||
} else if (SurfaceAngle <= AngleThresholds.Wall) {
|
||||
return E_SurfaceType.Wall;
|
||||
} else {
|
||||
return E_SurfaceType.Ceiling;
|
||||
}
|
||||
}
|
||||
|
||||
// Category: "Surface Detection"
|
||||
// Pure: true
|
||||
/**
|
||||
* Check if surface allows normal walking movement
|
||||
* @param SurfaceType - Surface type to check
|
||||
* @returns True if surface is walkable
|
||||
*/
|
||||
IsSurfaceWalkable(SurfaceType: E_SurfaceType): boolean {
|
||||
return SurfaceType === E_SurfaceType.Walkable;
|
||||
}
|
||||
|
||||
// Category: "Surface Detection"
|
||||
// Pure: true
|
||||
/**
|
||||
* Check if surface causes sliding behavior
|
||||
* @param SurfaceType - Surface type to check
|
||||
* @returns True if surface is steep slope
|
||||
*/
|
||||
IsSurfaceSteep(SurfaceType: E_SurfaceType): boolean {
|
||||
return SurfaceType === E_SurfaceType.SteepSlope;
|
||||
}
|
||||
|
||||
// Category: "Surface Detection"
|
||||
// Pure: true
|
||||
/**
|
||||
* Check if surface blocks movement (collision)
|
||||
* @param SurfaceType - Surface type to check
|
||||
* @returns True if surface is a wall
|
||||
*/
|
||||
IsSurfaceWall(SurfaceType: E_SurfaceType): boolean {
|
||||
return SurfaceType === E_SurfaceType.Wall;
|
||||
}
|
||||
|
||||
// Category: "Surface Detection"
|
||||
// Pure: true
|
||||
/**
|
||||
* Check if surface is overhead (ceiling)
|
||||
* @param SurfaceType - Surface type to check
|
||||
* @returns True if surface is ceiling
|
||||
*/
|
||||
IsSurfaceCeiling(SurfaceType: E_SurfaceType): boolean {
|
||||
return SurfaceType === E_SurfaceType.Ceiling;
|
||||
}
|
||||
|
||||
// Category: "Surface Detection"
|
||||
// Pure: true
|
||||
/**
|
||||
* Check if no surface detected (airborne state)
|
||||
* @param SurfaceType - Surface type to check
|
||||
* @returns True if no surface contact
|
||||
*/
|
||||
IsSurfaceNone(SurfaceType: E_SurfaceType): boolean {
|
||||
return SurfaceType === E_SurfaceType.None;
|
||||
}
|
||||
|
||||
// Category: "Debug"
|
||||
PrintDebugInfo() {
|
||||
Print('=== Movement System Initialized ===');
|
||||
Print(`Walkable: ≤ ${this.AngleThresholdsDegrees.Walkable}°, Steep: ${this.AngleThresholdsDegrees.Walkable}°-${this.AngleThresholdsDegrees.SteepSlope}°, Wall: ${this.AngleThresholdsDegrees.SteepSlope}°-${this.AngleThresholdsDegrees.Wall}°, Ceiling: >${this.AngleThresholdsDegrees.Wall}°`);
|
||||
Print(`Max Speed: ${this.MovementConstants.MaxSpeed}, Acceleration: ${this.MovementConstants.Acceleration}, Friction: ${this.MovementConstants.Friction}, Gravity: ${this.MovementConstants.Gravity}`);
|
||||
Print('==================================');
|
||||
}
|
||||
|
||||
// Category: "Debug"
|
||||
/**
|
||||
* Run comprehensive surface classification test suite
|
||||
* @returns True if all tests pass
|
||||
*/
|
||||
TestSurfaceClassification() {
|
||||
let AllTestsPassed: boolean = true;
|
||||
|
||||
const TestCases: S_SurfaceTestCase[] = [
|
||||
{ AngleDegrees: 0.0, ExpectedType: E_SurfaceType.Walkable, Description: "Flat surface" },
|
||||
{ AngleDegrees: 25.0, ExpectedType: E_SurfaceType.Walkable, Description: "Gentle slope" },
|
||||
{ AngleDegrees: 49.0, ExpectedType: E_SurfaceType.Walkable, Description: "Max walkable" },
|
||||
{ AngleDegrees: 51.0, ExpectedType: E_SurfaceType.SteepSlope, Description: "Steep slope" },
|
||||
{ AngleDegrees: 70.0, ExpectedType: E_SurfaceType.SteepSlope, Description: "Very steep" },
|
||||
{ AngleDegrees: 84.0, ExpectedType: E_SurfaceType.SteepSlope, Description: "Max steep" },
|
||||
{ AngleDegrees: 90.0, ExpectedType: E_SurfaceType.Wall, Description: "Vertical wall" },
|
||||
{ AngleDegrees: 94.0, ExpectedType: E_SurfaceType.Wall, Description: "Max wall" },
|
||||
{ AngleDegrees: 120.0, ExpectedType: E_SurfaceType.Ceiling, Description: "Overhang" },
|
||||
{ AngleDegrees: 180.0, ExpectedType: E_SurfaceType.Ceiling, Description: "Ceiling" }
|
||||
];
|
||||
|
||||
for (let i = 0; i < TestCases.length; i++) {
|
||||
const testCase = TestCases[i];
|
||||
const normal = this.GenerateNormalFromAngle(testCase.AngleDegrees);
|
||||
const result = this.ClassifySurface(normal, this.AngleThresholdsRads);
|
||||
|
||||
if (result === testCase.ExpectedType) {
|
||||
Print(`✅ Test ${i+1} PASS: ${testCase.Description} (${testCase.AngleDegrees}°) = ${testCase.ExpectedType}`);
|
||||
} else {
|
||||
Print(`❌ Test ${i+1} FAIL: ${testCase.Description} (${testCase.AngleDegrees}°) expected ${testCase.ExpectedType}, got ${result}`);
|
||||
AllTestsPassed = false;
|
||||
}
|
||||
}
|
||||
|
||||
return AllTestsPassed;
|
||||
}
|
||||
|
||||
// Category: "System"
|
||||
/**
|
||||
* Initialize movement system with angle conversion and testing
|
||||
* Converts degree thresholds to radians for runtime performance
|
||||
* Runs automated tests if debug mode enabled
|
||||
*/
|
||||
InitializeMovementSystem() {
|
||||
this.IsInitialized = true;
|
||||
|
||||
this.AngleThresholdsRads = {
|
||||
Walkable: D2R(this.AngleThresholdsDegrees.Walkable),
|
||||
SteepSlope: D2R(this.AngleThresholdsDegrees.SteepSlope),
|
||||
Wall: D2R(this.AngleThresholdsDegrees.Wall),
|
||||
};
|
||||
|
||||
if (this.ShowDebugInfo) {
|
||||
this.PrintDebugInfo();
|
||||
this.TestSurfaceClassification();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
|||
// Content/Movement/Enums/E_SurfaceType.ts
|
||||
|
||||
export enum E_SurfaceType {
|
||||
None = "None",
|
||||
Walkable = "Walkable",
|
||||
SteepSlope = "SteepSlope",
|
||||
Wall = "Wall",
|
||||
Ceiling = "Ceiling"
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
|||
// Content/Movement/Structs/S_AngleThresholds.ts
|
||||
|
||||
import type {Float} from "../../types.js";
|
||||
|
||||
export interface S_AngleThresholds {
|
||||
Walkable: Float,
|
||||
SteepSlope: Float,
|
||||
Wall: Float,
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,10 @@
|
|||
// Content/Movement/Structs/S_MovementConstants.ts
|
||||
|
||||
import type {Float} from "../../types.js";
|
||||
|
||||
export interface S_MovementConstants {
|
||||
MaxSpeed: Float;
|
||||
Acceleration: Float;
|
||||
Friction: Float;
|
||||
Gravity: Float;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,10 @@
|
|||
// Content/Movement/Structs/S_SurfaceTestCase.ts
|
||||
|
||||
import type {Float} from "../../types.js";
|
||||
import type {E_SurfaceType} from "../Enums/E_SurfaceType.js";
|
||||
|
||||
export interface S_SurfaceTestCase {
|
||||
AngleDegrees: Float;
|
||||
ExpectedType: E_SurfaceType;
|
||||
Description: string;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,30 @@
|
|||
// Content/functions.ts
|
||||
|
||||
import type {Float, Vector} from "./types.js";
|
||||
|
||||
// Math
|
||||
export function sin(x: Float): Float {
|
||||
return Math.sin(x);
|
||||
}
|
||||
|
||||
export function cos(x: Float): Float {
|
||||
return Math.cos(x);
|
||||
}
|
||||
|
||||
export function acos(x: Float): Float {
|
||||
return Math.acos(x);
|
||||
}
|
||||
|
||||
export function Dot(v1: Vector, v2: Vector): Float {
|
||||
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
|
||||
}
|
||||
|
||||
export function D2R(degrees: Float): Float {
|
||||
return degrees * (Math.PI / 180);
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
export function Print(message: string): void {
|
||||
console.log(message);
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Content/types.ts
|
||||
|
||||
export type Float = number;
|
||||
export type Vector = [Float, Float, Float];
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
[//]: # (Documentation/Algorithm_Of_Development.md)
|
||||
|
||||
# Алгоритм разработки фичи
|
||||
|
||||
## Этап 1: Планирование
|
||||
|
||||
### 1.1 Понимание требований
|
||||
**Читаем техническое задание:**
|
||||
- ✅ Что нужно реализовать
|
||||
- ✅ Входные данные
|
||||
- ✅ Выходные данные
|
||||
- ✅ Критерии успеха
|
||||
|
||||
### 1.2 Анализ архитектуры
|
||||
### 1.3 Планирование структуры файлов
|
||||
|
||||
## Этап 2: Настройка инфраструктуры
|
||||
Создание файлов, переменных, функций, настройка компонентов, разработка документации — описание системы, лог проектных решений.
|
||||
|
||||
## Этап 3: Реализация
|
||||
Реализаций функций, написание тестов
|
||||
|
||||
## Этап 4: Тестирование
|
||||
Чек-лист ручного тестирования, интеграция тестов
|
||||
|
||||
## Этап 5: Документирование
|
||||
Обновление TDD, инлайн документация
|
||||
|
||||
## Этап 6: Code Review и финализация
|
||||
|
||||
### 6.1 Самопроверка кода
|
||||
**Чеклист качества:**
|
||||
- [ ] Все переменные имеют понятные имена
|
||||
- [ ] Функции разбиты по категориям логично
|
||||
- [ ] Есть комментарии к сложным местам
|
||||
- [ ] Нет hardcoded значений
|
||||
- [ ] Тесты покрывают основную функциональность
|
||||
|
||||
### 6.2 Проверка производительности
|
||||
**Запустить Profiler и проверить:**
|
||||
- [ ] Инициализация занимает <1ms
|
||||
- [ ] Нет утечек памяти
|
||||
- [ ] FPS не проседает при запуске тестов
|
||||
|
||||
### 6.3 Финальный коммит
|
||||
**Подготовка к коммиту:**
|
||||
1. **Сохранить** все Blueprint'ы
|
||||
2. **Обновить** документацию
|
||||
3. **Запустить** все тесты финально
|
||||
4. **Сделать** git commit
|
||||
|
||||
# Шаблон этапов
|
||||
Этот алгоритм является шаблоном для всех последующих этапов:
|
||||
1. **Планирование** (понимание → архитектура → структура)
|
||||
2. **Инфраструктура** (файлы → базовая настройка → документация)
|
||||
3. **Реализация** (код → функции → интеграция)
|
||||
4. **Тестирование** (автотесты → ручные тесты → критерии)
|
||||
5. **Документирование** (TDD → комментарии → решения)
|
||||
6. **Финализация** (ревью → производительность → коммит)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[//]: # (Documentation/Movement/Architect_Principles.md)
|
||||
|
||||
# Архитектурные принципы
|
||||
- **Детерминированность** - приоритет #1 для всех решений
|
||||
- **Модульность** - каждая система независима и тестируема
|
||||
- **Производительность** - 60 FPS на целевом железе
|
||||
- **Расширяемость** - легкое добавление новых механик
|
||||
- **Отлаживаемость** - comprehensive debug tools
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
[//]: # (Documentation/Movement/Basics.md)
|
||||
|
||||
# Детерминированное управление для 3D-платформера в Unreal Engine 5
|
||||
|
||||
Необходимо реализовать детерменированное управление для 3D-платформера с помощью Blueprint в UnrealEngine 5 в стиле Super Mario Odyssey.
|
||||
|
||||
## Условия
|
||||
1. Не использовать стандартный CharacterController
|
||||
2. Не использовать стандартный Sweep
|
||||
3. Реализовать всю логику вплоть до коллизий с нуля
|
||||
|
||||
## Пояснения
|
||||
|
||||
От UE5 в моём случае требуется только отрисовка графики, возможно базовая встроенная физика, инструменты анимации. В общем всё, что касается механик должно быть написано с нуля.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
[//]: # (Documentation/Movement/ProjectDecisions/DecisionLog_01.md)
|
||||
|
||||
# Лог Проектных Решений - Этап 1
|
||||
|
||||
## Решение 1: Углы классификации
|
||||
- **Проблема:** Исходные углы (45°/90°/135°) не подходили для платформера
|
||||
- **Решение:** Обновили на 50°/85°/95°
|
||||
- **Дата:** 17.08.2025
|
||||
|
||||
## Решение 2: Floating point precision
|
||||
- **Проблема:** Тесты падали на границах углов
|
||||
- **Решение:** Используем радианы внутри, градусы снаружи
|
||||
- **Дата:** 17.08.2025
|
||||
|
||||
## Решение 3: Архитектура тестирования
|
||||
- **Проблема:** Хардкодные векторы сложно поддерживать
|
||||
- **Решение:** S_SurfaceTestCase + GenerateNormalFromAngle
|
||||
- **Дата:** 17.08.2025
|
||||
|
|
@ -0,0 +1,393 @@
|
|||
[//]: # (Documentation/Movement/Roadmap.md)
|
||||
|
||||
# Этап 1: Базовая настройка и константы
|
||||
**Цель:** Система классификации поверхностей по углам
|
||||
|
||||
**Результат:** Стабильная база для всех последующих расчетов
|
||||
|
||||
**Что реализуем:**
|
||||
- Переменные движения (MaxSpeed, Acceleration, Friction, Gravity)
|
||||
- Система углов поверхностей (Walkable ≤50°, SteepSlope ≤85°, Wall ≤95°, Ceiling >95°)
|
||||
- Конвертация градусы ↔ радианы
|
||||
- Функции инициализации и тестирования констант
|
||||
- Enhanced Input System интеграция
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Корректная конвертация углов (точность <0.001)
|
||||
- ✅ Все константы инициализируются при старте
|
||||
- ✅ Debug вывод показывает правильные значения
|
||||
- ✅ Автоматические тесты проходят
|
||||
|
||||
---
|
||||
|
||||
# Этап 2: Debug HUD система
|
||||
**Цель:** Статичный debug вывод для удобной отладки
|
||||
|
||||
**Результат:** Профессиональная debug система
|
||||
|
||||
**Что реализуем:**
|
||||
- Цветовое кодирование разных типов информации
|
||||
- Функции переключения debug режимов
|
||||
- Контроль частоты обновления HUD
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Информация отображается статично на экране
|
||||
- ✅ Цветовая дифференциация работает
|
||||
- ✅ Легкое включение/выключение debug режимов
|
||||
- ✅ Нет влияния на производительность
|
||||
|
||||
---
|
||||
|
||||
# Этап 3: Детекция поверхностей
|
||||
**Цель:** Надежное определение типа поверхности под персонажем
|
||||
|
||||
**Результат:** Стабильная классификация Walkable/SteepSlope/Wall/Ceiling
|
||||
|
||||
**Что реализуем:**
|
||||
- Функции классификации поверхности по нормали
|
||||
- Функции запросов состояния (IsSurfaceWalkable, IsSurfaceSteep, etc.)
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Точная классификация поверхностей по углам
|
||||
- ✅ Стабильное определение типа поверхности
|
||||
- ✅ Корректная работа с нормалями поверхностей
|
||||
- ✅ Детальная debug информация
|
||||
|
||||
---
|
||||
|
||||
# Этап 3: Базовое движение по земле
|
||||
**Цель:** Плавное детерминированное движение по плоским поверхностям
|
||||
|
||||
**Результат:** Отзывчивое управление без рывков и заиканий
|
||||
|
||||
**Что реализуем:**
|
||||
- VInterpTo для плавного ускорения и торможения
|
||||
- Применение гравитации с правильным обнулением на земле
|
||||
- Горизонтальное движение только на walkable поверхностях
|
||||
- Ограничение максимальной скорости
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Плавное ускорение при нажатии WASD и стика геймпада
|
||||
- ✅ Плавное торможение при отпускании клавиш/стика геймпада
|
||||
- ✅ Скорость не превышает MaxSpeed
|
||||
- ✅ Диагональное движение не быстрее прямого
|
||||
- ✅ Стабильное поведение на земле
|
||||
|
||||
---
|
||||
|
||||
# Этап 4: Поворот персонажа вслед за движением
|
||||
**Цель:** Плавный поворот персонажа в сторону движения
|
||||
|
||||
**Результат:** Персонаж естественно реагирует на направление движения
|
||||
|
||||
**Что реализуем:**
|
||||
- При использовании мыши или стика геймпада персонаж поворачивается в сторону движения
|
||||
- Учет наклона камеры для корректного поворота
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Персонаж плавно поворачивается в сторону движения
|
||||
- ✅ Поворот учитывает наклон камеры
|
||||
- ✅ Плавный переход между направлениями
|
||||
- ✅ Нет рывков при повороте
|
||||
- ✅ Персонаж не поворачивается, если не движется
|
||||
- ✅ Поворот не влияет на скорость движения
|
||||
|
||||
---
|
||||
|
||||
# Этап 5: Детерминированный Sweep collision
|
||||
**Цель:** Полное устранение tunneling через stepped collision detection
|
||||
**Результат:** Bullet-proof система коллизий
|
||||
|
||||
**Что реализуем:**
|
||||
- PerformDeterministicSweep с пошаговой проверкой
|
||||
- HandleSweepCollision для обработки ударов
|
||||
- Адаптивный размер шагов sweep
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Полное отсутствие tunneling при любых скоростях
|
||||
- ✅ Стабильная Z позиция (разброс <0.5 единиц)
|
||||
- ✅ Детерминированность (100% воспроизводимость)
|
||||
- ✅ Performance <25 collision checks за кадр
|
||||
|
||||
---
|
||||
|
||||
# Этап 6: Обработка стен и углов
|
||||
**Цель:** Плавное скольжение вдоль стен без застреваний
|
||||
**Результат:** Качественная навигация в сложной геометрии
|
||||
|
||||
**Что реализуем:**
|
||||
- Wall sliding - скольжение вдоль стен
|
||||
- Corner resolution - обработка внутренних углов
|
||||
- Multi-directional sweep - несколько попыток движения
|
||||
- Edge detection и step-up механика
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Персонаж не застревает в углах
|
||||
- ✅ Плавное скольжение вдоль стен любой геометрии
|
||||
- ✅ Автоматический step-up на небольшие препятствия
|
||||
- ✅ Работает в сложных лабиринтах
|
||||
|
||||
---
|
||||
|
||||
# Этап 7: Движение по склонам
|
||||
**Цель:** Реалистичное поведение на наклонных поверхностях
|
||||
**Результат:** Естественное движение по пандусам и скатывание
|
||||
|
||||
**Что реализуем:**
|
||||
- Slope walking - движение вверх/вниз по склонам ≤45°
|
||||
- Slope sliding - скатывание с крутых поверхностей >45°
|
||||
- Ground snapping - прилипание к неровной поверхности
|
||||
- Momentum preservation на склонах
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Плавный подъем по пандусам ≤45°
|
||||
- ✅ Реалистичное скатывание с крутых склонов >45°
|
||||
- ✅ Отсутствие "прыжков" на неровностях
|
||||
- ✅ Сохранение импульса при переходах между поверхностями
|
||||
|
||||
---
|
||||
|
||||
# Этап 8: Разделение физики и рендера
|
||||
**Цель:** Детерминированная физика + плавная визуализация
|
||||
**Результат:** AAA-качество визуального движения
|
||||
|
||||
**Что реализуем:**
|
||||
- Dual position system (physics + render positions)
|
||||
- Position interpolation для плавности
|
||||
- Fixed timestep для физики (120Hz physics, variable render)
|
||||
- Smooth transitions между состояниями
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Физика остается детерминированной
|
||||
- ✅ Визуально плавное движение без микрозаиканий
|
||||
- ✅ Stable 60+ FPS без влияния на физику
|
||||
- ✅ Smooth interpolation работает корректно
|
||||
|
||||
---
|
||||
|
||||
# Этап 9: Профессиональная камера система
|
||||
**Цель:** Плавная камера уровня AAA-игр
|
||||
**Результат:** Комфортная камера без рывков
|
||||
|
||||
**Что реализуем:**
|
||||
- Camera lag и damping для плавного следования
|
||||
- Look-ahead prediction (камера смотрит вперед при движении)
|
||||
- Smooth rotation следования за поворотами
|
||||
- Dead zone для микродвижений
|
||||
- Collision avoidance для камеры
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Камера не дергается при остановке/старте
|
||||
- ✅ Плавные повороты и наклоны
|
||||
- ✅ Предсказание направления движения
|
||||
- ✅ Нет проваливания камеры в стены
|
||||
|
||||
---
|
||||
|
||||
# Этап 10: Adaptive stepping optimization
|
||||
**Цель:** Оптимизация производительности sweep системы
|
||||
**Результат:** Меньше collision checks без потери качества
|
||||
|
||||
**Что реализуем:**
|
||||
- Variable step size в зависимости от скорости
|
||||
- Субпиксельная точность для медленного движения
|
||||
- Performance monitoring и auto-tuning
|
||||
- Spatial optimization для collision queries
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ <10 collision checks при обычном движении
|
||||
- ✅ Субпиксельная точность при медленном движении
|
||||
- ✅ Автоматическая адаптация под нагрузку
|
||||
- ✅ Stable performance в сложных сценах
|
||||
|
||||
---
|
||||
|
||||
# Этап 11: Enhanced ground snapping
|
||||
**Цель:** Плавное прилипание к неровным поверхностям
|
||||
**Результат:** Персонаж идет по неровной геометрии без отрыва
|
||||
|
||||
**Что реализуем:**
|
||||
- Multi-point ground detection
|
||||
- Intelligent surface normal blending
|
||||
- Smooth height transitions
|
||||
- Predictive ground snapping
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Плавное движение по ступенькам
|
||||
- ✅ Нет отрыва от неровной поверхности
|
||||
- ✅ Smooth transitions на изменениях высоты
|
||||
- ✅ Работает на любой сложности геометрии
|
||||
|
||||
---
|
||||
|
||||
# Этап 12: Система прыжков
|
||||
**Цель:** Отзывчивое воздушное управление уровня лучших платформеров
|
||||
**Результат:** Качественный платформинг с точным контролем
|
||||
|
||||
**Что реализуем:**
|
||||
- Variable jump height (короткое/длинное нажатие)
|
||||
- Air control с ограничениями и инерцией
|
||||
- Coyote time (прыжок после покидания платформы)
|
||||
- Jump buffering (ранние нажатия прыжка)
|
||||
- Multi-jump система (опционально)
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Точный контроль высоты прыжка
|
||||
- ✅ Forgiving jump timing (coyote + buffer)
|
||||
- ✅ Responsive но не overpowered air control
|
||||
- ✅ Плавные transitions между ground/air состояниями
|
||||
|
||||
---
|
||||
|
||||
# Этап 13: Воздушная физика
|
||||
**Цель:** Реалистичная но игровая воздушная физика
|
||||
**Результат:** Естественное поведение в полете
|
||||
|
||||
**Что реализуем:**
|
||||
- Air resistance и terminal velocity
|
||||
- Wind/updraft systems
|
||||
- Gliding механика
|
||||
- Landing impact detection и анимации
|
||||
- Air-to-ground transition smoothing
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Реалистичная траектория полета
|
||||
- ✅ Плавные приземления без "хлопков"
|
||||
- ✅ Terminal velocity ограничивает падение
|
||||
- ✅ Responsive air control без нарушения физики
|
||||
|
||||
---
|
||||
|
||||
# Этап 14: Продвинутые склоны и поверхности
|
||||
**Цель:** Сложные взаимодействия с геометрией
|
||||
**Результат:** Разнообразные типы поверхностей
|
||||
|
||||
**Что реализуем:**
|
||||
- Ice surfaces (скользкие поверхности с инерцией)
|
||||
- Conveyor belts (движущиеся платформы)
|
||||
- Bouncy surfaces (отскакивающие поверхности)
|
||||
- Sticky surfaces (замедляющие движение)
|
||||
- Slope acceleration/deceleration physics
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Каждый тип поверхности ощущается уникально
|
||||
- ✅ Плавные переходы между типами поверхностей
|
||||
- ✅ Детерминированное поведение всех типов
|
||||
- ✅ Легкая настройка параметров поверхностей
|
||||
|
||||
---
|
||||
|
||||
# Этап 15: Wall interactions
|
||||
**Цель:** Продвинутые взаимодействия со стенами
|
||||
**Результат:** Wall jumping, wall sliding, wall climbing
|
||||
|
||||
**Что реализуем:**
|
||||
- Wall jumping с momentum preservation
|
||||
- Wall sliding с контролем скорости
|
||||
- Wall climbing на специальных поверхностях
|
||||
- Corner grabbing и edge detection
|
||||
- Wall run система (опционально)
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Responsive wall jumping с правильными углами
|
||||
- ✅ Контролируемое wall sliding
|
||||
- ✅ Smooth transitions wall ↔ ground ↔ air
|
||||
- ✅ Интуитивное управление wall mechanics
|
||||
|
||||
---
|
||||
|
||||
# Этап 16: Специальные движения
|
||||
**Цель:** Уникальные движения для богатого геймплея
|
||||
**Результат:** Dash, ground pound, ledge grab и другие
|
||||
|
||||
**Что реализуем:**
|
||||
- Dash/dodge с invincibility frames
|
||||
- Ground pound с area impact
|
||||
- Ledge grabbing и climbing
|
||||
- Slide/crouch движения
|
||||
- Special movement abilities
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Каждое движение ощущается impact-ful
|
||||
- ✅ Smooth combinations между движениями
|
||||
- ✅ Balanced timing и cooldowns
|
||||
- ✅ Clear visual и audio feedback
|
||||
|
||||
---
|
||||
|
||||
# Этап 17: Performance optimization
|
||||
**Цель:** 60 FPS на целевом железе в любых сценариях
|
||||
**Результат:** Оптимизированная система коллизий
|
||||
|
||||
**Что реализуем:**
|
||||
- Spatial partitioning для collision objects
|
||||
- LOD system для collision complexity
|
||||
- Multi-threading collision checks
|
||||
- Memory pool для collision queries
|
||||
- Predictive collision culling
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Stable 60+ FPS на целевом железе
|
||||
- ✅ <5ms на collision detection в worst case
|
||||
- ✅ Scalable performance до 100+ collision objects
|
||||
- ✅ Minimal memory allocations в runtime
|
||||
|
||||
---
|
||||
|
||||
# Этап 18: Debug и профилирование tools
|
||||
**Цель:** Профессиональные инструменты для тонкой настройки
|
||||
**Результат:** Полный контроль над системой
|
||||
|
||||
**Что реализуем:**
|
||||
- Visual collision debugging (rays, sweeps, contacts)
|
||||
- Real-time performance metrics и profiling
|
||||
- Tweakable parameters в runtime через UI
|
||||
- Automated testing suite для regression testing
|
||||
- Replay system для детерминированности
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Visual debugging показывает все collision queries
|
||||
- ✅ Real-time параметры настраиваются без restart
|
||||
- ✅ Performance metrics точные и useful
|
||||
- ✅ Automated tests покрывают все основные сценарии
|
||||
|
||||
---
|
||||
|
||||
# Этап 19: Edge cases и stress testing
|
||||
**Цель:** Bullet-proof система для любых условий
|
||||
**Результат:** Система работает в экстремальных сценариях
|
||||
|
||||
**Что реализуем:**
|
||||
- Extreme velocity testing (10000+ units/sec)
|
||||
- Complex geometry stress tests
|
||||
- Memory leak detection и prevention
|
||||
- Determinism verification tools
|
||||
- Edge case handling (NaN, infinity, etc.)
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Система не ломается при экстремальных значениях
|
||||
- ✅ No memory leaks при длительной работе
|
||||
- ✅ Determinism поддерживается в любых условиях
|
||||
- ✅ Graceful degradation при перегрузке
|
||||
|
||||
---
|
||||
|
||||
# Этап 20: User experience polish
|
||||
**Время:** 3-4 дня | **Сложность:** Средняя
|
||||
**Цель:** Finalized user experience
|
||||
**Результат:** Система ощущается как в коммерческой игре
|
||||
|
||||
**Что реализуем:**
|
||||
- Input buffering и prediction
|
||||
- Haptic feedback integration (геймпады)
|
||||
- Audio feedback integration для movement
|
||||
- Visual effects integration (dust, particles)
|
||||
- Accessibility options
|
||||
|
||||
**Критерии успеха:**
|
||||
- ✅ Controls ощущаются максимально responsive
|
||||
- ✅ Rich feedback для всех действий
|
||||
- ✅ Поддержка различных input методов
|
||||
- ✅ Accessibility options работают корректно
|
||||
|
||||
---
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
[//]: # (Documentation/Movement/TechnicalDesign/TDD.md)
|
||||
|
||||
# Система Движения - Техническая Документация
|
||||
|
||||
## Этап 1: Базовая настройка и константы ✅
|
||||
|
||||
### Обзор
|
||||
Детерминированная система движения для 3D-платформера в стиле Super Mario Odyssey.
|
||||
|
||||
### Система Классификации Поверхностей
|
||||
- **Walkable:** 0° - 50° (обычное движение)
|
||||
- **SteepSlope:** 50° - 85° (механика скольжения)
|
||||
- **Wall:** 85° - 95° (блокировка коллизий)
|
||||
- **Ceiling:** >95° (потолочные поверхности)
|
||||
|
||||
### Соображения Производительности
|
||||
- Чистые функции для математических операций (готовы к миграции в C++)
|
||||
- Кэшированные пороги углов в радианах
|
||||
- Единое Math Expression для расчёта углов
|
||||
|
||||
### Структура Файлов
|
||||
```
|
||||
Content\
|
||||
Movement\
|
||||
Blueprints\
|
||||
BP_MainCharacter
|
||||
Components\
|
||||
AC_Movement
|
||||
Enums\
|
||||
E_SurfaceType
|
||||
Structs\
|
||||
S_AngleThresholds
|
||||
S_MovementConstants
|
||||
S_SurfaceTestCase
|
||||
```
|
||||
|
||||
### Покрытие тестами
|
||||
10 автоматизированных тестовых случаев, покрывающих граничные условия и краевые случаи.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
[//]: # (Documentation/Movement/Testing/TestResults_01.md)
|
||||
|
||||
# Результаты Тестирования - Этап 1
|
||||
|
||||
## Автоматические тесты: ✅ PASS
|
||||
- Test 1-10: Все тесты классификации поверхностей прошли
|
||||
|
||||
## Ручные тесты: ✅ PASS
|
||||
- [x] Персонаж спавнится без ошибок
|
||||
- [x] Debug информация выводится корректно
|
||||
- [x] Нет warning'ов в Output Log
|
||||
- [x] FPS стабильный
|
||||
|
||||
## Критерии успеха: ✅ ВЫПОЛНЕНО
|
||||
- [x] Корректная конвертация углов (точность <0.001)
|
||||
- [x] Все константы инициализируются при старте
|
||||
- [x] Debug вывод показывает правильные значения
|
||||
- [x] Автоматические тесты проходят
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "5.6",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "ModelingToolsEditorMode",
|
||||
"Enabled": true,
|
||||
"TargetAllowList": [
|
||||
"Editor"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue