Technical Design · Animation

Player Locomotion Animation Guide

คู่มือสร้าง locomotion แบบ In-Place สำหรับผู้เริ่มต้น โดยใช้ Blend Space 2D รวม Walk, Jog และ Run เฉพาะคลิป Loop แล้วควบคุม Idle, Start, Stop, Turn, Pivot, Spin, Crouch, Prone และ Jump ผ่าน Animation State Machines

Status: draftOwner: UnassignedUpdated: 2026-09-19
ขอบเขตเอกสาร: คู่มือนี้เป็นแผนการเชื่อมระบบ ความหมายเชิง naming ของ _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 ของแต่ละคลิปยังเป็น TBD

1. เป้าหมายและขอบเขต

สร้างฐาน 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. สิ่งที่มีอยู่แล้ว กับสิ่งที่จะสร้าง

หัวข้อสถานะที่ตรวจพบแนวทางในคู่มือนี้
EngineVerified: Lambeer.uproject ใช้ Unreal Engine 5.8ใช้ Animation Blueprint และ State Machine มาตรฐาน
Anim BlueprintVerified: /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 speedsVerified: Walk 165, Jog 442, Sprint 600, Crouch 110 cm/s ใน ULambeerLocomotionComponentWalk/Jog อิง Root Motion Forward Loop ที่วัดได้ประมาณ 164/442 cm/s; Run runtime 600 ใกล้ authored 660 และผ่าน visual feedback ปัจจุบัน
In-Place animationsVerified: 438 assets ใน 8 กลุ่มใช้ authored Start/Stop/Pivot แยกจาก Moving Blend Space

3. ชุด Animation ที่ตรวจพบ

438In-Place assets
104ชื่อมี Start
64ชื่อมี Loop
60ชื่อมี Pivot
55ชื่อมี Stop
42Spin assets
โฟลเดอร์จำนวนหน้าที่หลักตัวอย่างชื่อจริง
Idle33Idle, idle breaks, stance transitions, turn in placeA_INP_Idle, A_INP_IdleLt_90_Turn, A_INP_Idle2CrouchIdle
Walk70Directional walk, start, stop, pivot, moving turnA_INP_WalkFwd_Loop, A_INP_WalkBwdRt_45_Start, A_INP_WalkFwd2Bwd_Pivot_L
Jog70Directional jog, start, stop, pivot, moving turnA_INP_JogFwd_Loop, A_INP_JogFwdLt_45_Stop, A_INP_JogBwd2JogFwd_Pivot_R
Run33Forward sprint family และ angled starts/turnsA_INP_RunFwd_Loop, A_INP_RunFwd_135_Start_L, A_INP_RunFwd_Stop_Fast
Crouch68Directional crouch locomotion และ pivotsA_INP_CrouchFwd_Loop, A_INP_CrouchBwd_Start, A_INP_CrouchFwd2Bwd_Pivot_R
Prone24Directional prone start/loop/stopA_INP_ProneFwd_Loop, A_INP_ProneBwdLt135_Start, A_INP_ProneFwdRt90_Stop
Jump98Idle/gait jump และ directional multi-stage jumpA_INP_JumpJog_Start_LU, A_INP_JumpFwd_Apex, A_INP_Falling_Loop
Spin42การกลับทิศรุนแรงระหว่าง movementA_INP_JogFwd_Spin_Bwd_180_L, A_INP_RunFwdRt_Spin_JogBwdLt_90

4. สถาปัตยกรรมที่แนะนำ

IdleStartMoving 2DStopIdle
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 ก่อนสร้างกราฟ

  1. Duplicate หรือสำรอง ABP_Player และ Blend Space เดิมก่อนทดลอง
  2. เปิด A_INP_Idle แล้วบันทึก Skeleton ที่ใช้งาน
  3. เปิด ABP_Player แล้วเปรียบเทียบ Target Skeleton
  4. ถ้า Skeleton ต่างกัน ให้ retarget ด้วย IK Rig/IK Retargeter ก่อนสร้าง Blend Space
  5. เปิดตัวอย่างคลิปตรงหน้า ซ้าย ขวา หลัง และเปิด Show Bones เพื่อยืนยันแกนจริง
  6. ตรวจ Loop Animation เฉพาะไฟล์ลงท้าย _Loop; Start/Stop/Pivot ห้าม loop
  7. ตรวจ Root Motion setting ว่าชุด In-Place ไม่ได้ขยับ capsule โดยไม่ตั้งใจ
จุดหยุดตรวจ: ถ้า Mannequin กับ MetaHuman ใช้ pose/skeleton ไม่ตรง ห้ามเดินหน้าสร้าง sample จำนวนมากก่อนทำ retarget test 1 คลิปให้ผ่าน เพราะการแก้ย้อนหลังจะเสียเวลามาก

6. ตัวแปรที่ต้องใช้ใน ABP_Player

การสร้างตัวแปรอย่างเดียวยังไม่พอ: ตัวแปรในตารางจะคงค่า Default จนกว่าจะมีกราฟเขียนค่าให้มันทุก animation update โดยเฉพาะ bHasMovementInput ซึ่งเริ่มต้นเป็น false ถ้าไม่ทำข้อ 6.2 กฎ Idle → Start ในข้อ 9.4 จะไม่มีวันเป็นจริงและ animation จะค้างที่ Idle แม้ capsule เคลื่อนที่แล้ว
ตัวแปรชนิดที่มา/การคำนวณใช้ที่ใด
VelocityVectorCharacter Movement Velocityคำนวณ speed/direction
GroundSpeedFloatVector Length XY(Velocity)แกน Y ของ 2D Blend Space
MovementSpeedDeadZoneFloatค่า tuning/config ที่ตั้งใน Default Value หลังวัด idle noise; ไม่คำนวณหรือ Set ทุกเฟรม และค่าอนุมัติยังเป็น TBDแยก Stop กับ Idle ใน Transition Rules
LocalVelocityVectorUnrotate Velocity ด้วย Actor Rotationแยกหน้า/หลัง/ซ้าย/ขวา
DirectionFloatCalculate Direction หรือ local atan2แกน X ของ 2D Blend Space
AccelerationVectorCurrent Accelerationรู้ว่ามี movement input
bHasMovementInputBoolAcceleration 2D ไม่เป็นศูนย์Start/Stop/Pivot decisions
bIsFallingBoolCharacter Movement Is Fallingเข้า/ออก Airborne
GaitELambeerGaitอ่าน replicated ULambeerLocomotionComponent.CurrentGait ใน ULambeerAnimInstance::NativeUpdateAnimationเลือก Walk/Jog/Run family และ transition logic
StanceELambeerLocomotionStance (E_LocomotionStance)native ULambeerAnimInstance.Stance อ่านจาก ULambeerLocomotionComponent.GetPublishedStance(); simulated proxy อ่าน replicated CurrentStanceSM_Posture
bUrgentStandFromProneBoolnative ULambeerAnimInstance.bUrgentStandFromProne จาก locomotion เมื่อออกจาก Prone ด้วย Sprint/Jump-to-Stand; ห้ามสร้างตัวแปร Blueprint ชื่อซ้ำทับ inheritedลูกศรออกจาก State Prone ตามข้อ 13.5
RotationModeELambeerRotationModederive จาก AimMode ใน native AnimInstance: HipRelaxed = VelocityDirection, ShoulderAim/ADS = LookingDirection ตามข้อ 10.7เลือก turn-start กับ strafe-start
DesiredDirectionFloatมุม acceleration/input ใน local spaceเลือก Start/Pivot
DirectionDeltaFloatDelta angle ระหว่าง velocity กับ desired directionTurn/Pivot/Spin
FootPhaseELambeerFootPhaseruntime เขียน LeftUp/RightUp จากความสูงกระดูก foot_l/foot_r เป็นค่าประมาณ; Notify/curve ที่ตรวจเฟรมแล้วอนุญาตให้ overwrite ตามข้อ 10.7เลือก suffix _LU/_RU; ไม่ใช้เลือก _L/_R

6.1 สร้างตัวแปรขั้นต่ำสำหรับการทดสอบข้อ 9

  1. เปิด 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
  2. สำหรับ milestone Standing ในข้อ 9 ให้ตรวจว่า Stance ใช้ Enum ที่มีค่า Standing และตั้ง Default Value เป็น Standing ก่อน ส่วนการอัปเดตเป็น Crouched/Prone จะทำเมื่อสร้าง posture เหล่านั้นในข้อ 13
  3. 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 ทำงานถูกต้อง

  1. หา event Event Blueprint Update Animation หากไม่มี ให้คลิกขวาพื้นที่ว่างแล้วเพิ่ม event นี้
  2. จาก execution pin ของ event ต่อเข้า Cast To Character โดยเสียบ Try Get Pawn Owner เข้าที่ Object หากโปรเจกต์มี cast ไปคลาสผู้เล่นที่ยืนยันแล้วอยู่เดิม ให้ใช้ของเดิมและไม่ต้องสร้าง cast ซ้ำ
  3. จากผล Cast ดึง Get Character Movement เก็บสายอ้างอิงนี้ไว้ใช้กับ Get Current Acceleration และ Is Falling
  4. จาก Character ดึง Get Velocity แล้วต่อค่าเข้า Set Velocity
  5. นำค่า Velocity เดียวกันเข้า Vector Length XY หรือ VSizeXY แล้วต่อผลเข้า Set GroundSpeed
  6. นำ Velocity กับ Get Actor Rotation ของ Character เข้า Calculate Direction แล้วต่อผลเข้า Set Direction
  7. จาก Character Movement เรียก Get Current Acceleration แล้วต่อค่าเข้า Set Acceleration
  8. นำ Current Acceleration เข้า Vector Length XY แล้วเปรียบเทียบ Float > 0.0 ต่อผล Boolean เข้า Set bHasMovementInput สูตรนี้ใช้เป็นเกณฑ์ “Acceleration 2D ไม่เป็นศูนย์” สำหรับ milestone แรก; หาก analog input มี noise ให้ทดสอบ threshold ใหม่และบันทึกเป็นค่าที่อนุมัติภายหลัง
  9. จาก Character Movement เรียก Is Falling แล้วต่อผลเข้า Set bIsFalling
  10. ต่อสาย execution สีขาวให้ผ่าน Set nodes ทุกตัวตามลำดับ ห้ามวาง Set node ลอยไว้ เพราะ pure nodes อาจมีค่าถูกต้องแต่ตัวแปรจะไม่ถูกเขียน
  11. 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

  1. เริ่ม PIE เปิด ABP_Player และเลือก instance ผู้เล่นที่กำลังควบคุมอยู่ใน Debug Filter ห้ามดูค่า Default ของ class
  2. ตอนยืนนิ่ง: GroundSpeed ต้องอยู่ใกล้ศูนย์, bHasMovementInput=false และ bIsFalling=false
  3. ขณะกดเดิน: Acceleration ต้องไม่เป็นศูนย์, bHasMovementInput=true, GroundSpeed ต้องเพิ่ม และ Direction ต้องเปลี่ยนตามทิศเคลื่อนที่
  4. เมื่อปล่อย input แต่ capsule ยังไหล: bHasMovementInput ต้องกลับเป็น false ก่อน GroundSpeed ลงถึงศูนย์ พฤติกรรมนี้คือสิ่งที่กฎ Moving → Stop ในข้อ 9.4 ต้องใช้
  5. ถ้า Character เคลื่อนที่แต่ค่าทั้งหมดยังคงศูนย์ ให้ตรวจ Anim Class บน Skeletal Mesh, Debug Filter, Cast Failed และสาย execution ก่อนทำข้อ 7–9 ต่อ
เกณฑ์ผ่านข้อ 6: ห้ามเริ่มต่อ Transition Rules จนเห็น bHasMovementInput สลับ false → true → false จาก input จริงอย่างน้อยหนึ่งรอบ และเห็น GroundSpeed เปลี่ยนตามการเคลื่อนที่

6.4 ตัวแปรระยะถัดไป: ต้อง set เมื่อใดและจากอะไร

ตัวแปรทุกตัวต้องมี source of truth แต่ไม่จำเป็นต้องเขียนทุกตัวทุกเฟรม บางตัวเป็นค่าคงที่ของ milestone ปัจจุบัน บางตัวเปลี่ยนตาม event และบางตัวยังต้องเพิ่ม runtime contract ก่อนใช้งาน ตารางนี้เป็น gate ว่าต้องต่อให้เสร็จก่อนเริ่มหัวข้อใด

ตัวแปรต้องพร้อมก่อนวิธีอัปเดตในระบบปัจจุบันรูปแบบการเขียนค่า
MovementSpeedDeadZoneTransition ที่แยก Stop/Idle ในข้อ 9.4ตั้ง Default Value ให้สูงกว่า idle noise ที่วัดได้ แต่ต่ำกว่าความเร็วต่ำสุดที่ตั้งใจให้ถือว่าเคลื่อนที่Config/default; Transition ใช้ Get และไม่ Set ทุกเฟรม
LocalVelocityข้อ 10–11Unrotate Vector(Velocity, Actor Rotation)ทุก animation update
DesiredDirectionข้อ 10–11Calculate Direction(Acceleration, Actor Rotation) เฉพาะเมื่อ bHasMovementInput=trueทุก update ที่มี input; เก็บค่าล่าสุดเมื่อไม่มี input
DirectionDeltaข้อ 11Normalize Axis(DesiredDirection - Direction); contract ที่อนุมัติคือค่าบวก = ขวา และค่าลบ = ซ้ายตาม TBD-LOCO-004ทุก update หลัง Direction และ DesiredDirection
Stanceข้อ 9 สำหรับ default Standing; ข้อ 13 สำหรับการเปลี่ยน posturenative 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/_RUAnim Notify หรือ curve ที่เฟรมตรวจแล้วเขียน LeftUp/RightUp ตาม suffix contract ในข้อ 10.7Event-driven ไม่ต้อง Set ทุกเฟรม

ต่อ LocalVelocity, DesiredDirection และ DirectionDelta

  1. เพิ่ม Set LocalVelocity ต่อท้ายการคำนวณ Velocity ในข้อ 6.2 โดยใช้ node Unrotate Vector รับ Vector = Velocity และ Rotation = Actor Rotation
  2. หลังคำนวณ bHasMovementInput ใช้ Branch: ทาง True นำ Acceleration กับ Actor Rotation เข้า Calculate Direction แล้ว Set DesiredDirection; ทาง False ไม่ต้องเขียนทับ เพื่อเก็บทิศ input ล่าสุดไว้ให้ Stop/Pivot
  3. บนทาง True หลัง Set DesiredDirection ใช้ Float - คำนวณ DesiredDirection - Direction แล้วส่งเข้า Normalize Axis เพื่อบีบผลให้อยู่ในช่วง -180° ถึง 180° ก่อน Set DirectionDelta
  4. PIE และตรวจ input หน้า/หลัง/ซ้าย/ขวา ค่า DesiredDirection ต้องเปลี่ยนทันทีเมื่อกด แม้ Velocity ยังไม่ทันเปลี่ยน ส่วน DirectionDelta ต้องมีขนาดใหญ่เมื่อ input สวนกับทิศที่กำลังไหล

ต่อ Stance และกำหนด RotationMode ให้ตรง runtime

  1. นำ native Stance จาก ULambeerAnimInstance ไปใช้ใน SM_Posture โดยตรง; ห้ามสร้าง Boolean bIsCrouched/IsCrouching ซ้ำใน AnimInstance และห้าม map Character Movement → Is Crouching ทับค่า Prone
  2. ก่อน runtime contract ใหม่เสร็จ ให้ตั้ง Default Value ของ RotationMode เป็น VelocityDirection และตรวจใน PIE ว่าค่ายังคงตรงกับ Character ซึ่งหันตาม movement
  3. เมื่อ implement ข้อ 10.7 ให้ derive RotationMode จาก AimMode หลังฝั่ง Character สลับ rotation behavior สำเร็จแล้ว ห้ามให้ ABP สลับ mode ก่อน gameplay

Contract ของ Gait และ FootPhase

  • Gait: native อ่าน CurrentGait และ latch StartGait ตอน 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 เท่านั้น
ห้าม derive authoritative Gait จาก GroundSpeed: ระหว่างเร่ง เบรก ทางลาด และ encumbrance ความเร็วจริงอาจผ่านหลายช่วงทั้งที่ gameplay gait ไม่ได้เปลี่ยน 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 AxisName Direction, Min -180, Max 180เปิด Wrap Input ถ้าผลที่ seam หน้า -180/180 ถูกต้อง
Vertical AxisName 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 จริงหมายเหตุ
0A_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 และ 180A_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

ห้ามเติม Run ด้านหลังด้วย Jog ที่ Speed 600: เมื่อทิศ input เข้าฝั่งหลัง ให้ gameplay gait ลดกลับเป็น Jog เพื่อให้ GroundSpeed ไม่วิ่งขึ้นไปยังแถว Run ที่ไม่มี sample รองรับ

คลิปด้านข้างซ้ำที่ต้องตัดสินใจ

แพ็กมีทั้ง 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 ต้นแบบในคลิปเดินหน้า

  1. ใน Content Browser เปิด A_INP_WalkFwd_Loop ด้วย Animation Sequence Editor แล้วเปิดการเล่นแบบวนซ้ำใน Preview
  2. เปิดการแสดง Skeleton/Bones หรือ Bone Names ใน viewport จากนั้นลดความเร็ว Preview และลาก playhead ทีละเฟรมบริเวณที่เท้าซ้ายแตะพื้น
  3. เลือกเฟรมที่เท้าซ้าย เริ่มรับน้ำหนักและกำลังจะนิ่งกับพื้น ไม่ใช่เลือกจากตำแหน่งเท้าต่ำที่สุดเพียงอย่างเดียว ให้ดูทั้งส้น ปลายเท้า และการถ่ายน้ำหนักของสะโพกประกอบกัน
  4. ที่แถบ Notifies/Markers คลิกขวาตรงเวลานั้น เลือกเพิ่ม Sync Marker แล้วสร้างหรือเลือกชื่อ LeftPlant ชื่อนี้ต้องสะกดและใช้ตัวพิมพ์เหมือนกันทุกคลิป
  5. เลื่อนไปยังเฟรมที่เท้าขวาเริ่มรับน้ำหนัก แล้วเพิ่ม RightPlant ด้วยหลักเดียวกัน
  6. เล่นคลิปวนผ่านรอยต่อท้ายคลิปกลับไปต้นคลิป ตรวจว่าลำดับ marker สลับ LeftPlantRightPlantLeftPlant อย่างต่อเนื่อง หากช่วงก้าวคร่อมปลายคลิป ให้ตรวจทั้งสองด้านของรอยต่อก่อนขยับ marker
  7. บันทึก asset แล้วปิด–เปิดใหม่หนึ่งครั้งเพื่อยืนยันว่า marker ถูกเก็บใน Animation Sequence แล้ว
ยังไม่ยืนยัน foot-plant frame: ตำแหน่งเฟรมของ marker ต้องตัดสินจากภาพจริงของแต่ละคลิป ขณะนี้เอกสารยืนยันได้เฉพาะหลักการและชื่อเสนอ LeftPlant/RightPlant เท่านั้น ห้ามคัดลอกเวลาเฟรมจากคลิปหนึ่งไปใส่อีกคลิปโดยไม่ Preview

