commit 4e1e7be2df15092bf0d7bbc08d50f0b2723319c2 Author: Nikolay Petrov Date: Sun Aug 17 23:39:24 2025 +0500 [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 met diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c0f6ce8 --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de151b3 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini new file mode 100644 index 0000000..e69de29 diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini new file mode 100644 index 0000000..4e2793a --- /dev/null +++ b/Config/DefaultEngine.ini @@ -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= + diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini new file mode 100644 index 0000000..df039ef --- /dev/null +++ b/Config/DefaultGame.ini @@ -0,0 +1,7 @@ + + +[/Script/CommonUI.CommonUISettings] +CommonButtonAcceptKeyHandling=TriggerClick + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=56CEA3524FAE49EC0DF6D8A5178FEC04 diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini new file mode 100644 index 0000000..a919105 --- /dev/null +++ b/Config/DefaultInput.ini @@ -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 + diff --git a/Content/LevelPrototyping/Materials/MF_ProcGrid.uasset b/Content/LevelPrototyping/Materials/MF_ProcGrid.uasset new file mode 100644 index 0000000..767cf20 --- /dev/null +++ b/Content/LevelPrototyping/Materials/MF_ProcGrid.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa5d22837af536b9690ed2f790e288a59ca659c63e669fca99685afc2364ce82 +size 48741 diff --git a/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset new file mode 100644 index 0000000..3fd81bc --- /dev/null +++ b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65022a590b73953153898dfd370aa8c2609a84670cdb06dd205d813712d1c1a4 +size 12785 diff --git a/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset new file mode 100644 index 0000000..d727d35 --- /dev/null +++ b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa0f2a06a7d937127d7d737c13bacb3f8fe88c8e0479f5e6b7cda81fd225d077 +size 11922 diff --git a/Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset new file mode 100644 index 0000000..1eae7f9 --- /dev/null +++ b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:838034e5afc7ab3a70bfff6de2e1237aa02337fb423e95c1b536b3e30c61c982 +size 11957 diff --git a/Content/LevelPrototyping/Materials/MI_Solid_Blue.uasset b/Content/LevelPrototyping/Materials/MI_Solid_Blue.uasset new file mode 100644 index 0000000..a6e284e --- /dev/null +++ b/Content/LevelPrototyping/Materials/MI_Solid_Blue.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:417952e4c6986a99f1bc3fb25ebceb1308806028bd2b602501b66b874aa87cc6 +size 8669 diff --git a/Content/LevelPrototyping/Materials/M_PrototypeGrid.uasset b/Content/LevelPrototyping/Materials/M_PrototypeGrid.uasset new file mode 100644 index 0000000..32a19d9 --- /dev/null +++ b/Content/LevelPrototyping/Materials/M_PrototypeGrid.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1a5b000cfbe3030ff6f0bb67a2e4cc4bb803769d74ebd88d54f98918616d2d1 +size 40328 diff --git a/Content/LevelPrototyping/Materials/M_Solid.uasset b/Content/LevelPrototyping/Materials/M_Solid.uasset new file mode 100644 index 0000000..abe7dda --- /dev/null +++ b/Content/LevelPrototyping/Materials/M_Solid.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d754d45a7f34228c043d5389af994f3f6c9d6e4bd579bba6c65f11813db11662 +size 9609 diff --git a/Content/LevelPrototyping/Meshes/SM_ChamferCube.uasset b/Content/LevelPrototyping/Meshes/SM_ChamferCube.uasset new file mode 100644 index 0000000..7a5b944 --- /dev/null +++ b/Content/LevelPrototyping/Meshes/SM_ChamferCube.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6301fc4690ae3dbdc0b77203c6e5f662561351252c7df36c334fb72f6ffe2205 +size 22332 diff --git a/Content/LevelPrototyping/Meshes/SM_Cube.uasset b/Content/LevelPrototyping/Meshes/SM_Cube.uasset new file mode 100644 index 0000000..c4909ff --- /dev/null +++ b/Content/LevelPrototyping/Meshes/SM_Cube.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79cf9bb71346c1f0386362238aef3e01400be06211e6813dc1ce9bbae969fb07 +size 17034 diff --git a/Content/LevelPrototyping/Meshes/SM_Cylinder.uasset b/Content/LevelPrototyping/Meshes/SM_Cylinder.uasset new file mode 100644 index 0000000..0ab270f --- /dev/null +++ b/Content/LevelPrototyping/Meshes/SM_Cylinder.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a59270c4e29b708c93c0872970bd7f8cc2a9db0e898ba6792605364b25fee585 +size 20348 diff --git a/Content/LevelPrototyping/Meshes/SM_QuarterCylinder.uasset b/Content/LevelPrototyping/Meshes/SM_QuarterCylinder.uasset new file mode 100644 index 0000000..5edf332 --- /dev/null +++ b/Content/LevelPrototyping/Meshes/SM_QuarterCylinder.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8da43997ae75f32c7fba7047ca3f34ca88d68613845d556072b74c93de74e6a5 +size 17827 diff --git a/Content/LevelPrototyping/Meshes/SM_Ramp.uasset b/Content/LevelPrototyping/Meshes/SM_Ramp.uasset new file mode 100644 index 0000000..d099706 --- /dev/null +++ b/Content/LevelPrototyping/Meshes/SM_Ramp.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81a46d90c20408e4246e37c4c00bf764b5a05d3752eadd1c3b10e8946a79ffbe +size 17169 diff --git a/Content/LevelPrototyping/Textures/T_GridChecker_A.uasset b/Content/LevelPrototyping/Textures/T_GridChecker_A.uasset new file mode 100644 index 0000000..37cf466 --- /dev/null +++ b/Content/LevelPrototyping/Textures/T_GridChecker_A.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb8334767064b107ec4f4407ef1365e8b9e2dfeff5984e66d4a6232254ddd02a +size 9600 diff --git a/Content/Levels/TestLevel.umap b/Content/Levels/TestLevel.umap new file mode 100644 index 0000000..84b4408 --- /dev/null +++ b/Content/Levels/TestLevel.umap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98b4dc0768b3c145735a399e0817ec81cae9c7177f1642b78246059a2d2cb6e1 +size 45595 diff --git a/Content/Movement/Blueprints/BP_MainCharacter.ts b/Content/Movement/Blueprints/BP_MainCharacter.ts new file mode 100644 index 0000000..685707c --- /dev/null +++ b/Content/Movement/Blueprints/BP_MainCharacter.ts @@ -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(); + } +} diff --git a/Content/Movement/Blueprints/BP_MainCharacter.uasset b/Content/Movement/Blueprints/BP_MainCharacter.uasset new file mode 100644 index 0000000..6f49919 --- /dev/null +++ b/Content/Movement/Blueprints/BP_MainCharacter.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c8e21f8b302680f051d49aeba1a532ad9c1797d85967d759faf0460e3d008dc +size 42451 diff --git a/Content/Movement/Components/AC_Movement.ts b/Content/Movement/Components/AC_Movement.ts new file mode 100644 index 0000000..39ed999 --- /dev/null +++ b/Content/Movement/Components/AC_Movement.ts @@ -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(); + } + } +} diff --git a/Content/Movement/Components/AC_Movement.uasset b/Content/Movement/Components/AC_Movement.uasset new file mode 100644 index 0000000..f6ef645 --- /dev/null +++ b/Content/Movement/Components/AC_Movement.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eed317b008a23288b74ffc59a8a615c8897a3ad9cb06653385cfe9c3c4863717 +size 318160 diff --git a/Content/Movement/Enums/E_SurfaceType.ts b/Content/Movement/Enums/E_SurfaceType.ts new file mode 100644 index 0000000..191c414 --- /dev/null +++ b/Content/Movement/Enums/E_SurfaceType.ts @@ -0,0 +1,9 @@ +// Content/Movement/Enums/E_SurfaceType.ts + +export enum E_SurfaceType { + None = "None", + Walkable = "Walkable", + SteepSlope = "SteepSlope", + Wall = "Wall", + Ceiling = "Ceiling" +} diff --git a/Content/Movement/Enums/E_SurfaceType.uasset b/Content/Movement/Enums/E_SurfaceType.uasset new file mode 100644 index 0000000..794f061 --- /dev/null +++ b/Content/Movement/Enums/E_SurfaceType.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb5d8f633c0817624f06a6d534c3eba7e4fefad2f2792873cb201534d64a929f +size 3242 diff --git a/Content/Movement/Structs/S_AngleThresholds.ts b/Content/Movement/Structs/S_AngleThresholds.ts new file mode 100644 index 0000000..cd5eb82 --- /dev/null +++ b/Content/Movement/Structs/S_AngleThresholds.ts @@ -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, +} diff --git a/Content/Movement/Structs/S_AngleThresholds.uasset b/Content/Movement/Structs/S_AngleThresholds.uasset new file mode 100644 index 0000000..4f1cfd4 --- /dev/null +++ b/Content/Movement/Structs/S_AngleThresholds.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b8095323f3eecb13431e8e1e30c2d24de80d70b8fdb3b526f02451dd5e3e142 +size 5902 diff --git a/Content/Movement/Structs/S_MovementConstants.ts b/Content/Movement/Structs/S_MovementConstants.ts new file mode 100644 index 0000000..f951fe2 --- /dev/null +++ b/Content/Movement/Structs/S_MovementConstants.ts @@ -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; +} diff --git a/Content/Movement/Structs/S_MovementConstants.uasset b/Content/Movement/Structs/S_MovementConstants.uasset new file mode 100644 index 0000000..cd0afbb --- /dev/null +++ b/Content/Movement/Structs/S_MovementConstants.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:731f22ba9e0b6d6f51f1076708b5e87ab046734d77720006fcb5fa413d7ed93f +size 6915 diff --git a/Content/Movement/Structs/S_SurfaceTestCase.ts b/Content/Movement/Structs/S_SurfaceTestCase.ts new file mode 100644 index 0000000..959442e --- /dev/null +++ b/Content/Movement/Structs/S_SurfaceTestCase.ts @@ -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; +} diff --git a/Content/Movement/Structs/S_SurfaceTestCase.uasset b/Content/Movement/Structs/S_SurfaceTestCase.uasset new file mode 100644 index 0000000..a2781ec --- /dev/null +++ b/Content/Movement/Structs/S_SurfaceTestCase.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d6b3b96eae6b75b87cebf9e8f6dd61382770ebde19871a3f962264e11d162a7 +size 6204 diff --git a/Content/functions.ts b/Content/functions.ts new file mode 100644 index 0000000..39448bc --- /dev/null +++ b/Content/functions.ts @@ -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); +} diff --git a/Content/types.ts b/Content/types.ts new file mode 100644 index 0000000..e5e4177 --- /dev/null +++ b/Content/types.ts @@ -0,0 +1,4 @@ +// Content/types.ts + +export type Float = number; +export type Vector = [Float, Float, Float]; diff --git a/Documentation/Algorithm_of_Development.md b/Documentation/Algorithm_of_Development.md new file mode 100644 index 0000000..cdee9af --- /dev/null +++ b/Documentation/Algorithm_of_Development.md @@ -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. **Финализация** (ревью → производительность → коммит) diff --git a/Documentation/Movement/Architect_Principles.md b/Documentation/Movement/Architect_Principles.md new file mode 100644 index 0000000..3e71d3e --- /dev/null +++ b/Documentation/Movement/Architect_Principles.md @@ -0,0 +1,8 @@ +[//]: # (Documentation/Movement/Architect_Principles.md) + +# Архитектурные принципы +- **Детерминированность** - приоритет #1 для всех решений +- **Модульность** - каждая система независима и тестируема +- **Производительность** - 60 FPS на целевом железе +- **Расширяемость** - легкое добавление новых механик +- **Отлаживаемость** - comprehensive debug tools diff --git a/Documentation/Movement/Description.md b/Documentation/Movement/Description.md new file mode 100644 index 0000000..3e408a4 --- /dev/null +++ b/Documentation/Movement/Description.md @@ -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 в моём случае требуется только отрисовка графики, возможно базовая встроенная физика, инструменты анимации. В общем всё, что касается механик должно быть написано с нуля. diff --git a/Documentation/Movement/ProjectDecisions/DecisionLog_01.md b/Documentation/Movement/ProjectDecisions/DecisionLog_01.md new file mode 100644 index 0000000..f437ad7 --- /dev/null +++ b/Documentation/Movement/ProjectDecisions/DecisionLog_01.md @@ -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 diff --git a/Documentation/Movement/Roadmap.md b/Documentation/Movement/Roadmap.md new file mode 100644 index 0000000..4421c7c --- /dev/null +++ b/Documentation/Movement/Roadmap.md @@ -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 работают корректно + +--- diff --git a/Documentation/Movement/TechnicalDesign/TDD_01.md b/Documentation/Movement/TechnicalDesign/TDD_01.md new file mode 100644 index 0000000..2baf739 --- /dev/null +++ b/Documentation/Movement/TechnicalDesign/TDD_01.md @@ -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 автоматизированных тестовых случаев, покрывающих граничные условия и краевые случаи. diff --git a/Documentation/Movement/Testing/TestResults_01.md b/Documentation/Movement/Testing/TestResults_01.md new file mode 100644 index 0000000..94c6a45 --- /dev/null +++ b/Documentation/Movement/Testing/TestResults_01.md @@ -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] Автоматические тесты проходят diff --git a/TengriPlatformer.uproject b/TengriPlatformer.uproject new file mode 100644 index 0000000..d59fa9a --- /dev/null +++ b/TengriPlatformer.uproject @@ -0,0 +1,15 @@ +{ + "FileVersion": 3, + "EngineAssociation": "5.6", + "Category": "", + "Description": "", + "Plugins": [ + { + "Name": "ModelingToolsEditorMode", + "Enabled": true, + "TargetAllowList": [ + "Editor" + ] + } + ] +} \ No newline at end of file