_L/_R/_LU/_RU, rotation contract และ Gait contract ได้รับการอนุมัติแล้วในข้อ 10.7 Skeleton/retarget chain และ authored speed ของ Forward Loop ได้รับการตรวจจาก asset จริงแล้ว ส่วน authored speed ของทิศอื่น, sample positions ที่เหลือ และ foot-phase frames ของแต่ละคลิปยังเป็น TBD1. เป้าหมายและขอบเขต
สร้างฐาน locomotion ที่ลื่นและขยายต่อได้สำหรับผู้เล่น Lambeer โดยใช้คลิปจริงใต้ UE package root /Game/MaleLocomotionSet/Animations/Mannequin/InPlace การเคลื่อนที่จริงยังขับด้วย UCharacterMovementComponent; animation มีหน้าที่แสดง pose ให้ตรงกับ velocity, input, stance, gait และจังหวะเท้า
รวมอยู่ในคู่มือ
- Blend Space 2D สำหรับ Standing Walk/Jog/Run Loop
- State Machine สำหรับ Idle → Start → Moving → Stop
- Turn, Pivot, Spin และ Turn in Place
- Crouch, Prone, Jump/Fall/Land
- Sync Markers, Notifies, Inertialization, Distance/Stride matching roadmap
- ข้อควรระวังสำหรับ multiplayer และ MetaHuman
ไม่รวม
- การแก้ Blueprint/C++ หรือ Animation Assets จริง
- การอนุมัติค่าความเร็วหรือ transition threshold ขั้นสุดท้าย
- ระบบ vault/mantle, combat montage และ weapon-specific locomotion
2. สิ่งที่มีอยู่แล้ว กับสิ่งที่จะสร้าง
| หัวข้อ | สถานะที่ตรวจพบ | แนวทางในคู่มือนี้ |
|---|---|---|
| Engine | Verified: Lambeer.uproject ใช้ Unreal Engine 5.8 | ใช้ Animation Blueprint และ State Machine มาตรฐาน |
| Anim Blueprint | Verified: /Game/Lambeer/Characters/Player/Animation/ABP_Player | ต่อยอด asset เดิม ไม่สร้างระบบคู่ขนานโดยไม่จำเป็น |
| Blend Spaces เดิม | Verified: BS_Idle_Walk_Run, BS_Idle_Crouch | ตรวจของเดิมก่อน แล้วสร้าง/แทนที่ด้วย directional 2D ตามขั้นตอนนี้ |
| AnimInstance C++ | Verified: มี AimMode, Gait, Stance, StartGait, RotationMode และ FootPhase; ยกเลิก animation Boolean bIsCrouched แล้ว | ใช้ native ELambeerLocomotionStance เป็น posture contract; ACharacter::bIsCrouched ยังเป็นผลทางกายภาพของ Crouch เท่านั้น |
| Movement speeds | Verified: Walk 165, Jog 442, Sprint 600, Crouch 110 cm/s ใน ULambeerLocomotionComponent | Walk/Jog อิง Root Motion Forward Loop ที่วัดได้ประมาณ 164/442 cm/s; Run runtime 600 ใกล้ authored 660 และผ่าน visual feedback ปัจจุบัน |
| In-Place animations | Verified: 438 assets ใน 8 กลุ่ม | ใช้ authored Start/Stop/Pivot แยกจาก Moving Blend Space |
3. ชุด Animation ที่ตรวจพบ
| โฟลเดอร์ | จำนวน | หน้าที่หลัก | ตัวอย่างชื่อจริง |
|---|---|---|---|
| Idle | 33 | Idle, idle breaks, stance transitions, turn in place | A_INP_Idle, A_INP_IdleLt_90_Turn, A_INP_Idle2CrouchIdle |
| Walk | 70 | Directional walk, start, stop, pivot, moving turn | A_INP_WalkFwd_Loop, A_INP_WalkBwdRt_45_Start, A_INP_WalkFwd2Bwd_Pivot_L |
| Jog | 70 | Directional jog, start, stop, pivot, moving turn | A_INP_JogFwd_Loop, A_INP_JogFwdLt_45_Stop, A_INP_JogBwd2JogFwd_Pivot_R |
| Run | 33 | Forward sprint family และ angled starts/turns | A_INP_RunFwd_Loop, A_INP_RunFwd_135_Start_L, A_INP_RunFwd_Stop_Fast |
| Crouch | 68 | Directional crouch locomotion และ pivots | A_INP_CrouchFwd_Loop, A_INP_CrouchBwd_Start, A_INP_CrouchFwd2Bwd_Pivot_R |
| Prone | 24 | Directional prone start/loop/stop | A_INP_ProneFwd_Loop, A_INP_ProneBwdLt135_Start, A_INP_ProneFwdRt90_Stop |
| Jump | 98 | Idle/gait jump และ directional multi-stage jump | A_INP_JumpJog_Start_LU, A_INP_JumpFwd_Apex, A_INP_Falling_Loop |
| Spin | 42 | การกลับทิศรุนแรงระหว่าง movement | A_INP_JogFwd_Spin_Bwd_180_L, A_INP_RunFwdRt_Spin_JogBwdLt_90 |
4. สถาปัตยกรรมที่แนะนำ
ABP_Player
└─ AnimGraph
├─ SM_Posture
│ ├─ Standing → SM_Standing
│ ├─ StandToCrouch
│ ├─ Crouching → SM_Crouch
│ ├─ CrouchToProne
│ ├─ Prone → SM_Prone
│ └─ Airborne → SM_Airborne
├─ Inertialization
├─ Foot placement / Leg IK [ภายหลัง]
├─ Layered Blend per Bone (Aim)
└─ Output Pose
หลักสำคัญ: Blend Space 2D รับผิดชอบเฉพาะ pose ที่วนต่อเนื่อง ส่วนคลิปที่มีจังหวะและปลายทางชัดเจน เช่น Start, Stop, Pivot และ Spin อยู่ใน State แยก
5. เตรียม Asset ก่อนสร้างกราฟ
- Duplicate หรือสำรอง
ABP_Playerและ Blend Space เดิมก่อนทดลอง - เปิด
A_INP_Idleแล้วบันทึก Skeleton ที่ใช้งาน - เปิด
ABP_Playerแล้วเปรียบเทียบ Target Skeleton - ถ้า Skeleton ต่างกัน ให้ retarget ด้วย IK Rig/IK Retargeter ก่อนสร้าง Blend Space
- เปิดตัวอย่างคลิปตรงหน้า ซ้าย ขวา หลัง และเปิด Show Bones เพื่อยืนยันแกนจริง
- ตรวจ
Loop Animationเฉพาะไฟล์ลงท้าย_Loop; Start/Stop/Pivot ห้าม loop - ตรวจ Root Motion setting ว่าชุด In-Place ไม่ได้ขยับ capsule โดยไม่ตั้งใจ
6. ตัวแปรที่ต้องใช้ใน ABP_Player
bHasMovementInput ซึ่งเริ่มต้นเป็น false ถ้าไม่ทำข้อ 6.2 กฎ Idle → Start ในข้อ 9.4 จะไม่มีวันเป็นจริงและ animation จะค้างที่ Idle แม้ capsule เคลื่อนที่แล้ว| ตัวแปร | ชนิด | ที่มา/การคำนวณ | ใช้ที่ใด |
|---|---|---|---|
Velocity | Vector | Character Movement Velocity | คำนวณ speed/direction |
GroundSpeed | Float | Vector Length XY(Velocity) | แกน Y ของ 2D Blend Space |
MovementSpeedDeadZone | Float | ค่า tuning/config ที่ตั้งใน Default Value หลังวัด idle noise; ไม่คำนวณหรือ Set ทุกเฟรม และค่าอนุมัติยังเป็น TBD | แยก Stop กับ Idle ใน Transition Rules |
LocalVelocity | Vector | Unrotate Velocity ด้วย Actor Rotation | แยกหน้า/หลัง/ซ้าย/ขวา |
Direction | Float | Calculate Direction หรือ local atan2 | แกน X ของ 2D Blend Space |
Acceleration | Vector | Current Acceleration | รู้ว่ามี movement input |
bHasMovementInput | Bool | Acceleration 2D ไม่เป็นศูนย์ | Start/Stop/Pivot decisions |
bIsFalling | Bool | Character Movement Is Falling | เข้า/ออก Airborne |
Gait | ELambeerGait | อ่าน replicated ULambeerLocomotionComponent.CurrentGait ใน ULambeerAnimInstance::NativeUpdateAnimation | เลือก Walk/Jog/Run family และ transition logic |
Stance | ELambeerLocomotionStance (E_LocomotionStance) | native ULambeerAnimInstance.Stance อ่านจาก ULambeerLocomotionComponent.GetPublishedStance(); simulated proxy อ่าน replicated CurrentStance | SM_Posture |
bUrgentStandFromProne | Bool | native ULambeerAnimInstance.bUrgentStandFromProne จาก locomotion เมื่อออกจาก Prone ด้วย Sprint/Jump-to-Stand; ห้ามสร้างตัวแปร Blueprint ชื่อซ้ำทับ inherited | ลูกศรออกจาก State Prone ตามข้อ 13.5 |
RotationMode | ELambeerRotationMode | derive จาก AimMode ใน native AnimInstance: HipRelaxed = VelocityDirection, ShoulderAim/ADS = LookingDirection ตามข้อ 10.7 | เลือก turn-start กับ strafe-start |
DesiredDirection | Float | มุม acceleration/input ใน local space | เลือก Start/Pivot |
DirectionDelta | Float | Delta angle ระหว่าง velocity กับ desired direction | Turn/Pivot/Spin |
FootPhase | ELambeerFootPhase | runtime เขียน LeftUp/RightUp จากความสูงกระดูก foot_l/foot_r เป็นค่าประมาณ; Notify/curve ที่ตรวจเฟรมแล้วอนุญาตให้ overwrite ตามข้อ 10.7 | เลือก suffix _LU/_RU; ไม่ใช้เลือก _L/_R |
6.1 สร้างตัวแปรขั้นต่ำสำหรับการทดสอบข้อ 9
- เปิด
ABP_Playerแล้วไปที่My Blueprint → Variablesตรวจหรือสร้างVelocity(Vector),GroundSpeed(Float),Direction(Float),Acceleration(Vector),bHasMovementInput(Boolean),bIsFalling(Boolean) และMovementSpeedDeadZone(Float) ให้ชื่อและชนิดตรงกัน จากนั้น Compile โดยMovementSpeedDeadZoneจะกำหนด Default Value หลังวัดตามข้อ 9.4 ไม่ต้องมี Set node ใน Event Graph - สำหรับ milestone Standing ในข้อ 9 ให้ตรวจว่า
Stanceใช้ Enum ที่มีค่าStandingและตั้ง Default Value เป็นStandingก่อน ส่วนการอัปเดตเป็น Crouched/Prone จะทำเมื่อสร้าง posture เหล่านั้นในข้อ 13 LocalVelocity,DesiredDirection,DirectionDeltaและFootPhaseยังไม่จำเป็นต่อ flow เดินหน้ารอบแรก จึงยังไม่ต้องต่อกราฟของตัวแปรเหล่านี้ก่อนทดสอบข้อ 9
ABP_Player parent เป็น ULambeerAnimInstance; native อัปเดต AimMode, Gait, Stance, StartGait, RotationMode, NativeStartDirectionBucket, SelectedStartAnimation และ FootPhase แล้ว bIsSprinting กับ animation Boolean bIsCrouched ถูกลบจาก C++; หลัง 2026-09-04 ให้เลิก map Is Crouching ทับ Stance ใน Event Graph เพราะ native Stance มาจาก CurrentStance แล้ว6.2 เขียนค่าทุกเฟรมใน Event Blueprint Update Animation
ทำขั้นตอนนี้ใน Event Graph ของ ABP_Player ไม่ใช่ใน AnimGraph หรือ Transition Rule Graph สำหรับรอบแรกให้ใช้ Blueprint update ที่เห็นและ debug ได้ง่ายก่อน แล้วค่อยย้ายไป C++/Property Access หลัง flow ทำงานถูกต้อง
- หา event
Event Blueprint Update Animationหากไม่มี ให้คลิกขวาพื้นที่ว่างแล้วเพิ่ม event นี้ - จาก execution pin ของ event ต่อเข้า
Cast To CharacterโดยเสียบTry Get Pawn Ownerเข้าที่ Object หากโปรเจกต์มี cast ไปคลาสผู้เล่นที่ยืนยันแล้วอยู่เดิม ให้ใช้ของเดิมและไม่ต้องสร้าง cast ซ้ำ - จากผล Cast ดึง
Get Character Movementเก็บสายอ้างอิงนี้ไว้ใช้กับGet Current AccelerationและIs Falling - จาก Character ดึง
Get Velocityแล้วต่อค่าเข้าSet Velocity - นำค่า Velocity เดียวกันเข้า
Vector Length XYหรือVSizeXYแล้วต่อผลเข้าSet GroundSpeed - นำ Velocity กับ
Get Actor Rotationของ Character เข้าCalculate Directionแล้วต่อผลเข้าSet Direction - จาก Character Movement เรียก
Get Current Accelerationแล้วต่อค่าเข้าSet Acceleration - นำ Current Acceleration เข้า
Vector Length XYแล้วเปรียบเทียบ Float> 0.0ต่อผล Boolean เข้าSet bHasMovementInputสูตรนี้ใช้เป็นเกณฑ์ “Acceleration 2D ไม่เป็นศูนย์” สำหรับ milestone แรก; หาก analog input มี noise ให้ทดสอบ threshold ใหม่และบันทึกเป็นค่าที่อนุมัติภายหลัง - จาก Character Movement เรียก
Is Fallingแล้วต่อผลเข้าSet bIsFalling - ต่อสาย execution สีขาวให้ผ่าน Set nodes ทุกตัวตามลำดับ ห้ามวาง Set node ลอยไว้ เพราะ pure nodes อาจมีค่าถูกต้องแต่ตัวแปรจะไม่ถูกเขียน
- Compile และ Save หาก Cast Failed ให้ปล่อยโดยไม่เขียนค่าในรอบแรก แล้วใช้ข้อ 6.3 ตรวจว่า ABP กำลัง debug pawn instance ที่ถูกต้อง
ภาพรวมสายกราฟขั้นต่ำ
Event Blueprint Update Animation
→ Cast To Character (Object = Try Get Pawn Owner)
→ Set Velocity = Character.GetVelocity
→ Set GroundSpeed = VectorLengthXY(Velocity)
→ Set Direction = CalculateDirection(Velocity, Character.ActorRotation)
→ Set Acceleration = CharacterMovement.GetCurrentAcceleration
→ Set bHasMovementInput = VectorLengthXY(Acceleration) > 0.0
→ Set bIsFalling = CharacterMovement.IsFalling
MovementSpeedDeadZone ไม่อยู่ในสายข้างต้นโดยตั้งใจ เพราะเป็น threshold คงที่ที่ Transition Rules อ่านแบบ Get ไม่ใช่ข้อมูลสถานะที่ต้องสร้างใหม่ทุกเฟรม
6.3 จุดหยุดตรวจ: ยืนยันค่าจริงก่อนสร้าง State Machine
- เริ่ม PIE เปิด
ABP_Playerและเลือก instance ผู้เล่นที่กำลังควบคุมอยู่ในDebug Filterห้ามดูค่า Default ของ class - ตอนยืนนิ่ง:
GroundSpeedต้องอยู่ใกล้ศูนย์,bHasMovementInput=falseและbIsFalling=false - ขณะกดเดิน:
Accelerationต้องไม่เป็นศูนย์,bHasMovementInput=true,GroundSpeedต้องเพิ่ม และDirectionต้องเปลี่ยนตามทิศเคลื่อนที่ - เมื่อปล่อย input แต่ capsule ยังไหล:
bHasMovementInputต้องกลับเป็นfalseก่อนGroundSpeedลงถึงศูนย์ พฤติกรรมนี้คือสิ่งที่กฎ Moving → Stop ในข้อ 9.4 ต้องใช้ - ถ้า Character เคลื่อนที่แต่ค่าทั้งหมดยังคงศูนย์ ให้ตรวจ Anim Class บน Skeletal Mesh, Debug Filter, Cast Failed และสาย execution ก่อนทำข้อ 7–9 ต่อ
bHasMovementInput สลับ false → true → false จาก input จริงอย่างน้อยหนึ่งรอบ และเห็น GroundSpeed เปลี่ยนตามการเคลื่อนที่6.4 ตัวแปรระยะถัดไป: ต้อง set เมื่อใดและจากอะไร
ตัวแปรทุกตัวต้องมี source of truth แต่ไม่จำเป็นต้องเขียนทุกตัวทุกเฟรม บางตัวเป็นค่าคงที่ของ milestone ปัจจุบัน บางตัวเปลี่ยนตาม event และบางตัวยังต้องเพิ่ม runtime contract ก่อนใช้งาน ตารางนี้เป็น gate ว่าต้องต่อให้เสร็จก่อนเริ่มหัวข้อใด
| ตัวแปร | ต้องพร้อมก่อน | วิธีอัปเดตในระบบปัจจุบัน | รูปแบบการเขียนค่า |
|---|---|---|---|
MovementSpeedDeadZone | Transition ที่แยก Stop/Idle ในข้อ 9.4 | ตั้ง Default Value ให้สูงกว่า idle noise ที่วัดได้ แต่ต่ำกว่าความเร็วต่ำสุดที่ตั้งใจให้ถือว่าเคลื่อนที่ | Config/default; Transition ใช้ Get และไม่ Set ทุกเฟรม |
LocalVelocity | ข้อ 10–11 | Unrotate Vector(Velocity, Actor Rotation) | ทุก animation update |
DesiredDirection | ข้อ 10–11 | Calculate Direction(Acceleration, Actor Rotation) เฉพาะเมื่อ bHasMovementInput=true | ทุก update ที่มี input; เก็บค่าล่าสุดเมื่อไม่มี input |
DirectionDelta | ข้อ 11 | Normalize Axis(DesiredDirection - Direction); contract ที่อนุมัติคือค่าบวก = ขวา และค่าลบ = ซ้ายตาม TBD-LOCO-004 | ทุก update หลัง Direction และ DesiredDirection |
Stance | ข้อ 9 สำหรับ default Standing; ข้อ 13 สำหรับการเปลี่ยน posture | native ELambeerLocomotionStance จาก CurrentStance; ABP ต้องเลิก overwrite จาก Is Crouching | ทุก animation update จาก committed/predicted stance ของ locomotion component |
RotationMode | ข้อ 10 | อ่านค่าที่สอดคล้องกับ gameplay contract ในข้อ 10.7; ABP ห้ามเปลี่ยน rotation behavior ของ Character เอง | เปลี่ยนเมื่อ authoritative AimMode เปลี่ยน |
Gait | ข้อ 10 และ Jump แยก gait ในข้อ 14 | อ่าน CurrentGait จาก ULambeerLocomotionComponent ตาม TBD-LOCO-006 ที่ resolve แล้วในข้อ 10.7; ห้าม derive จาก GroundSpeed | อ่านจาก replicated locomotion runtime contract |
FootPhase | ข้อ 11 และ Jump/Stop ที่เลือก _LU/_RU | Anim Notify หรือ curve ที่เฟรมตรวจแล้วเขียน LeftUp/RightUp ตาม suffix contract ในข้อ 10.7 | Event-driven ไม่ต้อง Set ทุกเฟรม |
ต่อ LocalVelocity, DesiredDirection และ DirectionDelta
- เพิ่ม
Set LocalVelocityต่อท้ายการคำนวณ Velocity ในข้อ 6.2 โดยใช้ nodeUnrotate Vectorรับ Vector =Velocityและ Rotation = Actor Rotation - หลังคำนวณ
bHasMovementInputใช้ Branch: ทาง True นำAccelerationกับ Actor Rotation เข้าCalculate Directionแล้วSet DesiredDirection; ทาง False ไม่ต้องเขียนทับ เพื่อเก็บทิศ input ล่าสุดไว้ให้ Stop/Pivot - บนทาง True หลัง Set DesiredDirection ใช้ Float
-คำนวณDesiredDirection - Directionแล้วส่งเข้าNormalize Axisเพื่อบีบผลให้อยู่ในช่วง -180° ถึง 180° ก่อนSet DirectionDelta - PIE และตรวจ input หน้า/หลัง/ซ้าย/ขวา ค่า
DesiredDirectionต้องเปลี่ยนทันทีเมื่อกด แม้ Velocity ยังไม่ทันเปลี่ยน ส่วนDirectionDeltaต้องมีขนาดใหญ่เมื่อ input สวนกับทิศที่กำลังไหล
ต่อ Stance และกำหนด RotationMode ให้ตรง runtime
- นำ native
StanceจากULambeerAnimInstanceไปใช้ในSM_Postureโดยตรง; ห้ามสร้าง BooleanbIsCrouched/IsCrouchingซ้ำใน AnimInstance และห้าม mapCharacter Movement → Is Crouchingทับค่า Prone - ก่อน runtime contract ใหม่เสร็จ ให้ตั้ง Default Value ของ
RotationModeเป็นVelocityDirectionและตรวจใน PIE ว่าค่ายังคงตรงกับ Character ซึ่งหันตาม movement - เมื่อ implement ข้อ 10.7 ให้ derive
RotationModeจากAimModeหลังฝั่ง Character สลับ rotation behavior สำเร็จแล้ว ห้ามให้ ABP สลับ mode ก่อน gameplay
Contract ของ Gait และ FootPhase
- Gait: native อ่าน
CurrentGaitและ latchStartGaitตอน JustPressed แล้ว; Moving Blend Space ยังเลือกแถวด้วย GroundSpeed ตามข้อ 9 - FootPhase: native ประมาณ LeftUp/RightUp จากความสูง
foot_l/foot_r; ก่อนใช้เลือก Stop/Jump_LU/_RUแบบ production ให้ตรวจภาพแล้วใส่ Notify/curve ทับค่านี้ได้ ส่วน suffix เปล่า_L/_Rยังเลือกจาก direction เท่านั้น
CurrentGait จึงยังเป็น gameplay source of truth ข้อยกเว้นด้านภาพคือ Stop selector สามารถเลือก animation family ที่มี authored speed ใกล้ TransitionEntrySpeed ที่สุดได้ โดยห้ามเลือก family สูงกว่า CurrentGait และห้ามเขียนค่า gameplay gait กลับระยะยาวให้ย้ายข้อมูล gameplay-relevant ไป C++/component แล้วให้ ABP อ่านผ่าน Property Access หรือ thread-safe update; อย่า cast และค้น component ซ้ำหลายครั้งต่อเฟรม
7. สร้าง Standing Blend Space 2D
สร้าง asset ชื่อเสนอ BS_Standing_Locomotion_2D โดยเลือก Skeleton เดียวกับ ABP_Player
| แกน | ค่าตั้งต้น | หมายเหตุ |
|---|---|---|
| Horizontal Axis | Name Direction, Min -180, Max 180 | เปิด Wrap Input ถ้าผลที่ seam หน้า -180/180 ถูกต้อง |
| Vertical Axis | Name GroundSpeed, Min 0, Max 600 | ปิด Snap to Grid สำหรับแกนนี้เพื่อวางแถว Walk/Jog ที่ 165/442 ได้ตรง runtime; Run row คงที่ 600 ตาม SprintSpeed ที่ผ่าน visual feedback ปัจจุบัน |
แถว Walk — Speed ตั้งต้น 165
| Direction ตั้งต้น | Animation Sequence จริง | หมายเหตุ |
|---|---|---|
| 0 | A_INP_WalkFwd_Loop | เดินหน้า |
| ฝั่งซ้าย 45° | A_INP_WalkFwdLt_45_Loop | ยืนยันเครื่องหมายจาก debug |
| ฝั่งขวา 45° | A_INP_WalkFwdRt_45_Loop | ยืนยันเครื่องหมายจาก debug |
| ฝั่งซ้าย 90° | A_INP_WalkFwdLt_90_Loop | เลือก forward-side เป็น sample หลักก่อน |
| ฝั่งขวา 90° | A_INP_WalkFwdRt_90_Loop | เลือก forward-side เป็น sample หลักก่อน |
| ฝั่งหลังซ้าย 135° | A_INP_WalkBwdLt_45_Loop | ชื่อ 45° วัดจากแกนหลัง |
| ฝั่งหลังขวา 135° | A_INP_WalkBwdRt_45_Loop | ชื่อ 45° วัดจากแกนหลัง |
| -180 และ 180 | A_INP_WalkBwd_Loop | วางซ้ำสองขอบเพื่อให้ seam ต่อเนื่องถ้าจำเป็น |
แถว Jog — Speed 442 จาก Root Motion Forward Loop
วางโครงแบบเดียวกับ Walk โดยใช้ A_INP_JogFwd_Loop, A_INP_JogFwdLt_45_Loop, A_INP_JogFwdRt_45_Loop, A_INP_JogFwdLt_90_Loop, A_INP_JogFwdRt_90_Loop, A_INP_JogBwdLt_45_Loop, A_INP_JogBwdRt_45_Loop และ A_INP_JogBwd_Loop
แถว Run — Speed ตั้งต้น 600
ชุด Run มีเฉพาะ forward hemisphere: A_INP_RunFwd_Loop, A_INP_RunFwdLt_45_Loop, A_INP_RunFwdRt_45_Loop, A_INP_RunFwdLt_90_Loop, A_INP_RunFwdRt_90_Loop
คลิปด้านข้างซ้ำที่ต้องตัดสินใจ
แพ็กมีทั้ง A_INP_WalkFwdLt_90_Loop กับ A_INP_WalkBwdLt_90_Loop และรูปแบบเดียวกันฝั่งขวา/Jog/Crouch ทั้งสองคลิปอาจเดินไปด้านข้างเท่ากันแต่ให้น้ำหนักลำตัวคนละแบบ Blend Space จุดเดียววางสอง sample ทับกันไม่ได้ จึงให้ใช้ forward-side เป็นค่าตั้งต้น แล้วเก็บ backward-side สำหรับการทดสอบเปรียบเทียบหรือแยก Blend Space ในอนาคต
8. Sync Markers และจังหวะเท้า
เป้าหมายของขั้นตอนนี้คือให้ Walk, Jog และ Run ใช้ชื่อ marker และลำดับก้าวชุดเดียวกัน เพื่อให้การ blend ข้ามความเร็วหรือข้ามทิศไม่สลับเท้าที่รับน้ำหนักกะทันหัน เริ่มจากคลิปเดียวให้ถูกก่อน แล้วจึงทำซ้ำกับคลิปที่ใช้งานจริงทั้งหมด
8.1 วาง marker ต้นแบบในคลิปเดินหน้า
- ใน Content Browser เปิด
A_INP_WalkFwd_Loopด้วย Animation Sequence Editor แล้วเปิดการเล่นแบบวนซ้ำใน Preview - เปิดการแสดง Skeleton/Bones หรือ Bone Names ใน viewport จากนั้นลดความเร็ว Preview และลาก playhead ทีละเฟรมบริเวณที่เท้าซ้ายแตะพื้น
- เลือกเฟรมที่เท้าซ้าย เริ่มรับน้ำหนักและกำลังจะนิ่งกับพื้น ไม่ใช่เลือกจากตำแหน่งเท้าต่ำที่สุดเพียงอย่างเดียว ให้ดูทั้งส้น ปลายเท้า และการถ่ายน้ำหนักของสะโพกประกอบกัน
- ที่แถบ Notifies/Markers คลิกขวาตรงเวลานั้น เลือกเพิ่ม Sync Marker แล้วสร้างหรือเลือกชื่อ
LeftPlantชื่อนี้ต้องสะกดและใช้ตัวพิมพ์เหมือนกันทุกคลิป - เลื่อนไปยังเฟรมที่เท้าขวาเริ่มรับน้ำหนัก แล้วเพิ่ม
RightPlantด้วยหลักเดียวกัน - เล่นคลิปวนผ่านรอยต่อท้ายคลิปกลับไปต้นคลิป ตรวจว่าลำดับ marker สลับ
LeftPlant→RightPlant→LeftPlantอย่างต่อเนื่อง หากช่วงก้าวคร่อมปลายคลิป ให้ตรวจทั้งสองด้านของรอยต่อก่อนขยับ marker - บันทึก asset แล้วปิด–เปิดใหม่หนึ่งครั้งเพื่อยืนยันว่า marker ถูกเก็บใน Animation Sequence แล้ว
LeftPlant/RightPlant เท่านั้น ห้ามคัดลอกเวลาเฟรมจากคลิปหนึ่งไปใส่อีกคลิปโดยไม่ Preview8.2 ทำซ้ำกับ sample ที่อยู่ใน Blend Space
- เปิด
BS_Standing_Locomotion_2Dและจดรายชื่อ Animation Sequence ที่เป็น sample จริง เพื่อไม่เสียเวลาใส่ marker ให้ asset ที่ยังไม่ได้ใช้ - ทำ Walk ให้ครบทุกทิศก่อน จากนั้นทำ Jog และ Run โดยใช้ marker เพียงสองชื่อเดิม ห้ามสร้างชื่อแยกตาม gait เช่น
WalkLeftPlantหรือRunLeftPlant - สำหรับคลิปด้านข้างและด้านหลัง ให้ยึด เท้าซ้าย/ขวาตามร่างกายตัวละคร ไม่ใช่ซ้าย/ขวาตามกล้องหรือทิศการเคลื่อนที่
- หลังแก้แต่ละ family ให้ Preview เปรียบเทียบคลิปหน้า ซ้าย ขวา และหลังที่ความเร็วเดียวกัน ถ้า blend แล้วเข่างอผิดจังหวะหรือเท้าสลับ ให้กลับไปตรวจ marker ของ sample คู่นั้นก่อนแก้ Blend Space
8.3 ตรวจ marker ใน Blend Space ก่อนสร้าง State Machine
- เปิด
BS_Standing_Locomotion_2Dและตรวจอีกครั้งว่า sample ทุกตัวอ้างถึง Animation Sequence ที่ใส่LeftPlant/RightPlantแล้ว - Preview ทีละจุด sample เพื่อยืนยันว่าคลิปวนและลำดับเท้าถูกต้อง ก่อนทดสอบการ blend ระหว่างจุด
- ลากตำแหน่ง Preview ข้าม sample ที่อยู่ติดกัน ถ้าขากระตุกหรือเท้าสลับ ให้เปิด Animation Sequence คู่นั้นกลับไปเทียบตำแหน่ง marker
- บันทึก Blend Space เมื่อรายชื่อ sample และ marker ครบ การผูก Blend Space Player เข้ากับ Sync Group จะทำในข้อ 9.3 หลังสร้าง State
Movingแล้ว
ขอบเขตของสองระบบ: Sync Marker ใช้จับ phase ระหว่าง animation players แต่ไม่ยิง event เพื่อเขียน FootPhase ส่วน Anim Notify หรือ curve ใช้อัปเดต LeftUp/RightUp ที่เฟรมซึ่งตรวจแล้ว และ Anim Notify แยกสำหรับ foot contact ยังใช้เรียกเสียง ฝุ่น หรือรอยเท้าได้ ห้ามใช้ Notify แทน marker สำหรับการ sync และห้ามถือ foot contact ว่าเป็นข้อมูลเดียวกับ foot-up phase
8.4 ทดสอบก่อนผ่านขั้นตอนนี้
- ใน Blend Space Preview ลากจาก Walk 165 → Jog 442 → Run 600 ช้า ๆ แล้วลากกลับ โดยเฝ้าดูว่าเท้าข้างที่รับน้ำหนักไม่เปลี่ยนกลางจังหวะ
- ตรึง Speed ไว้ทีละแถว แล้วลาก Direction ผ่านหน้า → เฉียง → ข้าง → หลัง เพื่อตรวจ sample แต่ละคู่
- ทดสอบรอยต่อ
-180/180แยกต่างหาก เพราะเป็นจุดที่ sample ด้านหลังอาจกระโดด phase - เมื่อ Preview ผ่านแล้วจึงไปสร้าง
SM_Standingในข้อ 9 ส่วนการตรวจ Sync Group ใน PIE อยู่ในข้อ 9.6
9. สร้าง SM_Standing
ขั้นตอนนี้เริ่มจาก AnimGraph ที่ยังไม่มีโครง State Machine ตามข้อ 4 โดยจะสร้าง SM_Posture, State Standing และ SM_Standing ตามลำดับ แล้วทำโครงพื้นฐาน Idle → Start → Moving → Stop → Idle ให้เดินได้ก่อน
AnimGraph
└─ SM_Posture
└─ Standing
└─ SM_Standing
├─ Entry → Idle
├─ Idle → Start
├─ Start → Moving
├─ Moving → Stop
└─ Stop → Idle
เพิ่มภายหลัง:
Moving → Turn/Pivot/Spin → Moving [ข้อ 11]
Standing ↔ Crouching/Prone [ข้อ 13]
Standing ↔ Airborne [ข้อ 14]
9.1 สร้าง SM_Posture, Standing และ SM_Standing
- เปิด
ABP_Playerแล้ว Compile ให้ผ่านก่อนเริ่ม เพื่อแยก error เดิมออกจาก error ที่เกิดในขั้นตอนนี้ - ไปที่ AnimGraph ตรวจว่ามี State Machine ชื่อ
SM_Postureอยู่แล้วหรือไม่ หากมีให้เปิดของเดิมและห้ามสร้างชื่อซ้ำ หากยังไม่มี ให้คลิกขวาพื้นที่ว่าง เลือกเพิ่ม State Machine แล้วตั้งชื่อเสนอSM_Posture - ต่อ output pose ของ node
SM_Postureเข้าสาย animation หลัก หากกราฟมี Inertialization หรือ Layered Blend per Bone อยู่แล้ว ให้แทรกSM_Postureไว้ก่อน node เหล่านั้นตามโครงข้อ 4; ถ้ายังไม่มี ให้ต่อเข้ากับ Output Pose ชั่วคราว - ดับเบิลคลิก
SM_Postureสร้าง State ชื่อStandingแล้วลากเส้นจากEntryไปStandingขั้นตอนนี้ยังไม่ต้องสร้างCrouching,ProneหรือAirborneเพราะจะเพิ่มในข้อ 13–14 - ดับเบิลคลิก State
Standingเพื่อเปิด State Graph จากนั้นคลิกขวาพื้นที่ว่าง เพิ่ม State Machine แล้วตั้งชื่อเสนอSM_Standingต่อ output ของมันเข้าState Result - ดับเบิลคลิก node
SM_Standingแล้วสร้าง States ชื่อIdle,Start,MovingและStop - ลากเส้นจาก
EntryไปIdleจากนั้นสร้าง transition ตามลำดับ flow:Idle → Start → Moving → Stop → Idle - Compile เพื่อตรวจโครง parent/child ก่อนใส่ animation ถ้า compiler แจ้งว่า State ใดไม่มี pose ถือว่าเป็นอาการปกติในจุดนี้และต้องหายไปหลังทำข้อ 9.2
- ยังไม่ต้องสร้าง transition ไป Turn/Pivot/Spin จนกว่า state หลักทั้งสี่จะ Compile และทดสอบผ่าน
SM_Posture, Standing และ SM_Standing ในเอกสารนี้เป็นชื่อเสนอจนกว่าจะตรวจโครงภายใน asset จริง9.2 ใส่ Pose ภายในแต่ละ State
A_INP_WalkFwd_Start และ A_INP_WalkFwd_Stop_LU เป็นคลิปทดสอบชั่วคราว ยังไม่ต้องสร้างระบบเลือกทิศหรือเลือกเท้าในข้อ 9.2Idle — ใส่คลิปยืนรอ
- ใน
SM_Standingดับเบิลคลิก StateIdleเพื่อเปิด State Graph - ลาก
A_INP_Idleจาก Content Browser มาวางในกราฟ จะได้ Sequence Player ของคลิปนี้ - ต่อ output pose ของ Sequence Player เข้า
State Result - เลือก Sequence Player แล้วเปิด
Loop Animationใน Details เพราะ Idle ต้องเล่นวนต่อเนื่อง
Start — ใส่คลิปเริ่มเดินหน้าชั่วคราว
- กลับไป
SM_Standingแล้วดับเบิลคลิก StateStart - ลาก
A_INP_WalkFwd_Startมาวางและต่อเข้าState Result - เลือก Sequence Player แล้วปิด
Loop Animationคลิป Start ต้องเล่นจากต้นไปปลายเพียงครั้งเดียว - กลับไปเลือก State
Startในหน้า State Machine แล้วเปิดAlways Reset on Entryหากตัวเลือกนี้แสดงใน Details เพื่อให้การเข้า Start ครั้งใหม่เริ่มคลิปจากต้น - ขั้นตอนนี้รองรับเฉพาะการทดสอบเริ่มเดินหน้าแบบ Walk ไม่ต้องเพิ่ม Blend Poses, Select หรือคลิปทิศอื่นในตอนนี้
Moving — ใส่ Standing Blend Space
- กลับไป
SM_Standingแล้วดับเบิลคลิก StateMoving - ลาก
BS_Standing_Locomotion_2Dมาวางในกราฟให้เป็น Blend Space Player แล้วต่อ output เข้าState Result - ต่อ getter ของตัวแปร
Directionเข้ากับ input แกนนอนDirectionของ node - ต่อ getter ของตัวแปร
GroundSpeedเข้ากับ input แกนตั้งGroundSpeedของ node - ยังไม่ต้องเปลี่ยนรายละเอียด Sync ของ node ในขั้นนี้ การตั้ง Sync Group ทำต่อทันทีในข้อ 9.3
Stop — ใส่คลิปหยุดเดินหน้าชั่วคราว
- กลับไป
SM_Standingแล้วดับเบิลคลิก StateStop - ลาก
A_INP_WalkFwd_Stop_LUมาวางและต่อเข้าState Result - เลือก Sequence Player แล้วปิด
Loop Animationจากนั้นเปิดAlways Reset on Entryที่ StateStopหากตัวเลือกนี้แสดงใน Details LUถูกใช้เพื่อให้กราฟต้นแบบมีคลิป Stop ที่แน่นอนเท่านั้น ความหมาย suffix และการเลือกLU/RUตามเท้ายังเป็น TBD จึงห้ามถือการเลือกนี้เป็นค่าผลิตจริง
ตรวจ Pose ทั้งสี่ State
- เปิด State ทั้งสี่กลับไปตรวจว่าทุกกราฟมี animation node ต่อเข้า
State Resultและไม่มี Result ที่ปล่อยว่าง - Compile
ABP_Playerหากพบ error ให้แก้ State Result หรือ Skeleton mismatch ก่อนสร้าง Transition Rules - Save เมื่อ Compile ผ่าน ณ จุดนี้กราฟมี pose ครบแล้ว แต่ยังเปลี่ยน State ไม่ได้จนกว่าจะทำ Transition Rules ในข้อ 9.4
9.3 ผูก Moving เข้ากับ Sync Group
- เปิด State
Movingแล้วเลือก Blend Space Player ที่เล่นBS_Standing_Locomotion_2D - ใน Details เปิดหมวด Sync แล้วเปลี่ยน
MethodจากDo Not Syncเป็นSync Groupก่อน หากยังเป็นDo Not Syncnode จะไม่ยอมใช้ Group Name ที่กำหนด - ตั้ง
Group Nameเป็นชื่อเสนอGroundedLocomotion - ตั้ง
Group Roleเป็นCan Be Leaderสำหรับ Blend Space Player ตัวนี้ จากนั้น Compile และ Save - ถ้า Start หรือ Stop ถูกนำเข้ากลุ่มเดียวกันภายหลัง ให้เลือก leader/follower ตามช่วง transition ที่ทดสอบแล้ว อย่าตั้งทุก node เป็น leader พร้อมกัน เพราะ node ที่แย่งความเป็นผู้นำอาจทำให้ phase กระโดด
Group Name แล้ว แต่ Method ยังเป็น Do Not Sync ให้เลือก node เดิม เปลี่ยน Method → Sync Group แล้ว Compile ใหม่ ไม่ต้องลบหรือสร้าง Blend Space Player ใหม่9.4 สร้าง Transition Rules ทีละเส้น
Transition ใน State Machine เป็นทางเดียว ลูกศรไปและกลับจึงต้องสร้างแยกกันทุกเส้น เมื่อดับเบิลคลิกวงกลมบนลูกศรจะเปิด Transition Rule Graph ซึ่งต้องส่งค่า Boolean เข้า Can Enter Transition เป็น true จึงจะเปลี่ยน State
bHasMovementInput ยังเป็น false ตลอด การแก้ Transition Rule จะไม่ทำให้ State ออกจาก Idleเตรียม MovementSpeedDeadZone ก่อนต่อเงื่อนไข
- ใน My Blueprint สร้างตัวแปร Float ชื่อเสนอ
MovementSpeedDeadZoneหากยังไม่มี แล้ว Compile เพื่อให้ตัวแปรปรากฏใน Transition Rule Graph - เลือกตัวแปรนี้ใน My Blueprint แล้วกำหนดค่าที่ช่อง
Default Valueหลัง Compile หรือกำหนดผ่านClass DefaultsของABP_Playerไม่ต้องสร้างSet MovementSpeedDeadZoneในEvent Blueprint Update Animation - ระหว่าง PIE เลือก player instance ใน Debug Filter แล้วดู
GroundSpeedหลังปล่อย input จน capsule หยุดสนิท ทดสอบซ้ำหลายครั้งและจดค่าสูงสุดที่ยังเกิดขณะถือว่าหยุดเป็นMaxIdleNoise - ทดสอบ analog input ต่ำสุดที่ยังตั้งใจให้แสดง locomotion และจดค่าต่ำสุดเป็น
MinIntentionalMoveSpeed - เลือกค่าทดสอบให้เป็นไปตาม
MaxIdleNoise < MovementSpeedDeadZone < MinIntentionalMoveSpeedหากไม่มีช่วงว่างระหว่างสองค่านี้ ให้แก้แหล่ง noise หรือ movement settings ก่อน ไม่ควรฝืนเพิ่ม threshold จนกลืนการเดินช้า - ค่าขั้นสุดท้ายต้องทดสอบร่วมกับ braking/deceleration, ทางลาด, network smoothing และ remote proxy จึงยังเป็น TBD เอกสารไม่กำหนดตัวเลขตายตัวก่อนมีผลวัด
Idle → Start จึงไม่ใช่สาเหตุที่ State ไม่ยอมออกจาก Idle จุดนั้นให้ตรวจ bHasMovementInput, bIsFalling และ Stance ส่วน Dead Zone มีผลตอนเลือกว่าจะเข้า Stop หรือกลับ Idle โดยตรงหลังปล่อย inputIdle → Start — เริ่มเมื่อมี input
bHasMovementInput
AND NOT bIsFalling
AND Stance == Standing
- ดับเบิลคลิกวงกลมบนลูกศร
Idle → Start - ลาก
bHasMovementInputแบบ Get ลงกราฟและต่อเข้า Boolean AND - ลาก
bIsFallingแบบ Get ต่อผ่าน Boolean NOT แล้วต่อผลเข้า AND - ลาก
Stanceแบบ Get ต่อเข้า node เปรียบเทียบ Enum และเลือกค่าStandingจากนั้นต่อผลเข้า AND - ต่อผล AND เข้า
Can Enter Transitionแล้ว Compile การใช้ input แทน GroundSpeed ทำให้ Start เริ่มทันทีเมื่อกด ไม่ต้องรอ capsule เร่งความเร็วก่อน
Start → Moving — ออกเมื่อคลิป Start ใกล้จบ
- กลับไปหน้า
SM_Standingแล้วคลิกวงกลมบนลูกศรStart → Movingเพียงครั้งเดียวเพื่อให้ Details แสดงค่าของ transition - เปิด
Automatic Rule Based on Sequence Player in Stateระบบจะอ่านเวลาคงเหลือจาก Sequence Player ที่ relevant ที่สุดใน StateStart - คง
Automatic Rule Trigger Timeเป็นค่าติดลบ/default ในรอบแรก เพื่อให้เวลาเริ่ม transition อิงCrossfade Durationโดยยังไม่กำหนด timing ใหม่เอง - Compile แล้ว Preview: ลูกศรต้องเกิดหนึ่งครั้งใกล้ท้าย
A_INP_WalkFwd_Startและเข้าสู่Moving - วิธีอัตโนมัตินี้ใช้กับ Sequence Player เดี่ยวจากข้อ 9.2 ก่อน เมื่อข้อ 10 เปลี่ยน Start เป็นหลายคลิป ให้ตรวจ relevant player ใหม่ หรือเปลี่ยนเป็น Notify
StartCompleteที่ผ่านการตรวจเฟรมแล้ว
Moving → Stop — ปล่อย input ขณะ capsule ยังไหล
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
- ดับเบิลคลิกวงกลมบนลูกศร
Moving → Stop - ต่อ
bHasMovementInputผ่าน Boolean NOT และต่อbIsFallingผ่าน Boolean NOT แยกกัน - ลาก
GroundSpeedกับMovementSpeedDeadZoneแบบ Get แล้วต่อเข้า Float> - รวมผลทั้งสามด้วย Boolean AND แล้วต่อเข้า
Can Enter Transition - เหตุผลที่ใช้
>คือเข้า Stop ตอนผู้เล่นปล่อยปุ่มแต่ capsule ยังมีความเร็ว ไม่รอให้หยุดสนิทแล้วค่อยเล่น animation
Stop → Idle — ออกเมื่อคลิป Stop ใกล้จบ
- คลิกวงกลมบนลูกศร
Stop → Idleแล้วเปิดAutomatic Rule Based on Sequence Player in State - คง
Automatic Rule Trigger Timeเป็นค่าติดลบ/default ในรอบแรก เพื่อให้อิง Crossfade Duration เช่นเดียวกับStart → Moving - Compile แล้วตรวจว่า
A_INP_WalkFwd_Stop_LUเล่นเพียงครั้งเดียวและกลับ Idle ใกล้ท้ายคลิป ไม่ค้างที่เฟรมสุดท้าย
Moving → Idle — ทางหนีเมื่อหยุดอยู่ใน dead zone แล้ว
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
- ถ้ายังไม่มีลูกศรนี้ ให้ลากจากขอบ State
Movingไป StateIdleลูกศรต้องชี้ไป Idle - เปิด Rule Graph แล้วสร้าง NOT ของ
bHasMovementInput, NOT ของbIsFallingและ Float<=ระหว่างGroundSpeedกับMovementSpeedDeadZone - รวมทั้งสามด้วย AND แล้วต่อเข้า Result เส้นนี้จะป้องกันไม่ให้ค้างใน Moving เมื่อความเร็วตกถึง dead zone ก่อนที่กฎ
Moving → Stopจะเป็นจริง
Start → Stop — ปล่อย input ระหว่าง Start แต่ยังมีความเร็ว
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
- ลาก transition จาก
StartไปStopแล้วสร้างกราฟแบบเดียวกับMoving → Stop - เลือกวงกลมของ
Start → Stopและตั้งPriority Orderให้มีลำดับก่อนStart → Movingเพราะเมื่อผู้เล่นปล่อย input ระหว่าง Start ต้องยกเลิกการไป Moving
Start → Idle — ปล่อย input ระหว่าง Start และหยุดแล้ว
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
- ลาก transition จาก
StartไปIdleแล้วสร้างกราฟแบบเดียวกับMoving → Idle - ตั้ง
Priority Orderให้มีลำดับก่อนStart → Movingเช่นเดียวกับStart → Stopสองเส้นนี้ไม่ชนกันเพราะเส้นหนึ่งใช้>และอีกเส้นใช้<=
Stop → Start — กดเคลื่อนที่ใหม่ระหว่าง Stop
bHasMovementInput
AND NOT bIsFalling
AND Stance == Standing
- ลาก transition จาก
StopกลับไปStart - สร้าง Rule Graph แบบเดียวกับ
Idle → Startแล้วต่อผลเข้าCan Enter Transition - ตั้ง
Priority Orderให้มีลำดับก่อนStop → Idleเพื่อให้ input ใหม่ขัดจังหวะ Stop ได้ทันทีโดยไม่ต้องรอคลิปจบ - ตรวจว่า State
StartเปิดAlways Reset on Entryตามข้อ 9.2 มิฉะนั้น Start อาจกลับมาเล่นต่อจากเวลาก่อนหน้า
ตรวจ Priority และ Compile
- ถ้ามี transition ออกจาก State เดียวกันหลายเส้น ค่า
Priority Orderที่เป็นเลขน้อยกว่าจะถูกพิจารณาก่อน จัดให้เส้นยกเลิก/ตอบสนอง input มาก่อนเส้นอัตโนมัติที่รอคลิปจบ - อย่าเปิด
Bidirectionalเพื่อทดแทนลูกศรขากลับ ให้สร้าง transition แยกแต่ละทิศตามรายการด้านบนเพื่อให้ Rule ชัดเจน - Compile และ Save จากนั้น hover วงกลมแต่ละลูกศร ตรวจ preview expression ว่าตรงกับสูตรของเส้นนั้นและไม่มี Result ที่ไม่ได้ต่อ
- ค่า Crossfade Duration, Blend Logic และค่า dead zone ขั้นสุดท้ายยังเป็น TBD ให้ปรับหลัง flow ถูกต้องและทดสอบกับ animation จริงแล้ว
9.5 ตรวจเส้นทาง Pose ถึง Output Pose
- กลับไป State Graph ของ
Standingตรวจว่า output จากSM_Standingต่อเข้าState Result - กลับไป
SM_Postureตรวจว่าEntryต่อเข้าStandingและ State นี้แสดงว่า pose ถูกประเมินอยู่ - กลับไป AnimGraph ตรวจว่า output จาก
SM_Postureเดินทางต่อถึง Output Pose โดยไม่มีสายขาด - วาง Inertialization ตามสถาปัตยกรรมในข้อ 4 หลัง pose จาก posture state machine หาก node นี้ยังไม่มีในกราฟ
- Compile และ Save จากนั้นตรวจ Compiler Results ต้องไม่มี error หรือ warning ใหม่จาก state/transition ที่เพิ่ม
- ยังไม่ต้องสร้าง
Airborneหรือ transition ที่อ่านbIsFallingในขั้นตอนนี้ ให้เพิ่มพร้อม jump/fall/land ตามข้อ 14 เพื่อไม่ให้มี State ว่างค้างอยู่ในกราฟ
9.6 ทดสอบใน PIE แบบทีละกรณี
- เริ่ม PIE แล้วเลือก instance ผู้เล่นใน Debug Filter ของ
ABP_Playerเปิดหน้าต่าง State Machine ค้างไว้เพื่อดู state ที่ active - ไม่กด input: ต้องอยู่
Idleและคลิปวนโดยไม่กระโดด - กดเดินหน้าจากหยุด: ต้องเห็น
Idle → Start → Movingเพียงหนึ่งรอบ ไม่สลับ Start/Moving ทุกเฟรม - ปล่อย input ขณะยังเคลื่อนที่: ต้องเห็น
Moving → Stop → Idleและ Stop เริ่มก่อน GroundSpeed เป็นศูนย์ - กดใหม่ระหว่าง Stop: ต้องออกจาก Stop ได้โดยไม่รอคลิปจบ และไม่ค้างใน pose สุดท้าย
- สลับ Walk/Jog/Run ซ้ำ ๆ แล้วดูว่าเท้าข้างที่รับน้ำหนักไม่เปลี่ยนกลางจังหวะ ถ้ายังมี foot sliding ให้แยกสาเหตุระหว่าง marker ผิด phase กับ authored speed ไม่ตรง movement speed
- ยังไม่ทดสอบ Jump/Airborne ในขั้นตอนนี้ เพราะ State และ transition ดังกล่าวจะสร้างในข้อ 14
- ทดสอบ client เพิ่มเมื่อโครง local ผ่านแล้ว โดยดูว่า remote proxy ไม่สั่นระหว่าง Idle/Moving; รายละเอียด replication อยู่ในข้อ 16
| อาการ | จุดตรวจแรก |
|---|---|
| ไม่ออกจาก Idle | กลับไปตรวจกราฟอัปเดตในข้อ 6.2 ก่อน: bHasMovementInput ต้องเป็น true ขณะกด, bIsFalling ต้องเป็น false และ Stance ต้องเป็น Standing จากนั้นจึงตรวจทิศลูกศรและ Rule Idle → Start |
| Start เล่นวน | ปิด Loop ที่ Sequence Player และตรวจเงื่อนไข Start → Moving |
| ข้าม Stop ไป Idle หรือ Stop ไม่เล่น | ตรวจว่าเงื่อนไขใช้ input ร่วมกับ GroundSpeed และไม่ได้รอ GroundSpeed เป็นศูนย์ก่อนเข้า Stop |
| เท้ากระโดดเมื่อเข้า Moving | ตรวจ Sync Group, marker ของ Start/loop และเวลาที่ transition เกิดตามข้อ 8 |
| Compile ผ่านแต่ตัวละครไม่เปลี่ยน pose | ตรวจสาย SM_Standing → Standing State Result → SM_Posture → Output Pose ตามข้อ 9.5 |
10. เลือก Directional Start
เป้าหมายคือเลือกคลิป Start ให้ตรงกับทิศที่ผู้เล่นเพิ่งกด แล้วเล่นคลิปนั้นเพียงครั้งเดียวก่อนเข้า Moving อย่าเริ่มด้วย Walk/Jog/Run, สอง Rotation Modes และทุกทิศพร้อมกัน ให้ทำ Walk + VelocityDirection ทีละทิศก่อน
กด movement ครั้งใหม่
→ อ่าน DesiredDirection
→ แปลงมุมเป็น Direction Bucket
→ จำ Bucket ไว้ตลอด State Start
→ เลือก Walk Start clip ที่ตรง Bucket
→ เล่นครั้งเดียว → Moving
10.1 ขอบเขตที่ทำได้ตอนนี้
| ข้อมูล | หน้าที่ | สถานะปัจจุบัน |
|---|---|---|
bHasMovementInput | บอกว่ามีการกด movement | พร้อม หลังผ่านข้อ 6.2–6.3 |
DesiredDirection | มุม input เทียบกับหน้าตัวละคร | ตรวจ PIE ผ่าน ตามข้อ 10.8 |
RotationMode | เลือกคลิปแบบหมุนร่างหรือแบบ strafe | Character + AnimInstance implement แล้ว ตามข้อ 10.7 |
Gait | เลือก family Walk/Jog/Run | TBD-LOCO-006 implemented; native latch StartGait และเลือกคลิปที่ SelectedStartAnimation |
FootPhase | เลือก suffix _LU/_RU เมื่อ clip family ต้องใช้ | TBD-LOCO-002 contract; runtime ประมาณจาก foot_l/foot_r จนกว่าจะมี Notify ที่ตรวจเฟรมแล้ว |
Start bind SelectedStartAnimation เข้า pin Sequence ของ Sequence Player ที่ต่อกับ State Result แล้ว การทดสอบ PIE ตามข้อ 10.8 ผ่านครบทั้งเก้า bucket, Gait, RotationMode, latch และทางออก Idle → Start → Moving10.2 ตรวจ DesiredDirection ก่อนสร้างตัวเลือกคลิป
- เปิด
ABP_Playerแล้ว Compile ให้ไม่มี error จากนั้นเริ่ม PIE - กลับมาที่หน้าต่าง Animation Blueprint เลือกตัวละครผู้เล่นที่กำลังควบคุมจาก
Debug Filterห้ามปล่อยเป็นNo debug object selectedเพราะค่าบนสายกราฟจะไม่วิ่ง - เปิด
Event Graphคลิกขวาที่ getter ของDesiredDirectionแล้วใช้Watch this valueหรือดูค่าจากแผงตัวแปรระหว่าง debug - หันตัวละครไปทิศหนึ่งแล้วกดเดินหน้า ค่าเป้าหมายควรอยู่ใกล้
0° - กดเฉียง, ด้านข้าง และด้านหลังทีละทิศ ตรวจว่าขนาดมุมเข้าใกล้
45°,90°,135°และ180° - ยืนยันว่าค่าที่เห็นเป็นไปตาม contract
TBD-LOCO-004: ค่าบวก = ขวา และค่าลบ = ซ้าย หากผลใน Preview กลับด้าน ให้แก้ตำแหน่ง sample/asset mapping ที่ผิด ห้าม negateDirectionเป็นจุด ๆ - ถ้ากดทิศต่างกันแต่ค่าค้างที่ 0 ให้กลับไปแก้สาย
Acceleration → Calculate Direction → Set DesiredDirectionในข้อ 6.4 ก่อน ห้ามสร้างระบบเลือกคลิปต่อจนกว่าค่านี้จะเปลี่ยนตาม input
10.3 แปลงมุมเป็น Direction Bucket
ขั้น A — สร้าง Enum asset
- ใน Content Browser ไปยังโฟลเดอร์ Animation ที่เก็บ asset ของระบบ locomotion คลิกขวาพื้นที่ว่างแล้วเลือก
Blueprints → Enumeration - ตั้งชื่อเสนอ
E_StartDirectionBucketแล้วเปิด asset - เพิ่มค่าเรียงตามนี้:
Forward,Positive45,Negative45,Positive90,Negative90,Positive135,Negative135,Positive180,Negative180 - ใช้คำว่า Positive/Negative ไปก่อน อย่าใช้ Left/Right จนกว่าจะผ่านการตรวจ signed direction ในข้อ 10.2 แล้ว Save enum
ขั้น B — สร้างตัวแปรใน ABP_Player
- เปิด
ABP_Playerใน My Blueprint กด+ที่ Variables แล้วสร้างStartDirectionBucket - เปลี่ยน Variable Type เป็น
E_StartDirectionBucket, Compile แล้วตั้ง Default Value เป็นForward - สร้าง Boolean
bHadMovementInput, Compile แล้วตั้ง Default Value เป็นfalseตัวแปรนี้คือค่าจากเฟรมก่อน ไม่ใช่สำเนาที่ตั้งพร้อมbHasMovementInput
ขั้น C — สร้าง ResolveStartDirectionBucket
- ใน My Blueprint กด
+ที่ Functions ตั้งชื่อเสนอResolveStartDirectionBucket - เลือก Function Entry แล้วเพิ่ม Input ชื่อ
Angleชนิด Float - เลือก Return Node แล้วเพิ่ม Output ชื่อ
BucketชนิดE_StartDirectionBucket - ลาก
Angleเข้า nodeAbs (Float)แล้วใช้ผลเดียวกันเข้าชุดเปรียบเทียบ<ตามตารางด้านล่าง - สร้าง Branch ไล่จากมุมน้อยไปมาก:
< 22.5ก่อน จากนั้น< 67.5,< 112.5และ< 157.5เฉพาะสาย False ของข้อก่อนหน้า - สาย True ของ
< 22.5คืนForwardส่วนช่วงอื่นให้เปรียบเทียบAngle >= 0.0แล้วคืนค่า Positive ทาง True และ Negative ทาง False - ช่วงสุดท้ายที่ไม่ผ่าน
< 157.5ไม่ต้องเปรียบเทียบขนาดอีก ให้ใช้เครื่องหมาย Angle เลือกPositive180/Negative180 - ถ้าต้องการแยกปลายทางให้อ่านง่าย ให้เพิ่ม Return Node สำหรับแต่ละผลลัพธ์และกำหนดค่า
Bucketที่ node นั้น แล้ว Compile และ Save
Abs(Angle) | ขนาดมุม | Bucket ที่คืน |
|---|---|---|
< 22.5 | 0° | Forward |
< 67.5 | 45° | Positive45 หรือ Negative45 ตามเครื่องหมาย Angle |
< 112.5 | 90° | Positive90 หรือ Negative90 |
< 157.5 | 135° | Positive135 หรือ Negative135 |
| ค่าที่เหลือจนถึง 180 | 180° | Positive180 หรือ Negative180 |
ค่า 22.5/67.5/112.5/157.5 เป็นจุดกึ่งกลางระหว่างมุม animation 0/45/90/135/180 ใช้เพื่อเลือก sample ที่ใกล้ที่สุด ไม่ใช่ gameplay threshold ใหม่
AbsAngle < 22.5 → Forward
else AbsAngle < 67.5 → Angle >= 0 ? Positive45 : Negative45
else AbsAngle < 112.5 → Angle >= 0 ? Positive90 : Negative90
else AbsAngle < 157.5 → Angle >= 0 ? Positive135 : Negative135
else → Angle >= 0 ? Positive180 : Negative180
0, 30, -80, 140, -179 ต้องได้ Forward, Positive45, Negative90, Positive135, Negative180 ตามลำดับ ตัวเลขเหล่านี้เป็น test cases ของการแบ่งช่วง ไม่ใช่ tuning value ของ movement10.4 จำ Bucket เฉพาะตอนเริ่มกด
ห้าม Set StartDirectionBucket จาก DesiredDirection ทุกเฟรมระหว่างอยู่ใน Start เพราะถ้าผู้เล่นขยับ stick คลิปอาจสลับกลางทาง ให้จับค่าที่ขอบ false → true ของ movement input และต้องเรียง execution ตามขั้นต่อไปนี้:
- หา
Set bHasMovementInputในEvent Blueprint Update Animationจากข้อ 6.2 แล้วต่อ exec ออกจาก node นี้เข้าSequenceเพิ่ม pin ให้มีThen 0,Then 1และThen 2 Then 0ใช้อัปเดตทิศของเฟรมปัจจุบัน: ต่อเข้า Branch ที่ Condition =bHasMovementInputทาง True ต่อCalculate Directionโดยใช้Accelerationและ Actor Rotation แล้วSet DesiredDirectionจากนั้นค่อยอัปเดตDirectionDeltaตามข้อ 6.4 ทาง False ปล่อยให้DesiredDirectionเก็บค่าล่าสุด- สร้าง
NOT Booleanจาก getterbHadMovementInputแล้วนำผลไป AND กับ getterbHasMovementInputตั้งชื่อความหมายของสายนี้ว่าJustPressed; ไม่จำเป็นต้องสร้างเป็นตัวแปรใหม่ Then 1ต่อเข้า Branch ที่ Condition =JustPressedทาง True เรียกResolveStartDirectionBucketโดยต่อ getterDesiredDirectionเข้าAngleแล้วต่อ outputBucketเข้าSet StartDirectionBucket; ทาง False ไม่ต้อง SetThen 2ต่อเข้าSet bHadMovementInputและต่อ getterbHasMovementInputเข้าค่าใหม่ ขั้นนี้ต้องอยู่ท้ายสุดเพื่อให้ Then 1 ยังอ่านค่า previous frame ได้- Compile และ Save จากนั้น PIE แล้ว Watch
bHasMovementInput,bHadMovementInput,DesiredDirectionและStartDirectionBucketพร้อมกัน - ทดสอบ
กด → ถือ → ปล่อย → กดทิศใหม่: bucket ต้องเปลี่ยนเฉพาะเฟรมแรกของการกดแต่ละครั้ง, คงเดิมระหว่างถือ และพร้อมเปลี่ยนใหม่หลังมีอย่างน้อยหนึ่งเฟรมที่ input เป็น false
หลัง Set bHasMovementInput
Sequence
├─ Then 0 → Branch(bHasMovementInput)
│ └─ True → CalculateDirection(Acceleration, ActorRotation)
│ → Set DesiredDirection → Set DirectionDelta
├─ Then 1 → Branch(bHasMovementInput AND NOT bHadMovementInput)
│ └─ True → ResolveStartDirectionBucket(DesiredDirection)
│ → Set StartDirectionBucket
└─ Then 2 → Set bHadMovementInput = bHasMovementInput
bHadMovementInput ก่อน Branch ของ Then 1 นิพจน์ JustPressed จะเป็น false ตลอด; ถ้า Resolve ก่อน Then 0 ระบบจะอ่าน DesiredDirection ของการกดครั้งก่อน10.5 ต่อ Walk clips ใน State Start
Start ใช้ SelectedStartAnimation → Sequence Player → State Result; Blend Poses by E_StartDirectionBucket เดิมไม่ได้ต่อกับ State Result แล้วขั้น A — baseline เดิมสำหรับสร้างตัวเลือก pose
- เปิด
SM_Standing → Startเก็บ Sequence Player เดิมที่ต่อกับ State Result ไว้เป็น fallback จนกว่ากราฟใหม่ Compile ผ่าน - คลิกขวาพื้นที่ว่าง ค้นหา
Blend Poses by E_StartDirectionBucketแล้ววาง node; ถ้าค้นไม่พบ ให้ Compile/Save enum และ ABP ก่อน แล้วเปิดเมนูค้นหาใหม่ - node เริ่มต้นอาจยังไม่แสดง pose ของทุก enum ให้คลิกขวาที่ node แล้วเลือกคำสั่งเพิ่ม pose/pin สำหรับ
Forward,Positive45,Negative45,Positive90,Negative90,Positive135,Negative135,Positive180และNegative180ชื่อคำสั่งย่อยอาจแสดงเป็นAdd Pose Pinหรือชื่อ enum entry ตาม UI ของ editor - ลาก getter
StartDirectionBucketจาก My Blueprint มาต่อเข้าActive Enum Value - ลาก
A_INP_WalkFwd_Startมาวางให้เป็น Sequence Player ปิดLoop Animationแล้วต่อเข้า poseForward - สร้าง Sequence Player ของ
A_INP_WalkFwd_Startเพิ่มเป็น fallback สำหรับDefault Poseและทุก bucket ที่ยังไม่ได้ยืนยัน L/R อย่าปล่อย pose ที่อาจถูกเลือกว่าง - ต่อ output ของ
Blend Poses by E_StartDirectionBucketเข้าState Resultแล้ว Compile หากผ่านจึงถอดสายของ Sequence Player เดิมออกจาก Result
ขั้น B — แทน fallback ด้วยคลิปจริงทีละคู่
- เริ่มจากคู่ 45° ก่อน เปิด animation
_45_Lและ_45_Rใน Animation Editor ดูการหมุนของ root/สะโพกและก้าวแรก - ใช้ mapping ที่ยืนยันจาก PIE แล้วว่า DesiredDirection เครื่องหมายบวกใช้คลิป suffix
_Rและเครื่องหมายลบใช้_L: ต่อ_45_Rเข้าPositive45และ_45_Lเข้าNegative45 - Compile และ PIE ทดสอบทั้งสองฝั่งก่อนทำคู่ถัดไป ถ้าถูกจึงทำ 90°, 135° และ 180° ด้วยวิธีเดียวกัน
- Sequence Player ทุกตัวต้องตั้ง
Loop Animation=falseและไม่เปิดตัวเลือกที่ตัด node ออกจาก relevancy test เพราะ transition อัตโนมัติจำเป็นต้องรู้เวลาคงเหลือของ clip ที่ active - เลือก State
Startที่หน้า State Machine แล้วตรวจใน Details ว่าAlways Reset on Entryเปิดอยู่ เพื่อให้กดครั้งใหม่แล้วคลิปเริ่มจากต้น - ยังไม่ต้องกำหนด Blend Time ใหม่ในรอบแรก ใช้ค่าเดิมเพื่อพิสูจน์การเลือก clip ก่อน แล้วค่อย tune จากผล visual test โดยไม่เดาตัวเลขล่วงหน้า
ABP_Player ใช้ metahuman_base_skel แต่คลิปทิศ 45°/90°/135°/180° ใต้ /Game/MaleLocomotionSet ใช้ Mannequin skeleton จึงห้ามต่อ source asset เหล่านั้นเข้า ABP โดยตรง ต้องสร้าง AnimSequence ที่ Retarget ไปยัง MetaHuman skeleton ใต้โฟลเดอร์ Lambeer ก่อน ส่วน A_INP_WalkFwd_Start ที่อยู่ใต้ /Game/Lambeer/Characters/Player/Animation/Walk เป็นคลิป MetaHuman ที่ใช้เป็น fallback ได้Native StartGait + RotationMode + StartDirectionBucket
└─→ SelectedStartAnimation
SelectedStartAnimation ──→ Sequence Player (Loop=false) ──→ State Result
ขั้น C — ตรวจทางออก Start → Moving
- กลับหน้า
SM_StandingคลิกลูกศรStart → Movingแล้วตรวจว่าAutomatic Rule Based on Sequence Player in Stateยังเปิดอยู่ - PIE ทีละ bucket แล้วดูว่า transition เกิดใกล้ท้ายคลิปที่กำลังเล่นจริง ไม่ใช่อ้างเวลาจาก fallback หรือ player ที่ inactive
- ถ้าบาง bucket ออกทันทีหรือค้าง ให้ตรวจ relevancy ของ Sequence Player และสาย pose ก่อน
- ถ้าการใช้ relevant player ยังไม่เสถียร ให้เพิ่ม Anim Notify ชื่อเสนอ
StartCompleteในทุก Start clip ณ เฟรมที่ตรวจภาพแล้ว และเปลี่ยน transition ให้ใช้ event/flag จาก notify เดียวกัน ห้ามวาง notify ด้วยเลขเฟรมที่เดา
10.6 รายชื่อคลิป VelocityDirection
family นี้ใช้เมื่อ Character หันตามทิศ movement คลิป angled Start มีหน้าที่เริ่มก้าวพร้อมหมุนร่าง:
| Bucket | Walk prototype | สิ่งที่ต้องกรอกหลังทดสอบ | เพิ่มภายหลังเมื่อ Gait พร้อม |
|---|---|---|---|
Forward | A_INP_WalkFwd_Start | ยืนยันว่าค่าใกล้ 0° เล่นคลิปนี้ | A_INP_JogFwd_Start, A_INP_RunFwd_Start |
Positive45/Negative45 | A_INP_WalkFwd_Start_45_R/L | Positive = R, Negative = L | A_INP_JogFwd_45_Start_L/R, A_INP_RunFwd_45_Start_L/R |
Positive90/Negative90 | A_INP_WalkFwd_Start_90_R/L | Positive = R, Negative = L | A_INP_JogFwd_90_Start_L/R, A_INP_RunFwd_90_Start_L/R |
Positive135/Negative135 | A_INP_WalkFwd_Start_135_R/L | Positive = R, Negative = L | A_INP_JogFwd_135_Start_L/R, A_INP_RunFwd_135_Start_L/R |
Positive180/Negative180 | A_INP_WalkFwd_Start_180_R/L | Positive = R, Negative = L | A_INP_JogFwd_180_Start_L/R, A_INP_RunFwd_180_Start_L/R |
ผล PIE ของโปรเจกต์ยืนยัน signed-direction mapping สำหรับ Start แล้วว่า Positive → _R และ Negative → _L โดย _L/_R ใน family นี้หมายถึงทิศหมุนซ้าย/ขวา ไม่ใช่เท้า ส่วน gait-phase suffix _LU/_RU ใช้ contract แยกตามข้อ 10.7
10.7 Contract ที่อนุมัติ: LookingDirection, Gait และ suffix
ULambeerAnimInstance และ ULambeerLocomotionComponent แล้ว Start clips ที่ retarget ไป MetaHuman อยู่ครบใต้ Animation/Walk|Jog|Run Native เลือกคลิปไว้ที่ SelectedStartAnimation ตาม StartGait + RotationMode + bucket และ State Start bind ค่านี้เข้า Sequence Player ที่ active แล้ว การตรวจ visual ตามข้อ 10.8 ผ่านครบ ส่วน FootPhase ยังเป็นค่าประมาณจากกระดูกเท้าจนกว่าจะมี Notify/curve ที่ตรวจเฟรมรายคลิปLookingDirection rotation contract
AimMode == HipRelaxedใช้RotationMode = VelocityDirection: Character เปิดbOrientRotationToMovementและไม่ใช้ controller yaw หมุน actorAimMode == ShoulderAimหรือADSใช้RotationMode = LookingDirection: Character ปิดbOrientRotationToMovementและ gameplay rotation system ต้องหัน actor ตาม aim/control yaw ก่อนที่ ABP จะเลือก strafe poseAimModeที่ replicate อยู่แล้วเป็น input ของ contract แต่ ABP เป็นผู้บริโภคเท่านั้น ห้าม ABP เขียนค่า rotation behavior กลับไปยัง Character- คลิปอย่าง
A_INP_JogFwdLt_45_Start,A_INP_JogFwdRt_90_StartและA_INP_JogBwd_Startใช้เฉพาะ LookingDirection เพื่อรักษาหน้าเข้าหากล้อง/เป้า ส่วน VelocityDirection ใช้ angled start ที่หมุนร่างตาม movement - การสลับ HipRelaxed ↔ ShoulderAim/ADS ต้องทดสอบทั้ง local player และ remote proxy ว่า actor yaw, movement direction และ pose เปลี่ยนในเฟรมที่สอดคล้องกันโดยไม่เกิด state flapping
| Bucket | Walk Looking | Jog Looking | Run Looking |
|---|---|---|---|
Forward | A_INP_WalkFwd_Start | A_INP_JogFwd_Start | A_INP_RunFwd_Start |
Positive45 / Negative45 | WalkFwdRt/Lt_45_Start | JogFwdRt/Lt_45_Start | RunFwdRt/Lt_45_Start |
Positive90 / Negative90 | WalkFwdRt/Lt_90_Start | JogFwdRt/Lt_90_Start | RunFwdRt/Lt_90_Start |
Positive135 / Negative135 | WalkBwdRt/Lt_45_Start | JogBwdRt/Lt_45_Start | fallback RunFwdRt/Lt_90_Start เพราะยังไม่มี Run backward start |
Positive180 / Negative180 | WalkBwd_Start | JogBwd_Start | fallback RunFwdRt/Lt_90_Start |
Start ใช้ Sequence Player ที่เปิด pin Sequence และรับ inherited SelectedStartAnimation แล้ว Compile ผ่านและตรวจ PIE ตามข้อ 10.8 เรียบร้อยTBD-LOCO-006 — authoritative Gait contract [CONFIRMED]
- สร้าง enum กลาง
ELambeerGait { Walk, Jog, Run }และให้ULambeerLocomotionComponentเป็น source of truth ของ replicatedCurrentGait - ลำดับตัดสิน gait คือ Crouched → Walk; sprint intent ที่ผ่านเงื่อนไข gameplay → Run; analog input ที่มากกว่า 0 แต่ต่ำกว่า
WalkInputThreshold→ Walk; กรณีเคลื่อนที่ปกติ → Jog - Encumbrance ปรับ movement speed ผ่าน multiplier แต่ไม่เปลี่ยนชื่อ gait โดยตัวมันเอง ถ้าอนาคตต้องบังคับลด gait ต้องทำเป็น gameplay rule ที่เปลี่ยน
CurrentGaitอย่างชัดเจน - Autonomous proxy อัปเดตแบบ predicted เพื่อความตอบสนอง แล้ว server ยืนยันค่าเดียวกัน; simulated proxy อ่านค่าที่ replicate ห้าม derive จาก
GroundSpeedหรือMaxWalkSpeed - AnimInstance อ่าน
CurrentGaitเป็นGaitและใช้เงื่อนไขGait == RunแทนbIsSprintingทุกจุด เมื่อ migrate Blueprint/C++ references ครบแล้วให้ลบbIsSprintingเพื่อไม่ให้มีสอง source of truth - ในเฟรม JustPressed ให้จับ
StartGait = Gaitพร้อมStartDirectionBucketแล้วใช้สองค่าที่ latch ไว้เลือก Walk/Jog/Run Start; ห้ามเลือก gait ใหม่ทุกเฟรมกลางคลิป Start
LocomotionComponent.CurrentGait (replicated)
→ AnimInstance.Gait
→ JustPressed: latch StartGait + StartDirectionBucket
→ Select RotationMode
→ Select StartGait
→ Select StartDirectionBucket
→ Play Start once
TBD-LOCO-002 — suffix contract [CONFIRMED]
| Suffix | ความหมายที่อนุมัติ | ตัวเลือกที่ใช้ | ตัวอย่างอ้างอิง |
|---|---|---|---|
_L | หมุนหรือเปลี่ยนทิศไปทางซ้าย | Direction bucket / signed turn direction | A_RunFwd_45_Start_L = Idle หมุนซ้าย 45° แล้วเข้า Run forward |
_R | หมุนหรือเปลี่ยนทิศไปทางขวา | Direction bucket / signed turn direction | A_RunFwd_90_Start_R = Idle หมุนขวา 90° แล้วเข้า Run forward |
_LU | Left Up — เท้าซ้ายยกอยู่ใน locomotion loop cycle ณ จุดเข้า transition | FootPhase = LeftUp | A_RunFwd_Stop_LU |
_RU | Right Up — เท้าขวายกอยู่ใน locomotion loop cycle ณ จุดเข้า transition | FootPhase = RightUp | A_RunFwd_Stop_RU |
_L/_Rเป็น direction suffix และห้ามเลือกจาก foot state;_LU/_RUเป็น gait-phase suffix และห้ามเลือกจาก signed direction- ใช้
FootPhaseแบบ LeftUp/RightUp เป็นตัวเลือกตรงสำหรับ_LU/_RUถ้ายังคงตัวแปรเดิมLastPlantedFootชั่วคราว ให้ map แบบตรงข้ามในช่วง single support: LastPlantedFoot = Right → LeftUp และ LastPlantedFoot = Left → RightUp - ความหมาย suffix มาจาก asset-description reference ที่ผู้ใช้ยืนยันเมื่อ 2026-08-31 แต่ตำแหน่งเฟรมของ Notify/curve ต้องตรวจ Animation Editor รายคลิป ห้ามคัดลอกเลขเฟรมข้ามคลิปโดยเดา
ลำดับ migration ที่ไม่ทำให้ Blueprint แตก
- เพิ่ม
ELambeerGait, replicatedCurrentGaitและ getter ใน Locomotion Component ก่อน - เพิ่ม
Gait,StartGait,RotationModeและFootPhaseให้ AnimInstance/ABP แล้ว migrate graph ให้ใช้ค่าใหม่ - ค้น reference ของ
bIsSprintingทั้ง C++ และ Blueprint assets แทนด้วยGait == Runและ Compile/PIE/Network test ให้ผ่าน - ลบ
bIsSprintingหลังไม่มี reference เหลือเท่านั้น เพื่อหลีกเลี่ยง broken pins ใน Blueprint
10.8 ทดสอบทีละทิศ [VERIFIED 2026-09-01]
หลักการของการทดสอบคือ เลือกหนึ่งทิศ กดหนึ่งครั้ง แล้วตรวจผลให้ครบก่อนเปลี่ยนทิศ เริ่มจาก Forward ซึ่งตรวจง่ายที่สุด จากนั้นค่อยเพิ่มทิศเฉียง ด้านข้าง และด้านหลัง หากทิศใดไม่ผ่าน ให้หยุดแก้ทิศนั้นก่อนเพื่อไม่ให้หลายปัญหาปนกัน
StartDirectionBucket/StartGait, การเลือกคลิปตาม Gait และ RotationMode, suffix ซ้าย/ขวา และลำดับ Idle → Start → Movingขั้นที่ 1 — เตรียมหน้าจอ Debug
- เริ่ม PIE แล้วเลือกตัวละครที่กำลังควบคุมใน
Debug Filter - เปิด
SM_Standingให้มองเห็นว่า State ใดกำลังทำงานอยู่ - Watch ค่าต่อไปนี้:
DesiredDirection,bHasMovementInput,bHadMovementInputและStartDirectionBucket - ก่อนกดรอบใหม่ ให้ปล่อย movement input อย่างน้อยหนึ่งเฟรม และยืนยันว่า
bHadMovementInput = false
ขั้นที่ 2 — ทดสอบหนึ่งทิศตามลำดับนี้
- เริ่มจาก Idle แล้วกดทิศที่ต้องการทดสอบหนึ่งครั้ง
- ดู
DesiredDirectionว่าได้มุมตามทิศที่กด - ดู
StartDirectionBucketว่าเปลี่ยนเป็น bucket ที่ตรงกับมุมนั้น - ดูภาพ animation ว่าเลือกคลิป Start ตรงทิศ และหากชื่อคลิปมี
_L/_Rต้องหมุนไปด้านที่ถูกต้อง - ดู State Machine ว่าเดินตามลำดับ
Idle → Start → Movingเพียงหนึ่งรอบ คลิป Start ต้องไม่ Loop, ไม่ถูกข้ามทันที และไม่ค้างอยู่ใน Start - ปล่อย input ให้กลับไป Idle แล้วจึงเริ่มทดสอบทิศถัดไป
| ลำดับที่แนะนำ | มุมที่ตรวจ | Bucket ที่ต้องเห็น |
|---|---|---|
| 1. เดินหน้า | ใกล้ 0° | Forward |
| 2. เฉียงหน้า | ใกล้ +45° และ -45° | Positive45 และ Negative45 |
| 3. ด้านข้าง | ใกล้ +90° และ -90° | Positive90 และ Negative90 |
| 4. เฉียงหลัง | ใกล้ +135° และ -135° | Positive135 และ Negative135 |
| 5. ด้านหลัง | ใกล้ +180° และ -180° | Positive180 และ Negative180 |
Positive → _R และ Negative → _L ตามผล PIE ในข้อ 10.2, contract ข้อ 10.7 และผลทดสอบข้อ 10.8ขั้นที่ 3 — ทดสอบเคสขอบกับทุกทิศ
| สิ่งที่ทำ | ผลที่ต้องได้ |
|---|---|
| กดจาก Idle | จับ StartDirectionBucket และ StartGait หนึ่งครั้ง แล้วเล่น Start ที่ตรงกับค่าทั้งสอง |
| ปล่อย input ระหว่าง Start | ระบบออกจาก Start ได้ตาม Transition Rule โดยไม่ Loop หรือค้าง |
| กดใหม่ระหว่าง Stop | ระบบเริ่มรอบใหม่และจับ bucket จาก input ครั้งใหม่ |
| หยุดสนิท แล้วกดทิศใหม่ | bHadMovementInput กลับเป็น false ก่อนกด และ bucket เปลี่ยนตามทิศใหม่ |
| ถือ stick ค้าง แล้วเปลี่ยนทิศระหว่าง Start | StartDirectionBucket ต้องคงค่าเดิมจนคลิป Start จบ การเปลี่ยนทิศหลังเริ่มเคลื่อนที่ให้ Pivot/Turn ในข้อ 11 จัดการ |
ถ้าผลไม่ตรง ให้ไล่แก้จากอาการ
| อาการ | ตรวจจุดนี้ก่อน | วิธีแก้ |
|---|---|---|
DesiredDirection เปลี่ยน แต่ bucket ไม่เปลี่ยน | Then 1 อาจไม่ได้ใช้ JustPressed หรือ bHadMovementInput ถูก Set เร็วเกินไป | ตรวจลำดับ Sequence ในข้อ 10.4 |
bucket เป็น Forward ทุกครั้ง | Angle อาจไม่ได้ต่อจาก DesiredDirection หรือ function ต่อ Branch ผิด | เปิด ResolveStartDirectionBucket แล้ว Watch ทั้ง input และ output |
| bucket ถูก แต่คลิปซ้าย/ขวาผิด | mapping ใน native selector อาจจับ Positive/Negative กับ suffix _L/_R สลับกัน | ตรวจ mapping ที่สร้าง SelectedStartAnimation โดยคง contract Positive → _R, Negative → _L |
| คลิปเปลี่ยนกลาง State Start | ระบบกำลัง Set bucket ทุกเฟรม แทนที่จะ Set เฉพาะ JustPressed | ลบสาย Set รายเฟรม แล้วใช้ latch ตามข้อ 10.4 |
| ครั้งแรกถูก แต่ครั้งต่อไปไม่เลือกใหม่ | bHadMovementInput อาจไม่ถูกเขียนเป็น false ตอนปล่อย | ให้ Then 2 ทำงานทุก update โดยไม่ผ่าน Branch |
| Start ถูกข้ามทันทีหรือค้าง | Automatic Rule อาจอ่าน Sequence Player ที่ไม่ relevant | ตรวจ relevancy และ Transition Rule ตามขั้น C ของข้อ 10.5 |
| Compile warning หรือไม่มี pose | SelectedStartAnimation อาจเป็น null หรือ pin Sequence หลุด | ตรวจ fallback ใน native selector และสาย SelectedStartAnimation → Sequence Player |
StartDirectionBucket และ StartGait ถูกจับเพียงครั้งเดียวต่อการเริ่มกด; Start เล่นครั้งเดียวแล้วเข้า Moving; LookingDirection ตรงกับ gameplay rotation mode; และ _L/_R ไม่ปะปนกับ _LU/_RU11. Stop, Pivot, Turn และ Spin
ข้อ 9 ใช้ Stop เดินหน้าคงที่เป็น fallback และข้อ 10 ทำ Directional Start สำเร็จแล้ว ขั้นต่อไปคือเลือกคลิปตอน ปล่อย input หรือ เปลี่ยนทิศกะทันหันขณะยังเคลื่อนที่ ให้ถูกประเภท จากนั้น latch ผลไว้จนคลิปจบ ห้ามคำนวณเลือก Stop/Pivot/Turn/Spin ใหม่ทุกเฟรมกลาง State
Moving
├─ ปล่อย input → Stop
├─ ยังมี input + กลับทิศ → Pivot หรือ Spin
├─ ยังมี input + เปลี่ยน heading → Moving Turn
└─ ไม่เข้าเงื่อนไขใด → Moving 2D ต่อ
Transition State จบหรือถูกยกเลิก → Moving / Idle
TransitionType กับ selected animation เท่านั้น ลำดับ priority คือ Airborne/Posture → Stop → Spin → Pivot → Moving Turn ค่า Baseline v0.1 และ State ของ Pivot/MovingTurn/Spin integrate แล้ว, Compile/Save และ PIE startup ผ่าน; Fast Stop ปิดไว้ และยังต้องผ่าน visual/behavior กับ multiplayer gate ก่อนถือว่าพร้อม production11.1 เตรียมข้อมูลและกติกาการเลือกเพียงครั้งเดียว
ULambeerAnimInstance แล้ว ได้แก่ ELambeerLocomotionTransitionType, selected animation, gait/source/target/delta/foot-phase/entry-speed latch, transition lock และ debug fallback flags พร้อม replicated movement-intent สำหรับ simulated proxy; C++ build/link, inherited properties, Blueprint Compile/Save และ Dedicated Server PIE smoke test แบบ 2 clients ผ่านโดย remote proxy เลือก Stop clip หลังอีก client ปล่อย input เหลือการทดสอบภาพจริงทุกทิศ/foot phase, latency และ listen-server gate| ข้อมูล | ใช้ตัดสินอะไร | ข้อควรระวัง |
|---|---|---|
Gait | กำหนดเพดาน Walk/Jog/Run family ที่ selector อนุญาต | Stop latch authoritative Gait ตอนปล่อย input แต่เลือก visual family ที่ใกล้ entry speed ที่สุดโดยไม่เลือกสูงกว่า Gait; ไม่ใช้ StartGait และไม่แก้ gameplay gait |
LocalVelocity และ Direction | ทิศที่ร่างกายกำลังเคลื่อนจริง | Stop ต้องจับค่าก่อน GroundSpeed ลดจน direction ไม่นิ่ง |
DesiredDirection | ทิศที่ input ใหม่ต้องการ | เมื่อปล่อย input ให้คงค่าล่าสุดไว้ตามข้อ 6.4; ห้ามใช้เป็นทิศ Stop แทน velocity |
DirectionDelta | ขนาดและเครื่องหมายของการเปลี่ยนทิศ | Positive เลือก suffix _R, Negative เลือก _L ตาม contract ข้อ 10.7 |
| Normalized velocity/input dot product | แยกเปลี่ยน heading เล็กน้อยกับทิศสวนกัน | ใช้เป็นข้อมูลประกอบ ไม่กำหนด threshold โดยเดา |
FootPhase | เลือก Stop _LU/_RU | ใช้เฉพาะ suffix gait phase; ห้ามใช้เลือก Pivot/Turn _L/_R |
GroundSpeed และ MovementSpeedDeadZone | แยก moving/idle และเก็บความเร็วก่อนเบรก | Baseline v0.1 ปิด Fast Stop; หากจะเปิดภายหลังต้องวัดและอนุมัติเป็น tuning รอบใหม่ |
สร้าง data contract ตาม C++ convention ของโปรเจกต์ โดยโครงที่อนุมัติแล้วต้องมีอย่างน้อยคือ:
- enum
ELambeerLocomotionTransitionTypeมีNone,Stop,Pivot,MovingTurnและSpin SelectedTransitionAnimationชนิด Animation Sequence reference สำหรับต่อเข้าพินSequenceของ Sequence Player- ค่าที่ latch พร้อมกัน: transition type, gait, source direction, target direction, signed delta, foot phase และ entry speed
bTransitionLockedหรือ state-equivalent ป้องกัน selector เขียนค่าทับจนกว่าคลิปจบหรือเข้า cancel path
ABP_Player; hysteresis/cooldown/commit รวมศูนย์ใน native selector และ blend อยู่ที่ transition nodes ไม่กระจายเป็น magic number ใน rule graph11.2 Stop — ทำ baseline นี้ให้ผ่านก่อน
ABP_Player State Stop เปิดพิน Sequence และ bind inherited SelectedTransitionAnimation แล้ว, ปิด Loop, เปิด Always Reset on Entry และ Compile/Save ผ่าน Retargeted Stop clips บน MetaHuman skeleton ครบ 30 คลิป: Walk 12, Jog 12 และ Run forward hemisphere 6; ใช้ RTG_AutoGenerated โดย source เป็น Manny และ target เป็น SKM_MHC_Man_BodyMesh ส่วน Run backward ใช้ Jog fallback ตาม policy และ Fast Stop ยังปิดอยู่ Movement baseline แยก braking friction ออกจาก GroundFriction แล้ว โดยใช้ bUseSeparateBrakingFriction=true และ BrakingFriction=0; Walk/Run/Crouch คง BrakingDecelerationWalking=480 ส่วน Jog ใช้ JogBrakingDecelerationWalking=260 ซึ่ง derive จาก Root Motion Stop distance โดยรักษาสัดส่วนระยะหยุดของ Run ที่ผ่าน visual feedback ค่า Jog runtime 442 ลดถึงศูนย์ประมาณ 1.0 วินาทีใน automated probe; ยังต้องผ่าน visual/behavior test ก่อนถือเป็น tuning ที่อนุมัติStop เกิดเมื่อผู้เล่นเพิ่งปล่อย movement input แต่ capsule ยังมีความเร็ว ลำดับที่ใช้เลือกคือ Gait ceiling + entry speed → ทิศ velocity → FootPhase ส่วน Fast Stop เปิดใช้ภายหลังเมื่อมี threshold ที่อนุมัติ
JustReleased = (NOT bHasMovementInput) AND bHadMovementInput
JustReleased
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
AND NOT bTransitionLocked
→ latch Stop inputs
→ choose nearest allowed Walk/Jog/Run authored-speed family
→ resolve SelectedTransitionAnimation
→ TransitionType = Stop
- ในเฟรม JustReleased ให้บันทึก
Gait,Direction,FootPhaseและGroundSpeedก่อนอัปเดตค่าประวัติ input สำหรับเฟรมถัดไป - แปลง Direction ที่ latch ไว้เป็น bucket ชุดเดียวกับ Blend Space: Forward, ForwardLeft/Right, SideLeft/Right, BackwardLeft/Right และ Backward
- ใช้
Gaitปัจจุบันเป็นเพดาน แล้วเทียบTransitionEntrySpeedกับ Walk/Jog/Run speed หลังคูณ encumbrance เลือก family ที่ใกล้ที่สุด เช่นเพิ่งออกตัวด้วย Gait Jog แต่ปล่อยตอนความเร็วยังใกล้ Walk ให้เลือก Walk Stop; หากGait == Runแต่ทิศอยู่ใน backward hemisphere ให้ใช้ Jog fallback เพราะไม่มี Run backward Stop ทั้งสองกรณีไม่เปลี่ยน authoritative Gait - Forward/Backward family ที่มี
_LU/_RUให้เลือกจาก FootPhase; ถ้าFootPhase == Unknownให้ใช้ fallback ที่ทีมกำหนดและเก็บ warning/debug flag แทนการสุ่มทุกเฟรม - ทิศเฉียง/ด้านข้างใช้คลิปที่ชื่อระบุทิศและไม่มี foot suffix เช่น
A_INP_JogFwdLt_45_Stopห้ามต่อ FootPhase เข้า selector ส่วนนั้น - ต่อ
SelectedTransitionAnimationเข้าพิน Sequence ของ StateStop, ปิด Loop และเปิดAlways Reset on Entry - คง Transition Rules และ cancel paths จากข้อ 9.4: ปล่อยแล้วหยุดสนิทอาจไป Idle โดยตรง และ input ใหม่ระหว่าง Stop ต้องออกไป Start/Moving ก่อนรอคลิปจบ
| ทิศ/Gait | คลิปที่ตรวจพบ | กติกา |
|---|---|---|
| Forward · Walk/Jog/Run | WalkFwd_Stop_LU/RU, JogFwd_Stop_LU/RU, RunFwd_Stop_LU/RU | เลือก _LU/_RU จาก FootPhase |
| Backward · Walk/Jog | WalkBwd_Stop_LU/RU, JogBwd_Stop_LU/RU | Run ใช้ Jog fallback; ไม่มี Run backward Stop |
| Forward diagonal/side | Walk/Jog/RunFwdLt/Rt_45_Stop, Walk/Jog/RunFwdLt/Rt_90_Stop | เลือก signed side จาก Direction |
| Backward diagonal/side | Walk/JogBwdLt/Rt_45_Stop, Walk/JogBwdLt/Rt_90_Stop | Run ใช้ Jog fallback |
| Fast | WalkFwd/Bwd_Stop_Fast, JogFwd/Bwd_Stop_Fast, RunFwd_Stop_Fast | ยังไม่ใช้จน resolve entry-speed/deceleration threshold |
11.3 Pivot — เปลี่ยนจากทิศหนึ่งไปทิศสวนกัน
Pivot ใช้เมื่อยังถือ input อยู่ แต่ desired direction ใหม่สวนกับ velocity เดิมมากพอ ตัวตรวจต้องใช้ทั้ง DirectionDelta และ dot product เพื่อกันการเข้า Pivot จากการแก้ทิศเล็กน้อย
bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
AND Abs(DirectionDelta) >= PivotEnterAngle [Baseline 125°]
AND Dot(LocalVelocity2D, LocalDesired2D) < PivotDotThreshold [Baseline -0.35]
AND NOT bTransitionLocked
→ latch source + target + signed side
→ TransitionType = Pivot
- ทำ Pivot ใน Walk หรือ Jog ก่อนหนึ่งคู่ เช่น Forward → Backward และ Backward → Forward แล้วทดสอบ lock/cancel ให้ผ่านก่อนเติมทิศเฉียง
- เลือก source/target family จากทิศ velocity เดิมและ desired direction ใหม่ ไม่เลือกจาก target เพียงด้านเดียว เพราะชื่อ asset encode ทั้งสองฝั่ง เช่น
JogFwd2JogBwd - เลือก
_L/_Rจากเครื่องหมาย signed delta ตามข้อ 10.7 ไม่ใช้ FootPhase - Walk, Jog และ Crouch มี Pivot family ที่ตรวจพบ; ไม่พบ Run Pivot จึงให้ Run reversal ไป Spin หรือ Jog fallback ตาม selector ที่อนุมัติ
- ใน State
Pivotใช้ Sequence Player ที่เปิดพินSequence, ปิด Loop, เปิด Always Reset on Entry และตั้งทางออกหลักกลับMovingเมื่อคลิปใกล้จบ - เพิ่ม cancel path ไป Airborne/Posture ก่อน และ Stop เมื่อผู้เล่นปล่อย input กลาง Pivot; priority ต้องสูงกว่าทางจบอัตโนมัติ
| การเปลี่ยนทิศ | ตัวอย่าง asset จริง |
|---|---|
| Forward → Backward | A_INP_WalkFwd2Bwd_Pivot_L/R, A_INP_JogFwd2JogBwd_Pivot_L/R |
| Backward → Forward | A_INP_WalkBwd2Fwd_Pivot_L/R, A_INP_JogBwd2JogFwd_Pivot_L/R |
| Forward → Backward diagonal | A_INP_JogFwd2JogBwdLt_45_Pivot_L/R, A_INP_JogFwd2JogBwdRt_90_Pivot_L/R |
| Diagonal reversal | A_INP_WalkFwdLt2BwdLt_45_Pivot_L/R, A_INP_CrouchBwdRt2FwdRt_90_Pivot_L/R |
/Game/Lambeer/Characters/Player/Animation/Walk|Jog แล้ว Native selector เปิดใช้ milestone Forward ↔ Backward สำหรับ Walk/Jog และ Run → Jog fallback พร้อม signed side Positive → _R, Negative → _L. ABP_Player มี State Pivot ที่ bind SelectedTransitionAnimation, ปิด Loop, เปิด Always Reset on Entry, เข้าเมื่อ TransitionType == Pivot, cancel เมื่อ type ถูก clear และ auto-exit กลับ Moving; Compile/Save และ PIE startup ผ่าน แต่ diagonal/Crouch coverage กับ LOCO-11-P01 ยังต้องตรวจภาพจริง11.4 Moving Turn — เปลี่ยน heading แต่ยังไม่ใช่ reversal
Moving Turn ใช้เมื่อ source motion ยังอยู่ใน forward family และผู้เล่นเปลี่ยน heading ต่อเนื่อง คลิปที่ตรวจพบมีมุม 45°, 90°, 135° และ 180° สำหรับ Walk, Jog, Run และ Crouch
- เริ่มจาก 45° ใน Walk/Jog ก่อน เพราะแยกจาก Pivot ได้ง่ายที่สุด แล้วค่อยเพิ่ม 90°, 135° และ 180° หลัง threshold bands ไม่ทับกัน
- ตรวจว่า source velocity อยู่ใน forward region ก่อนเลือก
Fwd_*_Turn; ถ้ากำลังเคลื่อนหลังหรือด้านข้างให้ใช้ Blend Space/Pivot/Spin แทนตาม coverage - ใช้ขนาด
Abs(DirectionDelta)เลือก angle bucket และเครื่องหมายเลือก_L/_R; Baseline v0.1 เข้าเมื่อ 30° และแบ่ง bucket ที่ 67.5°/112.5°/157.5° พร้อม re-entry hysteresis 7.5° - เมื่อเข้า State ให้ latch animation และ target direction ห้ามสลับจาก 45 เป็น 90 กลางคลิปเพราะ input ขยับต่อ
- ทางออกหลักกลับ Moving; ถ้า delta เพิ่มถึง reversal region ระหว่างคลิป ให้รอ cancel window ที่กำหนดหรือออกผ่าน Spin/Pivot ตาม policy ที่อนุมัติ ห้ามให้สอง State แข่งขันกันทุกเฟรม
| Family | คลิปที่ตรวจพบ |
|---|---|
| Walk | A_INP_WalkFwd_45/90/135/180_Turn_L/R |
| Jog | A_INP_JogFwd_45/90/135/180_Turn_L/R |
| Run | A_INP_RunFwd_45/90/135/180_Turn_L/R |
| Crouch | A_INP_CrouchFwd_45/90/135/180_Turn_L/R |
MovingTurn bind selected animation, ปิด Loop, เปิด Always Reset on Entry, มี enum entry/cancel และ auto-exit กลับ Moving; Compile/Save และ PIE startup ผ่าน ส่วน Crouch Turn กับ LOCO-11-T01 ยังไม่ integrate/ยังต้องตรวจภาพจริง11.5 Spin — ใช้เฉพาะ reversal รุนแรงที่มี coverage
Spin เป็น transition ที่ภาพชัดและกินเวลามากกว่า Pivot จึงเพิ่มเป็นลำดับสุดท้าย ใช้เมื่อ source/target ตรงกับ asset จริงและเข้า spin region ที่ทดสอบแล้ว ไม่ใช่ fallback สำหรับทุก direction mismatch
- ทำ Pivot และ Moving Turn ให้เสถียรก่อน แล้วจึงเปิด selector ของ Spin เพื่อแยกสาเหตุเวลาทดสอบ
- Forward ↔ Backward มีคลิป 180° แบบ
_L/_R; diagonal cross-over มีคลิป 45°/90° ที่ชื่อ encode source และ target - Run Spin ที่ตรวจพบเปลี่ยนจาก Run Forward ไป Jog Backward เช่น
A_INP_RunFwd_Spin_JogBwd_180_L/Rหลังคลิปจบ animation family จึงต้องสอดคล้องกับ Jog fallback โดยไม่แก้ authoritative Gait ใน ABP - Spin ใช้ transition lock เช่นเดียวกับ Pivot และต้องมี cancel path สำหรับ Airborne, posture change และ input release
- Policy confirmed: Pivot เป็น reversal มาตรฐาน ส่วน Spin ใช้เฉพาะ source/target ที่ตรงกับ asset coverage; Baseline v0.1 ใช้ enter 165°, re-entry hysteresis 10°, cooldown 0.35 s และ commit 20% ของ normalized clip time
| Family | ตัวอย่าง asset จริง |
|---|---|
| Walk | A_INP_WalkFwd_Spin_Bwd_180_L/R, A_INP_WalkBwdLt_Spin_FwdRt_45/90 |
| Jog | A_INP_JogFwd_Spin_Bwd_180_L/R, A_INP_JogFwdRt_Spin_BwdLt_45/90 |
| Run → Jog | A_INP_RunFwd_Spin_JogBwd_180_L/R, A_INP_RunFwdLt_Spin_JogBwdRt_45/90 |
| Crouch | A_INP_CrouchFwd_Spin_Bwd_180_L/R, A_INP_CrouchBwdRt_Spin_FwdLt_45/90 |
/Game/Lambeer/Characters/Player/Animation/Spin Native selector เปิดใช้ Walk/Jog Forward ↔ Backward 180° และ Run Forward → Jog Backward 180° ตาม policy. State Spin bind selected animation, ปิด Loop, เปิด Always Reset on Entry, มี enum entry/cancel และ auto-exit กลับ Moving; Compile/Save และ PIE startup ผ่าน ส่วน diagonal/Crouch selector กับ LOCO-11-SP01 ยังไม่ integrate/ยังต้องตรวจภาพจริง11.6 ต่อ State และกำหนด priority ไม่ให้แข่งขันกัน
SM_Standing มี Pivot, MovingTurn และ Spin ครบ แต่ละ State ใช้ Sequence Player หนึ่งตัวรับ SelectedTransitionAnimation, Loop ปิด, Always Reset on Entry เปิด; entry/cancel/auto-exit และ blend Baseline v0.1 compile/save ผ่าน พร้อม PIE startup ที่ไม่พบ AnimBlueprint หรือ runtime errorSM_Standing
├─ Idle
├─ Start
├─ Moving
├─ Stop
├─ Pivot
├─ MovingTurn
└─ Spin
Moving → Stop เมื่อ TransitionType == Stop
Moving → Pivot เมื่อ TransitionType == Pivot
Moving → MovingTurn เมื่อ TransitionType == MovingTurn
Moving → Spin เมื่อ TransitionType == Spin
- สร้าง State ใหม่ทีละ State เริ่ม Pivot แล้ว Compile/Test ก่อนเพิ่ม MovingTurn และ Spin ทุก State ใช้ Sequence Player ที่เปิดพิน
Sequenceตัวเดียวต่อ State และรับค่าจากSelectedTransitionAnimation - ปิด Loop และเปิด Always Reset on Entry ทุก transition State
- Priority confirmed: selector คืน
TransitionTypeเพียงค่าเดียวต่อ event ตามลำดับ Airborne/Posture cancel → Stop เมื่อไม่มี input → Spin เฉพาะ coverage ที่อนุมัติ → Pivot สำหรับ reversal อื่น → Moving Turn → None - ใช้ enter threshold กับ re-entry hysteresis แยกกันเพื่อไม่ให้ค่าใกล้ขอบทำ State flapping; Baseline v0.1 ใช้ Turn 7.5° และ Pivot/Spin 10° และต้องกลับออกจาก enter band ก่อน rearm
- Baseline v0.1 ใช้ commit window แยกตาม family ที่ Turn 10%, Pivot 15% และ Spin 20% ของ normalized clip time; หาก visual test พบ foot plant/weight transfer ต่างกันรายคลิป ให้เพิ่ม Notify/curve override ใน tuning รอบถัดไปแทนการเปลี่ยนค่ากลางแบบเดา
- เมื่อ State จบ ให้ clear lock/type หลังระบบอ่านทางออกแล้ว ไม่ clear ก่อนหน้าเพราะอาจกลับเข้า State เดิมซ้ำในเฟรมเดียวกัน
- เพิ่ม debug output ที่แสดง transition type, selected asset name, latched source/target direction, signed delta, gait และ foot phase ระหว่าง PIE
11.7 ทดสอบทีละประเภทก่อนเปิดพร้อมกัน
| Test ID | วิธีทดสอบ | ผลที่ต้องเห็น |
|---|---|---|
| LOCO-11-S01 | ปล่อย input จาก Walk/Jog/Run Forward ทั้ง LeftUp/RightUp | Stop family และ _LU/_RU ถูกต้อง คลิปไม่เปลี่ยนกลาง State |
| LOCO-11-S02 | ปล่อยจาก 8 ทิศ | เลือกทิศจาก latched velocity; Run backward ใช้ Jog fallback โดย Gait source of truth ไม่ถูกเขียนทับ |
| LOCO-11-S03 | ปล่อยแล้วกดใหม่ระหว่าง Stop | ยกเลิก Stop ไป Start/Moving ได้ ไม่ค้างเฟรมท้าย |
| LOCO-11-P01 | Jog Forward แล้วกด Backward ค้าง | เข้า Pivot หนึ่งครั้ง เลือก L/R ตาม signed delta และกลับ Moving |
| LOCO-11-T01 | เปลี่ยน heading 45° แล้ว 90° ขณะเคลื่อนหน้า | เข้า Moving Turn bucket ที่ตรงมุมโดยไม่สลับกลางคลิป |
| LOCO-11-X01 | ทดสอบ reversal ที่กำหนดให้เป็น Spin | เข้า Spin แทน Pivot เพียง State เดียว และ Run Spin จบด้วย pose ที่เข้ากับ Jog backward fallback |
| LOCO-11-C01 | กระโดด/เปลี่ยน posture/ปล่อย input กลาง transition | cancel priority ถูกต้อง ไม่มี State ค้างหรือเล่นสอง transition ซ้อน |
| LOCO-11-N01 | ทดสอบ remote proxy พร้อม latency | ไม่มี transition flapping; capsule movement ยัง authoritative และไม่ replicate animation time ทุกเฟรม |
11.8 แก้ปัญหาจากอาการที่เห็น
| อาการ | ตรวจจุดแรก | แนวแก้ |
|---|---|---|
| ปล่อย input แล้ว capsule หยุดทันทีทั้งที่ Stop clip ยังเล่น | GroundFriction, BrakingFrictionFactor, bUseSeparateBrakingFriction และ BrakingDecelerationWalking | แยก braking friction ออกจาก steering friction และให้ gameplay movement ลดความเร็วต่อเนื่อง; ห้ามแก้ด้วย Root Motion หรือยืด animation อย่างเดียว |
| เพิ่งออกตัวช้า ๆ แต่เลือก Jog/Run Stop | TransitionEntrySpeed, authoritative Gait และ speed หลังคูณ encumbrance | ใช้ Gait เป็นเพดานและเลือก visual Stop family ที่ authored speed ใกล้ entry speed ที่สุด ห้ามเปลี่ยน gameplay gait ตาม animation |
| Stop เลือกทิศผิดหลังปล่อย | ค่า Direction ที่ latch ใน JustReleased | จับจาก velocity ก่อน speed เข้า dead zone ไม่อ่าน DesiredDirection หลังปล่อย |
| Stop สลับ LU/RU ระหว่างเล่น | selected asset ถูก resolve ทุกเฟรมหรือไม่ | latch FootPhase และ asset หนึ่งครั้งก่อนเข้า State แล้วเปิด transition lock |
| Pivot ยิงซ้ำทุกเฟรม | transition lock, edge detection และ hysteresis | trigger เฉพาะตอนข้าม enter threshold และ unlock หลังออก State |
| Pivot ซ้าย/ขวาสลับ | เครื่องหมาย DirectionDelta | คง mapping Positive → _R, Negative → _L; ห้ามใช้ FootPhase |
| Turn กับ Pivot/Spin แย่งกัน | selector คืนค่ามากกว่าหนึ่ง type | รวม decision ไว้จุดเดียวและใช้ priority ในข้อ 11.6 |
| Run reversal กระโดด pose หลังจบ | ปลายคลิป Run Spin และ Moving row ถัดไป | ใช้ Jog backward fallback ตามชื่อ asset และตรวจ transition blend |
| คลิปถูกแต่ capsule ไม่เปลี่ยนทิศ | gameplay movement/rotation | Animation เป็นผู้แสดง pose ไม่ใช่ผู้สั่ง movement; ตรวจ Character Movement และ rotation contract |
12. Turn in Place และ Idle Break
Turn in Place คือการหมุนท่ายืนโดย capsule ไม่ได้เคลื่อนที่ ส่วน Idle Break คือท่าพักสั้น ๆ ที่เพิ่มชีวิตให้ตัวละคร ทั้งสองระบบใช้คลิป one-shot แต่มี trigger และข้อจำกัดต่างกัน จึงต้องสร้าง State และ cancel policy แยกกัน
12.1 เข้าใจปัญหา rotation ก่อนสร้าง State
| Rotation Mode | พฤติกรรม runtime ปัจจุบัน | ผลต่อ Turn in Place |
|---|---|---|
VelocityDirection / HipRelaxed | bOrientRotationToMovement = true, bUseControllerRotationYaw = false | เมื่อ Idle การหมุนกล้องไม่ใช่คำสั่งให้ร่างกายหัน จึงไม่ควร trigger Turn in Place จาก control yaw โดยอัตโนมัติ |
LookingDirection / ShoulderAim หรือ ADS | bOrientRotationToMovement = false, bUseControllerRotationYaw = true | actor หมุนตาม controller ทันที ทำให้ ControlYaw - ActorYaw ใกล้ศูนย์และไม่มี offset สำหรับเลือกคลิป |
| Crouched | ใช้ rotation contract เดียวกับ AimMode | มี Crouch turn assets แต่ยังติดข้อจำกัด yaw offset เดียวกัน |
แนวทางที่อนุมัติสำหรับ implementation baseline คือ:
- Actor/capsule ownership: gameplay/server เป็น authoritative owner ต่อไป Animation ห้ามเขียน actor transform หรือหมุน capsule
- Mesh/Root-Yaw-Offset: ระหว่าง grounded idle ใน LookingDirection ให้สะสม signed turn request จาก actor-yaw delta และใช้ค่าตรงข้ามเป็น visual root-yaw compensation จน animation turn consume request
- Target-facing source: ShoulderAim/ADS ใช้ authoritative aim/facing yaw ที่ gameplay expose ไม่อ่าน local camera yaw ตรงใน AnimGraph; HipRelaxed ไม่เข้า Turn in Place จากกล้อง
- State entry: เมื่อ request ข้าม enter threshold ให้ latch stance, side, angle bucket และ animation เพียงครั้งเดียว แล้วคลาย visual offset ด้วย curve/notify ของคลิปที่ตรวจแล้ว
- Reset/cancel: movement, Airborne, posture/AimMode change, teleport และ network correction ต้อง clear หรือ rebase offset ตาม policy ที่ทดสอบแล้ว
- Root Motion: ไม่ใช้เป็น baseline; การเปิดใช้ในอนาคตต้องมีการตรวจ asset, collision และ network contract แยกต่างหาก
12.2 ข้อมูลที่ต้องมีหลัง rotation contract ได้รับอนุมัติ
ActorYawDelta = Normalize Axis(ActorYaw - PreviousActorYaw)
TurnRequestYaw = Normalize Axis(TurnRequestYaw + ActorYawDelta)
VisualRootYawOffset = -TurnRequestYaw
AbsTurnYaw = Abs(TurnRequestYaw)
Eligible = GroundSpeed <= MovementSpeedDeadZone
AND NOT bHasMovementInput
AND NOT bIsFalling
AND RotationMode == LookingDirection
AND gameplay อนุญาตให้หมุน
AND NOT bTransitionLocked
| ข้อมูล | หน้าที่ | สถานะ |
|---|---|---|
TargetFacingYaw | ทิศ authoritative ที่ gameplay ต้องการให้ตัวละครหัน | Contract confirmed สำหรับ LookingDirection; ชื่อ runtime signal และ replication path ยังเป็น TBD-LOCO-007 |
TurnRequestYaw / visual root-yaw offset | มุม signed ที่ mesh ยังต้อง turn ตาม actor yaw ที่ authoritative | สะสมเฉพาะ grounded idle ใน LookingDirection และ rebase ตาม cancel/correction policy |
| Turn angle bucket | เลือก 45/90/135/180 | ขอบ bucket และ hysteresis เป็น TBD-LOCO-007 |
| Turn side | เลือก Lt/Rt | Positive → Right, Negative → Left ตาม signed-angle contract ที่ยืนยันในข้อ 10.7 |
SelectedTurnAnimation | animation ที่ latch ก่อนเข้า State | ชื่อเสนอ; ยังไม่มีใน runtime ปัจจุบัน |
bRequestTurnInPlace | edge-trigger เข้า State หนึ่งครั้ง | ต้อง clear เมื่อ consume/cancel ห้ามคง true ทุกเฟรม |
- อ่าน actor yaw และ authoritative facing state จาก gameplay contract แล้วอัปเดต
TurnRequestYaw; ไม่อ่าน camera yaw ตรงใน AnimGraph - เมื่อ Eligible เปลี่ยนเป็น true และมุมข้าม enter threshold ให้ latch side, angle bucket, stance และ selected animation พร้อมกัน
- ล็อกค่าทั้งชุดตลอด turn clip แม้ target yaw เคลื่อนต่อ; การหมุนเพิ่มระหว่างคลิปให้เก็บเป็น pending turn หรือจัดการหลังคลิปจบตาม policy ที่อนุมัติ
- ใช้ hysteresis/cooldown ที่ทดสอบแล้วเพื่อกันการสั่นที่ขอบมุม แต่ไม่กำหนดตัวเลขก่อนเปิด asset และทดสอบใน PIE
12.3 สร้าง TurnInPlace State สำหรับ Preview และ integration
ทำ Standing ก่อน แล้วจึงทำ Crouch โดยใช้ selector เดียวกันแต่คนละ asset family:
| Bucket | Standing | Crouching |
|---|---|---|
| Left 45/90/135/180 | A_INP_IdleLt_45/90/135/180_Turn | A_INP_Crouch_IdleLt_45/90/135/180_Turn |
| Right 45/90/135/180 | A_INP_IdleRt_45/90/135/180_Turn | A_INP_Crouch_IdleRt_45/90/135/180_Turn |
- ใน
SM_Standingสร้าง StateTurnInPlaceและต่อIdle ↔ TurnInPlace - ใน State ใส่ Sequence Player ที่เปิดพิน
Sequenceแล้วต่อSelectedTurnAnimationเข้าพินนั้น ปิด Loop และเปิด Always Reset on Entry Idle → TurnInPlaceใช้bRequestTurnInPlaceร่วมกับ grounded/idle/stance guards ไม่ใช้AbsTurnYawตรง ๆ ทุกเฟรมTurnInPlace → Idleใช้ Automatic Rule Based on Sequence Player in State และ clear request/lock หลังออกจาก State- เพิ่ม cancel path ไป Start เมื่อมี movement input, Airborne เมื่อเริ่มตก และ outer posture transition เมื่อ Stance เปลี่ยน โดย cancel priority สูงกว่าทางจบอัตโนมัติ
- สำหรับ Crouch ให้สร้าง State รูปแบบเดียวกันใน
SM_Crouchหลังข้อ 13 ผ่านแล้ว และ selector ต้องเลือกA_INP_Crouch_Idle*จาก latched stance - ระหว่างที่ runtime signal, curve/commit และ network rebase ของ
TBD-LOCO-007ยังไม่ implement/verify ให้ trigger State ด้วย debug command/Boolean เฉพาะ Preview เพื่อเช็ก clip direction, skeleton, looping และ pose blend ห้ามปล่อย debug trigger เป็น production logic
12.4 Gate ทดสอบ Turn in Place
| Test ID | วิธีทดสอบ | ผลที่ต้องเห็น |
|---|---|---|
| LOCO-12-T01 | Preview Standing Lt/Rt ทุกมุมด้วย manual trigger | เลือก clip ถูกด้าน/มุม, เล่นครั้งเดียวและกลับ Idle |
| LOCO-12-T02 | ทดสอบ Crouch Lt/Rt หลังข้อ 13 ผ่าน | ใช้ Crouch family เท่านั้นและกลับ Crouch Idle |
| LOCO-12-T03 | ขยับ input กลาง turn | cancel ไป Start/Moving ตาม policy ไม่มี pose ค้าง |
| LOCO-12-T04 | เปลี่ยน AimMode หรือ posture กลาง turn | rotation owner, stance และ animation จบใน state เดียวกัน ไม่เกิด snap สองครั้ง |
| LOCO-12-T05 | หมุน target ต่อระหว่างคลิป | selected clip ไม่สลับกลาง State; residual yaw ถูกจัดการหลังจบตาม contract |
| LOCO-12-T06 | ทดสอบ remote proxy พร้อม latency | actor/capsule yaw ตรงกับ server และ mesh ไม่สะสม yaw offset หลัง correction |
12.5 Idle Break — แยกจาก locomotion และ Turn in Place
Idle Break เป็น cosmetic one-shot ขณะตัวละครว่าง ไม่ใช้แก้ yaw และไม่ใส่เป็น sample ใน locomotion Blend Space คลิปที่ตรวจพบคือ Standing A_INP_Idle_Break_01 ถึง 03, Crouch A_INP_Crouch_Idle_Break_01 ถึง 03 และ Prone A_INP_Prone_Idle_Break_01 ถึง 02
bCanPlayIdleBreak หรือ state-equivalent ที่สรุป action/GAS restrictions แล้ว ABP ห้ามรวบรวม attack, interaction, damage และ held-item state เอง เริ่ม production integration จาก Standing และใช้ stance-specific shuffle bag ที่ไม่เลือกคลิปเดิมซ้ำติดกัน แต่ runtime action signal และค่ารอ/cooldown ยังไม่มี implementation ที่ตรวจแล้ว- สร้าง State
IdleBreakในSM_Standingก่อน และเพิ่มในSM_Crouch/SM_Proneหลัง milestone ของ stance นั้นผ่าน; ใช้ Sequence Player ที่เปิดพินSequence, ปิด Loop และเปิด Always Reset on Entry - selector ใช้ shuffle bag แยกตาม stance, ไม่เลือกคลิปเดิมซ้ำติดกัน และเลือกเฉพาะ family ของ stance ที่ latch ตอน request ห้ามเปลี่ยน Standing break เป็น Crouch/Prone clip กลาง State
- เงื่อนไข eligible ขั้นต่ำ: GroundSpeed อยู่ใน dead zone, ไม่มี movement input, ไม่ falling, ไม่มี transition lock, posture คงที่ และ gameplay action state อนุญาต
- delay ก่อนเล่น, cooldown และน้ำหนัก/โอกาสของแต่ละคลิปยังเป็น TBD-LOCO-008 ให้ทดสอบ manual request ก่อนเพื่อไม่สร้างพฤติกรรมสุ่มที่ debug ยาก
- ทางออกปกติกลับ Idle ด้วย Automatic Rule ทางยกเลิกต้องครอบคลุม movement input, AimMode change, attack/action, jump, damage/reaction และ stance change ตาม signal ที่ gameplay expose
- Idle Break เป็น cosmetic local selection ได้และไม่ต้อง replicate animation time ทุกเฟรม แต่ action ที่มี gameplay meaning หรือกระทบ held item ต้องถูก block/อนุมัติจาก gameplay ก่อน
Idle
├─ bRequestTurnInPlace → TurnInPlace → Idle
└─ bRequestIdleBreak → IdleBreak → Idle
Movement / Airborne / Posture / Action cancel
→ ออกจาก one-shot ไป State ที่ authoritative
12.6 ทดสอบ Idle Break และ cancellation
| Test ID | วิธีทดสอบ | ผลที่ต้องเห็น |
|---|---|---|
| LOCO-12-I01 | manual request Standing break 01–03 ทีละคลิป | เล่นครั้งเดียว, ไม่ loop และกลับ Standing Idle |
| LOCO-12-I02 | กดเดิน/กระโดดกลาง Idle Break | ยกเลิกทันทีตาม priority และ locomotion/airborne รับ pose ต่อ |
| LOCO-12-I03 | เปลี่ยน Crouch กลาง Standing break | outer posture transition ชนะ ไม่มี Standing break เล่นต่อใน Crouch |
| LOCO-12-I04 | เข้า ShoulderAim/ADS หรือเริ่ม gameplay action | Idle Break ไม่เริ่ม หรือถูกยกเลิกเมื่อ authoritative action state เปลี่ยน |
| LOCO-12-I05 | ปล่อย Idle นานหลายรอบหลังกำหนด policy แล้ว | ไม่ยิงซ้ำติดกัน, cooldown ทำงาน และ selection ไม่เปลี่ยนกลางคลิป |
12.7 แก้ปัญหาจากอาการที่เห็น
| อาการ | ตรวจจุดแรก | แนวแก้ |
|---|---|---|
| Turn in Place ไม่เคย trigger ใน Aim | TurnRequestYaw สะสม actor-yaw delta หรือไม่ | ControlYaw - ActorYaw ใกล้ศูนย์เป็นพฤติกรรมปกติของ runtime ปัจจุบัน; ตรวจ Root-Yaw-Offset accumulator ตามข้อ 12.1 ไม่เพิ่ม threshold แบบสุ่ม |
| actor หมุนก่อนแล้ว animation หมุนซ้ำ | ABP กำลังหมุน actor transform เพิ่มหรือไม่ | คง gameplay/server เป็น actor owner และให้ ABP ชดเชยเฉพาะ visual root-yaw offset ตาม contract |
| เลือก Lt/Rt กลับด้าน | signed TurnRequestYaw | ใช้ Normalize Axis และ mapping Positive → Right, Negative → Left ตาม contract |
| turn clip เปลี่ยน 45 → 90 กลาง State | selector resolve ทุกเฟรม | latch bucket/asset ตอน request แล้ว lock จนออก State |
| Idle Break เล่นขณะโจมตีหรือถือ action | authoritative action lock | ห้าม production trigger จน gameplay expose bCanPlayIdleBreak หรือ signal ที่เทียบเท่า |
| กดเดินแล้ว Idle Break ยังเล่นต่อ | cancel priority และ transition direction | ให้ movement/airborne/posture/action cancel มาก่อน Automatic Rule |
| remote mesh สะสมมุมผิด | yaw offset reset และ network correction | รีเซ็ต offset ตาม contract เมื่อ correction/State exit และทดสอบ simulated latency |
13. Crouch และ Prone
เป้าหมายของหัวข้อนี้คือเพิ่มท่าก้มให้ระบบเดิมโดยไม่รื้อ SM_Standing ให้ทำ Crouch เป็น milestone ที่เล่นและทดสอบได้ครบก่อน จากนั้นต่อ SM_Prone ด้วย verification collision/movement ของข้อ 13.5 ค่าขนาด capsule และ ProneSpeed ยังไม่ใช่ production config จนกว่าจะวัดจาก MetaHuman และคลิปจริง
SM_Posture
├─ Standing → SM_Standing
├─ StandToCrouch → A_INP_Idle2CrouchIdle
├─ Crouching → SM_Crouch
├─ CrouchToStand → A_INP_CrouchIdle2Idle
├─ StandToProne → A_INP_Idle2ProneIdle
├─ CrouchToProne → A_INP_CrouchIdle2ProneIdle
├─ Prone → SM_Prone
├─ ProneToStand → A_INP_ProneIdle2Idle
└─ ProneToCrouch → A_INP_ProneIdle2CrouchIdle
SM_Prone
└─ Idle → Start → Moving → Stop → Idle
TBD-LOCO-009 ปิดนโยบายแล้ว, native runtime implement เมื่อ 2026-09-04 และคลิป Prone ที่ใช้รอบแรก retarget ครบแล้วในโฟลเดอร์ผู้เล่น13.1 ตรวจ runtime และตัวแปรก่อนสร้างกราฟ [IMPLEMENTED 2026-09-03]
สถานะที่ตรวจพบในโปรเจกต์ปัจจุบัน:
| ข้อมูล | Source of truth | วิธีใช้ใน ABP_Player |
|---|---|---|
| กำลังก้มอยู่หรือไม่ | Character Movement → Is Crouching ซึ่งสะท้อนสถานะทางกายภาพของ ACharacter | map เข้า E_LocomotionStance โดยตรงเพื่อเปลี่ยน Stance; ไม่มี animation Boolean bIsCrouched/IsCrouching ซ้ำ |
| ความเร็ว Crouch | ULambeerLocomotionComponent.CrouchSpeed = 110 cm/s ก่อนคูณ encumbrance | ใช้ GroundSpeed จริงขับ Blend Space; ค่า 110 เป็นจุดตั้งต้นสำหรับ Preview ไม่ใช่ authored speed ที่ยืนยันแล้ว |
| ทิศการเคลื่อนที่ | Direction จากข้อ 6.2 | ต่อเข้ากับแกน Direction ของ BS_Crouch_Locomotion_2D |
| มี input / อยู่กลางอากาศ | bHasMovementInput และ bIsFalling จากข้อ 6.2 | ใช้กฎเดียวกับ SM_Standing เพื่อเข้า Start, Stop และไม่เริ่ม grounded locomotion กลางอากาศ |
| จังหวะเท้า | FootPhase จาก native estimate หรือ Notify/curve ที่ตรวจแล้ว | เลือก _LU/_RU เฉพาะเมื่อทำ directional Stop ระยะถัดไป |
| Prone | TBD-LOCO-009 — runtime implemented 2026-09-04; collision/movement ยังเป็น verification values | อ่าน native ULambeerAnimInstance.Stance จาก GetPublishedStance(); ใช้ bUrgentStandFromProne สำหรับ Sprint/Jump-to-Stand; สร้าง SM_Prone ตามข้อ 13.5 หลัง C01–C08 ผ่าน ห้าม derive จาก Crouch Boolean |
- ทำ ข้อ 6.2 ให้ผ่านก่อน แล้วเริ่ม PIE เพื่อตรวจว่า
Stanceเปลี่ยนเป็นCrouchedตามสถานะที่ Character Movement ยืนยัน,GroundSpeedเปลี่ยนตาม capsule และDirectionมีค่าครบทุกทิศ - ตรวจตัวแปร
Stance: ค่า enum ใช้ชื่อCrouchedแต่ชื่อ State ในกราฟใช้Crouchingได้ ทั้งสองชื่อทำหน้าที่ต่างกันและไม่จำเป็นต้องสะกดเหมือนกัน - ใน Event Graph ให้เลิก map
Is Crouchingซ้ำเป็นStanceแล้วใช้ nativeULambeerAnimInstance.Stanceซึ่งมาจากCurrentStanceที่ locomotion component เป็นเจ้าของ; หลัง C01–C08 ผ่านแล้วให้เพิ่ม StateProneตามข้อ 13.5 - ทดสอบยืนขึ้นใต้เพดานต่ำหนึ่งครั้ง หาก Character Movement ปฏิเสธการ
UnCrouchค่าStanceต้องยังเป็นCrouchedและ animation ต้องคงอยู่ที่ Crouching ห้ามให้ ABP เดาเองว่าลุกสำเร็จแล้ว
LambeerEditor Win64 Development build สำเร็จตาม UBT result และ DLL ที่สร้างใหม่เมื่อ 2026-09-03, พร้อมกับ ABP_Player compile ผ่านแล้ว; ไม่ได้หมายความว่า LOCO-13-C01–C08 ผ่าน PIE13.2 สร้าง BS_Crouch_Locomotion_2D [ASSET INTEGRATED 2026-09-03]
- [VERIFIED 2026-09-03] สร้าง
/Game/Lambeer/Characters/Player/Animation/BS_Crouch_Locomotion_2Dสำหรับ Skeleton เดียวกับABP_Playerแล้ว - [CONFIRMED PROVISIONAL 2026-09-03 · TBD-LOCO-003] ตั้งแกนนอน
Direction = -180..180แบบ Wrap Input และแกนตั้งGroundSpeed = 0..110; ใช้ 110 cm/s เป็น sample Y baseline ตาม runtimeCrouchSpeedที่อนุมัติให้ใช้ระหว่างรอวัด authored speed จริง - [VERIFIED 2026-09-03] สร้าง moving row หนึ่งแถวที่ 110 cm/s จาก directional Loop 8 ตัว; ไม่ใส่ Idle, Start, Stop, Turn หรือ Pivot ลงใน Blend Space เพราะ pose/state และคลิป non-loop เหล่านั้นแยกออกจาก Moving
- วาง sample ตามตารางด้านล่าง โดยเริ่มจาก Forward, Backward และรอยต่อ
-180/180ก่อน จากนั้นค่อยเติมทิศเฉียงและด้านข้าง - ต่อ sample Backward ตัวเดียวกันไว้ทั้ง
-180และ180เพื่อปิด seam ด้านหลัง แล้วลาก Preview ข้าม seam ช้า ๆ - [CONFIRMED 2026-09-03 · TBD-LOCO-004] วางคลิป
Rtที่ค่าบวกและLtที่ค่าลบ ใช้ตระกูลFwdLt/FwdRt_90เป็น side-loop baseline สำหรับ Crouch; เก็บตระกูลBwdLt/BwdRt_90ไว้เปรียบเทียบภายหลัง หาก Preview กลับด้านให้แก้ sample/asset mapping ที่ผิด ห้าม negateDirectionเป็นจุด ๆ
| ตำแหน่งเชิงทิศ | Animation Sequence | หมายเหตุ |
|---|---|---|
หน้า 0° | A_INP_CrouchFwd_Loop | ใช้เป็น sample แรกและจุดอ้างอิงการ Preview |
เฉียงหน้า ±45° | A_INP_CrouchFwdLt_45_Loop, A_INP_CrouchFwdRt_45_Loop | วาง Lt/Rt ตาม signed Direction ที่ตรวจจริง |
ด้านข้าง ±90° | A_INP_CrouchFwdLt_90_Loop, A_INP_CrouchFwdRt_90_Loop | เปรียบเทียบกับ family CrouchBwdLt/Rt_90 ใน Preview แล้วเลือกชุดที่เชื่อมกับ sample ข้างเคียงดีที่สุด |
เฉียงหลัง ±135° | A_INP_CrouchBwdLt_45_Loop, A_INP_CrouchBwdRt_45_Loop | ตัวเลข 45 ในชื่อ asset วัดจากแนวหลัง จึงอยู่ห่างแนวหน้า 135° |
หลัง -180° และ 180° | A_INP_CrouchBwd_Loop | ใช้ sequence เดียวกันทั้งสองขอบของแกน |
CrouchSpeed เป็นความเร็ว capsule และยังถูกคูณด้วย encumbrance ส่วน authored speed ของคลิปต้องวัดจากภาพจริง หากเท้าไถลให้แยกตรวจ sample position, play rate/stride warping roadmap และ movement speed แทนการเปลี่ยนหลายค่าในครั้งเดียวใส่ Sync Markers ให้ sample ที่ใช้งานจริง
LeftPlant และ RightPlant ใน AnimSyncMarkerList ครบ ตำแหน่ง marker ถูกวางจาก foot contact โดยผู้ใช้; คุณภาพการ blend และ phase continuity ยังตรวจด้วย manual PIE gate ตามข้อ 13.6- เปิดเฉพาะ Animation Sequence ที่ถูกวางเป็น sample ใน
BS_Crouch_Locomotion_2D - ใส่
LeftPlantและRightPlantตามหลักใน ข้อ 8 โดยดู foot contact ของคลิป Crouch แต่ละตัว ห้ามคัดลอกเวลา marker จาก Standing - Preview การ blend ระหว่างหน้า → เฉียง → ข้าง → เฉียงหลัง → หลัง และย้อนกลับ ถ้าเข่าหรือเท้ากระโดด ให้แก้ marker ของคู่ที่มีปัญหาก่อน
- ยังไม่ต้องใส่ Crouch Start/Stop ใน Sync Group จน base flow เล่นถูกต้อง; เริ่มจาก Blend Space Player ใน State Moving เท่านั้น
Crouching → SM_Crouch และใส่ Idle/Moving poses แล้วทำข้อ 13.4 เพื่อเชื่อม PM_Posture ระหว่าง Standing กับ Crouching ก่อน ปัจจุบัน static graph inspection ยืนยันว่า ABP_Player ยังมีเฉพาะ posture Standing13.3 สร้าง SM_Crouch
โครงภายในใช้หลักเดียวกับ SM_Standing เพื่อให้ debug ง่าย ความต่างอยู่ที่ pose และ Blend Space ที่เลือก
- เปิด
ABP_Player → AnimGraph → SM_Postureสร้าง State ชื่อCrouchingแล้วเปิด State Graph ของมัน - เพิ่ม State Machine ชื่อเสนอ
SM_Crouchต่อ output เข้าState Result - ภายใน
SM_Crouchสร้างIdle,Start,MovingและStopแล้วต่อEntry → Idle → Start → Moving → Stop → Idle - เปิด
Idleใส่A_INP_Crouch_Idleและเปิด Loop Animation - เปิด
Startใส่A_INP_CrouchFwd_Startเป็น fallback รอบแรก ปิด Loop และเปิดAlways Reset on Entry - เปิด
Movingใส่ Blend Space Player ของBS_Crouch_Locomotion_2Dต่อDirectionและGroundSpeedเข้าพินที่ตรงกัน - เลือก Blend Space Player ใน Moving ตั้ง
Method = Sync Group,Group Name = GroundedLocomotionและGroup Role = Can Be Leaderให้ตรงกับข้อ 9.3 - เปิด
Stopใส่A_INP_CrouchFwd_Stop_LUเป็น fallback รอบแรกและปิด Loop หลัง base flow ผ่านแล้วจึงเพิ่มการเลือก_LU/_RUจากFootPhase - สร้าง Transition Rules ชุดเดียวกับ ข้อ 9.4 รวมทางหนี
Moving → Idle,Start → Stop,Start → IdleและStop → Startไม่ใช่สร้างเฉพาะลูกศรหลักสี่เส้น - เพิ่มเงื่อนไข
Stance == CrouchedและNOT bIsFallingในทางเข้า Start ที่ต้องป้องกันการทำงานผิด posture หากค่า Stance เปลี่ยนระหว่าง Start/Stop ให้ outerSM_Postureเป็นผู้ตัดออกจากCrouching - Compile และ Save จากนั้นเปิด Debug Filter ตรวจว่า
SM_Crouchวิ่งIdle → Start → Moving → Stop → Idleได้ขณะตัวละครอยู่ใน Crouch ก่อนเพิ่ม directional Start/Stop/Pivot
| State | Pose รอบแรก | Loop | เงื่อนไขออกหลัก |
|---|---|---|---|
Idle | A_INP_Crouch_Idle | เปิด | bHasMovementInput AND NOT bIsFalling AND Stance == Crouched |
Start | A_INP_CrouchFwd_Start | ปิด | Automatic Rule เมื่อคลิปใกล้จบ; ทางยกเลิกใช้กฎข้อ 9.4 |
Moving | BS_Crouch_Locomotion_2D | Blend Space loop | ปล่อย input แล้วแยก Stop/Idle ด้วย MovementSpeedDeadZone |
Stop | A_INP_CrouchFwd_Stop_LU เป็น fallback | ปิด | Automatic Rule; input ใหม่ออกไป Start ก่อน |
13.4 ต่อ Standing ↔ Crouching ใน SM_Posture
คลิป Idle2CrouchIdle และ CrouchIdle2Idle เป็น one-shot จึงควรอยู่ใน State ของตัวเอง ไม่ควรใส่ใน Transition Rule Graph ซึ่งมีหน้าที่ตัดสิน Boolean และ blend pose เท่านั้น
ทางเปลี่ยน posture ขณะหยุด
- ใน
SM_Postureสร้าง StateStandToCrouchใส่A_INP_Idle2CrouchIdle, ปิด Loop และเปิดAlways Reset on Entry - สร้าง State
CrouchToStandใส่A_INP_CrouchIdle2Idle, ปิด Loop และเปิดAlways Reset on Entry - สร้าง
Standing → StandToCrouchด้วยกฎStance == Crouched AND GroundSpeed <= MovementSpeedDeadZone - สร้าง
StandToCrouch → Crouchingด้วย Automatic Rule Based on Sequence Player in State - สร้าง
Crouching → CrouchToStandด้วยกฎStance == Standing AND GroundSpeed <= MovementSpeedDeadZone - สร้าง
CrouchToStand → Standingด้วย Automatic Rule Based on Sequence Player in State - เพิ่มทางยกเลิก:
StandToCrouch → CrouchToStandเมื่อStance == StandingและCrouchToStand → StandToCrouchเมื่อStance == Crouchedจากนั้นตั้ง Priority ให้ทางยกเลิกมาก่อนทางจบอัตโนมัติ
ทางเปลี่ยน posture ขณะกำลังเคลื่อนที่
คลิป idle-to-idle ไม่เหมาะกับตัวละครที่ capsule ยังเคลื่อนที่ ให้ใช้ direct transition ที่ blend จาก moving pose เดิมไป moving pose ของ posture ใหม่:
Standing → Crouching
Stance == Crouched AND GroundSpeed > MovementSpeedDeadZone
Crouching → Standing
Stance == Standing AND GroundSpeed > MovementSpeedDeadZone
- สร้างลูกศรตรง
Standing → CrouchingและCrouching → Standingเพิ่มจากเส้น idle one-shot โดยใช้กฎด้านบน กฎที่ใช้<=และ>แยกกันทำให้สองทางไม่แข่งขันในเฟรมเดียวกัน - [CONFIRMED BASELINE 2026-09-03] ใช้ Crossfade Duration
0.12 sสำหรับ direct moving posture transition ทั้งสองทิศ แล้วทดสอบภาพจริงก่อนปรับ การเปลี่ยนค่าภายหลังต้องบันทึกเป็น posture-transition tuning แยกจาก Baseline v0.1 ของ Pivot/MovingTurn/Spin - เมื่อเข้าถึง
Crouchingขณะยังมี input ให้ outer state ส่ง pose เข้าSM_Crouch; ถ้า nested machine เริ่มที่ Idle แล้วเห็น crouch idle แวบหนึ่ง ให้ใช้ transition initialization/entry behavior ที่ทดสอบแล้วเพื่อพา Idle → Start/Moving โดยไม่ invent ค่าเวลาใหม่ - อย่าใช้ Anim Notify สลับ collision capsule ฝั่ง Animation ระบบ Character Movement เป็น source of truth ของการ Crouch/UnCrouch และเป็นผู้ตรวจเพดานต่ำ
13.5 Prone — runtime พร้อมแล้ว ให้ต่อ Blend Space และ SM_Prone
ตรวจพบ Prone locomotion 24 assets ในกลุ่ม Start, Loop และ Stop พร้อม Prone Idle/transition assets ในกลุ่ม Idle มติ TBD-LOCO-009 อนุมัติ runtime architecture แล้ว และ native implementation เมื่อ 2026-09-04 มีดังนี้:
- ใช้ stance enum contract ชื่อ
E_LocomotionStance { Standing, Crouched, Prone }และให้ULambeerLocomotionComponentเป็นเจ้าของ replicatedCurrentStanceกับ request เปลี่ยน posture; ห้ามใช้ Boolean หลายตัวเป็น gameplay source of truth โดยรายละเอียด native representation ตอน implement Prone ต้องรักษาชื่อ/ค่าของ contract นี้ ACharacter::bIsCrouchedคงเป็นผลยืนยันทางกายภาพของ native Character Movement สำหรับ Crouch ส่วนCurrentStanceต้อง publish เฉพาะ committed posture ที่ gameplay/server ยอมรับแล้ว- ใช้ Input Action ของ Prone แยกจาก Crouch เพื่อให้ request/cancel และ network debug ชัดเจน
- [CONFIRMED 2026-09-03] ปุ่ม Crouch ทำงานแบบ
Standing ↔ Crouched; เมื่ออยู่ Prone ให้พยายามขึ้น Crouched หาก clearance ผ่าน - [CONFIRMED 2026-09-03] ปุ่ม Prone ทำให้ Standing/Crouched ลง Prone ได้โดยตรง; เมื่อกดซ้ำจาก Prone ให้พยายามขึ้น Standing ก่อน หากยืนไม่ได้ให้ fallback เป็น Crouched และหากทั้งสอง clearance ไม่ผ่านให้คง Prone
- ห้ามเข้า Prone กลางอากาศ และ gameplay action gate ต้องอนุญาตก่อนรับ request
- [CONFIRMED 2026-09-03] Prone ใช้ vertical root capsule ขนาดย่อเป็น Updated Component ของ Character Movement และใช้ server-authoritative prone body-envelope sweep ตามแนวนอนครอบคลุมลำตัว ห้ามพึ่ง secondary blocking component ที่ Character Movement ไม่ได้ sweep เป็นหลัก
- server ต้องทำ body-envelope sweep ตอนเข้า Prone, ระหว่างคลาน และตอนออกจาก Prone ก่อน commit capsule/stance; การชนระหว่างคลานต้องหยุดหรือ slide ตามผล sweep โดยไม่ปล่อย mesh ทะลุสิ่งกีดขวาง
- ขนาด root capsule และ body envelope ต้องวัดจาก MetaHuman target กับ extreme pose ของ animation จริง รวม safety margin ที่ผ่าน corridor/low-cover test แล้วจึงบันทึกเป็น runtime config; ห้ามคัดลอก Crouch capsule หรือใส่ค่าประมาณเป็น production default
- หากขอออกจาก Prone ไป Standing แต่พื้นที่ไม่พอยืนและยังพอ Crouch ให้ fallback เป็น
Crouched; หากไม่ผ่านทั้งสอง clearance ให้คง Prone โดยไม่เปลี่ยน collision ล่วงหน้า - [CONFIRMED 2026-09-03] Prone มี gait เดียวและใช้เพดานความเร็วเดียวกันทุกทิศ อนาล็อกปรับได้ตั้งแต่หยุดถึง
ProneSpeed; ไม่มี Prone Walk/Jog/Run แยกกัน ProneSpeed,ProneMaxAccelerationและProneBrakingDecelerationเป็น runtime config ใน locomotion system และใช้ encumbrance multiplier เดียวกับ stance อื่น ค่า production ต้อง derive จาก Prone Start/Loop/Stop ที่วัดจริงและติดตามในTBD-LOCO-003ห้ามคัดลอกCrouchSpeedมาใช้- [CONFIRMED 2026-09-03] เมื่อกด Sprint ขณะ Prone ให้ส่ง request ออกจาก Prone ไป Standing ทันทีโดยไม่ queue; server ตรวจ Standing clearance ก่อน commit หากผ่านให้รักษา Sprint-held intent และเลือก Run หลัง
CurrentStance == Standingหาก clearance ไม่ผ่านให้คง Prone และไม่เริ่ม Sprint - ทาง Sprint-to-Stand เป็น gameplay transition เร่งด่วน: ABP ต้องออกจาก Prone ด้วย direct short blend และไม่บังคับรอ one-shot
A_INP_ProneIdle2Idleจบก่อน capsule/ความเร็วเปลี่ยน - [CONFIRMED 2026-09-03] เมื่อกด Jump ขณะ Prone ให้ consume input ครั้งนั้นเป็น request ไป Standing เท่านั้น ไม่เรียก
Jump()ต่อใน input เดียวกัน; หาก server ยืนยัน Standing clearance ให้ลุกด้วย direct short blend หากไม่ผ่านให้คง Prone ผู้เล่นต้องปล่อยและกด Jump ใหม่หลังยืนแล้วจึงกระโดด - [CONFIRMED 2026-09-03] Autonomous proxy ตรวจ clearance ในเครื่องและเริ่ม predicted stance/collision/animation ทันที จากนั้นส่ง request พร้อม sequence ID ให้ server ตรวจซ้ำ; server ยังคงเป็นเจ้าของ collision และ committed
CurrentStanceขั้นสุดท้าย - เมื่อ server ยืนยัน request ให้ client ปิด pending prediction ด้วย sequence ID ที่ตรงกัน; หากปฏิเสธหรือ authoritative state ไม่ตรง ให้ rollback stance/collision ไปค่าของ server และใช้ inertial blend สั้นเพื่อลด pose pop โดยห้าม blend หรือคาดเดา collision authority
- Simulated proxy ไม่ทำ stance prediction และอ่าน replicated
CurrentStanceเท่านั้น Sprint/Jump-to-Stand ใช้ prediction contract เดียวกัน แต่ Jump input แรกยังถูก consume และไม่เรียกJump() - [CONFIRMED 2026-09-03] Interrupted-recovery priority คือ
Death/Ragdoll/Knockdown → Airborne → Posture transition; pending request ที่ยังไม่ commit ต้องถูกยกเลิกและคืน stance/collision ล่าสุดของ server - ถ้า server commit stance แล้วแต่ animation ถูกตัด ให้คง authoritative stance/collision และ inertial blend ไป pose ที่ถูกต้อง; response ของ sequence ID เก่าต้องถูกทิ้ง และ collision ห้ามเปลี่ยนจาก Anim Notify หรือเวลาในคลิป
- เมื่อตกขอบขณะ Prone ให้ Airborne pose/movement override โดยคง
CurrentStance = Prone; ตอนลงพื้น server ตรวจ body envelope ใหม่ก่อนคืน Prone locomotion หากไม่ผ่านให้หยุด movement และแก้ penetration โดยไม่บังคับขยาย capsule เป็น Crouched/Standing ในพื้นที่แคบ - Damage reaction ที่ไม่ใช่ knockdown เล่น additive/upper-body โดยไม่เปลี่ยน stance; Knockdown/Ragdoll เป็นเจ้าของ pose/collision ชั่วคราว และตอนฟื้นต้อง request stance ใหม่ผ่าน clearance contract เดิม
TBD-LOCO-003ULambeerLocomotionComponent เป็นเจ้าของ replicated CurrentStance, sequence-ID prediction, vertical prone capsule, server body-envelope sweep, gait เดียว, Sprint/Jump-to-Stand, bUrgentStandFromProne, data-driven action gate และ recovery hooks แล้ว Input ใช้ IA_Prone แยกจาก Crouch (ปุ่ม runtime เริ่มต้นคือ Z) ค่า verification ที่ยังห้ามถือเป็น production: ProneSpeed = 80, ProneMaxAcceleration = 240, ProneBrakingDeceleration = 480, capsule radius/half-height = 28, envelope half-extents = (90, 30, 18) ยังไม่ได้วัดจาก MetaHumanSM_Prone เปลี่ยน Start → Moving ด้วย Automatic Rule ได้ตามปกติ อาการค้างที่รายงานระหว่าง debug เกิดจากการกด/ตั้งค่าใน Editor ผิดและแก้แล้ว ไม่ได้ต้องเพิ่ม explicit remaining-time ruleApplyCollisionForStance() เรียก Character->CacheInitialMeshOffset(MeshRelativeLocation, Mesh->GetRelativeRotation()) ต่อจาก Mesh->SetRelativeLocation() เพื่ออัปเดต cached mesh base offset ที่ Character Movement ใช้เป็นเป้าหมายของ network smoothing; full LambeerEditor Win64 Development build ผ่าน ห้ามชดเชย offset ซ้ำด้วย Anim Notify หรือ Anim BlueprintProne action gate [CONFIRMED 2026-09-03]
ทุก gameplay action ต้องประกาศ stance policy แบบ data-driven เป็น Allowed, RequiresStanding หรือ Blocked และ server ตรวจ policy เดียวกัน ห้ามให้ ABP เป็นผู้อนุญาต gameplay action
| Action | Policy ขณะ Prone | Contract |
|---|---|---|
| Look/Camera, Prone locomotion | Allowed | ทำงานตามปกติภายใต้ prone movement/collision contract |
| Aim, fire, reload, weapon switch | Allowed เมื่อมี prone-compatible animation/montage | หาก coverage ยังไม่พร้อมให้ block action นั้น ห้ามรัน gameplay โดยแสดง pose ที่ผิด |
| Inventory, pickup และ interact ระยะใกล้ | Allowed | ยังต้องผ่าน reach, authority และ action-specific validation ปกติ |
| Sprint | RequiresStanding | ขอ Standing ทันที; เมื่อ clearance ผ่านให้รักษา Sprint-held intent และเริ่ม Run |
| Jump | RequiresStanding | input แรกใช้ลุกเท่านั้นและถูก consume; ต้องกดใหม่หลัง Standing จึงกระโดด |
| Mantle, vault, climb, building, ถือ/ลากวัตถุขนาดใหญ่ | Blocked | ปฏิเสธขณะ Prone โดยไม่บังคับลุกอัตโนมัติ |
| Melee และ throw | Blocked baseline | เปลี่ยนเป็น Allowed ได้เมื่อมี animation coverage และ acceptance test เฉพาะ |
Interrupted recovery [CONFIRMED 2026-09-03]
| เหตุการณ์ | ผล authoritative | ผล animation |
|---|---|---|
| ถูกขัดจังหวะก่อน commit | ยกเลิก pending request และคืน stance/collision ล่าสุดของ server | Inertial blend กลับ pose ของ committed stance |
| ถูกขัดจังหวะหลัง commit | คง committed stance/collision | ตัด one-shot และ blend ไป pose ที่ตรงกับ authoritative state |
| ตกขอบขณะ Prone | คง CurrentStance = Prone; Airborne override และตรวจ body envelope ใหม่ตอนลงพื้น | Airborne pose มาก่อน Prone; คืน locomotion หลัง clearance ผ่าน |
| Damage reaction ปกติ | ไม่เปลี่ยน stance | Additive/upper-body reaction เท่านั้น |
| Knockdown/Ragdoll | ระบบนั้นถือ collision ชั่วคราว; recovery ต้อง request stance ใหม่ | Override posture ทั้งหมดจน recovery commit |
| Response/request เก่ามาถึงช้า | ทิ้งเมื่อ sequence ID ไม่ใช่รายการล่าสุด | ไม่ย้อนเล่น transition เก่า |
| หน้าที่รอบแรก | Animation ที่ตรวจพบในโฟลเดอร์ผู้เล่น |
|---|---|
| Idle | A_INP_Prone_Idle |
| Standing ↔ Prone | A_INP_Idle2ProneIdle, A_INP_ProneIdle2Idle |
| Crouching ↔ Prone | A_INP_CrouchIdle2ProneIdle, A_INP_ProneIdle2CrouchIdle |
| Forward | A_INP_ProneFwd_Start, A_INP_ProneFwd_Loop, A_INP_ProneFwd_Stop |
| Forward diagonal / side | A_INP_ProneFwdLt45_*, A_INP_ProneFwdRt45_*, A_INP_ProneFwdLt90_*, A_INP_ProneFwdRt90_* |
| Backward | A_INP_ProneBwd_Start, A_INP_ProneBwd_Loop, A_INP_ProneBwd_Stop |
| Backward diagonal | A_INP_ProneBwdLt135_*, A_INP_ProneBwdRt135_* |
คลิป Prone ที่ใช้รอบแรกอยู่ที่ /Game/Lambeer/Characters/Player/Animation/Idle และ .../Animation/Prone แล้ว (ยืนยันจากไฟล์บนดิสก์เมื่อ 2026-09-04) ยังไม่ต่อ A_INP_Prone_Idle_Break_01/02 จนกว่า Idle Break ของข้อ 12 พร้อม
สร้าง BS_Prone_Locomotion_2D
- สร้าง
/Game/Lambeer/Characters/Player/Animation/BS_Prone_Locomotion_2Dสำหรับ Skeleton เดียวกับABP_Player - ตั้งแกนนอน
Direction = -180..180แบบ Wrap Input และแกนตั้งGroundSpeed = 0..80; ใช้ 80 cm/s เป็น sample Y ตาม runtime verificationProneSpeedห้ามคัดลอก 110 ของ Crouch - สร้าง moving row หนึ่งแถวที่ 80 cm/s จาก Loop 8 ทิศ; ไม่ใส่ Idle, Start, Stop, Turn หรือ Idle Break ลงใน Blend Space
- วาง sample ตามตารางด้านล่าง โดยเริ่มจาก Forward, Backward และรอยต่อ
-180/180ก่อน จากนั้นค่อยเติมทิศเฉียงและด้านข้าง - ต่อ sample Backward ตัวเดียวกันไว้ทั้ง
-180และ180เพื่อปิด seam ด้านหลัง แล้วลาก Preview ข้าม seam ช้า ๆ - วางคลิป
Rtที่ค่าบวกและLtที่ค่าลบ ตามTBD-LOCO-004หาก Preview กลับด้านให้แก้ sample mapping ห้าม negateDirection - ใส่
LeftPlantและRightPlantบน Loop ทั้ง 8 ตัวตามข้อ 8 โดยดู foot/hand contact ของคลิป Prone ห้ามคัดลอกเวลา marker จาก Crouch
| ตำแหน่งเชิงทิศ | Animation Sequence | หมายเหตุ |
|---|---|---|
หน้า 0° | A_INP_ProneFwd_Loop | sample แรกและจุด Preview |
เฉียงหน้า ±45° | A_INP_ProneFwdLt45_Loop, A_INP_ProneFwdRt45_Loop | ชื่อ asset ไม่มีขีดกลางก่อนตัวเลขมุม |
ด้านข้าง ±90° | A_INP_ProneFwdLt90_Loop, A_INP_ProneFwdRt90_Loop | ใช้ตระกูล FwdLt/FwdRt ตาม side-loop baseline |
เฉียงหลัง ±135° | A_INP_ProneBwdLt135_Loop, A_INP_ProneBwdRt135_Loop | ชุด Prone มี Bwd 135 โดยตรง ไม่ใช้ชื่อ BwdLt_45 แบบ Crouch |
หลัง -180° และ 180° | A_INP_ProneBwd_Loop | sequence เดียวกันทั้งสองขอบของแกน |
ProneSpeed ยังถูกคูณด้วย encumbrance และยังไม่ได้วัด authored speed จาก Root Motion หากเท้าไถลให้บันทึกใน TBD-LOCO-003 แยกจาก bug ของ State flowสร้าง SM_Prone ใน ABP_Player → SM_Posture
ทำหลังมี BS_Prone_Locomotion_2D และหลัง Live Coding/compile C++ จน inherited Stance กับ bUrgentStandFromProne ปรากฏใน ABP_Player ใช้โครงเดียวกับ SM_Crouch ความต่างอยู่ที่คลิป, Blend Space และค่า enum Prone
Stance, bUrgentStandFromProne, GroundSpeed, Direction, bHasMovementInput, bIsFalling และ MovementSpeedDeadZone ต้องเป็นของเดิมจากข้อ 6 / parent ULambeerAnimInstance หากไม่เห็น bUrgentStandFromProne ให้ Compile module แล้วเปิด ABP ใหม่ ห้ามสร้าง Boolean ชื่อเดียวกันใน My Blueprint เพราะจะทับ inheritedSM_Posture
└─ Prone ← State ใหม่ในหน้า SM_Posture (ยังไม่ต่อจาก Entry)
└─ SM_Prone
├─ Entry → Idle
├─ Idle → Start
├─ Start → Moving
├─ Moving → Stop
└─ Stop → Idle
ทางหนีใน SM_Prone (สร้างครบเหมือนข้อ 9.4):
Start → Stop, Start → Idle, Moving → Idle, Stop → Start
สร้าง State Prone และ nested SM_Prone
- เปิด
/Game/Lambeer/Characters/Player/Animation/ABP_Playerแล้ว Compile ให้ผ่านก่อนเพิ่ม State เพื่อแยก error เดิม - ไปที่ AnimGraph ดับเบิลคลิก
SM_Postureต้องเห็นStanding,Crouchingและ one-shot Crouch ที่ทำในข้อ 13.3–13.4 แล้ว - คลิกขวาพื้นที่ว่างใน
SM_Postureเลือก Add State ตั้งชื่อProneให้สะกดตรงนี้ อย่าลากเส้นจากEntryมาที่Proneเพราะ Entry ยังต้องชี้Standing - ดับเบิลคลิก State
Proneเพื่อเปิด State Graph ของมัน - คลิกขวาพื้นที่ว่าง เพิ่ม State Machine ตั้งชื่อเสนอ
SM_Proneแล้วลากพิน pose ของ node นี้เข้าState Result - ดับเบิลคลิก node
SM_Proneสร้าง States ชื่อIdle,Start,MovingและStopให้สะกดตรงกับSM_Crouch - ลากจาก
EntryไปIdleจากนั้นลากลูกศรหลักIdle → Start → Moving → Stop → Idle - ลากทางหนีเพิ่มอีกสี่เส้นทันที:
Moving → Idle,Start → Stop,Start → Idle,Stop → Startห้ามมีแค่ลูกศรหลักสี่เส้น - Compile ตอนนี้ compiler อาจเตือนว่า State ยังไม่มี pose นั่นปกติจนกว่าจะใส่คลิปด้านล่าง
Idle — คลิปนอนรอ
- ใน
SM_Proneดับเบิลคลิก StateIdle - ลาก
/Game/Lambeer/Characters/Player/Animation/Idle/A_INP_Prone_Idleจาก Content Browser มาวาง จะได้ Sequence Player - ต่อ output pose เข้า
State Result - เลือก Sequence Player เปิด
Loop Animationเพราะ Idle ต้องวน
Start — คลิปเริ่มคลานหน้าเป็น fallback รอบแรก
- กลับ
SM_Proneแล้วดับเบิลคลิก StateStart - ลาก
/Game/Lambeer/Characters/Player/Animation/Prone/A_INP_ProneFwd_Startมาวางและต่อเข้าState Result - เลือก Sequence Player แล้วปิด
Loop Animation - กลับหน้า
SM_Proneคลิก StateStart(ไม่ใช่คลิปในกราฟ) เปิดAlways Reset on Entryใน Details หากตัวเลือกนี้แสดงอยู่ - รอบนี้ยังไม่เลือกทิศอื่นใน Start; directional Start เป็นงานหลัง base flow ผ่าน
Moving — Blend Space คลาน
- ดับเบิลคลิก State
Moving - ลาก
/Game/Lambeer/Characters/Player/Animation/BS_Prone_Locomotion_2Dมาวางเป็น Blend Space Player แล้วต่อเข้าState Result - คลิกขวาพื้นที่ว่าง เลือก Get
Directionต่อเข้าพินแกนนอนชื่อDirectionของ node - Get
GroundSpeedต่อเข้าพินแกนตั้งชื่อGroundSpeedหากชื่อพินไม่ตรง ให้เปิด Details ของ Blend Space ตรวจชื่อแกน ห้ามสลับแกน - เลือก Blend Space Player เปิดหมวด Sync: เปลี่ยน
MethodจากDo Not Syncเป็นSync Groupก่อน แล้วตั้งGroup Name = GroundedLocomotionและGroup Role = Can Be Leaderให้ตรงข้อ 9.3 - ถ้า compiler ขึ้น “set to not use named sync groups” แปลว่าใส่ชื่อกลุ่มแล้วยังไม่ได้เปลี่ยน Method ให้แก้ที่ node เดิม ไม่ต้องสร้าง Player ใหม่
Stop — คลิปหยุดคลานหน้า
- ดับเบิลคลิก State
Stop - ลาก
/Game/Lambeer/Characters/Player/Animation/Prone/A_INP_ProneFwd_Stopมาวางและต่อเข้าState Result - ปิด
Loop Animationบน Sequence Player - กลับหน้า
SM_Proneคลิก StateStopเปิดAlways Reset on Entryหากมีใน Details - ชุด Prone ไม่มี suffix
_LU/_RUบน Stop จึงไม่ต้อง Select จากFootPhaseในรอบนี้
| State ใน SM_Prone | Asset | Loop | Always Reset on Entry |
|---|---|---|---|
Idle | A_INP_Prone_Idle | เปิด | ไม่บังคับ |
Start | A_INP_ProneFwd_Start | ปิด | เปิด |
Moving | BS_Prone_Locomotion_2D | Blend Space loop | ไม่ใช้กับ Blend Space Player |
Stop | A_INP_ProneFwd_Stop | ปิด | เปิด |
Transition Rules ภายใน SM_Prone ทีละเส้น
ดับเบิลคลิกวงกลมบนลูกศรเพื่อเปิด Rule Graph แล้วต่อ Boolean เข้า Can Enter Transition ใช้ Get ตัวแปรเดิม ห้ามเปิด Bidirectional แทนลูกศรขากลับ ค่า Priority Order ที่เลขน้อยกว่าถูกพิจารณาก่อน ตามข้อ 9.4
Idle → Start
bHasMovementInput
AND NOT bIsFalling
AND Stance == Prone
- ดับเบิลคลิกวงกลมบนลูกศร
Idle → Start - Get
bHasMovementInputต่อเข้า Boolean AND - Get
bIsFallingผ่าน Boolean NOT แล้วต่อเข้า AND - Get
Stanceต่อ Equal (Enum) เลือกProneแล้วต่อผลเข้า AND ห้ามใช้ค่าCrouchedจากSM_Crouchคัดลอกมาแล้วลืมเปลี่ยน - ต่อผล AND เข้า
Can Enter Transition
Start → Moving
- คลิกวงกลมบนลูกศร
Start → Movingครั้งเดียวให้ Details ของ transition ขึ้น - เปิด
Automatic Rule Based on Sequence Player in State - คง
Automatic Rule Trigger Timeเป็นค่าติดลบ/default ในรอบแรก ให้อิง Crossfade Duration
Moving → Stop
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
- ดับเบิลคลิกวงกลม
Moving → Stop - NOT ของ
bHasMovementInput, NOT ของbIsFallingและ Float>ระหว่างGroundSpeedกับMovementSpeedDeadZone - รวมด้วย AND เข้า Result
Stop → Idle
- คลิกวงกลม
Stop → Idleเปิด Automatic Rule Based on Sequence Player in State - คง Trigger Time เป็น default รอบแรก
Moving → Idle
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
- เปิด Rule ของลูกศร
Moving → Idleที่ลากไว้แล้ว - สร้างกราฟแบบ
Moving → Stopแต่เปลี่ยนตัวเปรียบเทียบเป็น<=
Start → Stop
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
- เปิด Rule ของ
Start → Stopให้เหมือนMoving → Stop - ตั้ง
Priority Orderให้พิจารณาก่อนStart → Moving
Start → Idle
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
- เปิด Rule ของ
Start → Idleให้เหมือนMoving → Idle - ตั้ง Priority ให้พิจารณาก่อน
Start → Movingเส้นนี้ไม่ชนกับStart → Stopเพราะใช้<=คนละเส้นกับ>
Stop → Start
bHasMovementInput
AND NOT bIsFalling
AND Stance == Prone
- เปิด Rule ของ
Stop → Startให้เหมือนIdle → Startรวม Equal Enum เป็นProne - ตั้ง Priority ให้พิจารณาก่อน Automatic
Stop → Idle
Stance เปลี่ยนเป็น Standing/Crouched ขณะยังอยู่ใน Start/Stop ของ SM_Prone อย่าสร้างกฎออกไป Standing ภายใน nested machine ให้ SM_Posture เป็นผู้ตัด State Prone ทั้งก้อนตามข้อถัดไป- กลับ State Graph ของ
Proneยืนยันว่าSM_Proneต่อถึงState Result - Compile และ Save ก่อนเริ่มลากลูกศรในหน้า
SM_Posture
ต่อ Standing/Crouching ↔ Prone ในหน้า SM_Posture
คลิป idle-to-idle เป็น one-shot จึงอยู่ใน State ของตัวเอง Transition Rule Graph มีหน้าที่ส่ง Boolean เท่านั้น ห้ามวาง Sequence Player ใน Rule Graph Sprint/Jump-to-Stand ต้องออกจาก Prone ด้วยลูกศรตรงที่อ่าน bUrgentStandFromProne ห้ามเข้า ProneToStand
สร้าง State one-shot สี่ตัว
คลิปอยู่ที่ /Game/Lambeer/Characters/Player/Animation/Idle/ สำหรับทุกชื่อด้านล่าง
- ในหน้า
SM_PostureAdd State ชื่อStandToProneดับเบิลคลิก ลากA_INP_Idle2ProneIdleต่อเข้าState Resultปิด Loop บน Sequence Player แล้วคลิก State ในหน้าเครื่องตั้ง เปิดAlways Reset on Entry - สร้าง
CrouchToProneใส่A_INP_CrouchIdle2ProneIdleปิด Loop เปิด Always Reset on Entry - สร้าง
ProneToStandใส่A_INP_ProneIdle2Idleปิด Loop เปิด Always Reset on Entry - สร้าง
ProneToCrouchใส่A_INP_ProneIdle2CrouchIdleปิด Loop เปิด Always Reset on Entry - Compile ยืนยันว่าทั้งสี่ State มี pose ถึง Result และไม่มีคลิปที่ Loop ค้าง
ลูกศร idle — เข้า Prone ตอนหยุด
Standing → StandToProne
Stance == Prone
AND GroundSpeed <= MovementSpeedDeadZone
- ลากจากขอบ
StandingไปStandToProneลูกศรต้องชี้ทางเดียว - ดับเบิลคลิกวงกลม Get
StanceEqual Enum เป็นProne - Get
GroundSpeedกับMovementSpeedDeadZoneเข้า Float<= - AND ทั้งสองเข้า
Can Enter Transition
Crouching → CrouchToProne
Stance == Prone
AND GroundSpeed <= MovementSpeedDeadZone
- ลาก
Crouching → CrouchToProneแล้วสร้าง Rule แบบเดียวกับเส้น Standing แต่ต้นทางเป็นCrouching
StandToProne → Prone และ CrouchToProne → Prone
- ลาก
StandToProne → Proneคลิกวงกลม เปิดAutomatic Rule Based on Sequence Player in Stateคง Trigger Time เป็น default - ลาก
CrouchToProne → Proneแล้วเปิด Automatic Rule แบบเดียวกัน
ลูกศร idle — ออกจาก Prone ตอนหยุด (ไม่ใช่ Sprint/Jump)
Prone → ProneToStand
Stance == Standing
AND GroundSpeed <= MovementSpeedDeadZone
AND NOT bUrgentStandFromProne
- ลาก
Prone → ProneToStand - Equal Enum
Stance == Standing, FloatGroundSpeed <= MovementSpeedDeadZone, Get inheritedbUrgentStandFromProneผ่าน Boolean NOT - AND ทั้งสามเข้า Result การใส่ NOT ทำให้ Sprint/Jump ไม่ไหลเข้า one-shot นี้
Prone → ProneToCrouch
Stance == Crouched
AND GroundSpeed <= MovementSpeedDeadZone
AND NOT bUrgentStandFromProne
- ลาก
Prone → ProneToCrouchแล้วสร้าง Rule แบบเส้น Stand แต่ Equal เป็นCrouched - เส้นนี้ใช้ตอนปุ่ม Crouch จาก Prone หรือตอน Prone key fallback เป็น Crouched ขณะหยุด
ProneToStand → Standing และ ProneToCrouch → Crouching
- ลาก
ProneToStand → Standingเปิด Automatic Rule Based on Sequence Player in State - ลาก
ProneToCrouch → Crouchingเปิด Automatic Rule เช่นกัน
ทางยกเลิก one-shot เมื่อ Stance กลับด้านก่อนคลิปจบ
ลากลูกศรข้ามระหว่าง one-shot ตามตาราง แล้วตั้ง Priority Order ของทางยกเลิกให้พิจารณาก่อน Automatic Rule ที่รอคลิปจบ
| ลูกศร | Rule | เหตุที่ต้องมี |
|---|---|---|
StandToProne → ProneToStand | Stance == Standing | กดยกเลิกก่อนคลิปลงนอนจบ |
CrouchToProne → ProneToCrouch | Stance == Crouched | กลับ Crouch ระหว่างคลิปลงนอนจากก้ม |
CrouchToProne → ProneToStand | Stance == Standing | ลุกยืนระหว่างคลิปนั้น เช่น clearance/fallback |
ProneToStand → StandToProne | Stance == Prone | กด Prone ซ้ำระหว่างคลิปลุก |
ProneToStand → ProneToCrouch | Stance == Crouched | server fallback เป็น Crouched ขณะกำลังลุกยืน |
ProneToCrouch → CrouchToProne | Stance == Prone | กดกลับนอนระหว่างคลิปขึ้นก้ม |
ProneToCrouch → ProneToStand | Stance == Standing | เปลี่ยนเป็นยืนระหว่างคลิปขึ้นก้ม |
- แต่ละเส้นเปิด Rule Graph แล้ว Equal Enum ตามคอลัมน์ Rule ต่อเข้า Result โดยตรง ไม่ต้องใส่ GroundSpeed ในทางยกเลิกเหล่านี้
- อย่าเปิด Bidirectional บนลูกศร idle หลัก
ลูกศรตรงขณะเคลื่อนที่ และ Sprint/Jump-to-Stand
คลิป idle-to-idle ไม่เหมาะตอน capsule ยังไหล และห้ามใช้ตอน urgent stand สร้างลูกศรตรงแยกจาก one-shot กฎ <= กับ > ทำให้ idle กับ moving ไม่แข่งขันในเฟรมเดียวกัน
Standing → Prone
Stance == Prone
AND GroundSpeed > MovementSpeedDeadZone
- ลากลูกศรตรง
Standing → Proneคนละเส้นกับStanding → StandToProne - Equal Enum
ProneAND FloatGroundSpeed > MovementSpeedDeadZone - คลิกวงกลมใน Details ตั้ง Duration / Crossfade Duration เป็น
0.12ตาม baseline ข้อ 13.4 ที่อนุมัติแล้วสำหรับ moving posture
Crouching → Prone
Stance == Prone
AND GroundSpeed > MovementSpeedDeadZone
- ลาก
Crouching → ProneแยกจากCrouching → CrouchToProneใช้ Rule และ Crossfade0.12 sแบบเดียวกัน
Prone → Standing
Stance == Standing
AND (GroundSpeed > MovementSpeedDeadZone OR bUrgentStandFromProne)
- ลากลูกศรตรง
Prone → Standingคนละเส้นกับProne → ProneToStand - Get
StanceEqualStanding - สร้าง Float
GroundSpeed > MovementSpeedDeadZone - Get
bUrgentStandFromProne(inherited, ไม่ Set) - Boolean OR ระหว่างเงื่อนไขความเร็วกับ
bUrgentStandFromProneจากนั้น AND กับ Equal Stance เข้า Result - ตั้ง Crossfade Duration
0.12 s - ตั้ง
Priority Orderของเส้นนี้ให้พิจารณาก่อนProne → ProneToStandหากไม่ทำ Sprint/Jump จะไหลเข้า one-shot แล้ว capsule ลุกก่อน pose
Prone → Crouching
Stance == Crouched
AND (GroundSpeed > MovementSpeedDeadZone OR bUrgentStandFromProne)
- ลากลูกศรตรง
Prone → CrouchingแยกจากProne → ProneToCrouch - สร้าง Rule แบบเส้น Standing แต่ Equal เป็น
Crouchedรวม OR กับbUrgentStandFromProneเพราะ Jump/Sprint ที่ fallback เป็น Crouch ต้องตัด one-shot เช่นกัน - Crossfade
0.12 sและ Priority ก่อนProne → ProneToCrouch
- เมื่อเข้า
Proneขณะยังมี input nestedSM_Proneเริ่มที่ Idle ตาม Entry หากเห็น prone idle แวบหนึ่งแล้วค่อยไป Start ให้ใช้วิธี entry/initialization เดียวกับที่ทดสอบแล้วในSM_Crouchข้อ 13.4 ห้าม invent เวลา blend ใหม่ - อย่าใส่ Anim Notify เพื่อสลับ capsule; locomotion component เป็นเจ้าของ collision
สรุปลูกศรใน SM_Posture ที่เกี่ยวกับ Prone
| ลูกศร | ชนิด | Rule โดยย่อ | Crossfade รอบนี้ |
|---|---|---|---|
Standing → StandToProne | idle one-shot | Stance==Prone AND GroundSpeed<=DeadZone | ไม่กำหนดใหม่ในเอกสารนี้ |
Crouching → CrouchToProne | idle one-shot | เช่นเดียวกับด้านบน จาก Crouching | ไม่กำหนดใหม่ |
StandToProne → Prone | auto | Automatic Sequence Player | อิง default/automatic |
CrouchToProne → Prone | auto | Automatic Sequence Player | อิง default/automatic |
Prone → ProneToStand | idle one-shot | Standing AND <=DeadZone AND NOT bUrgentStandFromProne | ไม่กำหนดใหม่ |
Prone → ProneToCrouch | idle one-shot | Crouched AND <=DeadZone AND NOT bUrgentStandFromProne | ไม่กำหนดใหม่ |
ProneToStand → Standing | auto | Automatic Sequence Player | อิง default/automatic |
ProneToCrouch → Crouching | auto | Automatic Sequence Player | อิง default/automatic |
Standing → Prone | direct moving | Stance==Prone AND GroundSpeed>DeadZone | 0.12 s |
Crouching → Prone | direct moving | เช่นเดียวกับด้านบน จาก Crouching | 0.12 s |
Prone → Standing | direct moving/urgent | Standing AND (>DeadZone OR bUrgentStandFromProne) | 0.12 s |
Prone → Crouching | direct moving/urgent | Crouched AND (>DeadZone OR bUrgentStandFromProne) | 0.12 s |
Compile, Save และจุดตรวจก่อน PIE
- Compile
ABP_Playerแก้ error เรื่อง Result ว่าง, Enum ผิดชนิด หรือ Sync Group ก่อน Save - Save asset
- ใน My Blueprint ตรวจว่า
bUrgentStandFromProneเป็น inherited ไม่ใช่ตัวแปรที่สร้างเอง - Hover วงกลมลูกศรทุกเส้นใน
SM_PostureและSM_Proneยืนยันว่า Result ถูกต่อและ preview expression ตรงตาราง - ยังไม่ต่อ Idle Break, directional Start/Stop หรือ Crouch Pivot ในรอบนี้
13.6 ทดสอบใน PIE
เลือก player instance ใน Debug Filter แล้วเปิดทั้ง SM_Posture, SM_Crouch และ SM_Prone สลับดูระหว่างทดสอบ Watch อย่างน้อย Stance, GroundSpeed, Direction, bHasMovementInput, bIsFalling และ bUrgentStandFromProne; ใช้ Character Movement → Is Crouching เมื่อต้องวินิจฉัย Crouch
| Test ID | วิธีทดสอบ | ผลที่ต้องเห็น |
|---|---|---|
| LOCO-13-C01 | ยืน Idle แล้วกด Crouch หนึ่งครั้ง | Standing → StandToCrouch → Crouching/Idle; transition clip เล่นครั้งเดียวและจบที่ A_INP_Crouch_Idle |
| LOCO-13-C02 | อยู่ Crouch Idle แล้วกดลุก | Crouching → CrouchToStand → Standing/Idle; ไม่ค้างที่เฟรมท้าย |
| LOCO-13-C03 | กดเดินหน้าใน Crouch แล้วปล่อย | Idle → Start → Moving → Stop → Idle; Start/Stop ไม่ loop |
| LOCO-13-C04 | เคลื่อนที่ Crouch รอบ 8 ทิศและข้ามรอยต่อหลัง | ทิศ pose ตรง input, ไม่มี seam กระโดดที่ -180/180 และเท้าไม่สลับ phase ชัดเจน |
| LOCO-13-C05 | กด Crouch และลุกขณะยังเคลื่อนที่ | ใช้ direct moving transition; ไม่เล่น idle-to-idle clip ขณะ capsule ไหล |
| LOCO-13-C06 | กด Crouch ซ้ำระหว่าง transition | ทางยกเลิกมี priority ถูกต้อง, ไม่ค้างใน StandToCrouch/CrouchToStand และ pose จบตรง Stance |
| LOCO-13-C07 | พยายามลุกใต้เพดานต่ำ | Character ยัง Crouch, Stance ยังคงเป็น Crouched และ animation ไม่ทะลุไป Standing |
| LOCO-13-C08 | ทดสอบ Listen Server และ remote client | remote proxy เปลี่ยน posture ตาม replicated Character Movement โดยไม่สั่นสลับ Standing/Crouching |
Prone acceptance tests หลังต่อ SM_Prone
ปุ่ม Prone runtime เริ่มต้นคือ Z (IA_Prone) หรือ console Lambeer.Prone ระหว่างทดสอบให้ดู Debug Filter ว่า active state ใน SM_Posture ตรงคอลัมน์ผล และ bUrgentStandFromProne เป็น true เฉพาะตอน Sprint/Jump-to-Stand
Start → Moving ยังคงใช้ Automatic Rule ตาม baseline เดิม ไม่ได้เพิ่ม explicit rule; LOCO-13-P07 ผ่านเมื่อ 2026-09-05 หลังอัปเดต cached mesh base offset| Test ID | วิธีทดสอบ | ผลที่ต้องเห็นใน SM_Posture / SM_Prone |
|---|---|---|
| LOCO-13-P01 | ยืน Idle แล้วกด Prone; ทำซ้ำจาก Crouch Idle | Standing → StandToProne → Prone/Idle และ Crouching → CrouchToProne → Prone/Idle; one-shot เล่นครั้งเดียว; server commit CurrentStance = Prone หลัง clearance |
| LOCO-13-P02 | ขณะ Prone Idle กด Prone ซ้ำใต้เพดานสูง, ใต้เพดานที่ก้มได้ และใต้เพดานที่นอนอย่างเดียว จากนั้นกด Crouch ในแต่ละกรณี | Prone key ขอ Standing แล้ว fallback Crouched; Crouch key ขอ Crouched; หากไม่ผ่านทั้งหมดคง Prone และไม่เข้า Standing; idle ใช้ ProneToStand/ProneToCrouch ไม่ใช้ลูกศร urgent |
| LOCO-13-P03 | กด Sprint ขณะ Prone Idle และขณะคลาน | bUrgentStandFromProne เป็น true, ใช้ลูกศรตรง Prone → Standing ไม่เล่น A_INP_ProneIdle2Idle; เมื่อ clearance ผ่านเข้า Run; หากไม่ผ่านคง Prone และไม่ Sprint |
| LOCO-13-P04 | กด Jump ขณะ Prone แล้วปล่อยและกดใหม่หลังยืน | ครั้งแรก bUrgentStandFromProne true, ลุกและไม่กระโดด; ครั้งถัดไปหลัง Standing จึง Jump |
| LOCO-13-P05 | คลาน 8 ทิศ ข้าม seam หลัง ช่องต่ำ ชิดผนัง และเลี้ยวในทางแคบ | SM_Prone วิ่ง Idle→Start→Moving→Stop; ทิศ pose ตรง input; capsule/envelope ไม่ทะลุ geometry |
| LOCO-13-P06 | ลอง Look, interact ใกล้, Sprint, Jump, และ action ที่ถูก Block | Allowed/RequiresStanding/Blocked ตรงตารางข้อ 13.5; ABP ไม่ใช่ผู้อนุญาต action |
| LOCO-13-P07 | Listen Server / remote client พร้อม latency และ packet loss ทั้ง accept และ reject | [PASSED 2026-09-05] ไม่มี stance flapping; simulated อ่าน replicated CurrentStance; rejection ใช้ inertial blend ไม่เดา collision และ mesh ไม่จมดิน |
| LOCO-13-P08 | ตกขอบขณะ Prone, รับ damage ปกติ, และ knockdown/ragdoll ระหว่าง transition | Airborne มาก่อนแต่ stance ยัง Prone; ไม่มี Anim Notify เปลี่ยน collision; recovery ขอ stance ใหม่ผ่าน clearance |
13.7 แก้ปัญหาจากอาการที่เห็น
| อาการ | ตรวจจุดแรก | แนวแก้ |
|---|---|---|
| กด Crouch แล้ว capsule ก้ม แต่ pose ยังยืน | Watch Character Movement → Is Crouching, Stance และ active state ใน SM_Posture | ตรวจ mapping ในข้อ 13.1, ทิศลูกศร และ Result ของ State Crouching |
| transition clip เล่นวนหรือค้าง | Loop setting และ Automatic Rule | ปิด Loop, เปิด Always Reset on Entry และยืนยันว่า Sequence Player ต่อถึง State Result |
| กดก้มขณะวิ่งแล้วเห็นท่ายืน/ก้ม idle แวบหนึ่ง | เส้น direct transition และ entry ของ SM_Crouch | ยืนยันว่ากฎ moving ใช้ GroundSpeed > MovementSpeedDeadZone และไม่วิ่งผ่าน StandToCrouch |
| เดิน Crouch ซ้าย–ขวาสลับกัน | ค่า signed Direction เทียบ Preview | สลับ sample Lt/Rt ใน Blend Space หลังยืนยันค่าจริง ไม่เปลี่ยนชื่อ asset หรือ negate หลายจุด |
| กระตุกตรงด้านหลัง | sample ที่ -180 และ 180 | ใช้ A_INP_CrouchBwd_Loop ตัวเดียวกันทั้งสองขอบและตรวจ Sync Marker |
| เท้าไถลทุกทิศ | GroundSpeed จริง, sample row และ encumbrance multiplier | วัด authored speed ก่อน แล้วจึงตัดสิน play rate/stride warping; อย่าแก้ด้วย transition duration |
| เจ้าของเห็น Prone ปกติ แต่ client อื่นเห็นตัวละครจมดิน | บน simulated proxy ตรวจ CurrentStance, capsule half-height, mesh relative Z และ cached base offset ที่ network smoothing ใช้ | หลัง Mesh->SetRelativeLocation(MeshRelativeLocation) ใน ApplyCollisionForStance() ให้เรียก Character->CacheInitialMeshOffset(MeshRelativeLocation, Mesh->GetRelativeRotation()); ทำทั้งตอนเข้าและออก Prone แล้ว retest LOCO-13-P07 ห้ามชดเชยซ้ำใน ABP |
| ลุกใต้เพดานทั้งที่ gameplay ยัง Crouch | กฎ Crouching → Standing | ใช้ Stance == Standing ซึ่ง map จากผล Is Crouching ของ Character Movement ห้ามใช้ปุ่มที่กดหรือ local toggle ใน ABP |
| Prone เข้าได้แต่ไม่มีทางออกที่เสถียร | ลูกศร Prone → ProneToStand/ProneToCrouch และ bUrgentStandFromProne | ตรวจ Equal Enum, DeadZone, NOT urgent บน one-shot และ Priority ของลูกศรตรง; อย่าถอด Prone จากกราฟหลัง runtime พร้อมแล้ว |
| กด Prone แล้ว capsule ต่ำแต่ pose ยังยืน/ก้ม | Watch Stance กับ active state ใน SM_Posture | ตรวจว่ามี State Prone ที่ต่อ SM_Prone ถึง Result และลูกศร idle/moving ออกจาก Standing/Crouching จริง |
| กด Prone แล้ว animation เล่น แต่ตัวละครทั้งตัวอยู่ใต้พื้นทันที | ดู capsule center, capsule bottom และ Mesh world/relative Z ก่อน–หลัง ApplyCollisionForStance() | เมื่อเลื่อน Actor ลงตามผลต่าง half-height เพื่อรักษาฐาน capsule ต้อง counter-offset Mesh relative Z จาก standing baseline ด้วย StandingCapsuleHalfHeight - TargetHalfHeight; rebuild C++ แล้ว retest LOCO-13-P01 ห้ามแก้ด้วย Root Bone หรือ Anim Notify |
SM_Prone ดูเหมือนค้างที่ Start | ยืนยันว่าเลือกวงกลมของลูกศร Start → Moving ถูกเส้น, Automatic Rule เปิดบนเส้นนั้นจริง, Sequence Player ปิด Loop และ Compile/Save ล่าสุดแล้ว | แก้ selection/setting ที่ผิดก่อน โดยคง Automatic Rule baseline เดิม; อย่าเพิ่ม explicit remaining-time rule หาก Automatic Rule ทำงานได้หลังตรวจ wiring |
กด Sprint จาก Prone แล้วยังเล่น A_INP_ProneIdle2Idle | bUrgentStandFromProne และ Priority ของ Prone → Standing | Compile C++ ให้ inherited bool ขึ้น, ใส่ OR ใน Rule ของลูกศรตรง และให้เส้นนั้นพิจารณาก่อน one-shot |
| คลานแล้วเห็น prone idle แวบหนึ่ง | เข้า Prone ผ่านลูกศรตรงแต่ nested เริ่มที่ Idle | ตรวจว่า moving ใช้ GroundSpeed > DeadZone ไม่ผ่าน StandToProne; entry ของ SM_Prone ให้แก้แบบเดียวกับ SM_Crouch ข้อ 13.4 |
| Start ใน SM_Prone ไม่เคยเข้า | Rule Idle → Start ยังเป็น Stance == Crouched หรือ Standing | เปลี่ยน Equal เป็น Prone และตรวจ bHasMovementInput |
| ซ้ายขวาสลับตอนคลาน | sample Lt/Rt ใน BS_Prone_Locomotion_2D | สลับตำแหน่ง sample หลังยืนยัน signed Direction ตามข้อ 13.5 Blend Space ห้าม negate หลายจุด |
14. Jump, Falling และ Landing
Grounded → JumpStart → Rising → Apex/Falling → Land → Recovery → Grounded
ชุดง่ายสำหรับ milestone แรก
A_INP_JumpIdle_Start → A_INP_JumpIdle_inAir → A_INP_JumpIdle_Land; ใช้ A_INP_Falling_Loop เมื่อตกโดยไม่ได้เริ่มจาก jump sequence ปกติ
กระโดดตาม gait และเท้า
- Walk:
A_INP_JumpWalkFwd_Start_LU/RU→inAir_LU/RU→Land_LU/RU - Jog:
A_INP_JumpJog_Start_LU/RU→inAir_LU/RU→Land_LU/RU - Run:
A_INP_JumpRun_Start_LU/RU→inAir_LU/RU→Land_LU/RU
Directional multi-stage family
ตัวอย่างทิศหน้า: A_INP_JumpFwd_Start → A_INP_JumpFwd_Start_Loop → A_INP_JumpFwd_Apex → A_INP_JumpFwd_Land_Fall_Loop → A_INP_JumpFwd_Land → A_INP_JumpFwd_Recovery ทิศ Bwd/Lt/Rt ใช้ชื่อรูปแบบเดียวกัน
15. ลำดับการเพิ่มความลื่น
- Sync Markers: ทำให้ Walk/Jog/Run ไม่สลับเท้าขณะ blend
- Inertialization: วางหลัง State Machine และใช้กับ transition ที่ interrupt บ่อย
- Footstep/Plant Notifies: เลือก Start/Stop/Pivot ให้ตรงเท้า
- Distance Matching: เพิ่ม curve และ predicted stop/ground distance หลัง base state machine ทำงานถูก
- Stride Warping: ชดเชยความต่างระหว่าง authored stride กับ capsule speed/encumbrance
- Orientation Warping: เติมช่องว่างมุมเล็ก ๆ ไม่ใช้แทน Pivot 180°
- Foot Placement IK: ทำหลัง animation phase ถูก มิฉะนั้น IK จะเพียงซ่อนปัญหา
Plugins ที่จำเป็นต่อ Distance/Pose Warping ยังไม่ได้ยืนยันว่าเปิดอยู่ใน Lambeer.uproject; การเปิด plugin, skeleton/IK bones, retarget/rig และ runtime configuration ติดตามแยกใน TBD-LOCO-010 จนถึงขั้น implementation
16. Multiplayer และ Persistence
UCharacterMovementComponent ไม่รับประกันว่า GetCurrentAcceleration() บน simulated proxy จะสะท้อนการกด/ปล่อย input ของเจ้าของ จึงให้ server คำนวณ ULambeerLocomotionComponent.bReplicatedHasMovementInput และ replicate เฉพาะ Boolean intent นี้ ส่วน autonomous proxy อ่าน MovementInputMagnitude ในเครื่องผ่าน HasMovementInputForAnimation() เพื่อไม่รอ round trip- ตำแหน่งจริงและ movement mode ยังคง authoritative ผ่าน replicated
UCharacterMovementComponent ALambeerCharacterresetMovementInputMagnitudeเมื่อ Move Action เกิดCompletedหรือCanceledเพื่อสร้างขอบสัญญาณtrue → falseที่แน่นอนบนเจ้าของULambeerAnimInstanceใช้HasMovementInputForAnimation()ตรวจJustReleased; simulated proxy อ่านค่าที่ server replicate แล้วเลือก Stop ในเครื่องจาก replicated velocity และ transition state ที่มีอยู่ ห้าม multicast การเล่น animation- Loop pose, direction และ local transition selection ควรคำนวณจาก replicated velocity/state บนแต่ละ proxy โดยไม่สมมติว่า acceleration ของ simulated proxy เชื่อถือได้
- เมื่อ runtime เปลี่ยน mesh offset จาก capsule ตาม stance ต้องเรียก
ACharacter::CacheInitialMeshOffset()หลังSetRelativeLocation()เพื่ออัปเดตเป้าหมาย network smoothing ของ simulated proxy; การตั้ง relative location อย่างเดียวอาจถูก smoothing เขียนทับ - อย่า replicate animation time ทุกเฟรม
- replicate เฉพาะ gameplay state ที่อนุมานไม่ได้อย่างเสถียร เช่น stance, aim mode หรือ movement intent ที่จำเป็นต่อ transition edge
- locomotion pose ไม่มีข้อมูลที่ต้อง persist ลง save game
- Dedicated Server PIE แบบ 2 clients ผ่าน smoke test ของ Stop แล้ว; ยังต้องทดสอบ Listen Server, simulated latency, packet loss และ Stop ทุกทิศ/foot phase ก่อนอนุมัติ multiplayer gate เต็มรูปแบบ
17. แผนลงมือทีละระยะ
- Skeleton gate: Retarget test
A_INP_IdleและA_INP_WalkFwd_Loop - 2D core: สร้าง
BS_Standing_Locomotion_2Dด้วย Walk/Jog/Run loops - Basic states: Idle → Start → Moving → Stop
- Foot phase: LeftPlant/RightPlant และ Sync Group
- Directional transitions: Start/Stop ตามทิศ
- Reversal: Pivot ก่อน แล้วจึง Turn/Spin
- Postures: Crouch แล้ว Prone
- Airborne: Jump/Fall/Land/Recovery
- Aim overlay: Layered Blend per Bone และ turn in place
- Polish: Distance Matching, Stride/Orientation Warping, Foot IK
- Network QA: ทดสอบ proxy และ packet simulation
18. Checklist ทดสอบ
| Test ID | สถานการณ์ | ผลที่ต้องเห็น |
|---|---|---|
| LOCO-01 | Idle → Walk → Jog → Run → Jog → Stop | เท้าไม่สลับ phase และไม่มี speed pop ชัดเจน |
| LOCO-02 | ลาก Direction รอบ -180..180 | ไม่มี seam กระตุกที่หลัง และซ้าย/ขวาถูกทิศ |
| LOCO-03 | Sprint แล้วกดถอย | ลดเป็น Jog; ไม่ใช้ Run pose เคลื่อนหลัง |
| LOCO-04 | ปล่อย input ทุกทิศและทุก foot phase | เลือก Stop ถูกทิศ/เท้า ไม่มี foot snap |
| LOCO-05 | กดกลับทิศกลาง Jog | เข้า Pivot หนึ่งครั้งและกลับ Moving ถูก family |
| LOCO-06 | Aim แล้ว strafe/turn in place | ลำตัวไม่หมุนหนีเป้าและขาไม่บิดเกินจริง |
| LOCO-07 | Crouch/Prone ระหว่างมี input | transition ไม่ค้างและ queued input ทำงานหลังช่วงอนุญาต |
| LOCO-08 | Jump จาก Idle/Walk/Jog/Run | เลือก family/foot ถูกและ Land ตรงเวลาสัมผัสพื้น |
| LOCO-09 | Encumbrance หลายระดับ | capsule speed เปลี่ยนโดย foot slide อยู่ในเกณฑ์ที่อนุมัติ |
| LOCO-10 | Remote proxy พร้อม latency | ไม่มี state flapping หรือ montage desync ที่มองเห็นชัด |
19. Technical Decisions และ TBD
bReplicatedHasMovementInput, autonomous proxy ใช้ local MovementInputMagnitude, simulated proxy ใช้ค่าที่ replicate และทุกเครื่องรัน Stop selector ใน AnimInstance ของตนเองโดยไม่ multicast animation timeProneSpeed, runtime acceleration/braking แยกและใช้ encumbrance multiplier เดิม; กด Sprint ขณะ Prone จะขอ Standing ทันทีและเริ่ม Run หลัง server ยืนยัน clearance โดย ABP ใช้ direct short blend แทนการรอ one-shotCurrentStance เท่านั้น- [PROPOSED] ใช้ Blend Space 2D เดียวรวม Standing Walk/Jog/Run Loop
- [CONFIRMED 2026-09-02] Idle, Start, Stop, Pivot, MovingTurn และ Spin เป็น State แยกใน
SM_Standing - [PROPOSED] ไม่อนุญาต Run ใน backward hemisphere เมื่อไม่มี animation coverage
- [CONFIRMED] HipRelaxed ใช้ VelocityDirection; ShoulderAim/ADS ใช้ LookingDirection และ gameplay เป็นผู้ควบคุม actor rotation ตามข้อ 10.7
- [CONFIRMED · TBD-LOCO-002]
_L/_Rคือทิศซ้าย/ขวา ส่วน_LU/_RUคือ Left Up/Right Up ของ gait phase ตามข้อ 10.7 - [CONFIRMED · TBD-LOCO-006]
ULambeerLocomotionComponent.CurrentGaitเป็น replicated source of truth ของ Walk/Jog/Run; AnimInstance ใช้Gait == RunแทนbIsSprinting - [CONFIRMED 2026-09-01 · TBD-LOCO-001] ใช้ Manny
SKM_Manny_Simpleเป็น source, MetaHumanSKM_MHC_Man_BodyMeshเป็น target และ/Game/MaleLocomotionSet/Animations/Mannequin/RTG_AutoGeneratedเป็น IK Retargeter - [PARTIALLY CONFIRMED 2026-09-03 · TBD-LOCO-003] Forward Loop วัดจาก Root Motion ได้ Walk ≈164, Jog ≈442 และ Run ≈660 cm/s; runtime/Standing Blend Space ใช้ 165/442/600 และ Jog braking 260 จาก Stop-distance ratio; Crouch Blend Space ใช้ provisional baseline 110 cm/s ตาม runtime
CrouchSpeedที่อนุมัติแล้ว ส่วน authored speed ของทิศอื่นยังต้องวัด - [CONFIRMED 2026-09-03 · TBD-LOCO-004] signed Direction ใช้ค่าบวก = ขวาและค่าลบ = ซ้าย; side-loop baseline ใช้ตระกูล
FwdLt/FwdRt_90ทุก gait/stance ที่มี coverage และเก็บตระกูล backward-side ไว้เปรียบเทียบภายหลัง - [CONFIRMED BASELINE v0.1 · 2026-09-02 · TBD-LOCO-005] selector กลางอยู่ใน C++, ABP เป็นผู้บริโภค, priority คือ Airborne/Posture → Stop → Spin → Pivot → Moving Turn; Turn/Pivot/Spin enter = 30°/125°/165°, Pivot dot < -0.35, Turn buckets = 67.5°/112.5°/157.5°, hysteresis = 7.5°/10°/10°, cooldown = 0.15/0.25/0.35 s, commit = 10%/15%/20%, blend เข้า/ออก = Turn 0.10/0.12 s, Pivot 0.08/0.12 s, Spin 0.08/0.15 s และ Fast Stop ปิดอยู่; implementation ผ่าน build, ABP compile/save และ PIE startup แต่ visual/behavior กับ multiplayer gate ยังไม่ผ่าน
- [PARTIALLY CONFIRMED · TBD-LOCO-007] Turn in Place ใช้ Mesh/Root-Yaw-Offset, gameplay/server เป็น actor/capsule owner, HipRelaxed ไม่ turn ตามกล้อง และไม่ใช้ Root Motion เป็น baseline; runtime aim-yaw signal, curve/commit, thresholds, residual yaw และ network rebase/correction ยังเป็น TBD
- [PARTIALLY CONFIRMED · TBD-LOCO-008] Idle Break ใช้ gameplay action gate, เริ่มจาก Standing, เลือกด้วย stance-specific shuffle bag แบบไม่ซ้ำติดกัน และ cancel ตาม authoritative state; runtime signal, delay, cooldown และ selection weights ยังเป็น TBD
- [IMPLEMENTED RUNTIME 2026-09-04 · ANIMATION GRAPH COMPLETED BY USER 2026-09-05 · TBD-LOCO-009] Prone ใช้ replicated
ELambeerLocomotionStance/CurrentStance, input paths, vertical root capsule + server body-envelope sweep, gait เดียว, Sprint/Jump-to-Stand,bUrgentStandFromProne, data-driven action gate และ recovery hooks ตามข้อ 13.5 แล้ว; คลิป retarget รอบแรกมีแล้วในโฟลเดอร์ผู้เล่น; native mesh-offset และ Prone Moving ผ่านเส้นทางที่ผู้ใช้ทดสอบ โดยStart → Movingใช้ Automatic Rule baseline เดิม ส่วนค่าขนาด collision กับProneSpeed = 80ยังเป็น verification placeholders จนกว่าจะวัดจาก MetaHuman - [TBD · TBD-LOCO-010] plugin/IK configuration
20. ภาคผนวก: รายชื่อ In-Place Animation จริงทั้งหมด
ตารางนี้สร้างจากไฟล์ .uasset ที่พบจริงใต้ Content/MaleLocomotionSet/Animations/Mannequin/InPlace เมื่อวันที่ 2026-08-29 ชื่อในตารางตัดนามสกุล .uasset เพื่อให้ตรงกับชื่อ UE asset
438 assets
| กลุ่ม | UE Asset Name | UE Package Folder |
|---|---|---|
| Crouch | A_INP_CrouchBwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwd_Stop_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwd_Stop_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwd2Fwd_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwd2Fwd_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt2FwdLt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt2FwdLt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt2FwdLt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdLt2FwdLt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt2FwdRt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt2FwdRt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt2FwdRt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchBwdRt2FwdRt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_135_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_135_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_135_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_135_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_180_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_180_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_180_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_180_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_45_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_45_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_45_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_45_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_90_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_90_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_90_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_90_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_Stop_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd_Stop_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd2Bwd_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwd2Bwd_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt2BwdLt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt2BwdLt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt2BwdLt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdLt2BwdLt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt2BwdRt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt2BwdRt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt2BwdRt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Crouch | A_INP_CrouchFwdRt2BwdRt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch |
| Idle | A_INP_Crouch_Idle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_Idle_Break_01 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_Idle_Break_02 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_Idle_Break_03 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleLt_135_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleLt_180_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleLt_45_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleLt_90_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleRt_135_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleRt_180_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleRt_45_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Crouch_IdleRt_90_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_CrouchIdle2Idle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_CrouchIdle2ProneIdle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Idle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Idle_Break_01 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Idle_Break_02 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Idle_Break_03 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Idle2CrouchIdle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Idle2ProneIdle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleLt_135_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleLt_180_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleLt_45_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleLt_90_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleRt_135_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleRt_180_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleRt_45_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_IdleRt_90_Turn | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Prone_Idle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Prone_Idle_Break_01 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_Prone_Idle_Break_02 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_ProneIdle2CrouchIdle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Idle | A_INP_ProneIdle2Idle | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle |
| Jog | A_INP_JogBwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwd_Stop_Fast | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwd_Stop_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwd_Stop_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwd2JogFwd_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwd2JogFwd_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt2JogFwdLt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt2JogFwdLt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt2JogFwdLt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdLt2JogFwdLt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt2JogFwdRt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt2JogFwdRt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt2JogFwdRt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogBwdRt2JogFwdRt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_135_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_135_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_135_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_135_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_180_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_180_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_180_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_180_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_45_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_45_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_45_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_45_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_90_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_90_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_90_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_90_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_Stop_Fast | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_Stop_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd_Stop_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwd_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwd_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdLt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdLt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdLt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdLt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdRt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdRt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdRt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwd2JogBwdRt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdLt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdLt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdLt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdLt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdRt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdRt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdRt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jog | A_INP_JogFwdRt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog |
| Jump | A_INP_Falling_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwd_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwd_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwd_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwd_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwd_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_45_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_45_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_45_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_45_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_45_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_90_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_90_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_90_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_90_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdLt_90_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_45_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_45_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_45_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_45_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_45_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_90_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_90_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_90_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_90_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpBwdRt_90_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwd_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwd_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwd_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwd_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwd_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_45_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_45_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_45_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_45_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_45_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_90_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_90_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_90_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_90_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdLt_90_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_45_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_45_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_45_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_45_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_45_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_90_Apex | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_90_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_90_Land_Fall_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_90_Recovery | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpFwdRt_90_Start_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpIdle_All | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpIdle_inAir | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpIdle_Land | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpIdle_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpIdleForward_All | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_All_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_All_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_inAir_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_inAir_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_Land_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_Land_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_Start_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpJog_Start_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_All_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_All_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_inAir_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_inAir_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_Land_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_Land_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_Start_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpRun_Start_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_All_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_All_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_inAir_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_inAir_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_Land_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_Land_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_Start_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkBwd_Start_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_All_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_All_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_inAir_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_inAir_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_Land_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_Land_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_Start_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Jump | A_INP_JumpWalkFwd_Start_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump |
| Prone | A_INP_ProneBwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwd_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwdLt135_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwdLt135_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwdLt135_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwdRt135_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwdRt135_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneBwdRt135_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwd_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdLt45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdLt45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdLt45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdLt90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdLt90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdLt90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdRt45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdRt45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdRt45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdRt90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdRt90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Prone | A_INP_ProneFwdRt90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone |
| Run | A_INP_RunFwd_135_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_135_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_135_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_135_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_180_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_180_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_180_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_180_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_45_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_45_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_45_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_45_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_90_Start_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_90_Start_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_90_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_90_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_Stop_Fast | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_Stop_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwd_Stop_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdLt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdLt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdLt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdLt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdRt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdRt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdRt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Run | A_INP_RunFwdRt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run |
| Spin | A_INP_CrouchBwd_Spin_Fwd_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchBwd_Spin_Fwd_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchBwdLt_Spin_FwdRt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchBwdLt_Spin_FwdRt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchBwdRt_Spin_FwdLt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchBwdRt_Spin_FwdLt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchFwd_Spin_Bwd_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchFwd_Spin_Bwd_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchFwdLt_Spin_BwdRt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchFwdLt_Spin_BwdRt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchFwdRt_Spin_BwdLt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_CrouchFwdRt_Spin_BwdLt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogBwd_Spin_Fwd_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogBwd_Spin_Fwd_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogBwdLt_Spin_FwdRt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogBwdLt_Spin_FwdRt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogBwdRt_Spin_FwdLt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogBwdRt_Spin_FwdLt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogFwd_Spin_Bwd_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogFwd_Spin_Bwd_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogFwdLt_Spin_BwdRt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogFwdLt_Spin_BwdRt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogFwdRt_Spin_BwdLt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_JogFwdRt_Spin_BwdLt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_RunFwd_Spin_JogBwd_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_RunFwd_Spin_JogBwd_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_RunFwdLt_Spin_JogBwdRt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_RunFwdLt_Spin_JogBwdRt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_RunFwdRt_Spin_JogBwdLt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_RunFwdRt_Spin_JogBwdLt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkBwd_Spin_Fwd_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkBwd_Spin_Fwd_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkBwdLt_Spin_FwdRt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkBwdLt_Spin_FwdRt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkBwdRt_Spin_FwdLt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkBwdRt_Spin_FwdLt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkFwd_Spin_Bwd_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkFwd_Spin_Bwd_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkFwdLt_Spin_BwdRt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkFwdLt_Spin_BwdRt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkFwdRt_Spin_BwdLt_45 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Spin | A_INP_WalkFwdRt_Spin_BwdLt_90 | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin |
| Walk | A_INP_WalkBwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwd_Stop_Fast | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwd_Stop_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwd_Stop_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwd2Fwd_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwd2Fwd_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt2FwdLt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt2FwdLt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt2FwdLt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdLt2FwdLt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt2FwdRt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt2FwdRt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt2FwdRt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkBwdRt2FwdRt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_135_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_135_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_180_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_180_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_45_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_45_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_90_Turn_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_90_Turn_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_135_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_135_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_180_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_180_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_45_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_45_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_90_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Start_90_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Stop_Fast | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Stop_LU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd_Stop_RU | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd2Bwd_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwd2Bwd_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt2BwdLt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt2BwdLt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt2BwdLt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdLt2BwdLt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt_45_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt_45_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt_45_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt_90_Loop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt_90_Start | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt_90_Stop | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt2BwdRt_45_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt2BwdRt_45_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt2BwdRt_90_Pivot_L | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
| Walk | A_INP_WalkFwdRt2BwdRt_90_Pivot_R | /Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk |
ไม่พบ animation ที่ตรงกับตัวกรอง
Retargeted pistol / rifle aim and walk — 2026-09-19
[CONFIRMED] Owner scope: use the supplied pistol and rifle Aim Offsets in ADS and ShoulderAim, including supported, unsupported and dual-pistol contexts in Standing, Crouched and Prone. Standing grounded aim uses the new full-body idle/walk blend; crouch/prone retain their existing lower-body locomotion and use the new content only through held upper-body/arm masks. Melee and unarmed have no new Aim Offset in this delivery and retain their prior presentation.
Source root: /Game/Lambeer/Characters/Player/Animation/Aim/. All pistol AO, samples, idle and walk content now resides in Aim/Pistol/; rifle content resides in Aim/Firearm2H/. Pistol/AO_Pistol uses Pistol/MF_Pistol_Idle_ADS_AO_{CD,CC,CU} and base Pistol/MF_Pistol_Idle_ADS. Firearm2H/AO_Rifle uses Firearm2H/MM_Rifle_Idle_ADS_AO_{CD,CC,CU} and base Firearm2H/MF_Rifle_Idle_ADS. Owner confirmed the two pistol idle assets were duplicates; references were consolidated into the existing Pistol-folder asset, and the unreferenced root duplicate was removed. All six AO samples are Mesh Space additive against frame 0 of their matching base; the two idle bases and sixteen walk clips are non-additive and share the MetaHuman base skeleton. Owner supplied the retargeted assets; no retarget/export was performed by the agent.
Pistol/BS_Pistol_AimWalk and Firearm2H/BS_Rifle_AimWalk use their corresponding MF_{Pistol,Rifle}_Walk_{Fwd,Fwd_Left,Fwd_Right,Left,Right,Bwd,Bwd_Left,Bwd_Right} clips. X is actor-relative movement direction from -180 to +180 degrees with a duplicated backward seam; Y is current planar speed divided by the current movement maximum, clamped to 0–1. Each space has nine idle samples at Y=0 and nine directional walk samples at Y=1. This defines blend coordinates, not a new gameplay speed or calibrated foot-speed claim.
Aim mapping: owner corrected the authored camera/base-aim pitch endpoints to -80 / 0 / +80 degrees, mapped to -1 / 0 / +1 on the existing AO Y axis. This supersedes the earlier approved -90/+90 mapping. Runtime normalizes wrapped pitch, divides by 80 and clamps to [-1, 1]. X remains zero because this delivery contains vertical samples only. Existing AO sample positions are preserved. Both camera modes share the same exact-context Aim entries. FLambeerHeldAnimEntry now exposes AimWalk and AimOffset beside Pose; eighteen pistol and six rifle Aim entries are bound, with Carry entries, masks and context keys preserved.
Evaluation: standing firearm idle/walk selection → Foot IK → each selected held sequence/walk blended with its own Aim Offset → existing UpperBody/right/left mesh-space rotation masks → DefaultSlot → finger poses / component-space controls → ADS alignment → procedural recoil → support IK. The walk players share the P09AimWalk sync group. Native per-layer Aim Offset selection suppresses the old clavicle pitch on those chains so elevation is not applied twice. Non-AO contexts retain the earlier arm-pitch path. Missing/incompatible content clears the new outputs; equipment or aim changes cannot retain a stale offset. Right-hand precedence selects full-body locomotion for dual/mixed pairs.
Existing support-hand targets, item grip transforms, sight alignment scope/settings, procedural recoil, action permissions and input bindings are preserved. Reusing the supplied supported-style clips for unsupported/dual and other stances is owner-approved content reuse; this does not establish a final independent left-pistol posture or visual contact quality. No new authored hand pose, mirror asset, yaw sample or stance-specific retarget is inferred. Presentation is transient and introduces no persistence fields or schema changes.
Initial non-PIE validation (insufficient directional coverage; corrected below): closed-Editor LambeerEditor Win64 Development build passed. MCP compiled ABP_Player with warnings treated as errors and saved the graph/profiles/blend spaces. Six focused automation tests passed with no warnings/errors, including Lambeer.HUD.Content.RetargetedFirearmAim, which evaluates both blend spaces at every direction and idle/half/full speed, plus AO center/endpoints and all authored profile stances. An Editor sample-array assertion occurred during initial rifle authoring; after reopening, the rifle samples were replaced without clearing the array, read back and passed evaluation. Evidence: Saved/P09RetargetedAim/.
Owner report and repair — 2026-09-19: owner confirms looking up/down now works. Cardinal movement remained idle and diagonal movement selected incorrect walk clips; weapon/body/view/network coverage of that report was not specified. A fresh-process regression reproduced the fault: full-speed pistol forward/right returned idle weight 1.0 instead of the expected walk. The earlier test accepted any clip from the same family, so its pass did not establish correct direction selection.
Root cause: sample properties had changed while the serialized runtime triangulation still described the duplicated source Blend Space. UE 5.8 UBlendSpace::PostEditChangeProperty does not rebuild that data by itself; SBlendSpaceEditor::Construct calls ResampleData. Opened both spaces through Unreal MCP's OpenEditorForAsset, rebuilt runtime samples, dirtied and saved each exact asset while restoring its original loop setting. Both use the original approved direction and speed axes; no movement/input tuning was changed.
Regression: Lambeer.HUD.Content.RetargetedFirearmAim now asserts the exact expected directional clip weight and idle weight at every cardinal/diagonal/backward-seam coordinate for idle, half and full speed, rather than merely accepting a family match. It failed with 72 assertions before repair and passed in-editor after resampling. Independent fresh-process on-disk validation passed 1/1 with zero warnings/errors, recorded in Saved/Automation/P09AimWalkAfterRepair; pre-repair evidence is Saved/Automation/P09AimWalkBeforeRepair. Owner visual retest of walking remains required.
Pitch correction and FPP report — 2026-09-19: owner reports elbow distortion when pitching during pistol ADS in FPP; rifle has not been tested. Owner A/B retest: disabling Sight Alignment removes the visible problem; the owner restored Enabled afterward, confirmed true through MCP. This isolates the enabled sight-alignment path as the trigger in this tested case, but does not identify the exact failing transform or IK operation. Affected arm remains unspecified; rifle remains untested. Inspection confirms both IK nodes already disable twist and use incoming lowerarm poles; these flags have not been changed again. The right-hand sight solve runs after AO for the enabled standing/right/supported ADS scope, followed by recoil and left support IK. Owner subsequently confirmed: use the new AO as the main aiming pose, refactor Sight Alignment, and retain sights aligned with the camera. The replacement solver and remaining visual acceptance are documented below. Pitch correction validation: closed-Editor LambeerEditor Win64 Development build succeeded; SightAlignment and RetargetedFirearmAim automation passed 2/2 without warnings/errors after reopening the exact project. The mapping regression covers neutral, half pitch, both authored endpoints, clamping and wrapped remote pitch. The A/B result is owner-reported; no permanent elbow fix or broader PIE acceptance is claimed.
AO-based sight alignment refactor — owner confirmed 2026-09-19
Implemented, owner visual acceptance pending: AO remains the base pose at the corrected -80/+80 pitch endpoints. Sight Alignment still targets the view using the existing SightReference, grip, eye distance, blend and sway settings. L1810 remains enabled in its existing standing/right/supported ADS scope. No opt-in, new socket calibration or wider ADS scope is inferred for rifle, other weapons, stances or dual hands.
The former right Two Bone IK and separate hand rotation replacement are replaced with one FAnimNode_LambeerArmAlignment. The left support Two Bone IK uses the same new arm solver. Each node reads shoulder/elbow/hand transforms from the incoming evaluated AO pose, transports that pose's bend plane from its original reach direction to the target reach direction, then solves the two segment lengths. Bone rotations follow the old/new limb frames, preserving authored twist without a fixed local-X swing/twist decomposition. This avoids projecting a stationary elbow pole onto a moving target direction, which can reverse its bend at a pole crossing. The synthetic crossing regression establishes that mechanism; it does not claim to have reproduced the exact owner's visual failure.
The right node corrects hand position and orientation under one SightAlignmentAlpha. Recoil follows, then the left node resolves the authored SupportHandIKBoneLocation against the current hand_r and retains the incoming wrist rotation relative to the forearm. Existing alpha gates, AO layers, foot IK, finger poses and action selection remain. No prior-frame solved skeleton is fed back as an elbow pole, and no limb stretching is enabled. Unreachable targets clamp to limb reach; exact sight placement still depends on reachable per-item calibration. Straight chains use the incoming upper-arm orientation as their bend fallback; opposite reach uses the incoming plane.
Runtime code resides in Lambeer; the AnimGraph wrapper resides in the uncooked-only LambeerAnimationEditor module. ABP_Player contains two new arm nodes; the old two IK nodes and separate right wrist ModifyBone were removed through MCP. The L1810 item settings were read back unchanged. This changes transient presentation only, without a persistence schema change.
Validation: closed-Editor LambeerEditor Win64 Development build passed. MCP compiled ABP_Player with warnings treated as errors, saved and verified a clean asset, both node configurations and all target/alpha/pose links. Seven focused automation tests passed with zero warnings/errors: ArmAlignment, SightAlignment, AimViewContract, GripContract, HeldAnimation, ProceduralRecoil and RetargetedFirearmAim. The new solver test sweeps 161 reach directions in two coordinate frames and checks continuous bend, unchanged segment lengths, target position/rotation, identity correction, authored local offsets, support wrist orientation, unreachable targets and degenerate reach. Evidence: Saved/P09SightRefactor/. No agent PIE or owner visual pass is claimed.
Owner retest: keep Sight Alignment enabled, hold standing supported L1810 and compare FPP ADS at neutral and through -80/+80 pitch. Check both elbows, wrist/contact and sight line, toggle ADS and switch views, walk/stop, then test recoil. Compare with TPP and supported crouch/prone; repeat owner/observer on Dedicated Server. Rifle and broader content still require their own owner validation.
FPP eye pivot / high-pitch sight ceiling — 2026-09-19
Owner report: after the arm refactor, raising aim past head height makes the pistol retreat behind the head instead of continuing upward. The supplied side-view screenshot shows the arms raised/folded around the head. This corrects any interpretation that the earlier arm refactor had established a visual fix.
Confirmed code defect: GetFirstPersonSocketOffset added BaseEyeHeight to SpringArm SocketOffset. UE rotates SocketOffset with view pitch around the capsule origin. MCP reads BP_PlayerCharacter BaseEyeHeight=80cm, FirstPersonSocketOffset=(4,0,0), boom location/TargetOffset=(0,0,0), and L1810 EyeDistanceCm=45. Ignoring existing small sway, the former sight target was Rotation(Pitch)*(49,0,80); its height peaks around31.5 degrees and then falls as its forward coordinate becomes negative. This reproduces the reported ceiling/backward-target behavior mathematically; it is independent of elbow pole choice or a physical ceiling collision.
Correction: retain authored offsets and tuning, but split first-person vertical eye/stance/root-compensation height into the upright SpringArm TargetOffset. Only the remaining boom offset rotates with view pitch. Interpolate the canonical socket offset and eye-pivot height separately, restore the original TargetOffset for TPP, and apply the same split when snapping the camera after appearance/possession. The local sight solve consumes the actual corrected boom fields; remote proxies reconstruct the same settled FPP geometry and include the camera-relative transform. The corrected neutral sight target is (0,0,80)+Rotation(Pitch)*(49,0,0), so it keeps rising and stays forward through -80/+80. No eye distance, camera height, animation endpoint, socket/grip or arm-length tuning was changed.
Validation: closed-Editor LambeerEditor Win64 Development build succeeded. AimViewContract, ArmAlignment, SightAlignment and Frontend.Rules passed4/4 without warnings/errors. MCP compiled BP_PlayerCharacter and ABP_Player with warnings treated as errors. Regression covers the former behind-head target,161 pitch values for three representative heights, unchanged sight distance/view direction, upright eye pivot, FPP snap and TPP restoration. Owner PIE must still verify the rendered pistol, camera position, extreme pitch/reach, crouch/prone transitions and remote observers. The earlier AO arm refactor remains in place; this corrects the upstream target trajectory.
Owner follow-up — 2026-09-19: eye-pivot change improves the motion, but Sight Alignment is correct only over part of downward aim; at other pitches the owner reports the barrel is not parallel to the view in FPP ADS. This is partial improvement, not final acceptance. No further sight/arm tuning has been made from that description. Added Lambeer.SightDump to compare rendered versus solve camera, requested/actual hand orientation, sight-axis error, reach and alignment/recoil state at a good, neutral and bad pitch. Exact item/context and measured fault location await runtime evidence; arm reach alone is not established as the cause of the reported angular error.
Owner runtime samples: sight target exceeds arm reach — 2026-09-19
Owner ran Lambeer.SightDump at three stationary pitches. All samples identify Item.Weapon.L1810, FPP=1, AimMode=ADS, alignment alpha1 and no recoil. Hand angular error and solve-versus-rendered view position/rotation errors are zero. Actual and requested sight-axis errors match (0.171–0.235 degrees), consistent with the existing authored sway. The visual report remains valid, but these samples locate the measured discrepancy in position/reach rather than failure to apply the requested hand rotation.
| Pitch | Required / maximum reach (cm) | Hand position error (cm) | Sight in view X/Y/Z (cm) |
|---|---|---|---|
| -28.00 degrees | 52.243 / 57.694 | 0.000 | 45.00 / -0.14 / 0.15 |
| -0.35 degrees | 65.465 / 57.694 | 7.771 | 37.39 / 1.63 / -0.56 |
| +41.30 degrees | 74.666 / 57.694 | 16.972 | 28.56 / 3.24 / 3.07 |
Evidence: Saved/P09SightReach/owner-samples.json. Requested45cm sight depth is reachable in the downward sample, but the neutral/upward targets exceed the right arm length. Radial clamping toward the shoulder moves the sight off the camera line. Owner approved adaptive sight depth. The right arm node now keeps the requested orientation and moves the hand target only along the camera sight line to its nearest reachable point. EyeDistanceCm remains the preferred depth (45cm on L1810) when reachable. Native ADSSightPullback supplies the permitted component-space segment down to the existing1cm minimum eye-distance contract, including current camera geometry and sway. The node intersects this segment with the current AO arm reach shell; it does not use previous-frame bones or extend arm lengths. Only the right sight node opts in; support-hand fitting is unchanged. If no point is reachable, the node retains its incoming AO pose and reports failure in debug output instead of applying the old off-axis radial clamp. The diagnostic command recomputes the reach result from the settled pose; SightLineReachable, PullbackCm and EffectiveHandErrorCm distinguish preferred from fitted targets. HandErrorCm continues to measure error against the preferred target and is expected to be nonzero during pullback. Presentation remains transient; no persistence schema changes.
Adaptive depth validation — 2026-09-19: closed-Editor LambeerEditor Win64 Development succeeded; ArmAlignment, SightAlignment and AimViewContract passed3/3 without warnings/errors. The added regression sweeps161 rotations with reachable/unreachable preferred depths, checking nearest boundary, permitted depth segment, no lateral/vertical deviation from the sight line, preserved orientation and arm solve reach. It also checks missing intersection, minimum-depth limits and the inner reach boundary. MCP compiled ABP_Player with warnings treated as errors, saved and verified clean, right-only adaptive setting and ADSSightPullback wiring. Evidence: Saved/P09SightReach/adaptive-tests.json and adaptive-wiring.json. Owner retest should repeat good/neutral/bad pitch using SightDump; look for SightLineReachable=1, EffectiveHandErrorCm near zero when settled without recoil, and view-space lateral/vertical offsets limited to existing sway. No owner visual pass is claimed.
Owner-only PIE acceptance remains open: test pistol and rifle in both views at neutral/up/down pitch; walk all eight directions, stop/start, and release aim; compare Standing with Crouched/Prone lower-body preservation; check supported/unsupported/dual and mixed-pair hand policy, grip/wrist/elbow contact and recoil. Repeat on both bodies and Dedicated Server with two client Preview windows (owner and observer). Agent checks do not prove visual/network acceptance; P09 remains Partial.
Shared aim pose and arm evaluation — 2026-09-18
Superseded firearm content/elevation: see the 2026-09-19 retargeted firearm Aim Offset contract. The migration and earlier validation below remain historical evidence; eligibility rules still apply. The arm solver below is superseded by the AO-based sight refactor.
Owner confirmed: ADS and ShoulderAim use one animation. Pose Context no longer contains AimMode; Action contains Carry, Aim and Attack only. Firearm recoil uses NotifyWeaponShot and procedural arm rotation. AimMode still controls camera, movement and gameplay rules.
Six profiles contain 141 unique entries: Pistol 39, Unarmed 3, Melee1H 57, Melee2H 12, Firearm2H 12 and TwoHandedItem 18. Seven identical camera-mode duplicates were merged from the inspected 148 rows. The owner's standing supported pistol clip is A_Aim_Pistol_Supported_Stand. No conflicting clips were chosen, no animation assets were deleted, and all 16 inspected item override arrays were empty. The older 294/147-entry counts below are migration history. Snapshot: Saved/P09AimSimplify/profiles-before.json.
bHasSupportHandTarget replaces bAllowFreeLeftHandSupport. A one-handed firearm uses its authored support target only while aiming and with the left inventory hand empty. Two-handed support remains available during Carry. Inventory handedness is unchanged. Without an enabled target, the selected Pose owns the left arm: UpperBody includes it; RightArm deliberately does not. Supported and unsupported rows remain separate, so edit the row matching the actual support state.
Evaluation order: locomotion → Foot IK → selected UpperBody/right/left overlays → DefaultSlot → right fingers → component space → right/left clavicle aim-pitch rotations → ADS hand IK and wrist orientation → procedural recoil → left support IK → local space → left fingers. Foot IK runs before held overlays so its output cannot replace the chosen upper-body pose. Empty left inventory hands do not receive a separate Carry overlay.
Both views apply camera elevation to the selected aiming arm chains before IK. Pitch is normalized and converted from actor axes into mesh component axes; a mixed-pair left Carry arm is not pitched. This is cosmetic pitch following, not third-person ballistic convergence or scope alignment. ADS sight alignment retains the existing enabled standing/right/supported L1810 scope and authored sight/grip/eye-distance/sway settings.
The former left pole was component origin (0,0,0); the former right pole was a camera-rotated offset derived outside pose evaluation. Both Two Bone IK nodes now use their incoming lowerarm bone as the pole in the current evaluated, pitched pose. The old right elbow offset field and previous-frame socket read are removed. Stretching remains disabled. Both nodes keep bMaintainEffectorRelRot so the wrist retains its incoming local rotation relative to the forearm. MCP 2026-09-19: both nodes now have bAllowTwist=false (TwistAxis remains local X). Engine Two Bone IK otherwise lets FindBetweenNormals roll the forearm at high/low pitch; with twist allowed, Maintain Effector Rel Rot carries that roll into the hand. Disabling twist restores the incoming pose twist after the reach solve. This does not consume SupportHandTarget Rotation. Visual acceptance still requires owner PIE.
Owner report — 2026-09-18: both elbows rotate erratically at high/low pitch in first-person ADS, standing with L1810 and an empty left hand. Third-person gun pitch following was absent and is included in the 2026-09-18 arm-pitch change.
Owner report — 2026-09-19: holding L1810, aiming, left support IK reaches the gun, but looking up/down spins the left hand. That matches the previous AllowTwist path on hand_l. The same flag is now off on the right ADS IK as well because the 2026-09-18 elbow report covered both arms. No owner retest of this change is claimed.
Validation: 2026-09-18 closed-Editor LambeerEditor Win64 Development passed; focused AimViewContract, GripContract, HeldAnimation, ProceduralRecoil and SightAlignment tests passed 5/5 with no warnings/errors. MCP compiled ABP_Player with warnings treated as errors, saved and verified the arm-pitch links, Foot IK order, bone-space elbow poles and clean assets. All six profiles exactly match the preserved content after removing duplicate camera keys; no AimMode/Fire or removed support flag remains in the reflected schema. 2026-09-19 is ABP-only: MCP compiled ABP_Player with warnings treated as errors, saved it (is_dirty false) and read back both Two Bone IK nodes with bAllowTwist=false, bMaintainEffectorRelRot=true, left effector still SupportHandIKBoneLocation in hand_r bone space, right effector still ADSHandLocation in component space. No native rebuild. Owner-only PIE: compare both views at neutral/high/low pitch, aim on/off, support target on/off with the matching UpperBody entry, occupied left hand, recoil, and Dedicated Server owner/observer. Verify the supporting left wrist no longer spins while looking up/down, plus wrist contact, elbow bend and no idle left-arm override. Presentation state is transient; no persistence schema or durable save fields change.
P09 Aim refactor — 2026-09-17
Owner-approved behavior: canonical RMB / weapon / camera matrix. Empty hands explicitly use the same toggle as melee. Switching view retains aim. The weapon follows camera direction without independent free look; first-person visual motion may depart from screen center without locking the camera behind the sight.
Native foundation: remove tap/hold timing and release-driven transitions. IA_Aim Started toggles requested aim. A camera-view change resubmits the same toggle with the new first-person flag. Server derives ShoulderAim/ADS from held-item family, re-evaluates equipment changes and refuses Item-family primary hands. An eligible firearm/melee equipment change reselects mode; an ineligible change clears the toggle. The existing local eye-height camera and FOV interpolation remain. Free-left firearm support eligibility now includes ADS, still requiring an authored target and an empty left hand.
Authoring update — 2026-09-17: the owner supplied SightReference on SKM_L1810 and bound A_ADS_Pistol_Supported_Stand to the shared Pistol profile (Aim, ADS, Standing, RightOnly, Right Firearm/Left Empty, supported). The owner confirmed +X toward the muzzle and +Z up and authorized per-gun experimental alignment/sway settings for this case. Current alignment implementation and calibration. This was the initial 2026-09-17 binding. The 2026-09-18 shared Aim contract supersedes the separate camera-mode key; other stance/topology/support distinctions remain exact.
Movement: the existing ShoulderAim Walk cap remains. This refactor does not introduce a new ADS gait/speed contract. Aim/view are transient and add no save schema fields.
Validation status: closed-Editor LambeerEditor Win64 Development succeeded; headless Lambeer.HUD passed 27/27 with no failed or warning tests (Saved/Automation/P09AimRefactor). Reopened Unreal MCP confirmed PIE=false, IA_Aim=RMB and IA_ToggleCameraView=V with no triggers/modifiers on those mappings; BP_PlayerCharacter references both actions. BP_PlayerCharacter and ABP_Player compiled with warnings treated as errors and were saved. Canonical HTML validation passed 45/45. These checks do not establish owner PIE or network/visual acceptance. Owner-only PIE must verify one press on/next press off, held/released RMB has no extra transition, first/third view changes preserve aim, firearm/melee/unarmed and mixed-hand precedence, ineligible equipment cancellation, throw cancellation, and Dedicated Server + two clients. ADS visual acceptance waits for authored assets.
22. P09 held-item animation design — 2026-09-16
2026-09-17 owner decision: all character recoil will be procedural. Animation Entry now contains Context, Mode, Layer, Pose and Montage only. Historical Fire/_Add sequence names below record 15 deleted placeholders. The arm-recoil event/debug path is implemented with per-item trial settings; owner visual/network acceptance remains open.
Partial implementation; content and owner acceptance pending. The owner requests the case matrix below, Layered Blend Per Bone, item-specific animation overrides, per-item finger Pose Assets, a folder/file plan and an Editor grip-calibration guide, and authorized feasible native/AnimBP implementation. The implementation record below separates completed foundations from proposed action/recoil work. No clips were retargeted, offsets measured, Pose Assets created or PIE started by the agent.
Existing evidence: source contains RightGripRelative, optional left/support transforms and body grip overrides on ULambeerItemDefinition. Live MCP pin inspection on 2026-09-16 confirms the existing Two Bone IK node receives SupportHandIKAlpha/SupportHandIKLocation and reaches Output through Component To Local. The inherited support query was two-handed-only and position-only. The original change enabled ShoulderAim support; the later P09 aim refactor also enables ADS support when the left hand is empty and an authored target exists. It remains position-only. Changing pistol handedness to TwoHanded would incorrectly remove dual-wield eligibility.
Owner movement rule — 2026-09-17: Carry uses the same upper-body pose across Walk/Jog/Run. Gait is removed from held-animation Context, lookup and overrides; lower-body locomotion retains its gait state. ShoulderAim returns Walk before sprint/input gait selection on authority and the owning client, preserving existing crouch/prone speed limits. Toggling aim off restores normal gait selection from current input. The ADS gait policy is unchanged. Build and owner PIE acceptance are tracked in the roadmap.
Validation after schema cleanup — 2026-09-17: closed-Editor native build succeeded; reopened MCP confirms no Gait/Recoil entry fields. Pistol now contains owner-authored standing supported AimIdle (UpperBody) and AimMove (RightArm) rows; the earlier empty-profile diagnosis below is historical. HUD regression passed 26/26 without errors/warnings, including held-animation resolution. The new ShoulderAim movement rule still requires owner PIE verification, including dedicated clients; these tests do not establish visual/network acceptance. Procedural recoil runtime remains pending.
Profile population — 2026-09-17
Owner requested all DA_AnimProfile assets be populated. Initial population saved 282 continuous-pose entries: Unarmed 6, Melee1H 120, Melee2H 24, Pistol 84, Firearm2H 24, TwoHandedItem 24. Preserved the two owner-authored Pistol standing supported Aim rows (both now UpperBody). Covers Standing/Crouched/Prone, idle/move, right-only and dual-item Carry, one/two-handed weapon ShoulderAim, free/busy support cases and mixed firearm/melee left-hand Carry while the right aims. Carry is shared across gaits. Item-family Carry aliases support the current non-melee tool definitions without changing gameplay eligibility.
All 42 referenced sequences were inspected through MCP: same MetaHuman base skeleton, AAT_None. Exact property readback matched all 282 entries; context keys are unique and all six assets saved cleanly. This validates authoring, not visual PIE acceptance; approved placeholder clips still need replacement. Empty-hand Carry stays on existing locomotion. No ADS mapping, left-only pose mirroring, Attack montage or Fire clip was invented. Attack montage/action playback and procedural recoil remain pending.
Owner decision resolved: Item-family tools must remain out of aim modes; right-hand precedence is confirmed and the native gate is implemented; Attack Montage preparation is explicitly deferred by the owner; this pass is Carry/ShoulderAim only. No Item-family aim entries are needed under the confirmed aim prohibition. Added 12 Melee-family ShoulderAim rows to TwoHandedItem for Sledgehammer (both support states and all three stances), making 36 entries in that profile and 294 across six profiles. Attack remains deferred. Left-only clips and cross-family left-only aiming beyond the approved matrix require authored content/decisions. Existing item AnimationProfile bindings and gameplay classifications are unchanged.
Family versus profile — inspected 2026-09-17
Family is computed by LambeerHeldAnimation::GetFamily, independently of ItemType and AnimationProfile: no definition means Empty; IsFirearm() means Firearm; otherwise bMeleeWeapon means Melee; otherwise Item. IsFirearm requires a non-magazine definition with a positive effective magazine capacity (including the existing L1810 ID fallback). Handedness controls one/two-hand topology, not Family. A profile is a pose lookup library, not a gameplay classification or permission to attack.
| Item asset suffix | Computed Family | Handedness | AnimationProfile suffix |
|---|---|---|---|
| L1810 | Firearm | OneHanded | Pistol |
| DoubleBarrelShotgun | Firearm (MagazineCapacity=2) | TwoHanded | Firearm2H |
| Crowbar / FireAxe / WoodenMallet | Melee (bMeleeWeapon=true; owner-authorized update) | OneHanded | Melee1H |
| Sledgehammer | Melee (bMeleeWeapon=true) | TwoHanded | TwoHandedItem |
| Pistol | Item (capacity=0, bMeleeWeapon=false) | OneHanded | None |
| Scissor / Wood / L1810_Magazine / Ammo_9x19 / Bandage / Pants / Glasses / BodyArmor / BackpackImprovised | Item | OneHanded | None |
These are readbacks of the 16 assets in /Game/Lambeer/Core/Data/Items, not newly approved classifications. MCP assigned DA_Item_DoubleBarrelShotgun to Firearm2H on 2026-09-17; support-hand target remains unauthored. In particular, generic DA_Item_Pistol is distinct from DA_Item_L1810. Do not change flags merely to make profile names match. Owner selected Carry/ShoulderAim only for this pass; Attack Montage preparation is deferred. Owner decision: Item-family tools cannot enter aim modes. Crowbar, FireAxe and WoodenMallet were updated through MCP to bMeleeWeapon=true and saved/read back; Sledgehammer was already true. Owner confirmed right-hand precedence: right Firearm/Melee may aim with Item on the left; right Item blocks aim regardless of the left; left-only Item blocks aim; empty hands retain unarmed aim. Native entry checks cover ShoulderAim and ADS, the server validates requests, and held-item changes cancel disallowed aim and clear latched input. Left Item keeps Carry presentation while the right weapon aims. Closed-Editor native build and HUD regression 26/26 passed. Reopened MCP verified saved flags and all 36 TwoHandedItem entries. Owner must verify Item RMB hold/tap refusal, weapon-right + Item-left aim, Item-right + weapon-left refusal, swapping to Item while aiming, and dedicated-client presentation. No agent PIE was performed.
Implemented foundation — 2026-09-16
ULambeerHeldAnimationProfileand itemAnimationProfile/AnimationOverrides: exact-context resolution with body-specific precedence and Replace/Disable/Inherit. Context includes Action, Topology, RightFamily, LeftFamily, Stance, Hand, support eligibility and PresetId. Each stance is authored explicitly; upper-body movement poses are shared across gaits.ULambeerAnimInstanceresolves continuous carry/aim sequences into UpperBody/RightArm/LeftArm outputs. Missing, additive or wrong-skeleton continuous poses have zero layer weight and setbMissingHeldPose. Empty-handed relaxed locomotion is unchanged. Asset references are hard references loaded with the item/profile, not per-frame loads.ABP_Playerevaluates Foot IK before held overlays, then arm pitch, ADS IK, recoil and support IK. See the current evaluation order. BranchFilter roots remain spine_01, clavicle_r/l and hand_r/l; UpperBody includes both arms.- One-handed firearm support uses
bHasSupportHandTarget; it requires ShoulderAim or ADS and an empty left inventory hand. It does not change item handedness. L1810 is opted in; no support target or numeric grip was fabricated. - Six profile assets exist under Profiles/. Crowbar, FireAxe and WoodenMallet reference Melee1H; Sledgehammer references TwoHandedItem; L1810 references Pistol; ABP unarmed defaults reference Unarmed. Melee2H and Firearm2H are available for future item assignment. Their current entries are documented in the shared Aim contract; owner visual acceptance remains pending.
- Live catalog readback: Crowbar/FireAxe/WoodenMallet have
bMeleeWeapon=false, so their current context family is Item despite the profile's intended future family name. Sledgehammer hasbMeleeWeapon=truebut remains hold/throw-only in the approved slice. No catalog flag or gameplay attack permission was changed. Author entries against actual context and resolve future melee classification in its owning workstream. ResolveHeldActionexposes exact-context attack content lookup and rejects left aim/actions for mixed firearm/melee pairs. Dual one-handed melee uses the right-side lookup key for one paired action and rejects a second independent left attack; this is a resolver convention, not an input binding. It does not play montages, implement dual-fire inputs, fire shots, apply damage or drive recoil. The Montage field and the independent-hand context are preparation for those consumers. The separate NotifyWeaponShot event and procedural arm recoil now include current-pose support-target evaluation. P10 shot integration, action slots and authored wrist orientation remain pending. Pistol/rifle Aim Offset integration is delivered in the current retargeted aim contract.- Validation: closed-Editor
LambeerEditor Win64 Developmentbuild passed;Lambeer.HUDheadless automation passed 26/26, no failed/warning tests; MCP compiled ABP with warnings-as-errors, saved it and read back the branch filters. No owner PIE acceptance.
Author a first profile entry after selecting a clip
Open a profile Data Asset → Entries → add a row → expand Context. For a supported standing L1810 ShoulderAim idle: Action=Aim, Topology=RightOnly, RightFamily=Firearm, LeftFamily=Empty, Stance=Standing, Hand=Right, bSupportHand=true, PresetId=None for shared body content. Set Mode=Replace, Layer=UpperBody, Pose=the owner-selected non-additive sequence retargeted to the actual player skeleton. This row matches only after a support target is authored; author a separate bSupportHand=false entry for unsupported carry/aim. This is an authoring example, not a selected clip or fabricated asset.
For dual melee Attack, author Topology=DualItems, RightFamily=Melee, LeftFamily=Melee, Hand=Right and the dedicated paired montage; use UpperBody or FullBody according to the approved clip/action. There is no separate left attack row for that paired action. Aim is shared between idle and movement. Add only the required distinct Crouched/Prone contexts using reviewed clips; no idle/move or per-gait rows are needed. Duplicate a row only when its clip is actually valid in that context. Per-item exceptions use AnimationOverrides with the same exact key; PresetId-specific rows win over shared rows. Leave Montage unset until the selected actions exist. Exact matching is deliberate; empty Entries explain why newly assigned profiles currently show the old locomotion.
Owner report and current data — 2026-09-17
Owner-reported: holding RMB now makes the left hand move into support, but the aim animation appears not to play. This is evidence of a visible support response in the owner's tested case, not full grip, aim-pose, all-stance or multiplayer acceptance. The item, body and network configuration of that test were not explicitly identified.
MCP read-only inspection: PIE was not running. DA_Item_L1810 has bAllowFreeLeftHandSupport=true and bHasSupportHandTarget=true with a non-identity authored target. Its AnimationProfile points to DA_AnimProfile_Pistol, whose Entries=[]; the item also has AnimationOverrides=[]. Numeric calibration remains canonical in the item Data Asset. Earlier statements that L1810's support target was unauthored describe the previous snapshot and are superseded by this observation; the agent did not alter the owner's target.
Diagnosis for this inspected L1810 configuration: support eligibility/target calculation and pose lookup are separate. An empty profile supplies no AimIdle/AimMove entry; the native resolver keeps pose weights at zero on a missing entry. A visible left-hand IK response therefore does not prove that the ShoulderAim pose is playing. No live runtime reproduction is claimed.
Next integration work: bind the reviewed supported/unsupported/dual ShoulderAim sequences to explicit profile contexts, followed by Carry contexts; cover stance, aim mode, topology, both hand families, selected hand, support eligibility and body override. Do not accidentally map ShoulderAim to ADS. Read back the profile and verify selected pose, missing-pose flag and layer alpha on the owner's next test. File existence (69/69) is not profile integration. Attack montage/slot playback and independent firearm-hand versus paired-melee arbitration remain separate work.
Procedural arm recoil implemented; owner acceptance pending: event/debug API, per-item trial settings and current-pose support IK. The 15 unused non-additive Fire placeholders were deleted. Camera feedback, slide/bolt and P10 firing remain separate. Closed-Editor native build passed, HUD automation passed 28/28 without test warnings/errors, and the rewired ABP_Player compiled warnings-as-errors and saved clean. Visual/contact and multiplayer acceptance require owner PIE.
22.1 Separate equipment, pose and action
- Equipment facts: right item, left item, per-item authored handedness, firearm/melee presentation family, body PresetId. Preserve the inventory contract: one item using both hands differs from two independent one-handed items.
- Presentation context: stance, existing aim mode, carry topology, support-hand availability and action hand. Derive this context from gameplay state; do not maintain an unrelated AnimBP equipment state.
- Continuous pose: Carry/Ready/Aim. Owner correction: all eight requested aiming cases mean ShoulderAim, not ADS. The existing ADS mode remains distinct.
- Transient action: Attack or Fire, with explicit Right/Left/Both participation and a resolved compatible animation entry. A two-handed swing is one action, not two independent hand montages.
- Mixed pair: pistol + melee is already allowed by FEAT-COMBAT. Resolve each hand's family independently; do not force the pair into a pistol-only or melee-only enum. Owner confirmed independent right/left firearm firing. With a firearm/melee mixed pair, only the right hand participates in aiming and combat actions; the left keeps holding its item. Dual one-handed melee uses its own authored paired animation. Exact button bindings remain open.
22.2 Required presentation matrix
These are logical entries, not a claim that every row needs a new exported clip. Shared compatible clips may be referenced by multiple entries. All new names below are proposed. Standing, Crouched, Prone and Sprint/Run coverage is required by the owner. The owner will supply animation files later. Exact source animations, additive reference frames and final clip count await that content; the delivery table below fixes destinations and names now.
| Group | Requested case / proposed key | Layer and support behavior |
|---|---|---|
| Idle / Move | Empty hands · Unarmed | Existing locomotion; no carry override. |
| Idle / Move | Right item only · RightOnly | Right-arm carry; preserve free left-arm locomotion where the authored pose permits. |
| Idle / Move | One item per hand · DualItems | Independent arm carries, or an explicitly authored pair pose; no hand may support the other item. |
| Idle / Move | One item using both hands · TwoHandedItem | Coherent upper-body carry; right attachment and authored left support target. |
| ShoulderAim | Empty hands · Unarmed | ShoulderAim ready/guard entry. |
| ShoulderAim | Right one-handed melee · Melee1H_Right | Ready pose; no automatic support-hand IK. |
| ShoulderAim | Two one-handed melee items · Melee1H_Dual | Two weapon hands; pair/arm composition with explicit torso ownership. |
| ShoulderAim | One two-handed melee item · Melee2H | Coherent upper-body pose; support IK only when the clip requires contact. |
| ShoulderAim | Right pistol, left free · Pistol_Supported | Left IK to an authored pistol support target; native eligibility extension implemented, target/pose still required. |
| ShoulderAim | Right pistol, left occupied · Pistol_Unsupported | Right aiming pose; left keeps its own carry/action; left support IK disabled. |
| ShoulderAim | One pistol per hand · Pistol_Dual | Independent weapon hands; no cross-weapon support IK. Independent right/left firing; ADS sight policy is outside this ShoulderAim request. |
| ShoulderAim | Two-handed firearm · Firearm2H | Coherent aiming pose plus left support IK. |
| Attack | Unarmed · Unarmed | Attack montage entry; limb participation and movement policy depend on the selected clip. |
| Attack | Right melee · Melee1H_Right | Right action, optional authored torso contribution. |
| Attack | Dual one-handed melee · Melee1H_Dual | Owner-confirmed dedicated paired animation; one action controls both weapons/arms, not independent left/right attack clips. |
| Attack | Two-handed melee · Melee2H | Both arms and torso act together; support release curve for intentional hand release. |
| Fire | Supported right pistol · Pistol_Supported | Character recoil + weapon mechanism event; support solve follows recoil. |
| Fire | Right pistol, left busy · Pistol_Unsupported | Right recoil only unless an authored torso layer is selected; preserve occupied left-hand pose. |
| Fire | Dual pistols · Pistol_Dual | Separate right/left procedural recoil channels; a shot identifies which weapon fired. |
| Fire | Two-handed firearm · Firearm2H | Coherent upper-body recoil plus support IK. |
Coverage edge: existing inventory permits left-only items and mixed pairs. Keep these contexts representable in the resolver; left-only visual behavior and asset selection need owner confirmation rather than mirroring right-hand clips. Two-handed support in Carry versus Aim is an explicit profile choice; the current runtime enables it whenever a valid two-handed support target exists.
22.3 Layered Blend Per Bone composition
- Cache the existing locomotion result as proposed
CP_Locomotion. Keep the standing/crouch/prone, start/stop/turn, jump and inertialization graphs intact. - Build the selected carry/aim pose separately. Feed the cached locomotion to Base Pose of Layered Blend Per Bone; feed the selected upper-body or arm pose to a Blend Pose input. Drive weight from resolved presentation state. Weight zero must reproduce the existing unarmed locomotion.
- Proposed skeleton blend masks:
BM_Player_UpperBody,BM_Player_RightArm,BM_Player_LeftArm. These are entries on the shared Skeleton, not standalone files. Exact spine/clavicle boundaries and weights require inspecting the target skeleton and selected animations; do not copy a mannequin bone name blindly. Keep locomotion root, pelvis and legs outside carry overlays. - Use UpperBody for a coherent two-handed pose; use arm masks for independent held items. Torso contribution needs one resolved owner, not two competing full-torso arm layers. Layer order matters on overlapping bones. Mesh Space Rotation Blend is a separate blending option, not a substitute for an additive clip or a support solve.
- Route action slots through the appropriate mask. Proposed independent groups/slots:
RightAction.RightUpperBody,LeftAction.LeftUpperBody,BothAction.UpperBody,FullBodyAction.FullBody. Separate groups permit independent montages, but gameplay arbitration must explicitly stop/suppress arm actions when a both-arm/full-body action owns those bones. - Use full-body override only for an authored action that needs lower-body motion. Movement locking, root-motion policy, attack cancellation and stance restrictions remain P10 decisions; do not flatten every melee swing into an upper-body mask and claim the feet will be correct.
- Apply procedural character recoil after the ready/aim pose and before the final support solve. Preserve the existing Control Rig behavior. Verify which bones it writes before choosing the final insertion point; no lower-body correction may overwrite the finished hand pose.
- Apply the occupying right item’s
RightHandPosewith Layered Blend Per Bone rooted athand_rafter Control Rig and before support IK, so fingers curl around the attached mesh while recoil still moves the gun. - For support contact during fast movement/recoil, prefer a target evaluated from the current right-hand pose plus the calibrated item-relative transform in the same evaluation. The procedural-recoil change now uses a hand_r-space target derived from the authored attachment/support transforms; the AnimGraph evaluates it after recoil. Match the runtime scale contract and verify contact in owner PIE.
- Apply the left finger Pose Asset after Two Bone IK, rooted at
hand_l: the independent left item’sLeftHandPose, or the right item’sLeftHandPosewhile support IK is active. IK sets the wrist; the Pose Asset curls fingers. Canonical contract:TBD-INVENTORY-GRIP-003.
Curves need explicit ownership through these blends: final SupportHandIK must come from the active contact/release action rather than be accidentally overwritten by a carry layer. Missing curve currently means full weight; gameplay eligibility must multiply that weight so occupied hands never receive support IK. The current effector remains position-only. Both Two Bone IK nodes now disable chain twist so looking up/down keeps the incoming forearm/wrist twist from the posed chain. Using SupportHandTarget Rotation to aim the wrist is still a separate validated design.
Pistol/rifle pitch correction now uses the bound Aim Offset; other families still have optional unprovided Aim Offset content. No yaw samples were supplied. Epic requires its samples to use Mesh Space additive animation. A ready pose and an Aim Offset solve different problems; do not mark every Carry/Aim clip additive automatically. See Epic: layered animations, blend masks and Aim Offset.
22.4 Shared animation profiles and item overrides
Recommendation: keep measured grip transforms on existing DA_Item_*. Use an asset reference to the implemented ULambeerHeldAnimationProfile Data Asset for reusable animation content. This is an animation profile, not a second grip database. A special weapon can reference its own profile or replace a small number of entries on its item definition.
| Proposed field / responsibility | Contract |
|---|---|
AnimationProfile on item definition | Shared family profile; no per-weapon AnimBP subclass. |
RightHandPose / LeftHandPose on item definition | Per-item character Pose Assets for fingers. Independent of family profile clips. Null keeps family fingers. |
AnimationOverrides on item definition | Sparse entries keyed by action, stance, hand participation and support mode; ADS/ShoulderAim share the same key. Explicit Replace / Disable / Inherit semantics; null alone must not ambiguously mean both disable and fallback. |
| Profile entries | Carry idle/move poses; ready/aim poses; optional Aim Offset; attack montage references; mask/slot route; support-contact policy. Implemented core entries are Context, Mode, Layer, Pose, AimWalk, AimOffset and Montage. Recoil sequence references were removed by owner decision on 2026-09-17; all character recoil is procedural. Pistol/rifle Aim Offset and directional AimWalk are implemented; richer contact/slot policies remain proposed. |
| Body-specific animation variant | Optional PresetId-scoped entry only if the same compatible skeleton clip does not fit both bodies. Existing GripBodyOverrides changes grip placement, not animation selection. |
| Resolution precedence | Implemented: item + body exact entry → item shared exact entry → profile body exact entry → profile shared exact entry → no match. Broader compatible fallback is not implemented. Do not silently reuse Standing for Prone or supported pistol for a busy left hand. |
| Validation / loading | Validate skeleton, additive type/reference, mask, slot group, required curves and supported context. Use hard asset references loaded with the item/profile in this implementation; avoid asset loads inside per-frame animation evaluation. Streaming profiles can be introduced later with an explicit asynchronous readiness state. Missing action clips block visual acceptance; they do not silently authorize invisible damage. |
Owner correction: use one Carry action for idle and movement, and one Aim action for idle and movement. Movement speed does not select a separate held pose. This avoids duplicating all existing Walk/Jog/Run directional clips for each weapon. A shared profile is preferable to putting an entire animation matrix on every item; Linked Anim Layers can be added later if families need different graph logic, not just different assets.
22.5 What makes hands lift when firing?
A weapon's mechanism animation moves its slide/bolt/trigger on the weapon skeleton. It does not automatically animate the character's arms. The recommended presentation has three separate consumers of the same validated shot: weapon mechanism, character recoil, and local camera response. Camera recoil alone cannot make another client see the arms kick.
Owner decision (2026-09-17): all character recoil uses a procedural response over the current holding/aim pose; no recoil Animation Sequence is required. The attached gun follows the right hand; solve the supporting left hand afterward. Unsupported pistol recoil preserves the left item's pose. Dual-wield events identify the firing hand and use independent recoil channels; the owner selected separate right/left firing; exact buttons remain unassigned. The procedural implementation and its strengths/timings remain pending; values must be authored and approved, not invented here. Weapon bolt recoil and character recoil must not both translate the entire gun by the same kick.
Gameplay shot direction, spread, ammo consumption and damage remain server-authoritative P10 behavior. Cosmetic recoil does not drive hit truth. Do not replicate per-frame bone transforms or animation time; the future shot/action event needs enough identity and timing to reproduce remote presentation and prevent owner prediction being played twice. Grip/profile assets are content; transient pose, IK weight and recoil do not add durable save records. Existing persisted held item identity remains the input after restore.
22.6 Proposed folder and file structure
Filesystem root: Content/Lambeer/Characters/Player/Animation/. Unreal package root: /Game/Lambeer/Characters/Player/Animation/. Keep existing assets in place: C++ currently loads locomotion paths under this root. Profiles/ and the six named profile Data Assets now exist. Other new branches below remain a naming plan; no existing animation assets were moved.
Animation/
ABP_Player.uasset [existing: extend in place]
Idle/ Walk/ Jog/ Run/ Crouch/ Prone/ Spin/ [existing: retain]
UnarmedAttack/ [existing: retain; audit clips]
Profiles/ [created; Entries empty]
DA_AnimProfile_Unarmed
DA_AnimProfile_Melee1H
DA_AnimProfile_Melee2H
DA_AnimProfile_Pistol
DA_AnimProfile_Firearm2H
DA_AnimProfile_TwoHandedItem [non-attack carry profile]
Items/
DA_AnimProfile_Crowbar [only if behavior differs]
Carry/
RightOnly/ A_Carry_RightOnly_Idle
DualItems/ A_Carry_DualItems_Idle
TwoHanded/ A_Carry_TwoHanded_Idle
Aim/
Unarmed/ A_ShoulderAim_Unarmed_Ready
Melee1H/ A_ShoulderAim_Melee1H_Right, A_ShoulderAim_Melee1H_Dual
Melee2H/ A_ShoulderAim_Melee2H_Ready
Pistol/ A_ShoulderAim_Pistol_Supported, A_ShoulderAim_Pistol_Unsupported
A_ShoulderAim_Pistol_Dual
Firearm2H/ A_ShoulderAim_Firearm2H_Ready
Attack/
Unarmed/ AM_Attack_Unarmed_01 [may wrap audited existing clip]
Melee1H/ A_Attack_Melee1H_Right_01, AM_Attack_Melee1H_Right_01
A_Attack_Melee1H_Dual_01, AM_Attack_Melee1H_Dual_01
Melee2H/ A_Attack_Melee2H_01, AM_Attack_Melee2H_01
Overrides/
Items/Crowbar/ A_Attack_Crowbar_Right_01, AM_Attack_Crowbar_Right_01
Bodies/ [create only for an actual authored body variant]
Calibration/
BP_GripCalibration [proposed editor preview helper; not created]
HandPoses/ [plan; no assets created]
L1810/ PA_L1810_RH, PA_L1810_LH
Crowbar/ PA_Crowbar_RH, PA_Crowbar_LH
FireAxe/ PA_FireAxe_RH, PA_FireAxe_LH
Sledgehammer/ PA_Sledgehammer_RH, PA_Sledgehammer_LH
WoodenMallet/ PA_WoodenMallet_RH, PA_WoodenMallet_LH
Omitted extensions above are .uasset. Naming tokens: A_ Animation Sequence, AM_ Montage, BS_ Blend Space, AO_ Aim Offset, PA_ Pose Asset, DA_AnimProfile_ proposed animation profile. Optional Aim Offset example: AO_Pistol_Supported. Suffix _Add is a content promise requiring correct additive settings. _01 is a variant ID, not a promise of combo count. Add stance/body tokens only when a distinct authored asset exists; _Stand, _Crouch, _Prone, _Man, _Woman are proposed. ShoulderAim and ADS get distinct entries if the owner wants different poses, even if they reference one shared asset.
Weapon mechanism clips remain with the weapon's content, not in the player animation tree. DA_Item_* stays under /Game/Lambeer/Core/Data/Items/. Skeleton masks and montage slots are named skeleton entries and are not folders/files. Do not move the existing UnarmedAttack clips until their skeleton, montage and runtime references have been audited.
22.7 Owner decisions and implementation gates
- Confirmed: the requested aim matrix is ShoulderAim, not ADS. Separate right/left firearm firing; mixed firearm/melee pairs use the right hand only for aim/actions.
- Confirmed 2026-09-17: each held item needs its own finger Pose Asset. Family Carry/ShoulderAim stays shared.
TBD-INVENTORY-GRIP-003. - Open in FEAT-COMBAT: exact button bindings, interruption, block/parry and reload constraints. Dual one-handed melee now uses a dedicated paired animation; firearm hands remain independent. Mixed firearm/melee eligibility is already confirmed.
- Confirmed: all poses are required: Standing, Crouched, Prone and Sprint/Run. Existing locomotion transition rules remain unchanged; this does not introduce a simultaneous Prone+Sprint stance. Exact clips and left-only authored presentation still need owner content.
- Authorized: implement feasible native and AnimBP presentation work in this task. P10 damage, firing mechanics, attack timings and unresolved dual-melee inputs are not inferred from this animation request.
Remaining sequence: owner selects/retargets compatible clips and per-item hand Pose Assets → author profile entries, measured grips and Pose Asset bindings → add validated action/recoil playback and slot arbitration where gameplay events exist → compile/save through MCP → owner performs visual/network PIE. Keep ROAD-P09-03's historical grip-slice completion. ROAD-P09-05 ABP/native wiring landed 2026-09-17; authored poses remain ROAD-P09-04. P09 does not close while only its former grip slice is implemented.
Acceptance: unarmed baseline unchanged; feet continue idle/move under arm overlays; no unsupported stance fallback; supported pistol toggles IK when the left hand becomes occupied/free; per-hand recoil does not overwrite the other hand; special-item override affects only the intended entries; release curves restore contact cleanly; Man/Woman and local/remote representations agree. Owner-only PIE: Dedicated Server with two client Preview windows, plus the existing relevant locomotion regressions. Source/profile validation and Blueprint compile are not visual acceptance.
22.8 Clip delivery destinations and names — owner will supply clips later
Owner direction: animation files will be supplied later; provide exact destinations and names now. This table supersedes shorter illustrative names in the folder overview. It defines naming, not the existence or acceptance of any clip. All paths below are relative to /Game/Lambeer/Characters/Player/Animation/ (filesystem Content/Lambeer/Characters/Player/Animation/).
Replace {Stance} with exactly Stand, Crouch or Prone. These filename tokens map to native Standing, Crouched and Prone. Use no literal braces in asset names. Unreal creates the .uasset extension; the owner delivers/retargets Animation Sequences (A_). Montage wrappers (AM_) can be created after clip selection and action rules are ready; an FBX must not merely be renamed to .uasset.
| Folder | Asset naming pattern | Coverage |
|---|---|---|
Carry/RightOnly/ | A_Carry_RightOnly_{Stance}_Idle | Right item only |
Carry/DualItems/ | A_Carry_DualItems_{Stance}_Idle | One item in each hand |
Carry/TwoHanded/ | A_Carry_TwoHanded_{Stance}_Idle | One item held with both hands |
Aim/Unarmed/ | A_ShoulderAim_Unarmed_{Stance} | Empty hands |
Aim/Melee1H/ | A_ShoulderAim_Melee1H_Right_{Stance} | Right one-handed melee |
Aim/Melee1H/ | A_ShoulderAim_Melee1H_Dual_{Stance} | Two one-handed melee weapons |
Aim/Melee2H/ | A_ShoulderAim_Melee2H_{Stance} | Two-handed melee |
Aim/Pistol/ | A_ShoulderAim_Pistol_Supported_{Stance} | Right pistol; free left supports it |
Aim/Pistol/ | A_ShoulderAim_Pistol_Unsupported_{Stance} | Right pistol; left occupied |
Aim/Pistol/ | A_ShoulderAim_Pistol_Dual_{Stance} | One pistol per hand |
Aim/Firearm2H/ | A_ShoulderAim_Firearm2H_{Stance} | Two-handed firearm |
Attack/Unarmed/ | A_Attack_Unarmed_{Stance}_01 | Unarmed attack |
Attack/Melee1H/ | A_Attack_Melee1H_Right_{Stance}_01 | Single right melee |
Attack/Melee1H/ | A_Attack_Melee1H_Dual_{Stance}_01 | Dedicated paired melee animation; exact input binding open |
Attack/Melee2H/ | A_Attack_Melee2H_{Stance}_01 | Two-handed melee attack |
Movement and Sprint: Carry uses the retained _Idle clip across idle, Walk/Jog/Run by owner decision (2026-09-17). Held-animation entries do not have a Gait field or gait-specific filenames. Carry and Aim each use one action across idle/move; ShoulderAim permits only Walk. Stance remains explicit: Standing, Crouched and Prone use their corresponding clips and existing stance speed limits. Run remains a locomotion gait, not a fourth stance. Existing profile rows that differed only in Gait now have the same key; keep one intended row per remaining context to avoid first-match duplicates.
No duplicate exports required: if the exact same compatible clip covers multiple contexts, keep one asset and reference it from multiple profile entries. The matrix specifies coverage, not a mandatory export count. Preserve existing empty-handed Idle/Walk/Jog/Run/Crouch/Prone content. Existing UnarmedAttack clips remain where they are until reviewed; do not duplicate or move them merely to satisfy naming.
Item exceptions: use Overrides/Items/Crowbar/A_Attack_Crowbar_Right_Stand_01, for example, then override the exact context on DA_Item_Crowbar. Body-specific files, only if genuinely needed, use Overrides/Bodies/Woman/ and a final _Woman token . A left-only carry/aim case may use an explicitly authored LeftOnly/Left variant; no mirrored right animation is assumed.
Recoil decision — 2026-09-17: all character recoil is procedural. The Fire/_Add names listed here are historical placeholder inventory, not required clip deliveries. Existing assets are retained, unused; no replacement recoil clips or additive reference poses are required. Weapon mechanism clips remain with the weapon asset.
Exact expanded asset names for Stand / Crouch / Prone
Carry/RightOnly/A_Carry_RightOnly_Stand_Idle
Carry/RightOnly/A_Carry_RightOnly_Crouch_Idle
Carry/RightOnly/A_Carry_RightOnly_Prone_Idle
Carry/DualItems/A_Carry_DualItems_Stand_Idle
Carry/DualItems/A_Carry_DualItems_Crouch_Idle
Carry/DualItems/A_Carry_DualItems_Prone_Idle
Carry/TwoHanded/A_Carry_TwoHanded_Stand_Idle
Carry/TwoHanded/A_Carry_TwoHanded_Crouch_Idle
Carry/TwoHanded/A_Carry_TwoHanded_Prone_Idle
Aim/Unarmed/A_ShoulderAim_Unarmed_Stand
Aim/Unarmed/A_ShoulderAim_Unarmed_Crouch
Aim/Unarmed/A_ShoulderAim_Unarmed_Prone
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Stand
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Crouch
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Prone
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Stand
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Crouch
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Prone
Aim/Melee2H/A_ShoulderAim_Melee2H_Stand
Aim/Melee2H/A_ShoulderAim_Melee2H_Crouch
Aim/Melee2H/A_ShoulderAim_Melee2H_Prone
Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand
Aim/Pistol/A_ShoulderAim_Pistol_Supported_Crouch
Aim/Pistol/A_ShoulderAim_Pistol_Supported_Prone
Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_Stand
Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_Crouch
Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_Prone
Aim/Pistol/A_ShoulderAim_Pistol_Dual_Stand
Aim/Pistol/A_ShoulderAim_Pistol_Dual_Crouch
Aim/Pistol/A_ShoulderAim_Pistol_Dual_Prone
Aim/Firearm2H/A_ShoulderAim_Firearm2H_Stand
Aim/Firearm2H/A_ShoulderAim_Firearm2H_Crouch
Aim/Firearm2H/A_ShoulderAim_Firearm2H_Prone
Attack/Unarmed/A_Attack_Unarmed_Stand_01
Attack/Unarmed/A_Attack_Unarmed_Crouch_01
Attack/Unarmed/A_Attack_Unarmed_Prone_01
Attack/Melee1H/A_Attack_Melee1H_Right_Stand_01
Attack/Melee1H/A_Attack_Melee1H_Right_Crouch_01
Attack/Melee1H/A_Attack_Melee1H_Right_Prone_01
Attack/Melee1H/A_Attack_Melee1H_Dual_Stand_01
Attack/Melee1H/A_Attack_Melee1H_Dual_Crouch_01
Attack/Melee1H/A_Attack_Melee1H_Dual_Prone_01
Attack/Melee2H/A_Attack_Melee2H_Stand_01
Attack/Melee2H/A_Attack_Melee2H_Crouch_01
Attack/Melee2H/A_Attack_Melee2H_Prone_01
22.9 Per-item hand pose — owner confirmed 2026-09-17
[CONFIRMED] Shared family Carry/ShoulderAim is not enough for visual contact. Each held item needs its own finger pose. Canonical contract: TBD-INVENTORY-GRIP-003. This is a P09 requirement, not a later polish pass.
Keep the existing three layers: family clips for arms/torso, item transforms for mesh placement, Two Bone IK for the support wrist. Add character Pose Assets for fingers. Do not create per-item locomotion, per-item AnimBP subclasses, a second WeaponAnimationData class, or item-mesh Grip_L sockets as a Static Mesh requirement. The guide’s left-hand IK role is already SupportHandTarget.
| Hand | Pose source | When |
|---|---|---|
| Right fingers | Right item RightHandPose | Right hand occupied |
| Left fingers, independent item | Left item LeftHandPose | Left inventory hand occupied; no support IK |
| Left fingers, support | Right item LeftHandPose | Support IK active and left inventory hand empty |
Native + ABP wiring, 2026-09-17: item fields and AnimInstance RightHeldHandPose / RightHeldHandPoseName / RightHeldHandPoseAlpha and the left equivalents exist. Alpha is 1 only for a Pose Asset on the actual player skeleton that contains at least one pose; the published name is that first pose. Null, empty, or wrong-skeleton assets keep family fingers and do not set bMissingHeldPose. MCP compiled and saved ABP_Player with Layered Blend Per Bone on hand_r after Control Rig / before Two Bone IK, and on hand_l after IK. Each blend evaluates the Pose Asset via Evaluate Pose, with Modify Curve setting the first pose name to weight 1. Closed-Editor native build and headless Lambeer.HUD 26/26 passed. No Pose Assets are bound, so no visual curl is claimed.
Proposed first-slice assets, not created: /Game/Lambeer/Characters/Player/Animation/HandPoses/{Item}/PA_{Item}_RH and PA_{Item}_LH for Crowbar, FireAxe, Sledgehammer, WoodenMallet and L1810. Owner authors/retargets; agent may bind supplied assets. Trigger-finger Safe/Trigger and item-switch blend times are not this pass.
23. Editor calibration → item Data Asset
Purpose: measure a mesh placement relative to the character's hand socket, then store that measured location/rotation on the item definition. Changing a shared socket to fit one weapon would change every weapon attached there. This is an owner-operated authoring workflow; the proposed preview helper does not exist yet. An agent can inspect/save supported operations through Unreal MCP, but may not use computer-use or retarget clips.
23.1 Prepare an Editor preview (no PIE required)
- Select the first item and a compatible retargeted carry pose, then the item’s right-hand Pose Asset when it exists. Without a suitable hand/finger pose, placement is provisional: grip offset moves the object but cannot curl fingers. Family Carry clips must not be treated as a substitute for
RightHandPose. Only the owner retargets/exports. For MaleLocomotionSet use the approved/Game/MaleLocomotionSet/Animations/Mannequin/RTG_AutoGenerated.RTG_AutoGenerated. - In Content Browser, create an Actor Blueprint named
BP_GripCalibrationunder the proposed Calibration folder. This is a preview helper, not a gameplay character replacement. Add a Skeletal Mesh componentBodyunder its scene root. - Use the actual chosen body's mesh and the same component transform as gameplay. Verified disk assets:
/Game/Lambeer/Characters/Player/MetaHumans/BP_MHC_Man/Body/SKM_MHC_Man_BodyMeshand/Game/Lambeer/Characters/Player/MetaHumans/BP_MHC_Woman/Body/SKM_MHC_Woman_BodyMesh. Set Body Animation Mode to Use Animation Asset and choose the owner's compatible carry animation. Review a steady grip frame in the preview; do not substitute a differently retargeted skeleton. If a placed helper does not animate in the Level Editor, select its Body component and enable the advanced instance-only Update Animation in Editor option; UE 5.8 declares this transient setting on USkeletalMeshComponent. Enable viewport Realtime. This does not require starting PIE. - Add a Static Mesh component named
HeldPreview, or a Skeletal Mesh component for the item's skeletal held mesh. Make it a child of Body and set its Parent Socket tohand_r. Assign the exact effective held mesh from the item's Data Asset. Disable collision for the helper. - Match runtime size before measuring. Runtime attaches with
SnapToTargetNotIncludingScale, preserves the pre-attach world scale and applies only grip location/rotation. A preview parent socket may introduce bone scale; verify the preview world size against the actual runtime item instead of assuming relative scale 1 is equivalent. Do not compensate an oversized Crowbar/FireAxe import with grip Scale; those mesh-size issues remain separately recorded.
23.2 Measure the right grip and write the Data Asset
- Select HeldPreview, not Body or the shared hand socket. In the Blueprint viewport move/rotate the item until its handle fits the palm/fingers in the chosen pose. Use front/side/top views and inspect wrist angle, trigger reach and clipping.
- Read HeldPreview's relative Location and Rotation in Details. Its parent is Body and its socket is hand_r, so these are the item placement relative to that socket. Do not copy actor/world location and do not invert the transform. Unreal location units are centimeters; copy Rotation fields by their labels.
- Open the selected
/Game/Lambeer/Core/Data/Items/DA_Item_*. Under Grip, expandRight Grip Relative(RightGripRelative) and paste those Location/Rotation values. Leave Scale as identity; runtime ignores authored grip scale. Save this Data Asset. - Record which item, body, mesh, animation/frame and measurements produced the result. Re-read the saved asset through MCP before calling it authored. Numeric values in this document remain unset until measured.
23.3 Left hand, support hand and body differences
- Independent left-held item: repeat with HeldPreview parent socket
hand_land an authored left pose. EnablebHasLeftGripRelativeand writeLeftGripRelative. Do not mirror the right offset. This is for a separate item held in the left hand. - Left supporting the right-held item: keep the item on hand_r. Add a Scene/Arrow component
SupportTargetPreviewas a child of HeldPreview. Position its origin where thehand_lbone origin must reach for the intended support grip. A palm contact point and the wrist/hand bone origin are not necessarily the same point. Read this marker's relative Location in item space. - Enable
bHasSupportHandTargetand copy the marker's item-local Location intoSupportHandTarget. Current code uses only its transformed location; its Rotation does not orient the wrist. Use the authored pose for wrist/fingers and validate elbow reach. The helper marker alone does not execute the gameplay IK solve. - Repeat inspection for
Preset.ManandPreset.Woman. If only item placement needs a different value, add aGripBodyOverridesrow with the exact PresetId and Hand, then paste the complete replacementGripRelative. It replaces, rather than adds to, the shared grip. Avoid duplicate keys. A matching Left override also enables left hold for that preset. - Current body overrides do not contain a separate support target or animation pose. If the item-local support point or wrist pose itself must differ by body, stop and extend the approved data contract first; do not place that data in an unrelated field.
- Per-item finger Pose Asset: author a Pose Asset on the actual player skeleton that writes only
hand_*and finger bones. Bind it toRightHandPoseorLeftHandPoseon the item Data Asset. Do not put finger curl into the family Carry clip or intoRightGripRelative. Support fingers use the right item’sLeftHandPose.
Current limitations: two-handed items consume SupportHandTarget as before. A one-handed firearm needs bHasSupportHandTarget, ShoulderAim or ADS, and an empty left hand. Support target Rotation still does not control the wrist; IK keeps the incoming pose twist instead of rolling the chain. Finger curl requires bound Pose Assets and ABP hand blends; native publish alone does not pose fingers. SupportHandIK curves can release contact, but cannot make an occupied hand available. InspectPreviewRelative is unrelated to grip calibration.
Owner validation after saving: in Dedicated Server + two clients, inspect idle, movement, the permitted aim modes, draw/stow/drop and the available actions on both bodies, locally and remotely. Check correct size, wrist/finger contact, no one-frame support lag, no elbow flip/overextension and no support IK on an occupied hand. Report results before ROAD-P09-04 is accepted. There are no invented offset numbers or claimed PIE results in this guide.
24. Owner-authorized placeholder sequences — 2026-09-16
The owner now explicitly permits copying the matching Stand sequence to missing stances. If there is no Stand source, use A_INP_Idle for Carry and melee aim, the supplied supported-pistol ShoulderAim for pistol aim and Fire, and A_Attack_Unarmed_Stand_01 for unarmed/melee attacks. This is an authoring exception for temporary assets, not approval of final poses. The runtime still uses explicit profile entries; it does not silently fall back between stances.
Historical file preparation completed with 69 sequences. The 2026-09-17 shared-action cleanup deleted nine unused Carry *_Move sequences, and the subsequent procedural-recoil cleanup deleted 15 unused A_Recoil_* placeholders, leaving 45 of that original inventory; the active delivery list excludes all 24 deleted files. Created/saved 65 placeholder copies through Unreal MCP, retained three correctly named existing sequences and renamed the owner's Pistol_Pistol_Dual_Stand to Pistol_Dual_Stand (old asset path no longer exists; no duplicate original retained). Owner confirmed Unarmed aim uses Idle and Firearm2H aim uses the supported-pistol pose. This was a file-preparation snapshot; the profiles have since been populated and consolidated as recorded in the current shared Aim contract. No retarget, asset export, gameplay change, numeric grip calibration or PIE was performed.
Every new copy carries asset metadata Lambeer.Placeholder=true, Lambeer.PlaceholderSource and Lambeer.PlaceholderNote. All inspected sources use the MetaHuman base skeleton. New Fire assets retain AAT_None: their existing planned _Add names are placeholders only and do not prove additive content. Do not pass them to Apply Additive or regard them as firing/recoil animation. Replacing a placeholder includes reviewing its notifies, curves, root motion, skeleton and additive settings, not only its name.
Created sequence provenance (paths relative to the player Animation root)
| Created asset | Copied from | Status |
|---|---|---|
Carry/RightOnly/A_Carry_RightOnly_Stand_Idle | Idle/A_INP_Idle | Placeholder; replace later |
Carry/RightOnly/A_Carry_RightOnly_Stand_Move | Idle/A_INP_Idle | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/RightOnly/A_Carry_RightOnly_Crouch_Idle | Carry/RightOnly/A_Carry_RightOnly_Stand_Idle | Placeholder; replace later |
Carry/RightOnly/A_Carry_RightOnly_Crouch_Move | Carry/RightOnly/A_Carry_RightOnly_Stand_Move | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/RightOnly/A_Carry_RightOnly_Prone_Idle | Carry/RightOnly/A_Carry_RightOnly_Stand_Idle | Placeholder; replace later |
Carry/RightOnly/A_Carry_RightOnly_Prone_Move | Carry/RightOnly/A_Carry_RightOnly_Stand_Move | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/DualItems/A_Carry_DualItems_Stand_Idle | Idle/A_INP_Idle | Placeholder; replace later |
Carry/DualItems/A_Carry_DualItems_Stand_Move | Idle/A_INP_Idle | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/DualItems/A_Carry_DualItems_Crouch_Idle | Carry/DualItems/A_Carry_DualItems_Stand_Idle | Placeholder; replace later |
Carry/DualItems/A_Carry_DualItems_Crouch_Move | Carry/DualItems/A_Carry_DualItems_Stand_Move | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/DualItems/A_Carry_DualItems_Prone_Idle | Carry/DualItems/A_Carry_DualItems_Stand_Idle | Placeholder; replace later |
Carry/DualItems/A_Carry_DualItems_Prone_Move | Carry/DualItems/A_Carry_DualItems_Stand_Move | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/TwoHanded/A_Carry_TwoHanded_Stand_Idle | Idle/A_INP_Idle | Placeholder; replace later |
Carry/TwoHanded/A_Carry_TwoHanded_Stand_Move | Idle/A_INP_Idle | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/TwoHanded/A_Carry_TwoHanded_Crouch_Idle | Carry/TwoHanded/A_Carry_TwoHanded_Stand_Idle | Placeholder; replace later |
Carry/TwoHanded/A_Carry_TwoHanded_Crouch_Move | Carry/TwoHanded/A_Carry_TwoHanded_Stand_Move | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Carry/TwoHanded/A_Carry_TwoHanded_Prone_Idle | Carry/TwoHanded/A_Carry_TwoHanded_Stand_Idle | Placeholder; replace later |
Carry/TwoHanded/A_Carry_TwoHanded_Prone_Move | Carry/TwoHanded/A_Carry_TwoHanded_Stand_Move | Deleted 2026-09-17 after shared-action migration; historical provenance only |
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Stand | Idle/A_INP_Idle | Placeholder; replace later |
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Crouch | Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Stand | Placeholder; replace later |
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Prone | Aim/Melee1H/A_ShoulderAim_Melee1H_Right_Stand | Placeholder; replace later |
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Stand | Idle/A_INP_Idle | Placeholder; replace later |
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Crouch | Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Stand | Placeholder; replace later |
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Prone | Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_Stand | Placeholder; replace later |
Aim/Melee2H/A_ShoulderAim_Melee2H_Stand | Idle/A_INP_Idle | Placeholder; replace later |
Aim/Melee2H/A_ShoulderAim_Melee2H_Crouch | Aim/Melee2H/A_ShoulderAim_Melee2H_Stand | Placeholder; replace later |
Aim/Melee2H/A_ShoulderAim_Melee2H_Prone | Aim/Melee2H/A_ShoulderAim_Melee2H_Stand | Placeholder; replace later |
Aim/Pistol/A_ShoulderAim_Pistol_Supported_Crouch | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Placeholder; replace later |
Aim/Pistol/A_ShoulderAim_Pistol_Supported_Prone | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Placeholder; replace later |
Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_Crouch | Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_Stand | Placeholder; replace later |
Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_Prone | Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_Stand | Placeholder; replace later |
Attack/Unarmed/A_Attack_Unarmed_Crouch_01 | Attack/Unarmed/A_Attack_Unarmed_Stand_01 | Placeholder; replace later |
Attack/Unarmed/A_Attack_Unarmed_Prone_01 | Attack/Unarmed/A_Attack_Unarmed_Stand_01 | Placeholder; replace later |
Attack/Melee1H/A_Attack_Melee1H_Right_Stand_01 | Attack/Unarmed/A_Attack_Unarmed_Stand_01 | Placeholder; replace later |
Attack/Melee1H/A_Attack_Melee1H_Right_Crouch_01 | Attack/Melee1H/A_Attack_Melee1H_Right_Stand_01 | Placeholder; replace later |
Attack/Melee1H/A_Attack_Melee1H_Right_Prone_01 | Attack/Melee1H/A_Attack_Melee1H_Right_Stand_01 | Placeholder; replace later |
Attack/Melee1H/A_Attack_Melee1H_Dual_Stand_01 | Attack/Unarmed/A_Attack_Unarmed_Stand_01 | Placeholder; replace later |
Attack/Melee1H/A_Attack_Melee1H_Dual_Crouch_01 | Attack/Melee1H/A_Attack_Melee1H_Dual_Stand_01 | Placeholder; replace later |
Attack/Melee1H/A_Attack_Melee1H_Dual_Prone_01 | Attack/Melee1H/A_Attack_Melee1H_Dual_Stand_01 | Placeholder; replace later |
Attack/Melee2H/A_Attack_Melee2H_Stand_01 | Attack/Unarmed/A_Attack_Unarmed_Stand_01 | Placeholder; replace later |
Attack/Melee2H/A_Attack_Melee2H_Crouch_01 | Attack/Melee2H/A_Attack_Melee2H_Stand_01 | Placeholder; replace later |
Attack/Melee2H/A_Attack_Melee2H_Prone_01 | Attack/Melee2H/A_Attack_Melee2H_Stand_01 | Placeholder; replace later |
Fire/Pistol/A_Recoil_Pistol_Supported_Stand_Add | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Supported_Crouch_Add | Fire/Pistol/A_Recoil_Pistol_Supported_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Supported_Prone_Add | Fire/Pistol/A_Recoil_Pistol_Supported_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Unsupported_Stand_Add | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Unsupported_Crouch_Add | Fire/Pistol/A_Recoil_Pistol_Unsupported_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Unsupported_Prone_Add | Fire/Pistol/A_Recoil_Pistol_Unsupported_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Dual_Right_Stand_Add | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Dual_Left_Stand_Add | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Dual_Right_Crouch_Add | Fire/Pistol/A_Recoil_Pistol_Dual_Right_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Dual_Left_Crouch_Add | Fire/Pistol/A_Recoil_Pistol_Dual_Left_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Dual_Right_Prone_Add | Fire/Pistol/A_Recoil_Pistol_Dual_Right_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Pistol/A_Recoil_Pistol_Dual_Left_Prone_Add | Fire/Pistol/A_Recoil_Pistol_Dual_Left_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Firearm2H/A_Recoil_Firearm2H_Stand_Add | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Firearm2H/A_Recoil_Firearm2H_Crouch_Add | Fire/Firearm2H/A_Recoil_Firearm2H_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Fire/Firearm2H/A_Recoil_Firearm2H_Prone_Add | Fire/Firearm2H/A_Recoil_Firearm2H_Stand_Add | Deleted 2026-09-17 after empty referencer audit; historical non-additive placeholder |
Additional copies after owner clarification
| Created asset | Copied from | Status |
|---|---|---|
Aim/Unarmed/A_ShoulderAim_Unarmed_Stand | Idle/A_INP_Idle | Placeholder; replace later |
Aim/Unarmed/A_ShoulderAim_Unarmed_Crouch | Aim/Unarmed/A_ShoulderAim_Unarmed_Stand | Placeholder; replace later |
Aim/Unarmed/A_ShoulderAim_Unarmed_Prone | Aim/Unarmed/A_ShoulderAim_Unarmed_Stand | Placeholder; replace later |
Aim/Firearm2H/A_ShoulderAim_Firearm2H_Stand | Aim/Pistol/A_ShoulderAim_Pistol_Supported_Stand | Placeholder; replace later |
Aim/Firearm2H/A_ShoulderAim_Firearm2H_Crouch | Aim/Firearm2H/A_ShoulderAim_Firearm2H_Stand | Placeholder; replace later |
Aim/Firearm2H/A_ShoulderAim_Firearm2H_Prone | Aim/Firearm2H/A_ShoulderAim_Firearm2H_Stand | Placeholder; replace later |
Aim/Pistol/A_ShoulderAim_Pistol_Dual_Crouch | Aim/Pistol/A_ShoulderAim_Pistol_Dual_Stand | Placeholder; replace later |
Aim/Pistol/A_ShoulderAim_Pistol_Dual_Prone | Aim/Pistol/A_ShoulderAim_Pistol_Dual_Stand | Placeholder; replace later |
Validation: MCP class/property readback passed for all 65 copies and the renamed sequence (AnimSequence, MetaHuman skeleton, positive duration, AAT_None). Filesystem coverage is 69/69. The old UnarmedAttack folder's four asset deletions were already present on entry to this task; this task used the owner-supplied Attack/Unarmed source and did not restore or delete those historical assets.
Procedural-only recoil — owner confirmed 2026-09-17
Owner-approved implementation scope, 2026-09-17: arms/weapon presentation only, a firing-hand event plus debug command, and experimental tuning editable per gun. Camera recoil, slide/bolt motion, ammunition, damage and the P10 firing loop are outside this change. Fifteen unused A_Recoil_* sequences were deleted through Unreal MCP after every referencer list was empty; no dedicated recoil clip or profile Recoil field remains required.
UHeldItemComponent::NotifyWeaponShot(Hand) is an authority-only cosmetic event for a future confirmed shot. It validates the held firearm and right-only mixed-pair rule, then sends an unreliable multicast to relevant clients. The owner also receives that multicast; no local prediction or duplicate owner impulse is added. It never fires a gun. Late joiners do not replay old recoil; presentation pulses are transient and do not extend the persistence schema.
Graph order: held/aim pose and right finger pose → component space → additive lowerarm_r rotation → additive lowerarm_l rotation → support-hand arm alignment → local space → left finger pose → output. Each hand has a bounded smooth kick/recovery envelope and clears on a held-item ID change. SupportHandIKBoneLocation is relative to hand_r, using the item's authored grip, scale and support target. The IK effector follows the current evaluated right hand after recoil; it does not chase a previous-frame visual-world target. Independent left weapons keep support IK disabled.
Per-gun tuning
- Open the gun's
DA_Item_*, searchRecoiland expand that struct. These values belong to the item, not DA_AnimProfile_*. Kick Rotationuses character-relative degrees: Pitch raises/lowers, Yaw moves sideways, Roll twists. The experimental shared default is Pitch 6, Yaw 0, Roll 0; it is converted to mesh-component space before applying to the firing forearm.Kick Secondsdefaults to 0.035 seconds;Recovery Secondsdefaults to 0.22 seconds.Max Stackdefaults to 2 and caps overlapping kicks. These are trial values, not approved balance; every gun may override them independently.- Save the Data Asset, let the owner start PIE with a firearm held, then run
Lambeer.Recoil RightorLambeer.Recoil Left. Re-enter the command for another pulse. The command sends a server request and reports the authority acceptance/rejection in LogLambeer.
Owner acceptance still required
Check a single shot and repeated shots, return to the original pose, supported pistol contact, independent dual hands, mixed-pair rejection for the left hand, item switching during recovery, all stances, both views, and Dedicated Server + two clients (owner and observer). ADS content/alignment remains a separate authoring and validation requirement. No agent-started PIE or visual/network acceptance is claimed.
L1810 bodycam ADS alignment — standing supported slice
Scope confirmed 2026-09-17: standing, right-held L1810, left inventory hand empty and supporting, with an authored non-additive shared Aim profile pose. The character camera drives the weapon target. The camera never attaches to SightReference. Sight alignment has no independent look input: arms follow view rotation, while subtle procedural sway and recoil may move the gun off center. Missing socket/content disables alignment instead of inventing a reference or pose. Other guns default to opt-out.
The runtime resolves the effective SightReference from the actual held visual, including its parent bone, socket rotation, authored grip and scale. Engine socket precedence is preserved (mesh override before skeleton); the tool's combined enumeration reported the name twice, and neither authored socket was deleted. The right-hand target cancels this measured sight-in-hand transform, placing the neutral sight at EyeDistanceCm along the view. The AO-based arm alignment node reaches the position without stretching and matches hand_r orientation in a single control using SightAlignmentAlpha. Existing recoil runs afterward, and support IK follows the resulting right hand.
Locally, the no-lag spring-arm view is reconstructed from the current control rotation and current camera offsets before animation; camera configuration updates run after movement and before mesh animation. If camera lag is enabled, the published camera transform is used. Remote proxies use replicated base aim rotation and first-person target geometry, not a replicated local camera. Remote interpolation/visual agreement still requires owner network PIE; this is presentation, not authoritative shooting or ballistic direction.
Non-PIE validation: closed-Editor LambeerEditor Win64 Development build passed. Lambeer.HUD automation passed 29/29 without test warnings/errors, including rotated/scaled sight-offset solve tests. MCP confirms L1810 opt-in settings, ADS pose binding, component-space right-hand IK and wrist replacement before recoil, and ABP compile with warnings-as-errors/save/clean readback. No owner visual or network PIE pass is claimed.
Calibration in DA_Item_L1810
- Open
/Game/Lambeer/Core/Data/Items/DA_Item_L1810, searchSight Alignment. Enabled is opt-in; Socket Name isSightReference. Keep the owner's socket axes and authored grip unchanged while first testing. - Set
Sway Translation CmandSway Rotation Degreesto zero to inspect neutral alignment.Eye Distance Cmstarts at 45; it controls how far the rear sight sits in front of the camera. Adjust in small steps while preserving reachable arm/contact geometry. The IK does not stretch arms to satisfy an unreachable distance. Blend Secondsstarts at 0.18 for alignment enter/exit. It blends the alignment controls, not the whole pose selection or camera transition.- Restore trial sway: translation (X0, Y0.2, Z0.15) cm, rotation (Pitch0.15, Yaw0.2, Roll0) degrees, frequency0.8 Hz. X is forward, Y right, Z up in view axes. Raise translation first for visible off-center motion; angular amplitude alters the sight line. Zero amplitudes disable sway. The two-axis waveform is deterministic presentation, not a recoil pattern or spread model.
- The arm solver transports the current AO bend plane with the new reach direction. The old Elbow Pole Offset Cm and Two Bone IK twist flags are not controls in the replacement nodes. Preserve the authored pose and reachable target calibration; see the current solver contract.
- Save the item before owner PIE. Hold L1810 in the right hand with the left hand empty, stand, switch to first person with V, toggle ADS using RMB. Test look up/down and turn, toggle off/on, then run
Lambeer.Recoil Right. The recoil must remain visible after alignment, and support contact must follow it.
Trial values, not approved balance: all settings are per-item and may be tuned independently. Owner acceptance must cover clipping, elbow reach, sight visibility, neutral alignment, sway, recoil, view transitions, and owner/observer on Dedicated Server + two clients. Crouched/prone, unsupported, left-only and dual ADS alignment are outside this initial enabled scope. No new persistence fields are needed for these transient presentation values; content settings persist in the Data Asset.
Beginner support-target measurement
- Finish the right-item placement first:
RightGripRelativeestablishes the gun relative to hand_r. A support target cannot correct a wrong right-hand attachment. - Create the proposed Actor Blueprint
BP_GripCalibrationfor an editor preview. Add Body using the actual player skeletal mesh and the selected shoulder-aim sequence in Use Animation Asset mode. The helper has not been created by this task. Use the same body/item world size as runtime. - Add HeldPreview under Body, Parent Socket=hand_r, using the exact item mesh. Apply the measured right-grip location/rotation. Keep the item's apparent/world size consistent with the runtime KeepWorld-scale attachment.
- Add an Arrow called LeftHandReference under Body, Parent Socket=hand_l, with zero relative Location/Rotation. Its origin marks the actual left-hand bone origin; it is not a new skeleton socket or a palm contact marker.
- Add another Arrow called SupportTargetPreview under HeldPreview, with no parent socket. Move its origin to LeftHandReference's origin when the source pose already grips correctly. If it does not, place the target at the desired wrist/hand-bone position near the intended contact and treat it as provisional until an IK-driven preview/owner PIE is evaluated. Do not aim the wrist origin directly at the center of the palm contact.
- Read SupportTargetPreview's relative Location. Because its parent is the item mesh, these coordinates are item-local. Copy them into the item's
SupportHandTarget.Locationand enablebHasSupportHandTarget. No invented vectors are supplied. The current runtime consumes position only; rotating this marker or SupportHandTarget does not rotate the wrist. - For a supported one-handed pistol enable
bHasSupportHandTarget, enter ShoulderAim or ADS and leave the left inventory hand empty. Do not author LeftGripRelative for this purpose: that field places a separate item held in the left hand. L1810 is already opted in; MCP 2026-09-17 observed an authored support target on that asset. Do not invent a replacement vector. - The editor helper in Use Animation Asset mode does not run ABP_Player or IK; moving the marker alone does not move the hand. A live check needs ABP_Player and a valid supported ShoulderAim profile entry referencing the selected pose. Profiles are populated; verify the matching supported/unsupported entry rather than assuming a sequence copy automatically binds it.
- Once the profile is bound, the owner starts PIE and checks hand contact. Stop PIE before editing the persistent Data Asset, save the adjusted target and re-enter to force a fresh check. Do not rely on unsaved runtime-instance edits surviving PIE.
- If the wrist reaches the target but fingers/twist are wrong, author or correct the item Pose Asset rather than hoping IK will curl fingers. If the elbow bends incorrectly, inspect Joint Target/pole behavior; shifting the grip randomly is not a reliable fix. Repeat for both bodies and the actual crouch/prone clips when they replace Stand placeholders.
Reference: Epic UE 5.8 Two Bone IK.
21. Relationships และแหล่งอ้างอิง
Document contract coverage
- CoveredProblem & Scope
- CoveredCurrent vs Proposed
- CoveredArchitecture
- CoveredData Model
- CoveredInterfaces & Events
- CoveredReplication / Persistence
- CoveredPerformance & Failure Handling
- CoveredImplementation Plan
- CoveredVerification Strategy
- CoveredTechnical Decisions / TBDs