8.2 ทำซ้ำกับ sample ที่อยู่ใน Blend Space

  1. เปิด BS_Standing_Locomotion_2D และจดรายชื่อ Animation Sequence ที่เป็น sample จริง เพื่อไม่เสียเวลาใส่ marker ให้ asset ที่ยังไม่ได้ใช้
  2. ทำ Walk ให้ครบทุกทิศก่อน จากนั้นทำ Jog และ Run โดยใช้ marker เพียงสองชื่อเดิม ห้ามสร้างชื่อแยกตาม gait เช่น WalkLeftPlant หรือ RunLeftPlant
  3. สำหรับคลิปด้านข้างและด้านหลัง ให้ยึด เท้าซ้าย/ขวาตามร่างกายตัวละคร ไม่ใช่ซ้าย/ขวาตามกล้องหรือทิศการเคลื่อนที่
  4. หลังแก้แต่ละ family ให้ Preview เปรียบเทียบคลิปหน้า ซ้าย ขวา และหลังที่ความเร็วเดียวกัน ถ้า blend แล้วเข่างอผิดจังหวะหรือเท้าสลับ ให้กลับไปตรวจ marker ของ sample คู่นั้นก่อนแก้ Blend Space

8.3 ตรวจ marker ใน Blend Space ก่อนสร้าง State Machine

  1. เปิด BS_Standing_Locomotion_2D และตรวจอีกครั้งว่า sample ทุกตัวอ้างถึง Animation Sequence ที่ใส่ LeftPlant/RightPlant แล้ว
  2. Preview ทีละจุด sample เพื่อยืนยันว่าคลิปวนและลำดับเท้าถูกต้อง ก่อนทดสอบการ blend ระหว่างจุด
  3. ลากตำแหน่ง Preview ข้าม sample ที่อยู่ติดกัน ถ้าขากระตุกหรือเท้าสลับ ให้เปิด Animation Sequence คู่นั้นกลับไปเทียบตำแหน่ง marker
  4. บันทึก 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

  1. เปิด ABP_Player แล้ว Compile ให้ผ่านก่อนเริ่ม เพื่อแยก error เดิมออกจาก error ที่เกิดในขั้นตอนนี้
  2. ไปที่ AnimGraph ตรวจว่ามี State Machine ชื่อ SM_Posture อยู่แล้วหรือไม่ หากมีให้เปิดของเดิมและห้ามสร้างชื่อซ้ำ หากยังไม่มี ให้คลิกขวาพื้นที่ว่าง เลือกเพิ่ม State Machine แล้วตั้งชื่อเสนอ SM_Posture
  3. ต่อ output pose ของ node SM_Posture เข้าสาย animation หลัก หากกราฟมี Inertialization หรือ Layered Blend per Bone อยู่แล้ว ให้แทรก SM_Posture ไว้ก่อน node เหล่านั้นตามโครงข้อ 4; ถ้ายังไม่มี ให้ต่อเข้ากับ Output Pose ชั่วคราว
  4. ดับเบิลคลิก SM_Posture สร้าง State ชื่อ Standing แล้วลากเส้นจาก Entry ไป Standing ขั้นตอนนี้ยังไม่ต้องสร้าง Crouching, Prone หรือ Airborne เพราะจะเพิ่มในข้อ 13–14
  5. ดับเบิลคลิก State Standing เพื่อเปิด State Graph จากนั้นคลิกขวาพื้นที่ว่าง เพิ่ม State Machine แล้วตั้งชื่อเสนอ SM_Standing ต่อ output ของมันเข้า State Result
  6. ดับเบิลคลิก node SM_Standing แล้วสร้าง States ชื่อ Idle, Start, Moving และ Stop
  7. ลากเส้นจาก Entry ไป Idle จากนั้นสร้าง transition ตามลำดับ flow: Idle → Start → Moving → Stop → Idle
  8. Compile เพื่อตรวจโครง parent/child ก่อนใส่ animation ถ้า compiler แจ้งว่า State ใดไม่มี pose ถือว่าเป็นอาการปกติในจุดนี้และต้องหายไปหลังทำข้อ 9.2
  9. ยังไม่ต้องสร้าง transition ไป Turn/Pivot/Spin จนกว่า state หลักทั้งสี่จะ Compile และทดสอบผ่าน
กรณี ABP_Player มีโครงเดิม: ใช้ State Machine/State ที่ทำหน้าที่เดียวกันอยู่แล้วและตรวจเส้นทาง pose ก่อนเปลี่ยนชื่อหรือสร้างซ้ำ ชื่อ SM_Posture, Standing และ SM_Standing ในเอกสารนี้เป็นชื่อเสนอจนกว่าจะตรวจโครงภายใน asset จริง

9.2 ใส่ Pose ภายในแต่ละ State

ขอบเขตรอบแรก: ทำให้ flow เดินหน้าแบบ Walk ใช้งานได้ครบหนึ่งรอบก่อน โดยใช้ A_INP_WalkFwd_Start และ A_INP_WalkFwd_Stop_LU เป็นคลิปทดสอบชั่วคราว ยังไม่ต้องสร้างระบบเลือกทิศหรือเลือกเท้าในข้อ 9.2

Idle — ใส่คลิปยืนรอ

  1. ใน SM_Standing ดับเบิลคลิก State Idle เพื่อเปิด State Graph
  2. ลาก A_INP_Idle จาก Content Browser มาวางในกราฟ จะได้ Sequence Player ของคลิปนี้
  3. ต่อ output pose ของ Sequence Player เข้า State Result
  4. เลือก Sequence Player แล้วเปิด Loop Animation ใน Details เพราะ Idle ต้องเล่นวนต่อเนื่อง

Start — ใส่คลิปเริ่มเดินหน้าชั่วคราว

  1. กลับไป SM_Standing แล้วดับเบิลคลิก State Start
  2. ลาก A_INP_WalkFwd_Start มาวางและต่อเข้า State Result
  3. เลือก Sequence Player แล้วปิด Loop Animation คลิป Start ต้องเล่นจากต้นไปปลายเพียงครั้งเดียว
  4. กลับไปเลือก State Start ในหน้า State Machine แล้วเปิด Always Reset on Entry หากตัวเลือกนี้แสดงใน Details เพื่อให้การเข้า Start ครั้งใหม่เริ่มคลิปจากต้น
  5. ขั้นตอนนี้รองรับเฉพาะการทดสอบเริ่มเดินหน้าแบบ Walk ไม่ต้องเพิ่ม Blend Poses, Select หรือคลิปทิศอื่นในตอนนี้

Moving — ใส่ Standing Blend Space

  1. กลับไป SM_Standing แล้วดับเบิลคลิก State Moving
  2. ลาก BS_Standing_Locomotion_2D มาวางในกราฟให้เป็น Blend Space Player แล้วต่อ output เข้า State Result
  3. ต่อ getter ของตัวแปร Direction เข้ากับ input แกนนอน Direction ของ node
  4. ต่อ getter ของตัวแปร GroundSpeed เข้ากับ input แกนตั้ง GroundSpeed ของ node
  5. ยังไม่ต้องเปลี่ยนรายละเอียด Sync ของ node ในขั้นนี้ การตั้ง Sync Group ทำต่อทันทีในข้อ 9.3

Stop — ใส่คลิปหยุดเดินหน้าชั่วคราว

  1. กลับไป SM_Standing แล้วดับเบิลคลิก State Stop
  2. ลาก A_INP_WalkFwd_Stop_LU มาวางและต่อเข้า State Result
  3. เลือก Sequence Player แล้วปิด Loop Animation จากนั้นเปิด Always Reset on Entry ที่ State Stop หากตัวเลือกนี้แสดงใน Details
  4. LU ถูกใช้เพื่อให้กราฟต้นแบบมีคลิป Stop ที่แน่นอนเท่านั้น ความหมาย suffix และการเลือก LU/RU ตามเท้ายังเป็น TBD จึงห้ามถือการเลือกนี้เป็นค่าผลิตจริง

ตรวจ Pose ทั้งสี่ State

  1. เปิด State ทั้งสี่กลับไปตรวจว่าทุกกราฟมี animation node ต่อเข้า State Result และไม่มี Result ที่ปล่อยว่าง
  2. Compile ABP_Player หากพบ error ให้แก้ State Result หรือ Skeleton mismatch ก่อนสร้าง Transition Rules
  3. Save เมื่อ Compile ผ่าน ณ จุดนี้กราฟมี pose ครบแล้ว แต่ยังเปลี่ยน State ไม่ได้จนกว่าจะทำ Transition Rules ในข้อ 9.4

9.3 ผูก Moving เข้ากับ Sync Group

  1. เปิด State Moving แล้วเลือก Blend Space Player ที่เล่น BS_Standing_Locomotion_2D
  2. ใน Details เปิดหมวด Sync แล้วเปลี่ยน Method จาก Do Not Sync เป็น Sync Group ก่อน หากยังเป็น Do Not Sync node จะไม่ยอมใช้ Group Name ที่กำหนด
  3. ตั้ง Group Name เป็นชื่อเสนอ GroundedLocomotion
  4. ตั้ง Group Role เป็น Can Be Leader สำหรับ Blend Space Player ตัวนี้ จากนั้น Compile และ Save
  5. ถ้า Start หรือ Stop ถูกนำเข้ากลุ่มเดียวกันภายหลัง ให้เลือก leader/follower ตามช่วง transition ที่ทดสอบแล้ว อย่าตั้งทุก node เป็น leader พร้อมกัน เพราะ node ที่แย่งความเป็นผู้นำอาจทำให้ phase กระโดด
ถ้าขึ้น error “set to not use named sync groups”: หมายความว่าใส่ 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

Prerequisite ก่อนทำข้อ 9.4: ทำ ข้อ 6.2 และผ่าน จุดตรวจข้อ 6.3 แล้ว หาก bHasMovementInput ยังเป็น false ตลอด การแก้ Transition Rule จะไม่ทำให้ State ออกจาก Idle

เตรียม MovementSpeedDeadZone ก่อนต่อเงื่อนไข

  1. ใน My Blueprint สร้างตัวแปร Float ชื่อเสนอ MovementSpeedDeadZone หากยังไม่มี แล้ว Compile เพื่อให้ตัวแปรปรากฏใน Transition Rule Graph
  2. เลือกตัวแปรนี้ใน My Blueprint แล้วกำหนดค่าที่ช่อง Default Value หลัง Compile หรือกำหนดผ่าน Class Defaults ของ ABP_Player ไม่ต้องสร้าง Set MovementSpeedDeadZone ใน Event Blueprint Update Animation
  3. ระหว่าง PIE เลือก player instance ใน Debug Filter แล้วดู GroundSpeed หลังปล่อย input จน capsule หยุดสนิท ทดสอบซ้ำหลายครั้งและจดค่าสูงสุดที่ยังเกิดขณะถือว่าหยุดเป็น MaxIdleNoise
  4. ทดสอบ analog input ต่ำสุดที่ยังตั้งใจให้แสดง locomotion และจดค่าต่ำสุดเป็น MinIntentionalMoveSpeed
  5. เลือกค่าทดสอบให้เป็นไปตาม MaxIdleNoise < MovementSpeedDeadZone < MinIntentionalMoveSpeed หากไม่มีช่วงว่างระหว่างสองค่านี้ ให้แก้แหล่ง noise หรือ movement settings ก่อน ไม่ควรฝืนเพิ่ม threshold จนกลืนการเดินช้า
  6. ค่าขั้นสุดท้ายต้องทดสอบร่วมกับ braking/deceleration, ทางลาด, network smoothing และ remote proxy จึงยังเป็น TBD เอกสารไม่กำหนดตัวเลขตายตัวก่อนมีผลวัด
ผลต่ออาการค้าง Idle: ค่านี้ไม่ถูกใช้ในกฎ Idle → Start จึงไม่ใช่สาเหตุที่ State ไม่ยอมออกจาก Idle จุดนั้นให้ตรวจ bHasMovementInput, bIsFalling และ Stance ส่วน Dead Zone มีผลตอนเลือกว่าจะเข้า Stop หรือกลับ Idle โดยตรงหลังปล่อย input

Idle → Start — เริ่มเมื่อมี input

bHasMovementInput
AND NOT bIsFalling
AND Stance == Standing
  1. ดับเบิลคลิกวงกลมบนลูกศร Idle → Start
  2. ลาก bHasMovementInput แบบ Get ลงกราฟและต่อเข้า Boolean AND
  3. ลาก bIsFalling แบบ Get ต่อผ่าน Boolean NOT แล้วต่อผลเข้า AND
  4. ลาก Stance แบบ Get ต่อเข้า node เปรียบเทียบ Enum และเลือกค่า Standing จากนั้นต่อผลเข้า AND
  5. ต่อผล AND เข้า Can Enter Transition แล้ว Compile การใช้ input แทน GroundSpeed ทำให้ Start เริ่มทันทีเมื่อกด ไม่ต้องรอ capsule เร่งความเร็วก่อน

Start → Moving — ออกเมื่อคลิป Start ใกล้จบ

  1. กลับไปหน้า SM_Standing แล้วคลิกวงกลมบนลูกศร Start → Moving เพียงครั้งเดียวเพื่อให้ Details แสดงค่าของ transition
  2. เปิด Automatic Rule Based on Sequence Player in State ระบบจะอ่านเวลาคงเหลือจาก Sequence Player ที่ relevant ที่สุดใน State Start
  3. คง Automatic Rule Trigger Time เป็นค่าติดลบ/default ในรอบแรก เพื่อให้เวลาเริ่ม transition อิง Crossfade Duration โดยยังไม่กำหนด timing ใหม่เอง
  4. Compile แล้ว Preview: ลูกศรต้องเกิดหนึ่งครั้งใกล้ท้าย A_INP_WalkFwd_Start และเข้าสู่ Moving
  5. วิธีอัตโนมัตินี้ใช้กับ Sequence Player เดี่ยวจากข้อ 9.2 ก่อน เมื่อข้อ 10 เปลี่ยน Start เป็นหลายคลิป ให้ตรวจ relevant player ใหม่ หรือเปลี่ยนเป็น Notify StartComplete ที่ผ่านการตรวจเฟรมแล้ว

Moving → Stop — ปล่อย input ขณะ capsule ยังไหล

NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
  1. ดับเบิลคลิกวงกลมบนลูกศร Moving → Stop
  2. ต่อ bHasMovementInput ผ่าน Boolean NOT และต่อ bIsFalling ผ่าน Boolean NOT แยกกัน
  3. ลาก GroundSpeed กับ MovementSpeedDeadZone แบบ Get แล้วต่อเข้า Float >
  4. รวมผลทั้งสามด้วย Boolean AND แล้วต่อเข้า Can Enter Transition
  5. เหตุผลที่ใช้ > คือเข้า Stop ตอนผู้เล่นปล่อยปุ่มแต่ capsule ยังมีความเร็ว ไม่รอให้หยุดสนิทแล้วค่อยเล่น animation

Stop → Idle — ออกเมื่อคลิป Stop ใกล้จบ

  1. คลิกวงกลมบนลูกศร Stop → Idle แล้วเปิด Automatic Rule Based on Sequence Player in State
  2. คง Automatic Rule Trigger Time เป็นค่าติดลบ/default ในรอบแรก เพื่อให้อิง Crossfade Duration เช่นเดียวกับ Start → Moving
  3. Compile แล้วตรวจว่า A_INP_WalkFwd_Stop_LU เล่นเพียงครั้งเดียวและกลับ Idle ใกล้ท้ายคลิป ไม่ค้างที่เฟรมสุดท้าย

Moving → Idle — ทางหนีเมื่อหยุดอยู่ใน dead zone แล้ว

NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
  1. ถ้ายังไม่มีลูกศรนี้ ให้ลากจากขอบ State Moving ไป State Idle ลูกศรต้องชี้ไป Idle
  2. เปิด Rule Graph แล้วสร้าง NOT ของ bHasMovementInput, NOT ของ bIsFalling และ Float <= ระหว่าง GroundSpeed กับ MovementSpeedDeadZone
  3. รวมทั้งสามด้วย AND แล้วต่อเข้า Result เส้นนี้จะป้องกันไม่ให้ค้างใน Moving เมื่อความเร็วตกถึง dead zone ก่อนที่กฎ Moving → Stop จะเป็นจริง

Start → Stop — ปล่อย input ระหว่าง Start แต่ยังมีความเร็ว

NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
  1. ลาก transition จาก Start ไป Stop แล้วสร้างกราฟแบบเดียวกับ Moving → Stop
  2. เลือกวงกลมของ Start → Stop และตั้ง Priority Order ให้มีลำดับก่อน Start → Moving เพราะเมื่อผู้เล่นปล่อย input ระหว่าง Start ต้องยกเลิกการไป Moving

Start → Idle — ปล่อย input ระหว่าง Start และหยุดแล้ว

NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
  1. ลาก transition จาก Start ไป Idle แล้วสร้างกราฟแบบเดียวกับ Moving → Idle
  2. ตั้ง Priority Order ให้มีลำดับก่อน Start → Moving เช่นเดียวกับ Start → Stop สองเส้นนี้ไม่ชนกันเพราะเส้นหนึ่งใช้ > และอีกเส้นใช้ <=

Stop → Start — กดเคลื่อนที่ใหม่ระหว่าง Stop

bHasMovementInput
AND NOT bIsFalling
AND Stance == Standing
  1. ลาก transition จาก Stop กลับไป Start
  2. สร้าง Rule Graph แบบเดียวกับ Idle → Start แล้วต่อผลเข้า Can Enter Transition
  3. ตั้ง Priority Order ให้มีลำดับก่อน Stop → Idle เพื่อให้ input ใหม่ขัดจังหวะ Stop ได้ทันทีโดยไม่ต้องรอคลิปจบ
  4. ตรวจว่า 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

  1. กลับไป State Graph ของ Standing ตรวจว่า output จาก SM_Standing ต่อเข้า State Result
  2. กลับไป SM_Posture ตรวจว่า Entry ต่อเข้า Standing และ State นี้แสดงว่า pose ถูกประเมินอยู่
  3. กลับไป AnimGraph ตรวจว่า output จาก SM_Posture เดินทางต่อถึง Output Pose โดยไม่มีสายขาด
  4. วาง Inertialization ตามสถาปัตยกรรมในข้อ 4 หลัง pose จาก posture state machine หาก node นี้ยังไม่มีในกราฟ
  5. Compile และ Save จากนั้นตรวจ Compiler Results ต้องไม่มี error หรือ warning ใหม่จาก state/transition ที่เพิ่ม
  6. ยังไม่ต้องสร้าง Airborne หรือ transition ที่อ่าน bIsFalling ในขั้นตอนนี้ ให้เพิ่มพร้อม jump/fall/land ตามข้อ 14 เพื่อไม่ให้มี State ว่างค้างอยู่ในกราฟ

9.6 ทดสอบใน PIE แบบทีละกรณี

  1. เริ่ม PIE แล้วเลือก instance ผู้เล่นใน Debug Filter ของ ABP_Player เปิดหน้าต่าง State Machine ค้างไว้เพื่อดู state ที่ active
  2. ไม่กด input: ต้องอยู่ Idle และคลิปวนโดยไม่กระโดด
  3. กดเดินหน้าจากหยุด: ต้องเห็น Idle → Start → Moving เพียงหนึ่งรอบ ไม่สลับ Start/Moving ทุกเฟรม
  4. ปล่อย input ขณะยังเคลื่อนที่: ต้องเห็น Moving → Stop → Idle และ Stop เริ่มก่อน GroundSpeed เป็นศูนย์
  5. กดใหม่ระหว่าง Stop: ต้องออกจาก Stop ได้โดยไม่รอคลิปจบ และไม่ค้างใน pose สุดท้าย
  6. สลับ Walk/Jog/Run ซ้ำ ๆ แล้วดูว่าเท้าข้างที่รับน้ำหนักไม่เปลี่ยนกลางจังหวะ ถ้ายังมี foot sliding ให้แยกสาเหตุระหว่าง marker ผิด phase กับ authored speed ไม่ตรง movement speed
  7. ยังไม่ทดสอบ Jump/Airborne ในขั้นตอนนี้ เพราะ State และ transition ดังกล่าวจะสร้างในข้อ 14
  8. ทดสอบ 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เลือกคลิปแบบหมุนร่างหรือแบบ strafeCharacter + AnimInstance implement แล้ว ตามข้อ 10.7
Gaitเลือก family Walk/Jog/RunTBD-LOCO-006 implemented; native latch StartGait และเลือกคลิปที่ SelectedStartAnimation
FootPhaseเลือก suffix _LU/_RU เมื่อ clip family ต้องใช้TBD-LOCO-002 contract; runtime ประมาณจาก foot_l/foot_r จนกว่าจะมี Notify ที่ตรวจเฟรมแล้ว
ผลลัพธ์ของข้อ 10 [VERIFIED 2026-09-01]: State Start bind SelectedStartAnimation เข้า pin Sequence ของ Sequence Player ที่ต่อกับ State Result แล้ว การทดสอบ PIE ตามข้อ 10.8 ผ่านครบทั้งเก้า bucket, Gait, RotationMode, latch และทางออก Idle → Start → Moving

10.2 ตรวจ DesiredDirection ก่อนสร้างตัวเลือกคลิป

  1. เปิด ABP_Player แล้ว Compile ให้ไม่มี error จากนั้นเริ่ม PIE
  2. กลับมาที่หน้าต่าง Animation Blueprint เลือกตัวละครผู้เล่นที่กำลังควบคุมจาก Debug Filter ห้ามปล่อยเป็น No debug object selected เพราะค่าบนสายกราฟจะไม่วิ่ง
  3. เปิด Event Graph คลิกขวาที่ getter ของ DesiredDirection แล้วใช้ Watch this value หรือดูค่าจากแผงตัวแปรระหว่าง debug
  4. หันตัวละครไปทิศหนึ่งแล้วกดเดินหน้า ค่าเป้าหมายควรอยู่ใกล้
  5. กดเฉียง, ด้านข้าง และด้านหลังทีละทิศ ตรวจว่าขนาดมุมเข้าใกล้ 45°, 90°, 135° และ 180°
  6. ยืนยันว่าค่าที่เห็นเป็นไปตาม contract TBD-LOCO-004: ค่าบวก = ขวา และค่าลบ = ซ้าย หากผลใน Preview กลับด้าน ให้แก้ตำแหน่ง sample/asset mapping ที่ผิด ห้าม negate Direction เป็นจุด ๆ
  7. ถ้ากดทิศต่างกันแต่ค่าค้างที่ 0 ให้กลับไปแก้สาย Acceleration → Calculate Direction → Set DesiredDirection ในข้อ 6.4 ก่อน ห้ามสร้างระบบเลือกคลิปต่อจนกว่าค่านี้จะเปลี่ยนตาม input

10.3 แปลงมุมเป็น Direction Bucket

ขั้น A — สร้าง Enum asset

  1. ใน Content Browser ไปยังโฟลเดอร์ Animation ที่เก็บ asset ของระบบ locomotion คลิกขวาพื้นที่ว่างแล้วเลือก Blueprints → Enumeration
  2. ตั้งชื่อเสนอ E_StartDirectionBucket แล้วเปิด asset
  3. เพิ่มค่าเรียงตามนี้: Forward, Positive45, Negative45, Positive90, Negative90, Positive135, Negative135, Positive180, Negative180
  4. ใช้คำว่า Positive/Negative ไปก่อน อย่าใช้ Left/Right จนกว่าจะผ่านการตรวจ signed direction ในข้อ 10.2 แล้ว Save enum

ขั้น B — สร้างตัวแปรใน ABP_Player

  1. เปิด ABP_Player ใน My Blueprint กด + ที่ Variables แล้วสร้าง StartDirectionBucket
  2. เปลี่ยน Variable Type เป็น E_StartDirectionBucket, Compile แล้วตั้ง Default Value เป็น Forward
  3. สร้าง Boolean bHadMovementInput, Compile แล้วตั้ง Default Value เป็น false ตัวแปรนี้คือค่าจากเฟรมก่อน ไม่ใช่สำเนาที่ตั้งพร้อม bHasMovementInput

ขั้น C — สร้าง ResolveStartDirectionBucket

  1. ใน My Blueprint กด + ที่ Functions ตั้งชื่อเสนอ ResolveStartDirectionBucket
  2. เลือก Function Entry แล้วเพิ่ม Input ชื่อ Angle ชนิด Float
  3. เลือก Return Node แล้วเพิ่ม Output ชื่อ Bucket ชนิด E_StartDirectionBucket
  4. ลาก Angle เข้า node Abs (Float) แล้วใช้ผลเดียวกันเข้าชุดเปรียบเทียบ < ตามตารางด้านล่าง
  5. สร้าง Branch ไล่จากมุมน้อยไปมาก: < 22.5 ก่อน จากนั้น < 67.5, < 112.5 และ < 157.5 เฉพาะสาย False ของข้อก่อนหน้า
  6. สาย True ของ < 22.5 คืน Forward ส่วนช่วงอื่นให้เปรียบเทียบ Angle >= 0.0 แล้วคืนค่า Positive ทาง True และ Negative ทาง False
  7. ช่วงสุดท้ายที่ไม่ผ่าน < 157.5 ไม่ต้องเปรียบเทียบขนาดอีก ให้ใช้เครื่องหมาย Angle เลือก Positive180/Negative180
  8. ถ้าต้องการแยกปลายทางให้อ่านง่าย ให้เพิ่ม Return Node สำหรับแต่ละผลลัพธ์และกำหนดค่า Bucket ที่ node นั้น แล้ว Compile และ Save
Abs(Angle)ขนาดมุมBucket ที่คืน
< 22.5Forward
< 67.545°Positive45 หรือ Negative45 ตามเครื่องหมาย Angle
< 112.590°Positive90 หรือ Negative90
< 157.5135°Positive135 หรือ Negative135
ค่าที่เหลือจนถึง 180180°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
ตรวจ Function แยกก่อนต่อ Event Graph: วาง call node ชั่วคราวหรือใช้ Blueprint Debugger ตรวจ input ตัวอย่าง 0, 30, -80, 140, -179 ต้องได้ Forward, Positive45, Negative90, Positive135, Negative180 ตามลำดับ ตัวเลขเหล่านี้เป็น test cases ของการแบ่งช่วง ไม่ใช่ tuning value ของ movement

10.4 จำ Bucket เฉพาะตอนเริ่มกด

ห้าม Set StartDirectionBucket จาก DesiredDirection ทุกเฟรมระหว่างอยู่ใน Start เพราะถ้าผู้เล่นขยับ stick คลิปอาจสลับกลางทาง ให้จับค่าที่ขอบ false → true ของ movement input และต้องเรียง execution ตามขั้นต่อไปนี้:

  1. หา Set bHasMovementInput ใน Event Blueprint Update Animation จากข้อ 6.2 แล้วต่อ exec ออกจาก node นี้เข้า Sequence เพิ่ม pin ให้มี Then 0, Then 1 และ Then 2
  2. Then 0 ใช้อัปเดตทิศของเฟรมปัจจุบัน: ต่อเข้า Branch ที่ Condition = bHasMovementInput ทาง True ต่อ Calculate Direction โดยใช้ Acceleration และ Actor Rotation แล้ว Set DesiredDirection จากนั้นค่อยอัปเดต DirectionDelta ตามข้อ 6.4 ทาง False ปล่อยให้ DesiredDirection เก็บค่าล่าสุด
  3. สร้าง NOT Boolean จาก getter bHadMovementInput แล้วนำผลไป AND กับ getter bHasMovementInput ตั้งชื่อความหมายของสายนี้ว่า JustPressed; ไม่จำเป็นต้องสร้างเป็นตัวแปรใหม่
  4. Then 1 ต่อเข้า Branch ที่ Condition = JustPressed ทาง True เรียก ResolveStartDirectionBucket โดยต่อ getter DesiredDirection เข้า Angle แล้วต่อ output Bucket เข้า Set StartDirectionBucket; ทาง False ไม่ต้อง Set
  5. Then 2 ต่อเข้า Set bHadMovementInput และต่อ getter bHasMovementInput เข้าค่าใหม่ ขั้นนี้ต้องอยู่ท้ายสุดเพื่อให้ Then 1 ยังอ่านค่า previous frame ได้
  6. Compile และ Save จากนั้น PIE แล้ว Watch bHasMovementInput, bHadMovementInput, DesiredDirection และ StartDirectionBucket พร้อมกัน
  7. ทดสอบ กด → ถือ → ปล่อย → กดทิศใหม่: 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
ลำดับห้ามสลับ: ถ้า Set bHadMovementInput ก่อน Branch ของ Then 1 นิพจน์ JustPressed จะเป็น false ตลอด; ถ้า Resolve ก่อน Then 0 ระบบจะอ่าน DesiredDirection ของการกดครั้งก่อน

10.5 ต่อ Walk clips ใน State Start

สถานะปัจจุบัน [SUPERSEDED BY VERIFIED DYNAMIC PLAYER]: ขั้น A–B ด้านล่างบันทึก baseline เดิมที่ใช้พิสูจน์ bucket ด้วย Walk + VelocityDirection ปัจจุบัน output ของ State Start ใช้ SelectedStartAnimation → Sequence Player → State Result; Blend Poses by E_StartDirectionBucket เดิมไม่ได้ต่อกับ State Result แล้ว

ขั้น A — baseline เดิมสำหรับสร้างตัวเลือก pose

  1. เปิด SM_Standing → Start เก็บ Sequence Player เดิมที่ต่อกับ State Result ไว้เป็น fallback จนกว่ากราฟใหม่ Compile ผ่าน
  2. คลิกขวาพื้นที่ว่าง ค้นหา Blend Poses by E_StartDirectionBucket แล้ววาง node; ถ้าค้นไม่พบ ให้ Compile/Save enum และ ABP ก่อน แล้วเปิดเมนูค้นหาใหม่
  3. node เริ่มต้นอาจยังไม่แสดง pose ของทุก enum ให้คลิกขวาที่ node แล้วเลือกคำสั่งเพิ่ม pose/pin สำหรับ Forward, Positive45, Negative45, Positive90, Negative90, Positive135, Negative135, Positive180 และ Negative180 ชื่อคำสั่งย่อยอาจแสดงเป็น Add Pose Pin หรือชื่อ enum entry ตาม UI ของ editor
  4. ลาก getter StartDirectionBucket จาก My Blueprint มาต่อเข้า Active Enum Value
  5. ลาก A_INP_WalkFwd_Start มาวางให้เป็น Sequence Player ปิด Loop Animation แล้วต่อเข้า pose Forward
  6. สร้าง Sequence Player ของ A_INP_WalkFwd_Start เพิ่มเป็น fallback สำหรับ Default Pose และทุก bucket ที่ยังไม่ได้ยืนยัน L/R อย่าปล่อย pose ที่อาจถูกเลือกว่าง
  7. ต่อ output ของ Blend Poses by E_StartDirectionBucket เข้า State Result แล้ว Compile หากผ่านจึงถอดสายของ Sequence Player เดิมออกจาก Result

ขั้น B — แทน fallback ด้วยคลิปจริงทีละคู่

  1. เริ่มจากคู่ 45° ก่อน เปิด animation _45_L และ _45_R ใน Animation Editor ดูการหมุนของ root/สะโพกและก้าวแรก
  2. ใช้ mapping ที่ยืนยันจาก PIE แล้วว่า DesiredDirection เครื่องหมายบวกใช้คลิป suffix _R และเครื่องหมายลบใช้ _L: ต่อ _45_R เข้า Positive45 และ _45_L เข้า Negative45
  3. Compile และ PIE ทดสอบทั้งสองฝั่งก่อนทำคู่ถัดไป ถ้าถูกจึงทำ 90°, 135° และ 180° ด้วยวิธีเดียวกัน
  4. Sequence Player ทุกตัวต้องตั้ง Loop Animation=false และไม่เปิดตัวเลือกที่ตัด node ออกจาก relevancy test เพราะ transition อัตโนมัติจำเป็นต้องรู้เวลาคงเหลือของ clip ที่ active
  5. เลือก State Start ที่หน้า State Machine แล้วตรวจใน Details ว่า Always Reset on Entry เปิดอยู่ เพื่อให้กดครั้งใหม่แล้วคลิปเริ่มจากต้น
  6. ยังไม่ต้องกำหนด Blend Time ใหม่ในรอบแรก ใช้ค่าเดิมเพื่อพิสูจน์การเลือก clip ก่อน แล้วค่อย tune จากผล visual test โดยไม่เดาตัวเลขล่วงหน้า
Retarget prerequisite ที่ตรวจพบ: 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

  1. กลับหน้า SM_Standing คลิกลูกศร Start → Moving แล้วตรวจว่า Automatic Rule Based on Sequence Player in State ยังเปิดอยู่
  2. PIE ทีละ bucket แล้วดูว่า transition เกิดใกล้ท้ายคลิปที่กำลังเล่นจริง ไม่ใช่อ้างเวลาจาก fallback หรือ player ที่ inactive
  3. ถ้าบาง bucket ออกทันทีหรือค้าง ให้ตรวจ relevancy ของ Sequence Player และสาย pose ก่อน
  4. ถ้าการใช้ relevant player ยังไม่เสถียร ให้เพิ่ม Anim Notify ชื่อเสนอ StartComplete ในทุก Start clip ณ เฟรมที่ตรวจภาพแล้ว และเปลี่ยน transition ให้ใช้ event/flag จาก notify เดียวกัน ห้ามวาง notify ด้วยเลขเฟรมที่เดา

10.6 รายชื่อคลิป VelocityDirection

family นี้ใช้เมื่อ Character หันตามทิศ movement คลิป angled Start มีหน้าที่เริ่มก้าวพร้อมหมุนร่าง:

BucketWalk prototypeสิ่งที่ต้องกรอกหลังทดสอบเพิ่มภายหลังเมื่อ Gait พร้อม
ForwardA_INP_WalkFwd_Startยืนยันว่าค่าใกล้ 0° เล่นคลิปนี้A_INP_JogFwd_Start, A_INP_RunFwd_Start
Positive45/Negative45A_INP_WalkFwd_Start_45_R/LPositive = R, Negative = LA_INP_JogFwd_45_Start_L/R, A_INP_RunFwd_45_Start_L/R
Positive90/Negative90A_INP_WalkFwd_Start_90_R/LPositive = R, Negative = LA_INP_JogFwd_90_Start_L/R, A_INP_RunFwd_90_Start_L/R
Positive135/Negative135A_INP_WalkFwd_Start_135_R/LPositive = R, Negative = LA_INP_JogFwd_135_Start_L/R, A_INP_RunFwd_135_Start_L/R
Positive180/Negative180A_INP_WalkFwd_Start_180_R/LPositive = R, Negative = LA_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

สถานะ implementation (2026-09-01) [VERIFIED]: runtime contract ของ Gait/RotationMode/Start latch ทำงานใน 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 หมุน actor
  • AimMode == ShoulderAim หรือ ADS ใช้ RotationMode = LookingDirection: Character ปิด bOrientRotationToMovement และ gameplay rotation system ต้องหัน actor ตาม aim/control yaw ก่อนที่ ABP จะเลือก strafe pose
  • AimMode ที่ 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
BucketWalk LookingJog LookingRun Looking
ForwardA_INP_WalkFwd_StartA_INP_JogFwd_StartA_INP_RunFwd_Start
Positive45 / Negative45WalkFwdRt/Lt_45_StartJogFwdRt/Lt_45_StartRunFwdRt/Lt_45_Start
Positive90 / Negative90WalkFwdRt/Lt_90_StartJogFwdRt/Lt_90_StartRunFwdRt/Lt_90_Start
Positive135 / Negative135WalkBwdRt/Lt_45_StartJogBwdRt/Lt_45_Startfallback RunFwdRt/Lt_90_Start เพราะยังไม่มี Run backward start
Positive180 / Negative180WalkBwd_StartJogBwd_Startfallback RunFwdRt/Lt_90_Start
การ bind ABP [VERIFIED 2026-09-01]: State 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 ของ replicated CurrentGait
  • ลำดับตัดสิน 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 directionA_RunFwd_45_Start_L = Idle หมุนซ้าย 45° แล้วเข้า Run forward
_Rหมุนหรือเปลี่ยนทิศไปทางขวาDirection bucket / signed turn directionA_RunFwd_90_Start_R = Idle หมุนขวา 90° แล้วเข้า Run forward
_LULeft Up — เท้าซ้ายยกอยู่ใน locomotion loop cycle ณ จุดเข้า transitionFootPhase = LeftUpA_RunFwd_Stop_LU
_RURight Up — เท้าขวายกอยู่ใน locomotion loop cycle ณ จุดเข้า transitionFootPhase = RightUpA_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 แตก

  1. เพิ่ม ELambeerGait, replicated CurrentGait และ getter ใน Locomotion Component ก่อน
  2. เพิ่ม Gait, StartGait, RotationMode และ FootPhase ให้ AnimInstance/ABP แล้ว migrate graph ให้ใช้ค่าใหม่
  3. ค้น reference ของ bIsSprinting ทั้ง C++ และ Blueprint assets แทนด้วย Gait == Run และ Compile/PIE/Network test ให้ผ่าน
  4. ลบ bIsSprinting หลังไม่มี reference เหลือเท่านั้น เพื่อหลีกเลี่ยง broken pins ใน Blueprint

10.8 ทดสอบทีละทิศ [VERIFIED 2026-09-01]

หลักการของการทดสอบคือ เลือกหนึ่งทิศ กดหนึ่งครั้ง แล้วตรวจผลให้ครบก่อนเปลี่ยนทิศ เริ่มจาก Forward ซึ่งตรวจง่ายที่สุด จากนั้นค่อยเพิ่มทิศเฉียง ด้านข้าง และด้านหลัง หากทิศใดไม่ผ่าน ให้หยุดแก้ทิศนั้นก่อนเพื่อไม่ให้หลายปัญหาปนกัน

ผลทดสอบ: ผู้ใช้ยืนยันว่าขั้นตอนและ acceptance criteria ในข้อ 10.8 ผ่านเรียบร้อยแล้วเมื่อ 2026-09-01 ครอบคลุมเก้า direction buckets, การ latch StartDirectionBucket/StartGait, การเลือกคลิปตาม Gait และ RotationMode, suffix ซ้าย/ขวา และลำดับ Idle → Start → Moving

ขั้นที่ 1 — เตรียมหน้าจอ Debug

  1. เริ่ม PIE แล้วเลือกตัวละครที่กำลังควบคุมใน Debug Filter
  2. เปิด SM_Standing ให้มองเห็นว่า State ใดกำลังทำงานอยู่
  3. Watch ค่าต่อไปนี้: DesiredDirection, bHasMovementInput, bHadMovementInput และ StartDirectionBucket
  4. ก่อนกดรอบใหม่ ให้ปล่อย movement input อย่างน้อยหนึ่งเฟรม และยืนยันว่า bHadMovementInput = false

ขั้นที่ 2 — ทดสอบหนึ่งทิศตามลำดับนี้

  1. เริ่มจาก Idle แล้วกดทิศที่ต้องการทดสอบหนึ่งครั้ง
  2. ดู DesiredDirection ว่าได้มุมตามทิศที่กด
  3. ดู StartDirectionBucket ว่าเปลี่ยนเป็น bucket ที่ตรงกับมุมนั้น
  4. ดูภาพ animation ว่าเลือกคลิป Start ตรงทิศ และหากชื่อคลิปมี _L/_R ต้องหมุนไปด้านที่ถูกต้อง
  5. ดู State Machine ว่าเดินตามลำดับ Idle → Start → Moving เพียงหนึ่งรอบ คลิป Start ต้องไม่ Loop, ไม่ถูกข้ามทันที และไม่ค้างอยู่ใน Start
  6. ปล่อย input ให้กลับไป Idle แล้วจึงเริ่มทดสอบทิศถัดไป
ลำดับที่แนะนำมุมที่ตรวจBucket ที่ต้องเห็น
1. เดินหน้าใกล้ Forward
2. เฉียงหน้าใกล้ +45° และ -45°Positive45 และ Negative45
3. ด้านข้างใกล้ +90° และ -90°Positive90 และ Negative90
4. เฉียงหลังใกล้ +135° และ -135°Positive135 และ Negative135
5. ด้านหลังใกล้ +180° และ -180°Positive180 และ Negative180
Signed mapping ที่ยืนยันแล้ว: สำหรับ Directional Start ของโปรเจกต์นี้ 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 ค้าง แล้วเปลี่ยนทิศระหว่าง StartStartDirectionBucket ต้องคงค่าเดิมจนคลิป 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 หรือไม่มี poseSelectedStartAnimation อาจเป็น null หรือ pin Sequence หลุดตรวจ fallback ใน native selector และสาย SelectedStartAnimation → Sequence Player
ข้อ 10 [VERIFIED 2026-09-01]: ทั้ง 9 bucket เลือก animation ได้; StartDirectionBucket และ StartGait ถูกจับเพียงครั้งเดียวต่อการเริ่มกด; Start เล่นครั้งเดียวแล้วเข้า Moving; LookingDirection ตรงกับ gameplay rotation mode; และ _L/_R ไม่ปะปนกับ _LU/_RU

11. 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
Architecture contract [CONFIRMED BASELINE v0.1 · 2026-09-02 · TBD-LOCO-005]: selector กลางใน C++ เป็นผู้ตรวจ event, latch ข้อมูล และเลือก transition เพียงค่าเดียว ส่วน Animation Blueprint บริโภค 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 ก่อนถือว่าพร้อม production

11.1 เตรียมข้อมูลและกติกาการเลือกเพียงครั้งเดียว

สถานะ implementation 2026-09-03 — INTEGRATED / VISUAL GATE PENDING: เพิ่ม native data contract และ Stop selector ใน 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
ลำดับอัปเดตที่ปลอดภัย: อัปเดต velocity/direction → ตรวจ event เริ่ม transition → latch inputs → resolve animation หนึ่งครั้ง → lock → เข้า State → unlock เมื่อออกจาก State อย่า resolve asset หลัง Sequence Player เริ่มเล่นแล้ว
Baseline v0.1 [CONFIRMED 2026-09-02 · TBD-LOCO-005]: Moving Turn enter 30°; bucket 45/90/135/180 แบ่งที่ 67.5°/112.5°/157.5°; Pivot enter 125° พร้อม dot < -0.35; Spin enter 165°. Re-entry hysteresis คือ Turn 7.5° และ Pivot/Spin 10°. Cooldown คือ Turn 0.15 s, Pivot 0.25 s, Spin 0.35 s; commit window ตาม normalized clip time คือ 10%/15%/20%. Blend เข้า/ออกคือ Turn 0.10/0.12 s, Pivot 0.08/0.12 s และ Spin 0.08/0.15 s. Thresholds อยู่ใน defaults ของ ABP_Player; hysteresis/cooldown/commit รวมศูนย์ใน native selector และ blend อยู่ที่ transition nodes ไม่กระจายเป็น magic number ใน rule graph

11.2 Stop — ทำ baseline นี้ให้ผ่านก่อน

สถานะ ABP/asset 2026-09-01 — INTEGRATED / VISUAL GATE PENDING: ใน 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
  1. ในเฟรม JustReleased ให้บันทึก Gait, Direction, FootPhase และ GroundSpeed ก่อนอัปเดตค่าประวัติ input สำหรับเฟรมถัดไป
  2. แปลง Direction ที่ latch ไว้เป็น bucket ชุดเดียวกับ Blend Space: Forward, ForwardLeft/Right, SideLeft/Right, BackwardLeft/Right และ Backward
  3. ใช้ Gait ปัจจุบันเป็นเพดาน แล้วเทียบ TransitionEntrySpeed กับ Walk/Jog/Run speed หลังคูณ encumbrance เลือก family ที่ใกล้ที่สุด เช่นเพิ่งออกตัวด้วย Gait Jog แต่ปล่อยตอนความเร็วยังใกล้ Walk ให้เลือก Walk Stop; หาก Gait == Run แต่ทิศอยู่ใน backward hemisphere ให้ใช้ Jog fallback เพราะไม่มี Run backward Stop ทั้งสองกรณีไม่เปลี่ยน authoritative Gait
  4. Forward/Backward family ที่มี _LU/_RU ให้เลือกจาก FootPhase; ถ้า FootPhase == Unknown ให้ใช้ fallback ที่ทีมกำหนดและเก็บ warning/debug flag แทนการสุ่มทุกเฟรม
  5. ทิศเฉียง/ด้านข้างใช้คลิปที่ชื่อระบุทิศและไม่มี foot suffix เช่น A_INP_JogFwdLt_45_Stop ห้ามต่อ FootPhase เข้า selector ส่วนนั้น
  6. ต่อ SelectedTransitionAnimation เข้าพิน Sequence ของ State Stop, ปิด Loop และเปิด Always Reset on Entry
  7. คง Transition Rules และ cancel paths จากข้อ 9.4: ปล่อยแล้วหยุดสนิทอาจไป Idle โดยตรง และ input ใหม่ระหว่าง Stop ต้องออกไป Start/Moving ก่อนรอคลิปจบ
ทิศ/Gaitคลิปที่ตรวจพบกติกา
Forward · Walk/Jog/RunWalkFwd_Stop_LU/RU, JogFwd_Stop_LU/RU, RunFwd_Stop_LU/RUเลือก _LU/_RU จาก FootPhase
Backward · Walk/JogWalkBwd_Stop_LU/RU, JogBwd_Stop_LU/RURun ใช้ Jog fallback; ไม่มี Run backward Stop
Forward diagonal/sideWalk/Jog/RunFwdLt/Rt_45_Stop, Walk/Jog/RunFwdLt/Rt_90_Stopเลือก signed side จาก Direction
Backward diagonal/sideWalk/JogBwdLt/Rt_45_Stop, Walk/JogBwdLt/Rt_90_StopRun ใช้ Jog fallback
FastWalkFwd/Bwd_Stop_Fast, JogFwd/Bwd_Stop_Fast, RunFwd_Stop_Fastยังไม่ใช้จน resolve entry-speed/deceleration threshold
อย่าเลือก Stop จาก Direction หลังปล่อยนานแล้ว: เมื่อ velocity ใกล้ศูนย์ ค่า Calculate Direction อาจกระโดดหรือกลับเป็นค่า default การ latch ในเฟรม JustReleased ทำให้ชื่อคลิปไม่สลับกลาง Stop

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
  1. ทำ Pivot ใน Walk หรือ Jog ก่อนหนึ่งคู่ เช่น Forward → Backward และ Backward → Forward แล้วทดสอบ lock/cancel ให้ผ่านก่อนเติมทิศเฉียง
  2. เลือก source/target family จากทิศ velocity เดิมและ desired direction ใหม่ ไม่เลือกจาก target เพียงด้านเดียว เพราะชื่อ asset encode ทั้งสองฝั่ง เช่น JogFwd2JogBwd
  3. เลือก _L/_R จากเครื่องหมาย signed delta ตามข้อ 10.7 ไม่ใช้ FootPhase
  4. Walk, Jog และ Crouch มี Pivot family ที่ตรวจพบ; ไม่พบ Run Pivot จึงให้ Run reversal ไป Spin หรือ Jog fallback ตาม selector ที่อนุมัติ
  5. ใน State Pivot ใช้ Sequence Player ที่เปิดพิน Sequence, ปิด Loop, เปิด Always Reset on Entry และตั้งทางออกหลักกลับ Moving เมื่อคลิปใกล้จบ
  6. เพิ่ม cancel path ไป Airborne/Posture ก่อน และ Stop เมื่อผู้เล่นปล่อย input กลาง Pivot; priority ต้องสูงกว่าทางจบอัตโนมัติ
การเปลี่ยนทิศตัวอย่าง asset จริง
Forward → BackwardA_INP_WalkFwd2Bwd_Pivot_L/R, A_INP_JogFwd2JogBwd_Pivot_L/R
Backward → ForwardA_INP_WalkBwd2Fwd_Pivot_L/R, A_INP_JogBwd2JogFwd_Pivot_L/R
Forward → Backward diagonalA_INP_JogFwd2JogBwdLt_45_Pivot_L/R, A_INP_JogFwd2JogBwdRt_90_Pivot_L/R
Diagonal reversalA_INP_WalkFwdLt2BwdLt_45_Pivot_L/R, A_INP_CrouchBwdRt2FwdRt_90_Pivot_L/R
สถานะ implementation 2026-09-02 — INTEGRATED / VISUAL GATE PENDING: Retargeted Walk Pivot 20 คลิปและ Jog Pivot 20 คลิปอยู่ใต้ /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

  1. เริ่มจาก 45° ใน Walk/Jog ก่อน เพราะแยกจาก Pivot ได้ง่ายที่สุด แล้วค่อยเพิ่ม 90°, 135° และ 180° หลัง threshold bands ไม่ทับกัน
  2. ตรวจว่า source velocity อยู่ใน forward region ก่อนเลือก Fwd_*_Turn; ถ้ากำลังเคลื่อนหลังหรือด้านข้างให้ใช้ Blend Space/Pivot/Spin แทนตาม coverage
  3. ใช้ขนาด Abs(DirectionDelta) เลือก angle bucket และเครื่องหมายเลือก _L/_R; Baseline v0.1 เข้าเมื่อ 30° และแบ่ง bucket ที่ 67.5°/112.5°/157.5° พร้อม re-entry hysteresis 7.5°
  4. เมื่อเข้า State ให้ latch animation และ target direction ห้ามสลับจาก 45 เป็น 90 กลางคลิปเพราะ input ขยับต่อ
  5. ทางออกหลักกลับ Moving; ถ้า delta เพิ่มถึง reversal region ระหว่างคลิป ให้รอ cancel window ที่กำหนดหรือออกผ่าน Spin/Pivot ตาม policy ที่อนุมัติ ห้ามให้สอง State แข่งขันกันทุกเฟรม
Familyคลิปที่ตรวจพบ
WalkA_INP_WalkFwd_45/90/135/180_Turn_L/R
JogA_INP_JogFwd_45/90/135/180_Turn_L/R
RunA_INP_RunFwd_45/90/135/180_Turn_L/R
CrouchA_INP_CrouchFwd_45/90/135/180_Turn_L/R
สถานะ implementation 2026-09-02 — INTEGRATED / VISUAL GATE PENDING: Retargeted Moving Turn ครบ 45°/90°/135°/180° สำหรับ Walk, Jog และ Run รวม 24 คลิป Native resolver เปิดใช้ทั้งสาม gait และ signed side ตาม Baseline v0.1. State 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

  1. ทำ Pivot และ Moving Turn ให้เสถียรก่อน แล้วจึงเปิด selector ของ Spin เพื่อแยกสาเหตุเวลาทดสอบ
  2. Forward ↔ Backward มีคลิป 180° แบบ _L/_R; diagonal cross-over มีคลิป 45°/90° ที่ชื่อ encode source และ target
  3. Run Spin ที่ตรวจพบเปลี่ยนจาก Run Forward ไป Jog Backward เช่น A_INP_RunFwd_Spin_JogBwd_180_L/R หลังคลิปจบ animation family จึงต้องสอดคล้องกับ Jog fallback โดยไม่แก้ authoritative Gait ใน ABP
  4. Spin ใช้ transition lock เช่นเดียวกับ Pivot และต้องมี cancel path สำหรับ Airborne, posture change และ input release
  5. 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 จริง
WalkA_INP_WalkFwd_Spin_Bwd_180_L/R, A_INP_WalkBwdLt_Spin_FwdRt_45/90
JogA_INP_JogFwd_Spin_Bwd_180_L/R, A_INP_JogFwdRt_Spin_BwdLt_45/90
Run → JogA_INP_RunFwd_Spin_JogBwd_180_L/R, A_INP_RunFwdLt_Spin_JogBwdRt_45/90
CrouchA_INP_CrouchFwd_Spin_Bwd_180_L/R, A_INP_CrouchBwdRt_Spin_FwdLt_45/90
สถานะ implementation 2026-09-02 — INTEGRATED / VISUAL GATE PENDING: Retargeted Spin coverage ทั้ง source folder ครบ 42 คลิปไว้ใต้ /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 ไม่ให้แข่งขันกัน

สถานะ 2026-09-02 — INTEGRATED / VISUAL GATE PENDING: 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 error
SM_Standing
├─ Idle
├─ Start
├─ Moving
├─ Stop
├─ Pivot
├─ MovingTurn
└─ Spin

Moving → Stop       เมื่อ TransitionType == Stop
Moving → Pivot      เมื่อ TransitionType == Pivot
Moving → MovingTurn เมื่อ TransitionType == MovingTurn
Moving → Spin       เมื่อ TransitionType == Spin
  1. สร้าง State ใหม่ทีละ State เริ่ม Pivot แล้ว Compile/Test ก่อนเพิ่ม MovingTurn และ Spin ทุก State ใช้ Sequence Player ที่เปิดพิน Sequence ตัวเดียวต่อ State และรับค่าจาก SelectedTransitionAnimation
  2. ปิด Loop และเปิด Always Reset on Entry ทุก transition State
  3. Priority confirmed: selector คืน TransitionType เพียงค่าเดียวต่อ event ตามลำดับ Airborne/Posture cancel → Stop เมื่อไม่มี input → Spin เฉพาะ coverage ที่อนุมัติ → Pivot สำหรับ reversal อื่น → Moving Turn → None
  4. ใช้ enter threshold กับ re-entry hysteresis แยกกันเพื่อไม่ให้ค่าใกล้ขอบทำ State flapping; Baseline v0.1 ใช้ Turn 7.5° และ Pivot/Spin 10° และต้องกลับออกจาก enter band ก่อน rearm
  5. 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 รอบถัดไปแทนการเปลี่ยนค่ากลางแบบเดา
  6. เมื่อ State จบ ให้ clear lock/type หลังระบบอ่านทางออกแล้ว ไม่ clear ก่อนหน้าเพราะอาจกลับเข้า State เดิมซ้ำในเฟรมเดียวกัน
  7. เพิ่ม 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/RightUpStop 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-P01Jog 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 กลาง transitioncancel 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 StopTransitionEntrySpeed, 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 และ hysteresistrigger เฉพาะตอนข้าม 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/rotationAnimation เป็นผู้แสดง pose ไม่ใช่ผู้สั่ง movement; ตรวจ Character Movement และ rotation contract

12. Turn in Place และ Idle Break

Turn in Place คือการหมุนท่ายืนโดย capsule ไม่ได้เคลื่อนที่ ส่วน Idle Break คือท่าพักสั้น ๆ ที่เพิ่มชีวิตให้ตัวละคร ทั้งสองระบบใช้คลิป one-shot แต่มี trigger และข้อจำกัดต่างกัน จึงต้องสร้าง State และ cancel policy แยกกัน

Turn in Place contract [PARTIALLY CONFIRMED 2026-09-01 · TBD-LOCO-007]: ใช้ Mesh/Root-Yaw-Offset เป็น baseline โดย gameplay/server ยังคงเป็นเจ้าของ actor/capsule yaw; Animation Blueprint สะสม visual yaw request จากการเปลี่ยน actor yaw ขณะ grounded idle แล้ว latch คลิปที่ตรงมุมเพื่อคลาย offset กลับศูนย์ ไม่ใช้ Root Motion เป็น baseline และไม่ trigger จากการหมุนกล้องใน HipRelaxed ส่วน curve/commit timing, threshold และ network correction ยังต้องทดสอบก่อน production

12.1 เข้าใจปัญหา rotation ก่อนสร้าง State

Rotation Modeพฤติกรรม runtime ปัจจุบันผลต่อ Turn in Place
VelocityDirection / HipRelaxedbOrientRotationToMovement = true, bUseControllerRotationYaw = falseเมื่อ Idle การหมุนกล้องไม่ใช่คำสั่งให้ร่างกายหัน จึงไม่ควร trigger Turn in Place จาก control yaw โดยอัตโนมัติ
LookingDirection / ShoulderAim หรือ ADSbOrientRotationToMovement = false, bUseControllerRotationYaw = trueactor หมุนตาม 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 แยกต่างหาก
ส่วนที่ยังเปิดภายใต้ TBD-LOCO-007: runtime property/event ที่ expose authoritative aim yaw, curve/notify และ commit timing รายคลิป, turn bucket thresholds/hysteresis, residual-yaw policy, interrupt behavior และวิธี rebase หลัง network correction การสร้าง selector/State เพื่อ Preview ทำได้ แต่ยังห้ามถือว่า production-ready

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/RtPositive → Right, Negative → Left ตาม signed-angle contract ที่ยืนยันในข้อ 10.7
SelectedTurnAnimationanimation ที่ latch ก่อนเข้า Stateชื่อเสนอ; ยังไม่มีใน runtime ปัจจุบัน
bRequestTurnInPlaceedge-trigger เข้า State หนึ่งครั้งต้อง clear เมื่อ consume/cancel ห้ามคง true ทุกเฟรม
  1. อ่าน actor yaw และ authoritative facing state จาก gameplay contract แล้วอัปเดต TurnRequestYaw; ไม่อ่าน camera yaw ตรงใน AnimGraph
  2. เมื่อ Eligible เปลี่ยนเป็น true และมุมข้าม enter threshold ให้ latch side, angle bucket, stance และ selected animation พร้อมกัน
  3. ล็อกค่าทั้งชุดตลอด turn clip แม้ target yaw เคลื่อนต่อ; การหมุนเพิ่มระหว่างคลิปให้เก็บเป็น pending turn หรือจัดการหลังคลิปจบตาม policy ที่อนุมัติ
  4. ใช้ hysteresis/cooldown ที่ทดสอบแล้วเพื่อกันการสั่นที่ขอบมุม แต่ไม่กำหนดตัวเลขก่อนเปิด asset และทดสอบใน PIE

12.3 สร้าง TurnInPlace State สำหรับ Preview และ integration

ทำ Standing ก่อน แล้วจึงทำ Crouch โดยใช้ selector เดียวกันแต่คนละ asset family:

BucketStandingCrouching
Left 45/90/135/180A_INP_IdleLt_45/90/135/180_TurnA_INP_Crouch_IdleLt_45/90/135/180_Turn
Right 45/90/135/180A_INP_IdleRt_45/90/135/180_TurnA_INP_Crouch_IdleRt_45/90/135/180_Turn
  1. ใน SM_Standing สร้าง State TurnInPlace และต่อ Idle ↔ TurnInPlace
  2. ใน State ใส่ Sequence Player ที่เปิดพิน Sequence แล้วต่อ SelectedTurnAnimation เข้าพินนั้น ปิด Loop และเปิด Always Reset on Entry
  3. Idle → TurnInPlace ใช้ bRequestTurnInPlace ร่วมกับ grounded/idle/stance guards ไม่ใช้ AbsTurnYaw ตรง ๆ ทุกเฟรม
  4. TurnInPlace → Idle ใช้ Automatic Rule Based on Sequence Player in State และ clear request/lock หลังออกจาก State
  5. เพิ่ม cancel path ไป Start เมื่อมี movement input, Airborne เมื่อเริ่มตก และ outer posture transition เมื่อ Stance เปลี่ยน โดย cancel priority สูงกว่าทางจบอัตโนมัติ
  6. สำหรับ Crouch ให้สร้าง State รูปแบบเดียวกันใน SM_Crouch หลังข้อ 13 ผ่านแล้ว และ selector ต้องเลือก A_INP_Crouch_Idle* จาก latched stance
  7. ระหว่างที่ 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-T01Preview Standing Lt/Rt ทุกมุมด้วย manual triggerเลือก clip ถูกด้าน/มุม, เล่นครั้งเดียวและกลับ Idle
LOCO-12-T02ทดสอบ Crouch Lt/Rt หลังข้อ 13 ผ่านใช้ Crouch family เท่านั้นและกลับ Crouch Idle
LOCO-12-T03ขยับ input กลาง turncancel ไป Start/Moving ตาม policy ไม่มี pose ค้าง
LOCO-12-T04เปลี่ยน AimMode หรือ posture กลาง turnrotation owner, stance และ animation จบใน state เดียวกัน ไม่เกิด snap สองครั้ง
LOCO-12-T05หมุน target ต่อระหว่างคลิปselected clip ไม่สลับกลาง State; residual yaw ถูกจัดการหลังจบตาม contract
LOCO-12-T06ทดสอบ remote proxy พร้อม latencyactor/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

Idle Break contract [PARTIALLY CONFIRMED 2026-09-01 · TBD-LOCO-008]: gameplay ต้อง expose read-only 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 ที่ตรวจแล้ว
  1. สร้าง State IdleBreak ใน SM_Standing ก่อน และเพิ่มใน SM_Crouch/SM_Prone หลัง milestone ของ stance นั้นผ่าน; ใช้ Sequence Player ที่เปิดพิน Sequence, ปิด Loop และเปิด Always Reset on Entry
  2. selector ใช้ shuffle bag แยกตาม stance, ไม่เลือกคลิปเดิมซ้ำติดกัน และเลือกเฉพาะ family ของ stance ที่ latch ตอน request ห้ามเปลี่ยน Standing break เป็น Crouch/Prone clip กลาง State
  3. เงื่อนไข eligible ขั้นต่ำ: GroundSpeed อยู่ใน dead zone, ไม่มี movement input, ไม่ falling, ไม่มี transition lock, posture คงที่ และ gameplay action state อนุญาต
  4. delay ก่อนเล่น, cooldown และน้ำหนัก/โอกาสของแต่ละคลิปยังเป็น TBD-LOCO-008 ให้ทดสอบ manual request ก่อนเพื่อไม่สร้างพฤติกรรมสุ่มที่ debug ยาก
  5. ทางออกปกติกลับ Idle ด้วย Automatic Rule ทางยกเลิกต้องครอบคลุม movement input, AimMode change, attack/action, jump, damage/reaction และ stance change ตาม signal ที่ gameplay expose
  6. 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-I01manual request Standing break 01–03 ทีละคลิปเล่นครั้งเดียว, ไม่ loop และกลับ Standing Idle
LOCO-12-I02กดเดิน/กระโดดกลาง Idle Breakยกเลิกทันทีตาม priority และ locomotion/airborne รับ pose ต่อ
LOCO-12-I03เปลี่ยน Crouch กลาง Standing breakouter posture transition ชนะ ไม่มี Standing break เล่นต่อใน Crouch
LOCO-12-I04เข้า ShoulderAim/ADS หรือเริ่ม gameplay actionIdle Break ไม่เริ่ม หรือถูกยกเลิกเมื่อ authoritative action state เปลี่ยน
LOCO-12-I05ปล่อย Idle นานหลายรอบหลังกำหนด policy แล้วไม่ยิงซ้ำติดกัน, cooldown ทำงาน และ selection ไม่เปลี่ยนกลางคลิป

12.7 แก้ปัญหาจากอาการที่เห็น

อาการตรวจจุดแรกแนวแก้
Turn in Place ไม่เคย trigger ใน AimTurnRequestYaw สะสม 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 กลาง Stateselector resolve ทุกเฟรมlatch bucket/asset ตอน request แล้ว lock จนออก State
Idle Break เล่นขณะโจมตีหรือถือ actionauthoritative 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
Milestone contract [CONFIRMED 2026-09-03 · C01–C08 PASSED 2026-09-04]: Crouch ผ่าน LOCO-13-C01–C08 แล้ว จึงเริ่ม Prone animation graph ได้ ยังไม่เพิ่ม Crouch Pivot/Turn ในรอบนี้ 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 ซึ่งสะท้อนสถานะทางกายภาพของ ACharactermap เข้า E_LocomotionStance โดยตรงเพื่อเปลี่ยน Stance; ไม่มี animation Boolean bIsCrouched/IsCrouching ซ้ำ
ความเร็ว CrouchULambeerLocomotionComponent.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 ระยะถัดไป
ProneTBD-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
  1. ทำ ข้อ 6.2 ให้ผ่านก่อน แล้วเริ่ม PIE เพื่อตรวจว่า Stance เปลี่ยนเป็น Crouched ตามสถานะที่ Character Movement ยืนยัน, GroundSpeed เปลี่ยนตาม capsule และ Direction มีค่าครบทุกทิศ
  2. ตรวจตัวแปร Stance: ค่า enum ใช้ชื่อ Crouched แต่ชื่อ State ในกราฟใช้ Crouching ได้ ทั้งสองชื่อทำหน้าที่ต่างกันและไม่จำเป็นต้องสะกดเหมือนกัน
  3. ใน Event Graph ให้เลิก map Is Crouching ซ้ำเป็น Stance แล้วใช้ native ULambeerAnimInstance.Stance ซึ่งมาจาก CurrentStance ที่ locomotion component เป็นเจ้าของ; หลัง C01–C08 ผ่านแล้วให้เพิ่ม State Prone ตามข้อ 13.5
  4. ทดสอบยืนขึ้นใต้เพดานต่ำหนึ่งครั้ง หาก Character Movement ปฏิเสธการ UnCrouch ค่า Stance ต้องยังเป็น Crouched และ animation ต้องคงอยู่ที่ Crouching ห้ามให้ ABP เดาเองว่าลุกสำเร็จแล้ว
Manual verification gate: ตามข้อกำหนดโปรเจกต์ การเริ่มและทดสอบ PIE ทำโดยผู้ใช้เท่านั้น สถานะ Implemented ของข้อ 13.1 หมายถึง LambeerEditor Win64 Development build สำเร็จตาม UBT result และ DLL ที่สร้างใหม่เมื่อ 2026-09-03, พร้อมกับ ABP_Player compile ผ่านแล้ว; ไม่ได้หมายความว่า LOCO-13-C01–C08 ผ่าน PIE

13.2 สร้าง BS_Crouch_Locomotion_2D [ASSET INTEGRATED 2026-09-03]

  1. [VERIFIED 2026-09-03] สร้าง /Game/Lambeer/Characters/Player/Animation/BS_Crouch_Locomotion_2D สำหรับ Skeleton เดียวกับ ABP_Player แล้ว
  2. [CONFIRMED PROVISIONAL 2026-09-03 · TBD-LOCO-003] ตั้งแกนนอน Direction = -180..180 แบบ Wrap Input และแกนตั้ง GroundSpeed = 0..110; ใช้ 110 cm/s เป็น sample Y baseline ตาม runtime CrouchSpeed ที่อนุมัติให้ใช้ระหว่างรอวัด authored speed จริง
  3. [VERIFIED 2026-09-03] สร้าง moving row หนึ่งแถวที่ 110 cm/s จาก directional Loop 8 ตัว; ไม่ใส่ Idle, Start, Stop, Turn หรือ Pivot ลงใน Blend Space เพราะ pose/state และคลิป non-loop เหล่านั้นแยกออกจาก Moving
  4. วาง sample ตามตารางด้านล่าง โดยเริ่มจาก Forward, Backward และรอยต่อ -180/180 ก่อน จากนั้นค่อยเติมทิศเฉียงและด้านข้าง
  5. ต่อ sample Backward ตัวเดียวกันไว้ทั้ง -180 และ 180 เพื่อปิด seam ด้านหลัง แล้วลาก Preview ข้าม seam ช้า ๆ
  6. [CONFIRMED 2026-09-03 · TBD-LOCO-004] วางคลิป Rt ที่ค่าบวกและ Lt ที่ค่าลบ ใช้ตระกูล FwdLt/FwdRt_90 เป็น side-loop baseline สำหรับ Crouch; เก็บตระกูล BwdLt/BwdRt_90 ไว้เปรียบเทียบภายหลัง หาก Preview กลับด้านให้แก้ sample/asset mapping ที่ผิด ห้าม negate Direction เป็นจุด ๆ
ตำแหน่งเชิงทิศAnimation Sequenceหมายเหตุ
หน้า 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 เดียวกันทั้งสองขอบของแกน
ค่า 110 เป็น implementation baseline ที่อนุมัติ ไม่ใช่ authored-speed measurement: CrouchSpeed เป็นความเร็ว capsule และยังถูกคูณด้วย encumbrance ส่วน authored speed ของคลิปต้องวัดจากภาพจริง หากเท้าไถลให้แยกตรวจ sample position, play rate/stride warping roadmap และ movement speed แทนการเปลี่ยนหลายค่าในครั้งเดียว

ใส่ Sync Markers ให้ sample ที่ใช้งานจริง

[VERIFIED 2026-09-03] Sync Markers พร้อมแล้ว: Unreal Asset Registry ยืนยันว่า moving samples ทั้ง 8 ตัวมี LeftPlant และ RightPlant ใน AnimSyncMarkerList ครบ ตำแหน่ง marker ถูกวางจาก foot contact โดยผู้ใช้; คุณภาพการ blend และ phase continuity ยังตรวจด้วย manual PIE gate ตามข้อ 13.6
  1. เปิดเฉพาะ Animation Sequence ที่ถูกวางเป็น sample ใน BS_Crouch_Locomotion_2D
  2. ใส่ LeftPlant และ RightPlant ตามหลักใน ข้อ 8 โดยดู foot contact ของคลิป Crouch แต่ละตัว ห้ามคัดลอกเวลา marker จาก Standing
  3. Preview การ blend ระหว่างหน้า → เฉียง → ข้าง → เฉียงหลัง → หลัง และย้อนกลับ ถ้าเข่าหรือเท้ากระโดด ให้แก้ marker ของคู่ที่มีปัญหาก่อน
  4. ยังไม่ต้องใส่ Crouch Start/Stop ใน Sync Group จน base flow เล่นถูกต้อง; เริ่มจาก Blend Space Player ใน State Moving เท่านั้น
ข้อ 13.2 ยังไม่ทำให้ Crouch pose แสดงในเกม: Blend Space เป็น asset ที่ยังไม่มีผู้ใช้ใน AnimGraph การเห็น Crouch animation ต้องทำข้อ 13.3 เพื่อสร้าง Crouching → SM_Crouch และใส่ Idle/Moving poses แล้วทำข้อ 13.4 เพื่อเชื่อม PM_Posture ระหว่าง Standing กับ Crouching ก่อน ปัจจุบัน static graph inspection ยืนยันว่า ABP_Player ยังมีเฉพาะ posture Standing

13.3 สร้าง SM_Crouch

โครงภายในใช้หลักเดียวกับ SM_Standing เพื่อให้ debug ง่าย ความต่างอยู่ที่ pose และ Blend Space ที่เลือก

  1. เปิด ABP_Player → AnimGraph → SM_Posture สร้าง State ชื่อ Crouching แล้วเปิด State Graph ของมัน
  2. เพิ่ม State Machine ชื่อเสนอ SM_Crouch ต่อ output เข้า State Result
  3. ภายใน SM_Crouch สร้าง Idle, Start, Moving และ Stop แล้วต่อ Entry → Idle → Start → Moving → Stop → Idle
  4. เปิด Idle ใส่ A_INP_Crouch_Idle และเปิด Loop Animation
  5. เปิด Start ใส่ A_INP_CrouchFwd_Start เป็น fallback รอบแรก ปิด Loop และเปิด Always Reset on Entry
  6. เปิด Moving ใส่ Blend Space Player ของ BS_Crouch_Locomotion_2D ต่อ Direction และ GroundSpeed เข้าพินที่ตรงกัน
  7. เลือก Blend Space Player ใน Moving ตั้ง Method = Sync Group, Group Name = GroundedLocomotion และ Group Role = Can Be Leader ให้ตรงกับข้อ 9.3
  8. เปิด Stop ใส่ A_INP_CrouchFwd_Stop_LU เป็น fallback รอบแรกและปิด Loop หลัง base flow ผ่านแล้วจึงเพิ่มการเลือก _LU/_RU จาก FootPhase
  9. สร้าง Transition Rules ชุดเดียวกับ ข้อ 9.4 รวมทางหนี Moving → Idle, Start → Stop, Start → Idle และ Stop → Start ไม่ใช่สร้างเฉพาะลูกศรหลักสี่เส้น
  10. เพิ่มเงื่อนไข Stance == Crouched และ NOT bIsFalling ในทางเข้า Start ที่ต้องป้องกันการทำงานผิด posture หากค่า Stance เปลี่ยนระหว่าง Start/Stop ให้ outer SM_Posture เป็นผู้ตัดออกจาก Crouching
  11. Compile และ Save จากนั้นเปิด Debug Filter ตรวจว่า SM_Crouch วิ่ง Idle → Start → Moving → Stop → Idle ได้ขณะตัวละครอยู่ใน Crouch ก่อนเพิ่ม directional Start/Stop/Pivot
StatePose รอบแรกLoopเงื่อนไขออกหลัก
IdleA_INP_Crouch_IdleเปิดbHasMovementInput AND NOT bIsFalling AND Stance == Crouched
StartA_INP_CrouchFwd_StartปิดAutomatic Rule เมื่อคลิปใกล้จบ; ทางยกเลิกใช้กฎข้อ 9.4
MovingBS_Crouch_Locomotion_2DBlend Space loopปล่อย input แล้วแยก Stop/Idle ด้วย MovementSpeedDeadZone
StopA_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 ขณะหยุด

  1. ใน SM_Posture สร้าง State StandToCrouch ใส่ A_INP_Idle2CrouchIdle, ปิด Loop และเปิด Always Reset on Entry
  2. สร้าง State CrouchToStand ใส่ A_INP_CrouchIdle2Idle, ปิด Loop และเปิด Always Reset on Entry
  3. สร้าง Standing → StandToCrouch ด้วยกฎ Stance == Crouched AND GroundSpeed <= MovementSpeedDeadZone
  4. สร้าง StandToCrouch → Crouching ด้วย Automatic Rule Based on Sequence Player in State
  5. สร้าง Crouching → CrouchToStand ด้วยกฎ Stance == Standing AND GroundSpeed <= MovementSpeedDeadZone
  6. สร้าง CrouchToStand → Standing ด้วย Automatic Rule Based on Sequence Player in State
  7. เพิ่มทางยกเลิก: 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
  1. สร้างลูกศรตรง Standing → Crouching และ Crouching → Standing เพิ่มจากเส้น idle one-shot โดยใช้กฎด้านบน กฎที่ใช้ <= และ > แยกกันทำให้สองทางไม่แข่งขันในเฟรมเดียวกัน
  2. [CONFIRMED BASELINE 2026-09-03] ใช้ Crossfade Duration 0.12 s สำหรับ direct moving posture transition ทั้งสองทิศ แล้วทดสอบภาพจริงก่อนปรับ การเปลี่ยนค่าภายหลังต้องบันทึกเป็น posture-transition tuning แยกจาก Baseline v0.1 ของ Pivot/MovingTurn/Spin
  3. เมื่อเข้าถึง Crouching ขณะยังมี input ให้ outer state ส่ง pose เข้า SM_Crouch; ถ้า nested machine เริ่มที่ Idle แล้วเห็น crouch idle แวบหนึ่ง ให้ใช้ transition initialization/entry behavior ที่ทดสอบแล้วเพื่อพา Idle → Start/Moving โดยไม่ invent ค่าเวลาใหม่
  4. อย่าใช้ Anim Notify สลับ collision capsule ฝั่ง Animation ระบบ Character Movement เป็น source of truth ของการ Crouch/UnCrouch และเป็นผู้ตรวจเพดานต่ำ
เหตุผลที่มีสองทาง: ตอนหยุดใช้คลิปเปลี่ยนท่าที่ authored มาโดยตรงเพื่อรักษาน้ำหนักตัว ส่วนตอนเคลื่อนที่ข้ามไป blend locomotion pose เพื่อลดอาการเท้าล็อกแต่ capsule ไหล หากภายหลังมี moving stance-transition clips จริง ค่อยแทน direct blend ด้วย State เฉพาะ

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 เป็นเจ้าของ replicated CurrentStance กับ 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 เดิม
[CONFIRMED 2026-09-03 · TBD-LOCO-009] Policy closed: stance authority, input paths, collision/clearance model, movement behavior, Sprint/Jump-to-Stand, action restrictions, prediction/reconciliation และ interrupted recovery ได้รับอนุมัติครบแล้ว ค่าขนาด collision กับ movement tuning เป็น measurement/verification gate ที่ต้องเก็บจาก MetaHuman และ animation จริง ไม่ใช่ design TBD; authored speed/sample positions ติดตามใน TBD-LOCO-003
[IMPLEMENTED 2026-09-04 · TBD-LOCO-009 runtime] ULambeerLocomotionComponent เป็นเจ้าของ 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) ยังไม่ได้วัดจาก MetaHuman
ผล PIE จากผู้ใช้ 2026-09-05: native mesh counter-offset แก้อาการตัวละครลงใต้พื้นบน autonomous proxy/PIE ที่เป็นเจ้าของได้แล้ว และ SM_Prone เปลี่ยน Start → Moving ด้วย Automatic Rule ได้ตามปกติ อาการค้างที่รายงานระหว่าง debug เกิดจากการกด/ตั้งค่าใน Editor ผิดและแก้แล้ว ไม่ได้ต้องเพิ่ม explicit remaining-time rule
Multiplayer regression resolved [PASSED 2026-09-05 · LOCO-13-P07]: ผู้ใช้ยืนยันว่า simulated proxy บน client อื่นเห็นตัวละคร Prone อยู่ระดับพื้นถูกต้องแล้ว หลัง ApplyCollisionForStance() เรียก Character->CacheInitialMeshOffset(MeshRelativeLocation, Mesh->GetRelativeRotation()) ต่อจาก Mesh->SetRelativeLocation() เพื่ออัปเดต cached mesh base offset ที่ Character Movement ใช้เป็นเป้าหมายของ network smoothing; full LambeerEditor Win64 Development build ผ่าน ห้ามชดเชย offset ซ้ำด้วย Anim Notify หรือ Anim Blueprint

Prone action gate [CONFIRMED 2026-09-03]

ทุก gameplay action ต้องประกาศ stance policy แบบ data-driven เป็น Allowed, RequiresStanding หรือ Blocked และ server ตรวจ policy เดียวกัน ห้ามให้ ABP เป็นผู้อนุญาต gameplay action

ActionPolicy ขณะ ProneContract
Look/Camera, Prone locomotionAllowedทำงานตามปกติภายใต้ prone movement/collision contract
Aim, fire, reload, weapon switchAllowed เมื่อมี prone-compatible animation/montageหาก coverage ยังไม่พร้อมให้ block action นั้น ห้ามรัน gameplay โดยแสดง pose ที่ผิด
Inventory, pickup และ interact ระยะใกล้Allowedยังต้องผ่าน reach, authority และ action-specific validation ปกติ
SprintRequiresStandingขอ Standing ทันที; เมื่อ clearance ผ่านให้รักษา Sprint-held intent และเริ่ม Run
JumpRequiresStandinginput แรกใช้ลุกเท่านั้นและถูก consume; ต้องกดใหม่หลัง Standing จึงกระโดด
Mantle, vault, climb, building, ถือ/ลากวัตถุขนาดใหญ่Blockedปฏิเสธขณะ Prone โดยไม่บังคับลุกอัตโนมัติ
Melee และ throwBlocked baselineเปลี่ยนเป็น Allowed ได้เมื่อมี animation coverage และ acceptance test เฉพาะ

Interrupted recovery [CONFIRMED 2026-09-03]

เหตุการณ์ผล authoritativeผล animation
ถูกขัดจังหวะก่อน commitยกเลิก pending request และคืน stance/collision ล่าสุดของ serverInertial 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 ปกติไม่เปลี่ยน stanceAdditive/upper-body reaction เท่านั้น
Knockdown/Ragdollระบบนั้นถือ collision ชั่วคราว; recovery ต้อง request stance ใหม่Override posture ทั้งหมดจน recovery commit
Response/request เก่ามาถึงช้าทิ้งเมื่อ sequence ID ไม่ใช่รายการล่าสุดไม่ย้อนเล่น transition เก่า
หน้าที่รอบแรกAnimation ที่ตรวจพบในโฟลเดอร์ผู้เล่น
IdleA_INP_Prone_Idle
Standing ↔ ProneA_INP_Idle2ProneIdle, A_INP_ProneIdle2Idle
Crouching ↔ ProneA_INP_CrouchIdle2ProneIdle, A_INP_ProneIdle2CrouchIdle
ForwardA_INP_ProneFwd_Start, A_INP_ProneFwd_Loop, A_INP_ProneFwd_Stop
Forward diagonal / sideA_INP_ProneFwdLt45_*, A_INP_ProneFwdRt45_*, A_INP_ProneFwdLt90_*, A_INP_ProneFwdRt90_*
BackwardA_INP_ProneBwd_Start, A_INP_ProneBwd_Loop, A_INP_ProneBwd_Stop
Backward diagonalA_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

  1. สร้าง /Game/Lambeer/Characters/Player/Animation/BS_Prone_Locomotion_2D สำหรับ Skeleton เดียวกับ ABP_Player
  2. ตั้งแกนนอน Direction = -180..180 แบบ Wrap Input และแกนตั้ง GroundSpeed = 0..80; ใช้ 80 cm/s เป็น sample Y ตาม runtime verification ProneSpeed ห้ามคัดลอก 110 ของ Crouch
  3. สร้าง moving row หนึ่งแถวที่ 80 cm/s จาก Loop 8 ทิศ; ไม่ใส่ Idle, Start, Stop, Turn หรือ Idle Break ลงใน Blend Space
  4. วาง sample ตามตารางด้านล่าง โดยเริ่มจาก Forward, Backward และรอยต่อ -180/180 ก่อน จากนั้นค่อยเติมทิศเฉียงและด้านข้าง
  5. ต่อ sample Backward ตัวเดียวกันไว้ทั้ง -180 และ 180 เพื่อปิด seam ด้านหลัง แล้วลาก Preview ข้าม seam ช้า ๆ
  6. วางคลิป Rt ที่ค่าบวกและ Lt ที่ค่าลบ ตาม TBD-LOCO-004 หาก Preview กลับด้านให้แก้ sample mapping ห้าม negate Direction
  7. ใส่ LeftPlant และ RightPlant บน Loop ทั้ง 8 ตัวตามข้อ 8 โดยดู foot/hand contact ของคลิป Prone ห้ามคัดลอกเวลา marker จาก Crouch
ตำแหน่งเชิงทิศAnimation Sequenceหมายเหตุ
หน้า A_INP_ProneFwd_Loopsample แรกและจุด 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_Loopsequence เดียวกันทั้งสองขอบของแกน
ค่า 80 เป็น verification baseline: 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 เพราะจะทับ inherited
SM_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
  1. เปิด /Game/Lambeer/Characters/Player/Animation/ABP_Player แล้ว Compile ให้ผ่านก่อนเพิ่ม State เพื่อแยก error เดิม
  2. ไปที่ AnimGraph ดับเบิลคลิก SM_Posture ต้องเห็น Standing, Crouching และ one-shot Crouch ที่ทำในข้อ 13.3–13.4 แล้ว
  3. คลิกขวาพื้นที่ว่างใน SM_Posture เลือก Add State ตั้งชื่อ Prone ให้สะกดตรงนี้ อย่าลากเส้นจาก Entry มาที่ Prone เพราะ Entry ยังต้องชี้ Standing
  4. ดับเบิลคลิก State Prone เพื่อเปิด State Graph ของมัน
  5. คลิกขวาพื้นที่ว่าง เพิ่ม State Machine ตั้งชื่อเสนอ SM_Prone แล้วลากพิน pose ของ node นี้เข้า State Result
  6. ดับเบิลคลิก node SM_Prone สร้าง States ชื่อ Idle, Start, Moving และ Stop ให้สะกดตรงกับ SM_Crouch
  7. ลากจาก Entry ไป Idle จากนั้นลากลูกศรหลัก Idle → Start → Moving → Stop → Idle
  8. ลากทางหนีเพิ่มอีกสี่เส้นทันที: Moving → Idle, Start → Stop, Start → Idle, Stop → Start ห้ามมีแค่ลูกศรหลักสี่เส้น
  9. Compile ตอนนี้ compiler อาจเตือนว่า State ยังไม่มี pose นั่นปกติจนกว่าจะใส่คลิปด้านล่าง
Idle — คลิปนอนรอ
  1. ใน SM_Prone ดับเบิลคลิก State Idle
  2. ลาก /Game/Lambeer/Characters/Player/Animation/Idle/A_INP_Prone_Idle จาก Content Browser มาวาง จะได้ Sequence Player
  3. ต่อ output pose เข้า State Result
  4. เลือก Sequence Player เปิด Loop Animation เพราะ Idle ต้องวน
Start — คลิปเริ่มคลานหน้าเป็น fallback รอบแรก
  1. กลับ SM_Prone แล้วดับเบิลคลิก State Start
  2. ลาก /Game/Lambeer/Characters/Player/Animation/Prone/A_INP_ProneFwd_Start มาวางและต่อเข้า State Result
  3. เลือก Sequence Player แล้วปิด Loop Animation
  4. กลับหน้า SM_Prone คลิก State Start (ไม่ใช่คลิปในกราฟ) เปิด Always Reset on Entry ใน Details หากตัวเลือกนี้แสดงอยู่
  5. รอบนี้ยังไม่เลือกทิศอื่นใน Start; directional Start เป็นงานหลัง base flow ผ่าน
Moving — Blend Space คลาน
  1. ดับเบิลคลิก State Moving
  2. ลาก /Game/Lambeer/Characters/Player/Animation/BS_Prone_Locomotion_2D มาวางเป็น Blend Space Player แล้วต่อเข้า State Result
  3. คลิกขวาพื้นที่ว่าง เลือก Get Direction ต่อเข้าพินแกนนอนชื่อ Direction ของ node
  4. Get GroundSpeed ต่อเข้าพินแกนตั้งชื่อ GroundSpeed หากชื่อพินไม่ตรง ให้เปิด Details ของ Blend Space ตรวจชื่อแกน ห้ามสลับแกน
  5. เลือก Blend Space Player เปิดหมวด Sync: เปลี่ยน Method จาก Do Not Sync เป็น Sync Group ก่อน แล้วตั้ง Group Name = GroundedLocomotion และ Group Role = Can Be Leader ให้ตรงข้อ 9.3
  6. ถ้า compiler ขึ้น “set to not use named sync groups” แปลว่าใส่ชื่อกลุ่มแล้วยังไม่ได้เปลี่ยน Method ให้แก้ที่ node เดิม ไม่ต้องสร้าง Player ใหม่
Stop — คลิปหยุดคลานหน้า
  1. ดับเบิลคลิก State Stop
  2. ลาก /Game/Lambeer/Characters/Player/Animation/Prone/A_INP_ProneFwd_Stop มาวางและต่อเข้า State Result
  3. ปิด Loop Animation บน Sequence Player
  4. กลับหน้า SM_Prone คลิก State Stop เปิด Always Reset on Entry หากมีใน Details
  5. ชุด Prone ไม่มี suffix _LU/_RU บน Stop จึงไม่ต้อง Select จาก FootPhase ในรอบนี้
State ใน SM_ProneAssetLoopAlways Reset on Entry
IdleA_INP_Prone_Idleเปิดไม่บังคับ
StartA_INP_ProneFwd_Startปิดเปิด
MovingBS_Prone_Locomotion_2DBlend Space loopไม่ใช้กับ Blend Space Player
StopA_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
  1. ดับเบิลคลิกวงกลมบนลูกศร Idle → Start
  2. Get bHasMovementInput ต่อเข้า Boolean AND
  3. Get bIsFalling ผ่าน Boolean NOT แล้วต่อเข้า AND
  4. Get Stance ต่อ Equal (Enum) เลือก Prone แล้วต่อผลเข้า AND ห้ามใช้ค่า Crouched จาก SM_Crouch คัดลอกมาแล้วลืมเปลี่ยน
  5. ต่อผล AND เข้า Can Enter Transition
Start → Moving
  1. คลิกวงกลมบนลูกศร Start → Moving ครั้งเดียวให้ Details ของ transition ขึ้น
  2. เปิด Automatic Rule Based on Sequence Player in State
  3. คง Automatic Rule Trigger Time เป็นค่าติดลบ/default ในรอบแรก ให้อิง Crossfade Duration
Moving → Stop
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
  1. ดับเบิลคลิกวงกลม Moving → Stop
  2. NOT ของ bHasMovementInput, NOT ของ bIsFalling และ Float > ระหว่าง GroundSpeed กับ MovementSpeedDeadZone
  3. รวมด้วย AND เข้า Result
Stop → Idle
  1. คลิกวงกลม Stop → Idle เปิด Automatic Rule Based on Sequence Player in State
  2. คง Trigger Time เป็น default รอบแรก
Moving → Idle
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
  1. เปิด Rule ของลูกศร Moving → Idle ที่ลากไว้แล้ว
  2. สร้างกราฟแบบ Moving → Stop แต่เปลี่ยนตัวเปรียบเทียบเป็น <=
Start → Stop
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed > MovementSpeedDeadZone
  1. เปิด Rule ของ Start → Stop ให้เหมือน Moving → Stop
  2. ตั้ง Priority Order ให้พิจารณาก่อน Start → Moving
Start → Idle
NOT bHasMovementInput
AND NOT bIsFalling
AND GroundSpeed <= MovementSpeedDeadZone
  1. เปิด Rule ของ Start → Idle ให้เหมือน Moving → Idle
  2. ตั้ง Priority ให้พิจารณาก่อน Start → Moving เส้นนี้ไม่ชนกับ Start → Stop เพราะใช้ <= คนละเส้นกับ >
Stop → Start
bHasMovementInput
AND NOT bIsFalling
AND Stance == Prone
  1. เปิด Rule ของ Stop → Start ให้เหมือน Idle → Start รวม Equal Enum เป็น Prone
  2. ตั้ง Priority ให้พิจารณาก่อน Automatic Stop → Idle
หาก Stance เปลี่ยนเป็น Standing/Crouched ขณะยังอยู่ใน Start/Stop ของ SM_Prone อย่าสร้างกฎออกไป Standing ภายใน nested machine ให้ SM_Posture เป็นผู้ตัด State Prone ทั้งก้อนตามข้อถัดไป
  1. กลับ State Graph ของ Prone ยืนยันว่า SM_Prone ต่อถึง State Result
  2. 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 สี่ตัวและใส่คลิปให้ Compile ผ่านก่อน แล้วค่อยลากลูกศร idle จากนั้นจึงลากลูกศรตรง (moving/urgent) สุดท้ายตั้ง Priority บนทุกเส้นที่ออกจาก State เดียวกัน
สร้าง State one-shot สี่ตัว

คลิปอยู่ที่ /Game/Lambeer/Characters/Player/Animation/Idle/ สำหรับทุกชื่อด้านล่าง

  1. ในหน้า SM_Posture Add State ชื่อ StandToProne ดับเบิลคลิก ลาก A_INP_Idle2ProneIdle ต่อเข้า State Result ปิด Loop บน Sequence Player แล้วคลิก State ในหน้าเครื่องตั้ง เปิด Always Reset on Entry
  2. สร้าง CrouchToProne ใส่ A_INP_CrouchIdle2ProneIdle ปิด Loop เปิด Always Reset on Entry
  3. สร้าง ProneToStand ใส่ A_INP_ProneIdle2Idle ปิด Loop เปิด Always Reset on Entry
  4. สร้าง ProneToCrouch ใส่ A_INP_ProneIdle2CrouchIdle ปิด Loop เปิด Always Reset on Entry
  5. Compile ยืนยันว่าทั้งสี่ State มี pose ถึง Result และไม่มีคลิปที่ Loop ค้าง
ลูกศร idle — เข้า Prone ตอนหยุด
Standing → StandToProne
Stance == Prone
AND GroundSpeed <= MovementSpeedDeadZone
  1. ลากจากขอบ Standing ไป StandToProne ลูกศรต้องชี้ทางเดียว
  2. ดับเบิลคลิกวงกลม Get Stance Equal Enum เป็น Prone
  3. Get GroundSpeed กับ MovementSpeedDeadZone เข้า Float <=
  4. AND ทั้งสองเข้า Can Enter Transition
Crouching → CrouchToProne
Stance == Prone
AND GroundSpeed <= MovementSpeedDeadZone
  1. ลาก Crouching → CrouchToProne แล้วสร้าง Rule แบบเดียวกับเส้น Standing แต่ต้นทางเป็น Crouching
StandToProne → Prone และ CrouchToProne → Prone
  1. ลาก StandToProne → Prone คลิกวงกลม เปิด Automatic Rule Based on Sequence Player in State คง Trigger Time เป็น default
  2. ลาก CrouchToProne → Prone แล้วเปิด Automatic Rule แบบเดียวกัน
ลูกศร idle — ออกจาก Prone ตอนหยุด (ไม่ใช่ Sprint/Jump)
Prone → ProneToStand
Stance == Standing
AND GroundSpeed <= MovementSpeedDeadZone
AND NOT bUrgentStandFromProne
  1. ลาก Prone → ProneToStand
  2. Equal Enum Stance == Standing, Float GroundSpeed <= MovementSpeedDeadZone, Get inherited bUrgentStandFromProne ผ่าน Boolean NOT
  3. AND ทั้งสามเข้า Result การใส่ NOT ทำให้ Sprint/Jump ไม่ไหลเข้า one-shot นี้
Prone → ProneToCrouch
Stance == Crouched
AND GroundSpeed <= MovementSpeedDeadZone
AND NOT bUrgentStandFromProne
  1. ลาก Prone → ProneToCrouch แล้วสร้าง Rule แบบเส้น Stand แต่ Equal เป็น Crouched
  2. เส้นนี้ใช้ตอนปุ่ม Crouch จาก Prone หรือตอน Prone key fallback เป็น Crouched ขณะหยุด
ProneToStand → Standing และ ProneToCrouch → Crouching
  1. ลาก ProneToStand → Standing เปิด Automatic Rule Based on Sequence Player in State
  2. ลาก ProneToCrouch → Crouching เปิด Automatic Rule เช่นกัน
ทางยกเลิก one-shot เมื่อ Stance กลับด้านก่อนคลิปจบ

ลากลูกศรข้ามระหว่าง one-shot ตามตาราง แล้วตั้ง Priority Order ของทางยกเลิกให้พิจารณาก่อน Automatic Rule ที่รอคลิปจบ

ลูกศรRuleเหตุที่ต้องมี
StandToProne → ProneToStandStance == Standingกดยกเลิกก่อนคลิปลงนอนจบ
CrouchToProne → ProneToCrouchStance == Crouchedกลับ Crouch ระหว่างคลิปลงนอนจากก้ม
CrouchToProne → ProneToStandStance == Standingลุกยืนระหว่างคลิปนั้น เช่น clearance/fallback
ProneToStand → StandToProneStance == Proneกด Prone ซ้ำระหว่างคลิปลุก
ProneToStand → ProneToCrouchStance == Crouchedserver fallback เป็น Crouched ขณะกำลังลุกยืน
ProneToCrouch → CrouchToProneStance == Proneกดกลับนอนระหว่างคลิปขึ้นก้ม
ProneToCrouch → ProneToStandStance == Standingเปลี่ยนเป็นยืนระหว่างคลิปขึ้นก้ม
  1. แต่ละเส้นเปิด Rule Graph แล้ว Equal Enum ตามคอลัมน์ Rule ต่อเข้า Result โดยตรง ไม่ต้องใส่ GroundSpeed ในทางยกเลิกเหล่านี้
  2. อย่าเปิด Bidirectional บนลูกศร idle หลัก
ลูกศรตรงขณะเคลื่อนที่ และ Sprint/Jump-to-Stand

คลิป idle-to-idle ไม่เหมาะตอน capsule ยังไหล และห้ามใช้ตอน urgent stand สร้างลูกศรตรงแยกจาก one-shot กฎ <= กับ > ทำให้ idle กับ moving ไม่แข่งขันในเฟรมเดียวกัน

Standing → Prone
Stance == Prone
AND GroundSpeed > MovementSpeedDeadZone
  1. ลากลูกศรตรง Standing → Prone คนละเส้นกับ Standing → StandToProne
  2. Equal Enum Prone AND Float GroundSpeed > MovementSpeedDeadZone
  3. คลิกวงกลมใน Details ตั้ง Duration / Crossfade Duration เป็น 0.12 ตาม baseline ข้อ 13.4 ที่อนุมัติแล้วสำหรับ moving posture
Crouching → Prone
Stance == Prone
AND GroundSpeed > MovementSpeedDeadZone
  1. ลาก Crouching → Prone แยกจาก Crouching → CrouchToProne ใช้ Rule และ Crossfade 0.12 s แบบเดียวกัน
Prone → Standing
Stance == Standing
AND (GroundSpeed > MovementSpeedDeadZone OR bUrgentStandFromProne)
  1. ลากลูกศรตรง Prone → Standing คนละเส้นกับ Prone → ProneToStand
  2. Get Stance Equal Standing
  3. สร้าง Float GroundSpeed > MovementSpeedDeadZone
  4. Get bUrgentStandFromProne (inherited, ไม่ Set)
  5. Boolean OR ระหว่างเงื่อนไขความเร็วกับ bUrgentStandFromProne จากนั้น AND กับ Equal Stance เข้า Result
  6. ตั้ง Crossfade Duration 0.12 s
  7. ตั้ง Priority Order ของเส้นนี้ให้พิจารณาก่อน Prone → ProneToStand หากไม่ทำ Sprint/Jump จะไหลเข้า one-shot แล้ว capsule ลุกก่อน pose
Prone → Crouching
Stance == Crouched
AND (GroundSpeed > MovementSpeedDeadZone OR bUrgentStandFromProne)
  1. ลากลูกศรตรง Prone → Crouching แยกจาก Prone → ProneToCrouch
  2. สร้าง Rule แบบเส้น Standing แต่ Equal เป็น Crouched รวม OR กับ bUrgentStandFromProne เพราะ Jump/Sprint ที่ fallback เป็น Crouch ต้องตัด one-shot เช่นกัน
  3. Crossfade 0.12 s และ Priority ก่อน Prone → ProneToCrouch
เหตุผลที่มีสองทาง: ตอนหยุดใช้คลิป authored เพื่อรักษาน้ำหนักตัว ตอนเคลื่อนที่หรือ Sprint/Jump ใช้ direct blend เพื่อไม่ให้เท้าล็อกขณะ capsule เปลี่ยนความสูง หากภายหลังมี moving stance-transition clips จริง ค่อยแทนลูกศรตรงด้วย State เฉพาะ
  1. เมื่อเข้า Prone ขณะยังมี input nested SM_Prone เริ่มที่ Idle ตาม Entry หากเห็น prone idle แวบหนึ่งแล้วค่อยไป Start ให้ใช้วิธี entry/initialization เดียวกับที่ทดสอบแล้วใน SM_Crouch ข้อ 13.4 ห้าม invent เวลา blend ใหม่
  2. อย่าใส่ Anim Notify เพื่อสลับ capsule; locomotion component เป็นเจ้าของ collision
สรุปลูกศรใน SM_Posture ที่เกี่ยวกับ Prone
ลูกศรชนิดRule โดยย่อCrossfade รอบนี้
Standing → StandToProneidle one-shotStance==Prone AND GroundSpeed<=DeadZoneไม่กำหนดใหม่ในเอกสารนี้
Crouching → CrouchToProneidle one-shotเช่นเดียวกับด้านบน จาก Crouchingไม่กำหนดใหม่
StandToProne → ProneautoAutomatic Sequence Playerอิง default/automatic
CrouchToProne → ProneautoAutomatic Sequence Playerอิง default/automatic
Prone → ProneToStandidle one-shotStanding AND <=DeadZone AND NOT bUrgentStandFromProneไม่กำหนดใหม่
Prone → ProneToCrouchidle one-shotCrouched AND <=DeadZone AND NOT bUrgentStandFromProneไม่กำหนดใหม่
ProneToStand → StandingautoAutomatic Sequence Playerอิง default/automatic
ProneToCrouch → CrouchingautoAutomatic Sequence Playerอิง default/automatic
Standing → Pronedirect movingStance==Prone AND GroundSpeed>DeadZone0.12 s
Crouching → Pronedirect movingเช่นเดียวกับด้านบน จาก Crouching0.12 s
Prone → Standingdirect moving/urgentStanding AND (>DeadZone OR bUrgentStandFromProne)0.12 s
Prone → Crouchingdirect moving/urgentCrouched AND (>DeadZone OR bUrgentStandFromProne)0.12 s
Compile, Save และจุดตรวจก่อน PIE
  1. Compile ABP_Player แก้ error เรื่อง Result ว่าง, Enum ผิดชนิด หรือ Sync Group ก่อน Save
  2. Save asset
  3. ใน My Blueprint ตรวจว่า bUrgentStandFromProne เป็น inherited ไม่ใช่ตัวแปรที่สร้างเอง
  4. Hover วงกลมลูกศรทุกเส้นใน SM_Posture และ SM_Prone ยืนยันว่า Result ถูกต่อและ preview expression ตรงตาราง
  5. ยังไม่ต่อ Idle Break, directional Start/Stop หรือ Crouch Pivot ในรอบนี้
งาน Editor นี้ทำโดยผู้ใช้: Agent ห้ามแก้กราฟ Blueprint และห้ามเริ่ม PIE ตามกฎโปรเจกต์ เมื่อ Compile/Save แล้วให้ทดสอบตามข้อ 13.6

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 clientremote proxy เปลี่ยน posture ตาม replicated Character Movement โดยไม่สั่นสลับ Standing/Crouching
Gate Crouch ผ่านแล้ว [PASSED 2026-09-04]: ผู้ใช้ยืนยัน LOCO-13-C01–C08 ผ่าน PIE แล้ว จึงเริ่ม Prone animation ตามข้อ 13.5 ได้ หากยังมี foot sliding แต่ State flow ถูก ให้บันทึกเป็น authored-speed/warping แยกจาก transition bug

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

สถานะล่าสุด: Prone Moving และ native mesh-offset ผ่านทั้ง autonomous proxy และ simulated proxy ที่ผู้ใช้ทดสอบแล้ว โดย 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 IdleStanding → 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 ที่ถูก BlockAllowed/RequiresStanding/Blocked ตรงตารางข้อ 13.5; ABP ไม่ใช่ผู้อนุญาต action
LOCO-13-P07Listen 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 ระหว่าง transitionAirborne มาก่อนแต่ 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_ProneIdle2IdlebUrgentStandFromProne และ Priority ของ Prone → StandingCompile 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_StartA_INP_JumpIdle_inAirA_INP_JumpIdle_Land; ใช้ A_INP_Falling_Loop เมื่อตกโดยไม่ได้เริ่มจาก jump sequence ปกติ

กระโดดตาม gait และเท้า

  • Walk: A_INP_JumpWalkFwd_Start_LU/RUinAir_LU/RULand_LU/RU
  • Jog: A_INP_JumpJog_Start_LU/RUinAir_LU/RULand_LU/RU
  • Run: A_INP_JumpRun_Start_LU/RUinAir_LU/RULand_LU/RU

Directional multi-stage family

ตัวอย่างทิศหน้า: A_INP_JumpFwd_StartA_INP_JumpFwd_Start_LoopA_INP_JumpFwd_ApexA_INP_JumpFwd_Land_Fall_LoopA_INP_JumpFwd_LandA_INP_JumpFwd_Recovery ทิศ Bwd/Lt/Rt ใช้ชื่อรูปแบบเดียวกัน

15. ลำดับการเพิ่มความลื่น

  1. Sync Markers: ทำให้ Walk/Jog/Run ไม่สลับเท้าขณะ blend
  2. Inertialization: วางหลัง State Machine และใช้กับ transition ที่ interrupt บ่อย
  3. Footstep/Plant Notifies: เลือก Start/Stop/Pivot ให้ตรงเท้า
  4. Distance Matching: เพิ่ม curve และ predicted stop/ground distance หลัง base state machine ทำงานถูก
  5. Stride Warping: ชดเชยความต่างระหว่าง authored stride กับ capsule speed/encumbrance
  6. Orientation Warping: เติมช่องว่างมุมเล็ก ๆ ไม่ใช้แทน Pivot 180°
  7. 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

Stop replication contract [IMPLEMENTED 2026-09-03]: UCharacterMovementComponent ไม่รับประกันว่า GetCurrentAcceleration() บน simulated proxy จะสะท้อนการกด/ปล่อย input ของเจ้าของ จึงให้ server คำนวณ ULambeerLocomotionComponent.bReplicatedHasMovementInput และ replicate เฉพาะ Boolean intent นี้ ส่วน autonomous proxy อ่าน MovementInputMagnitude ในเครื่องผ่าน HasMovementInputForAnimation() เพื่อไม่รอ round trip
  • ตำแหน่งจริงและ movement mode ยังคง authoritative ผ่าน replicated UCharacterMovementComponent
  • ALambeerCharacter reset MovementInputMagnitude เมื่อ 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. แผนลงมือทีละระยะ

  1. Skeleton gate: Retarget test A_INP_Idle และ A_INP_WalkFwd_Loop
  2. 2D core: สร้าง BS_Standing_Locomotion_2D ด้วย Walk/Jog/Run loops
  3. Basic states: Idle → Start → Moving → Stop
  4. Foot phase: LeftPlant/RightPlant และ Sync Group
  5. Directional transitions: Start/Stop ตามทิศ
  6. Reversal: Pivot ก่อน แล้วจึง Turn/Spin
  7. Postures: Crouch แล้ว Prone
  8. Airborne: Jump/Fall/Land/Recovery
  9. Aim overlay: Layered Blend per Bone และ turn in place
  10. Polish: Distance Matching, Stride/Orientation Warping, Foot IK
  11. Network QA: ทดสอบ proxy และ packet simulation

18. Checklist ทดสอบ

Test IDสถานการณ์ผลที่ต้องเห็น
LOCO-01Idle → Walk → Jog → Run → Jog → Stopเท้าไม่สลับ phase และไม่มี speed pop ชัดเจน
LOCO-02ลาก Direction รอบ -180..180ไม่มี seam กระตุกที่หลัง และซ้าย/ขวาถูกทิศ
LOCO-03Sprint แล้วกดถอยลดเป็น Jog; ไม่ใช้ Run pose เคลื่อนหลัง
LOCO-04ปล่อย input ทุกทิศและทุก foot phaseเลือก Stop ถูกทิศ/เท้า ไม่มี foot snap
LOCO-05กดกลับทิศกลาง Jogเข้า Pivot หนึ่งครั้งและกลับ Moving ถูก family
LOCO-06Aim แล้ว strafe/turn in placeลำตัวไม่หมุนหนีเป้าและขาไม่บิดเกินจริง
LOCO-07Crouch/Prone ระหว่างมี inputtransition ไม่ค้างและ queued input ทำงานหลังช่วงอนุญาต
LOCO-08Jump จาก Idle/Walk/Jog/Runเลือก family/foot ถูกและ Land ตรงเวลาสัมผัสพื้น
LOCO-09Encumbrance หลายระดับcapsule speed เปลี่ยนโดย foot slide อยู่ในเกณฑ์ที่อนุมัติ
LOCO-10Remote proxy พร้อม latencyไม่มี state flapping หรือ montage desync ที่มองเห็นชัด

19. Technical Decisions และ TBD

[CONFIRMED 2026-09-03] Stop movement-intent replication: server เป็นเจ้าของ bReplicatedHasMovementInput, autonomous proxy ใช้ local MovementInputMagnitude, simulated proxy ใช้ค่าที่ replicate และทุกเครื่องรัน Stop selector ใน AnimInstance ของตนเองโดยไม่ multicast animation time
[CONFIRMED 2026-09-03 · TBD-LOCO-009] Prone collision model: ใช้ vertical root capsule ขนาดย่อร่วมกับ server-authoritative horizontal body-envelope sweep ตอนเข้า Prone, ระหว่างคลาน และตอนออก; ค่าขนาดต้องวัดจาก MetaHuman target และ extreme animation poses ก่อนอนุมัติ production config
[CONFIRMED 2026-09-03 · TBD-LOCO-009] Prone movement: ใช้ gait เดียว, เพดานความเร็วเท่ากันทุกทิศ, analog 0–ProneSpeed, runtime acceleration/braking แยกและใช้ encumbrance multiplier เดิม; กด Sprint ขณะ Prone จะขอ Standing ทันทีและเริ่ม Run หลัง server ยืนยัน clearance โดย ABP ใช้ direct short blend แทนการรอ one-shot
[CONFIRMED 2026-09-03 · TBD-LOCO-009] Prone action gate: Look/locomotion, prone-compatible weapon actions, inventory และ nearby interaction ใช้งานได้; mantle/vault/climb/building/large-object handling ถูก block, melee/throw ปิดจนมี coverage; Sprint และ Jump ขอ Standing โดย Jump input แรกถูก consume และไม่กระโดดต่อในครั้งเดียว
[CONFIRMED 2026-09-03 · TBD-LOCO-009] Prone network prediction: autonomous proxy เริ่ม predicted stance/collision/animation หลัง local clearance และส่ง sequence ID ให้ server; server เป็น authority และ client rollback พร้อม inertial blend เมื่อถูกปฏิเสธ ส่วน simulated proxy อ่าน replicated CurrentStance เท่านั้น
[CONFIRMED 2026-09-03 · TBD-LOCO-009] Prone interrupted recovery: Death/Ragdoll/Knockdown และ Airborne มี priority เหนือ posture; request ก่อน commit ถูกยกเลิก, state หลัง commit คง authoritative, stale sequence ถูกทิ้ง, การตกขอบคง Prone stance ระหว่าง Airborne แล้วตรวจ envelope ตอนลงพื้น และ ragdoll recovery ต้อง request stance ใหม่
  • [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, MetaHuman SKM_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 NameUE Package Folder
CrouchA_INP_CrouchBwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwd_Stop_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwd_Stop_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwd2Fwd_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwd2Fwd_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt2FwdLt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt2FwdLt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt2FwdLt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdLt2FwdLt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt2FwdRt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt2FwdRt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt2FwdRt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchBwdRt2FwdRt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_135_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_135_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_135_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_135_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_180_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_180_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_180_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_180_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_45_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_45_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_45_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_45_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_90_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_90_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_90_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_90_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_Stop_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd_Stop_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd2Bwd_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwd2Bwd_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt2BwdLt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt2BwdLt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt2BwdLt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdLt2BwdLt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt2BwdRt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt2BwdRt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt2BwdRt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
CrouchA_INP_CrouchFwdRt2BwdRt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Crouch
IdleA_INP_Crouch_Idle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_Idle_Break_01/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_Idle_Break_02/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_Idle_Break_03/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleLt_135_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleLt_180_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleLt_45_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleLt_90_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleRt_135_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleRt_180_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleRt_45_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Crouch_IdleRt_90_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_CrouchIdle2Idle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_CrouchIdle2ProneIdle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Idle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Idle_Break_01/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Idle_Break_02/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Idle_Break_03/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Idle2CrouchIdle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Idle2ProneIdle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleLt_135_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleLt_180_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleLt_45_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleLt_90_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleRt_135_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleRt_180_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleRt_45_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_IdleRt_90_Turn/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Prone_Idle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Prone_Idle_Break_01/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_Prone_Idle_Break_02/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_ProneIdle2CrouchIdle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
IdleA_INP_ProneIdle2Idle/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Idle
JogA_INP_JogBwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwd_Stop_Fast/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwd_Stop_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwd_Stop_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwd2JogFwd_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwd2JogFwd_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt2JogFwdLt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt2JogFwdLt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt2JogFwdLt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdLt2JogFwdLt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt2JogFwdRt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt2JogFwdRt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt2JogFwdRt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogBwdRt2JogFwdRt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_135_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_135_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_135_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_135_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_180_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_180_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_180_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_180_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_45_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_45_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_45_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_45_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_90_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_90_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_90_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_90_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_Stop_Fast/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_Stop_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd_Stop_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwd_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwd_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdLt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdLt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdLt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdLt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdRt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdRt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdRt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwd2JogBwdRt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdLt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdLt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdLt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdLt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdRt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdRt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdRt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JogA_INP_JogFwdRt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jog
JumpA_INP_Falling_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwd_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwd_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwd_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwd_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwd_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_45_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_45_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_45_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_45_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_45_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_90_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_90_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_90_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_90_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdLt_90_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_45_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_45_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_45_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_45_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_45_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_90_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_90_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_90_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_90_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpBwdRt_90_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwd_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwd_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwd_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwd_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwd_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_45_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_45_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_45_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_45_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_45_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_90_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_90_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_90_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_90_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdLt_90_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_45_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_45_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_45_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_45_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_45_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_90_Apex/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_90_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_90_Land_Fall_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_90_Recovery/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpFwdRt_90_Start_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpIdle_All/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpIdle_inAir/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpIdle_Land/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpIdle_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpIdleForward_All/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_All_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_All_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_inAir_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_inAir_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_Land_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_Land_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_Start_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpJog_Start_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_All_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_All_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_inAir_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_inAir_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_Land_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_Land_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_Start_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpRun_Start_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_All_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_All_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_inAir_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_inAir_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_Land_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_Land_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_Start_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkBwd_Start_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_All_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_All_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_inAir_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_inAir_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_Land_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_Land_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_Start_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
JumpA_INP_JumpWalkFwd_Start_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Jump
ProneA_INP_ProneBwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwd_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwdLt135_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwdLt135_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwdLt135_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwdRt135_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwdRt135_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneBwdRt135_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwd_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdLt45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdLt45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdLt45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdLt90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdLt90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdLt90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdRt45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdRt45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdRt45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdRt90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdRt90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
ProneA_INP_ProneFwdRt90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Prone
RunA_INP_RunFwd_135_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_135_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_135_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_135_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_180_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_180_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_180_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_180_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_45_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_45_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_45_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_45_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_90_Start_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_90_Start_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_90_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_90_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_Stop_Fast/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_Stop_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwd_Stop_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdLt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdLt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdLt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdLt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdRt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdRt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdRt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
RunA_INP_RunFwdRt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Run
SpinA_INP_CrouchBwd_Spin_Fwd_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchBwd_Spin_Fwd_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchBwdLt_Spin_FwdRt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchBwdLt_Spin_FwdRt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchBwdRt_Spin_FwdLt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchBwdRt_Spin_FwdLt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchFwd_Spin_Bwd_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchFwd_Spin_Bwd_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchFwdLt_Spin_BwdRt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchFwdLt_Spin_BwdRt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchFwdRt_Spin_BwdLt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_CrouchFwdRt_Spin_BwdLt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogBwd_Spin_Fwd_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogBwd_Spin_Fwd_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogBwdLt_Spin_FwdRt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogBwdLt_Spin_FwdRt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogBwdRt_Spin_FwdLt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogBwdRt_Spin_FwdLt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogFwd_Spin_Bwd_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogFwd_Spin_Bwd_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogFwdLt_Spin_BwdRt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogFwdLt_Spin_BwdRt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogFwdRt_Spin_BwdLt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_JogFwdRt_Spin_BwdLt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_RunFwd_Spin_JogBwd_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_RunFwd_Spin_JogBwd_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_RunFwdLt_Spin_JogBwdRt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_RunFwdLt_Spin_JogBwdRt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_RunFwdRt_Spin_JogBwdLt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_RunFwdRt_Spin_JogBwdLt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkBwd_Spin_Fwd_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkBwd_Spin_Fwd_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkBwdLt_Spin_FwdRt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkBwdLt_Spin_FwdRt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkBwdRt_Spin_FwdLt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkBwdRt_Spin_FwdLt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkFwd_Spin_Bwd_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkFwd_Spin_Bwd_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkFwdLt_Spin_BwdRt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkFwdLt_Spin_BwdRt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkFwdRt_Spin_BwdLt_45/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
SpinA_INP_WalkFwdRt_Spin_BwdLt_90/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Spin
WalkA_INP_WalkBwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwd_Stop_Fast/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwd_Stop_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwd_Stop_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwd2Fwd_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwd2Fwd_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt2FwdLt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt2FwdLt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt2FwdLt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdLt2FwdLt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt2FwdRt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt2FwdRt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt2FwdRt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkBwdRt2FwdRt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_135_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_135_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_180_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_180_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_45_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_45_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_90_Turn_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_90_Turn_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_135_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_135_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_180_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_180_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_45_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_45_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_90_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Start_90_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Stop_Fast/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Stop_LU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd_Stop_RU/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd2Bwd_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwd2Bwd_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt2BwdLt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt2BwdLt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt2BwdLt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdLt2BwdLt_90_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt_45_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt_45_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt_45_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt_90_Loop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt_90_Start/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt_90_Stop/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt2BwdRt_45_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt2BwdRt_45_Pivot_R/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_INP_WalkFwdRt2BwdRt_90_Pivot_L/Game/MaleLocomotionSet/Animations/Mannequin/InPlace/Walk
WalkA_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.

PitchRequired / maximum reach (cm)Hand position error (cm)Sight in view X/Y/Z (cm)
-28.00 degrees52.243 / 57.6940.00045.00 / -0.14 / 0.15
-0.35 degrees65.465 / 57.6947.77137.39 / 1.63 / -0.56
+41.30 degrees74.666 / 57.69416.97228.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 shared Carry / Aim actions — 2026-09-17

Historical shared-action decision: idle and movement used the same held upper-body pose. The 2026-09-19 firearm delivery now adds AimWalk content within the same Aim context, without restoring separate idle/move actions. Context.Action exposes Carry, Aim and Attack. Fire is removed because shots use procedural recoil. CarryIdle/CarryMove migrate to Carry; AimIdle/AimMove migrate to Aim through enum value redirects. Native selection no longer branches on movement speed. Stance, hand topology, family, support eligibility and PresetId remain distinct. AimMode remains gameplay/camera state only. Locomotion movement/gait is unchanged.

Authoring: assign the Animation Sequence to Pose on one Context.Action=Aim row for both ADS and ShoulderAim. Do not duplicate this row for movement. Per-item exceptions remain AnimationOverrides; family defaults remain profile Entries. Existing non-additive/player-skeleton and exact-context rules remain.

Migration complete: owner selected Idle clips for all pairs. Six profiles were reduced from 294 to 147 unique entries: Unarmed 3, TwoHandedItem 18, Firearm2H 12, Melee1H 60, Pistol 42, Melee2H 12. All 16 inspected item definitions had empty AnimationOverrides. Exact property readback matched the intended merge. Nine obsolete Carry *_Move sequences were deleted through Unreal MCP after empty referencer checks and no source/config references. Original property snapshots are stored under Saved/P09ActionMigration/before.json. Do not interpret the earlier 294-entry history as the current target count. The retained *_Idle filenames now serve both idle and movement; no rename is required. The nine removed *_Move files are no longer delivery requirements.

Validation: closed-Editor LambeerEditor Win64 Development succeeded; final headless Lambeer.HUD 27/27 passed with no warnings/errors, including legacy enum redirect checks. Reopened MCP verified PIE=false, profile readback and unique keys; ABP_Player compiled with warnings treated as errors and was saved. HTML validation passed 45/45. The first test run incorrectly expected name lookup to ignore redirects; corrected the test to inspect enum choices separately and assert old-name migration. Owner PIE should compare held pose while starting/stopping movement, remaining stance differences and both aim views. No new durable gameplay state is introduced.

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 suffixComputed FamilyHandednessAnimationProfile suffix
L1810FirearmOneHandedPistol
DoubleBarrelShotgunFirearm (MagazineCapacity=2)TwoHandedFirearm2H
Crowbar / FireAxe / WoodenMalletMelee (bMeleeWeapon=true; owner-authorized update)OneHandedMelee1H
SledgehammerMelee (bMeleeWeapon=true)TwoHandedTwoHandedItem
PistolItem (capacity=0, bMeleeWeapon=false)OneHandedNone
Scissor / Wood / L1810_Magazine / Ammo_9x19 / Bandage / Pants / Glasses / BodyArmor / BackpackImprovisedItemOneHandedNone

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

  • ULambeerHeldAnimationProfile and item AnimationProfile/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.
  • ULambeerAnimInstance resolves continuous carry/aim sequences into UpperBody/RightArm/LeftArm outputs. Missing, additive or wrong-skeleton continuous poses have zero layer weight and set bMissingHeldPose. Empty-handed relaxed locomotion is unchanged. Asset references are hard references loaded with the item/profile, not per-frame loads.
  • ABP_Player evaluates 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 has bMeleeWeapon=true but 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.
  • ResolveHeldAction exposes 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 Development build passed; Lambeer.HUD headless 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.

GroupRequested case / proposed keyLayer and support behavior
Idle / MoveEmpty hands · UnarmedExisting locomotion; no carry override.
Idle / MoveRight item only · RightOnlyRight-arm carry; preserve free left-arm locomotion where the authored pose permits.
Idle / MoveOne item per hand · DualItemsIndependent arm carries, or an explicitly authored pair pose; no hand may support the other item.
Idle / MoveOne item using both hands · TwoHandedItemCoherent upper-body carry; right attachment and authored left support target.
ShoulderAimEmpty hands · UnarmedShoulderAim ready/guard entry.
ShoulderAimRight one-handed melee · Melee1H_RightReady pose; no automatic support-hand IK.
ShoulderAimTwo one-handed melee items · Melee1H_DualTwo weapon hands; pair/arm composition with explicit torso ownership.
ShoulderAimOne two-handed melee item · Melee2HCoherent upper-body pose; support IK only when the clip requires contact.
ShoulderAimRight pistol, left free · Pistol_SupportedLeft IK to an authored pistol support target; native eligibility extension implemented, target/pose still required.
ShoulderAimRight pistol, left occupied · Pistol_UnsupportedRight aiming pose; left keeps its own carry/action; left support IK disabled.
ShoulderAimOne pistol per hand · Pistol_DualIndependent weapon hands; no cross-weapon support IK. Independent right/left firing; ADS sight policy is outside this ShoulderAim request.
ShoulderAimTwo-handed firearm · Firearm2HCoherent aiming pose plus left support IK.
AttackUnarmed · UnarmedAttack montage entry; limb participation and movement policy depend on the selected clip.
AttackRight melee · Melee1H_RightRight action, optional authored torso contribution.
AttackDual one-handed melee · Melee1H_DualOwner-confirmed dedicated paired animation; one action controls both weapons/arms, not independent left/right attack clips.
AttackTwo-handed melee · Melee2HBoth arms and torso act together; support release curve for intentional hand release.
FireSupported right pistol · Pistol_SupportedCharacter recoil + weapon mechanism event; support solve follows recoil.
FireRight pistol, left busy · Pistol_UnsupportedRight recoil only unless an authored torso layer is selected; preserve occupied left-hand pose.
FireDual pistols · Pistol_DualSeparate right/left procedural recoil channels; a shot identifies which weapon fired.
FireTwo-handed firearm · Firearm2HCoherent 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

Existing locomotion + Foot IKCarry + Aim compositionMasked actionsRight hand poseArm pitch + ADS alignmentProcedural recoilSupport IKLeft finger poseOutput
  1. Cache the existing locomotion result as proposed CP_Locomotion. Keep the standing/crouch/prone, start/stop/turn, jump and inertialization graphs intact.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. Apply the occupying right item’s RightHandPose with Layered Blend Per Bone rooted at hand_r after Control Rig and before support IK, so fingers curl around the attached mesh while recoil still moves the gun.
  9. 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.
  10. Apply the left finger Pose Asset after Two Bone IK, rooted at hand_l: the independent left item’s LeftHandPose, or the right item’s LeftHandPose while 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 / responsibilityContract
AnimationProfile on item definitionShared family profile; no per-weapon AnimBP subclass.
RightHandPose / LeftHandPose on item definitionPer-item character Pose Assets for fingers. Independent of family profile clips. Null keeps family fingers.
AnimationOverrides on item definitionSparse 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 entriesCarry 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 variantOptional PresetId-scoped entry only if the same compatible skeleton clip does not fit both bodies. Existing GripBodyOverrides changes grip placement, not animation selection.
Resolution precedenceImplemented: 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 / loadingValidate 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.

FolderAsset naming patternCoverage
Carry/RightOnly/A_Carry_RightOnly_{Stance}_IdleRight item only
Carry/DualItems/A_Carry_DualItems_{Stance}_IdleOne item in each hand
Carry/TwoHanded/A_Carry_TwoHanded_{Stance}_IdleOne 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}_01Unarmed attack
Attack/Melee1H/A_Attack_Melee1H_Right_{Stance}_01Single right melee
Attack/Melee1H/A_Attack_Melee1H_Dual_{Stance}_01Dedicated paired melee animation; exact input binding open
Attack/Melee2H/A_Attack_Melee2H_{Stance}_01Two-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.

HandPose sourceWhen
Right fingersRight item RightHandPoseRight hand occupied
Left fingers, independent itemLeft item LeftHandPoseLeft inventory hand occupied; no support IK
Left fingers, supportRight item LeftHandPoseSupport 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)
  1. 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.
  2. In Content Browser, create an Actor Blueprint named BP_GripCalibration under the proposed Calibration folder. This is a preview helper, not a gameplay character replacement. Add a Skeletal Mesh component Body under its scene root.
  3. 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_BodyMesh and /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.
  4. 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 to hand_r. Assign the exact effective held mesh from the item's Data Asset. Disable collision for the helper.
  5. 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
  1. 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.
  2. 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.
  3. Open the selected /Game/Lambeer/Core/Data/Items/DA_Item_*. Under Grip, expand Right Grip Relative (RightGripRelative) and paste those Location/Rotation values. Leave Scale as identity; runtime ignores authored grip scale. Save this Data Asset.
  4. 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
  1. Independent left-held item: repeat with HeldPreview parent socket hand_l and an authored left pose. Enable bHasLeftGripRelative and write LeftGripRelative. Do not mirror the right offset. This is for a separate item held in the left hand.
  2. Left supporting the right-held item: keep the item on hand_r. Add a Scene/Arrow component SupportTargetPreview as a child of HeldPreview. Position its origin where the hand_l bone 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.
  3. Enable bHasSupportHandTarget and copy the marker's item-local Location into SupportHandTarget. 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.
  4. Repeat inspection for Preset.Man and Preset.Woman. If only item placement needs a different value, add a GripBodyOverrides row with the exact PresetId and Hand, then paste the complete replacement GripRelative. It replaces, rather than adds to, the shared grip. Avoid duplicate keys. A matching Left override also enables left hold for that preset.
  5. 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.
  6. Per-item finger Pose Asset: author a Pose Asset on the actual player skeleton that writes only hand_* and finger bones. Bind it to RightHandPose or LeftHandPose on the item Data Asset. Do not put finger curl into the family Carry clip or into RightGripRelative. Support fingers use the right item’s LeftHandPose.

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 assetCopied fromStatus
Carry/RightOnly/A_Carry_RightOnly_Stand_IdleIdle/A_INP_IdlePlaceholder; replace later
Carry/RightOnly/A_Carry_RightOnly_Stand_MoveIdle/A_INP_IdleDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/RightOnly/A_Carry_RightOnly_Crouch_IdleCarry/RightOnly/A_Carry_RightOnly_Stand_IdlePlaceholder; replace later
Carry/RightOnly/A_Carry_RightOnly_Crouch_MoveCarry/RightOnly/A_Carry_RightOnly_Stand_MoveDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/RightOnly/A_Carry_RightOnly_Prone_IdleCarry/RightOnly/A_Carry_RightOnly_Stand_IdlePlaceholder; replace later
Carry/RightOnly/A_Carry_RightOnly_Prone_MoveCarry/RightOnly/A_Carry_RightOnly_Stand_MoveDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/DualItems/A_Carry_DualItems_Stand_IdleIdle/A_INP_IdlePlaceholder; replace later
Carry/DualItems/A_Carry_DualItems_Stand_MoveIdle/A_INP_IdleDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/DualItems/A_Carry_DualItems_Crouch_IdleCarry/DualItems/A_Carry_DualItems_Stand_IdlePlaceholder; replace later
Carry/DualItems/A_Carry_DualItems_Crouch_MoveCarry/DualItems/A_Carry_DualItems_Stand_MoveDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/DualItems/A_Carry_DualItems_Prone_IdleCarry/DualItems/A_Carry_DualItems_Stand_IdlePlaceholder; replace later
Carry/DualItems/A_Carry_DualItems_Prone_MoveCarry/DualItems/A_Carry_DualItems_Stand_MoveDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/TwoHanded/A_Carry_TwoHanded_Stand_IdleIdle/A_INP_IdlePlaceholder; replace later
Carry/TwoHanded/A_Carry_TwoHanded_Stand_MoveIdle/A_INP_IdleDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/TwoHanded/A_Carry_TwoHanded_Crouch_IdleCarry/TwoHanded/A_Carry_TwoHanded_Stand_IdlePlaceholder; replace later
Carry/TwoHanded/A_Carry_TwoHanded_Crouch_MoveCarry/TwoHanded/A_Carry_TwoHanded_Stand_MoveDeleted 2026-09-17 after shared-action migration; historical provenance only
Carry/TwoHanded/A_Carry_TwoHanded_Prone_IdleCarry/TwoHanded/A_Carry_TwoHanded_Stand_IdlePlaceholder; replace later
Carry/TwoHanded/A_Carry_TwoHanded_Prone_MoveCarry/TwoHanded/A_Carry_TwoHanded_Stand_MoveDeleted 2026-09-17 after shared-action migration; historical provenance only
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_StandIdle/A_INP_IdlePlaceholder; replace later
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_CrouchAim/Melee1H/A_ShoulderAim_Melee1H_Right_StandPlaceholder; replace later
Aim/Melee1H/A_ShoulderAim_Melee1H_Right_ProneAim/Melee1H/A_ShoulderAim_Melee1H_Right_StandPlaceholder; replace later
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_StandIdle/A_INP_IdlePlaceholder; replace later
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_CrouchAim/Melee1H/A_ShoulderAim_Melee1H_Dual_StandPlaceholder; replace later
Aim/Melee1H/A_ShoulderAim_Melee1H_Dual_ProneAim/Melee1H/A_ShoulderAim_Melee1H_Dual_StandPlaceholder; replace later
Aim/Melee2H/A_ShoulderAim_Melee2H_StandIdle/A_INP_IdlePlaceholder; replace later
Aim/Melee2H/A_ShoulderAim_Melee2H_CrouchAim/Melee2H/A_ShoulderAim_Melee2H_StandPlaceholder; replace later
Aim/Melee2H/A_ShoulderAim_Melee2H_ProneAim/Melee2H/A_ShoulderAim_Melee2H_StandPlaceholder; replace later
Aim/Pistol/A_ShoulderAim_Pistol_Supported_CrouchAim/Pistol/A_ShoulderAim_Pistol_Supported_StandPlaceholder; replace later
Aim/Pistol/A_ShoulderAim_Pistol_Supported_ProneAim/Pistol/A_ShoulderAim_Pistol_Supported_StandPlaceholder; replace later
Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_CrouchAim/Pistol/A_ShoulderAim_Pistol_Unsupported_StandPlaceholder; replace later
Aim/Pistol/A_ShoulderAim_Pistol_Unsupported_ProneAim/Pistol/A_ShoulderAim_Pistol_Unsupported_StandPlaceholder; replace later
Attack/Unarmed/A_Attack_Unarmed_Crouch_01Attack/Unarmed/A_Attack_Unarmed_Stand_01Placeholder; replace later
Attack/Unarmed/A_Attack_Unarmed_Prone_01Attack/Unarmed/A_Attack_Unarmed_Stand_01Placeholder; replace later
Attack/Melee1H/A_Attack_Melee1H_Right_Stand_01Attack/Unarmed/A_Attack_Unarmed_Stand_01Placeholder; replace later
Attack/Melee1H/A_Attack_Melee1H_Right_Crouch_01Attack/Melee1H/A_Attack_Melee1H_Right_Stand_01Placeholder; replace later
Attack/Melee1H/A_Attack_Melee1H_Right_Prone_01Attack/Melee1H/A_Attack_Melee1H_Right_Stand_01Placeholder; replace later
Attack/Melee1H/A_Attack_Melee1H_Dual_Stand_01Attack/Unarmed/A_Attack_Unarmed_Stand_01Placeholder; replace later
Attack/Melee1H/A_Attack_Melee1H_Dual_Crouch_01Attack/Melee1H/A_Attack_Melee1H_Dual_Stand_01Placeholder; replace later
Attack/Melee1H/A_Attack_Melee1H_Dual_Prone_01Attack/Melee1H/A_Attack_Melee1H_Dual_Stand_01Placeholder; replace later
Attack/Melee2H/A_Attack_Melee2H_Stand_01Attack/Unarmed/A_Attack_Unarmed_Stand_01Placeholder; replace later
Attack/Melee2H/A_Attack_Melee2H_Crouch_01Attack/Melee2H/A_Attack_Melee2H_Stand_01Placeholder; replace later
Attack/Melee2H/A_Attack_Melee2H_Prone_01Attack/Melee2H/A_Attack_Melee2H_Stand_01Placeholder; replace later
Fire/Pistol/A_Recoil_Pistol_Supported_Stand_AddAim/Pistol/A_ShoulderAim_Pistol_Supported_StandDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Supported_Crouch_AddFire/Pistol/A_Recoil_Pistol_Supported_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Supported_Prone_AddFire/Pistol/A_Recoil_Pistol_Supported_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Unsupported_Stand_AddAim/Pistol/A_ShoulderAim_Pistol_Supported_StandDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Unsupported_Crouch_AddFire/Pistol/A_Recoil_Pistol_Unsupported_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Unsupported_Prone_AddFire/Pistol/A_Recoil_Pistol_Unsupported_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Dual_Right_Stand_AddAim/Pistol/A_ShoulderAim_Pistol_Supported_StandDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Dual_Left_Stand_AddAim/Pistol/A_ShoulderAim_Pistol_Supported_StandDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Dual_Right_Crouch_AddFire/Pistol/A_Recoil_Pistol_Dual_Right_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Dual_Left_Crouch_AddFire/Pistol/A_Recoil_Pistol_Dual_Left_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Dual_Right_Prone_AddFire/Pistol/A_Recoil_Pistol_Dual_Right_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Pistol/A_Recoil_Pistol_Dual_Left_Prone_AddFire/Pistol/A_Recoil_Pistol_Dual_Left_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Firearm2H/A_Recoil_Firearm2H_Stand_AddAim/Pistol/A_ShoulderAim_Pistol_Supported_StandDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Firearm2H/A_Recoil_Firearm2H_Crouch_AddFire/Firearm2H/A_Recoil_Firearm2H_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Fire/Firearm2H/A_Recoil_Firearm2H_Prone_AddFire/Firearm2H/A_Recoil_Firearm2H_Stand_AddDeleted 2026-09-17 after empty referencer audit; historical non-additive placeholder
Additional copies after owner clarification
Created assetCopied fromStatus
Aim/Unarmed/A_ShoulderAim_Unarmed_StandIdle/A_INP_IdlePlaceholder; replace later
Aim/Unarmed/A_ShoulderAim_Unarmed_CrouchAim/Unarmed/A_ShoulderAim_Unarmed_StandPlaceholder; replace later
Aim/Unarmed/A_ShoulderAim_Unarmed_ProneAim/Unarmed/A_ShoulderAim_Unarmed_StandPlaceholder; replace later
Aim/Firearm2H/A_ShoulderAim_Firearm2H_StandAim/Pistol/A_ShoulderAim_Pistol_Supported_StandPlaceholder; replace later
Aim/Firearm2H/A_ShoulderAim_Firearm2H_CrouchAim/Firearm2H/A_ShoulderAim_Firearm2H_StandPlaceholder; replace later
Aim/Firearm2H/A_ShoulderAim_Firearm2H_ProneAim/Firearm2H/A_ShoulderAim_Firearm2H_StandPlaceholder; replace later
Aim/Pistol/A_ShoulderAim_Pistol_Dual_CrouchAim/Pistol/A_ShoulderAim_Pistol_Dual_StandPlaceholder; replace later
Aim/Pistol/A_ShoulderAim_Pistol_Dual_ProneAim/Pistol/A_ShoulderAim_Pistol_Dual_StandPlaceholder; 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

  1. Open the gun's DA_Item_*, search Recoil and expand that struct. These values belong to the item, not DA_AnimProfile_*.
  2. Kick Rotation uses 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.
  3. Kick Seconds defaults to 0.035 seconds; Recovery Seconds defaults to 0.22 seconds. Max Stack defaults to 2 and caps overlapping kicks. These are trial values, not approved balance; every gun may override them independently.
  4. Save the Data Asset, let the owner start PIE with a firearm held, then run Lambeer.Recoil Right or Lambeer.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

  1. Open /Game/Lambeer/Core/Data/Items/DA_Item_L1810, search Sight Alignment. Enabled is opt-in; Socket Name is SightReference. Keep the owner's socket axes and authored grip unchanged while first testing.
  2. Set Sway Translation Cm and Sway Rotation Degrees to zero to inspect neutral alignment. Eye Distance Cm starts 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.
  3. Blend Seconds starts at 0.18 for alignment enter/exit. It blends the alignment controls, not the whole pose selection or camera transition.
  4. 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.
  5. 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.
  6. 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

  1. Finish the right-item placement first: RightGripRelative establishes the gun relative to hand_r. A support target cannot correct a wrong right-hand attachment.
  2. Create the proposed Actor Blueprint BP_GripCalibration for 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.
  3. 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.
  4. 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.
  5. 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.
  6. Read SupportTargetPreview's relative Location. Because its parent is the item mesh, these coordinates are item-local. Copy them into the item's SupportHandTarget.Location and enable bHasSupportHandTarget. No invented vectors are supplied. The current runtime consumes position only; rotating this marker or SupportHandTarget does not rotate the wrist.
  7. 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.
  8. 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.
  9. 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.
  10. 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