MassBattle Docsv1.19.20

MassBattlePhysicsTypes.h

struct ×5

Source/MassBattle/Public/Physics/MassBattlePhysicsTypes.h

概述 Overview

独立多次子步 PBD 物理引擎的共享类型(纯 C++ struct,无 USTRUCT 反射)。按「GridUpdateProcessor + MoveProcessor 两段式模型」的评审结论组织为三段:REGISTER(注册段)遍历 agent,把每个打包为恰好一条 64 字节缓存行的 FAgentPhysicsData 记录注册进 2D 平坦哈希网格——记录本身就是网格负载(位置住在网格里,不在侧边数组);SOLVE(求解段)只遍历槽位数组(绝不碰 fragment、绝不碰 TMap):每个 body 读邻居的 Pre 记录(各一条缓存行、cell 切片连续),写自己的 Current 记录,Pre/Current 双版本规则——读只见 Pre、写只进 Current,子步之间由网格更新把 Current 提升为 Pre(零拷贝 swap),成员身份每帧注册一次、遍历余量随运行最大累计位移自适应,跨格漂移绝不丢对;WRITEBACK(写回段)再遍历 agent 一次——唯一的 fragment 写。分桶保持 2D 的原因:精确 3D 胶囊对测试负责垂直判定,2D 使遍历为 (2R/256)² 次探测而非 (2R/256)³;生产 agent 网格 Z=5000cm 实际同为 2D,同理。

Shared types for the standalone multi-substep PBD physics engine (plain C++ structs, no USTRUCT reflection). Organized per the GridUpdate + Move two-pass design review into three passes: REGISTER walks agents and packs each into ONE exactly-64-byte cache-line FAgentPhysicsData record registered into the 2D flat hash grid — the record IS the grid payload (positions live in the grid, not in side arrays). SOLVE iterates SLOT ARRAYS ONLY (never fragments, never TMap): every body reads neighbors' Pre records (one cache line each, contiguous cell slices) and writes its own Current record; the Pre/Current double-version rule — reads see only Pre, writes go only to Current; between substeps the grid update promotes Current → Pre (a zero-copy swap); membership is registered once per frame and the walk margin adapts to the running max cumulative displacement so cell-boundary drift can never drop a pair. WRITEBACK walks agents once more — the ONLY fragment writes. Bucketing stays 2D because the exact 3D capsule pair test handles verticality, and 2D keeps a walk at ~(2R/256)² probes instead of (2R/256)³; the production agent grid runs Z=5000 cm — effectively 2D — for the same reason.

通道规则:本文件全部为帧内派生数据——绝不注册进网络 hash / 快照。双端从相同同步 fragment 跑相同代码,锁步安全来自计算一致。引擎宿主为 Processors/MassBattlePhysicsProcessor.h。

Channel rule: everything here is frame-internal derived data — NEVER registered into the network hash/snapshot. Both peers run the same code from the same synced fragments; lockstep safety comes from identical computation. The engine host is Processors/MassBattlePhysicsProcessor.h.

FPhysicsHandoffRecord Move → Physics 帧内交接

Move 处理器 → 物理处理器的帧内交接记录。独立引擎启用时,Move 处理器在其尾部并行(TLS 直写实体索引槽位)推送;物理处理器在注册段开头串行合并消费(超容溢出部分排空)。

The per-frame handoff record from the Move processors to the physics processor. When the standalone engine is armed, the Move processors push records (parallel, direct entity-indexed writes) at their tail; the physics processor merges + consumes them (draining capacity overflow) serially at register time.

成员变量 Members

名称 Name类型 Type默认值说明 Description
EntityFMassEntityHandle—交接对应的实体句柄(用于实体索引直写)。Entity handle the record belongs to (drives the direct entity-indexed write).
UniqueIDint32-1实体 UID(-1 = 无效)。Entity UID (-1 = invalid).
ObstaclePlanesTArray<FPlane, TInlineAllocator<4>>空从 RVO 场景命中转化的墙体平面(Move SECTION 8 第 1 步产物);物理求解段作为硬约束平面消费。Scene wall planes converted from RVO scene hits (Move SECTION 8 step 1); consumed by the solve pass as hard-constraint planes.
bIsTouchingSteepSlopeboolfalse地面采样得到的陡坡接触(留在 Move 侧判定);物理写回时 OR 进 bCachedTouchingObst。Steep-slope contact from the ground trace (the test itself stays in Move); OR-ed into bCachedTouchingObst at physics writeback.
DetectObstDistfloat0.fMove 本帧算出的障碍采集半径(避障派生);物理障碍缓存原样复用,保证两条路径看到同一障碍集合。Obstacle gather reach Move computed this frame (avoidance-derived); the physics obstacle cache reuses it verbatim so both paths see the same set.

FAgentPhysicsData 网格/槽位热记录

网格/槽位热记录——恰好一条 64 字节缓存行(static_assert(sizeof == 64) 强制)。每槽存在两个版本(Pre / Current):子步读邻居的 Pre、写自身 Current(整记录拷贝 + 新位置),因此每次邻居读取都是一条连续缓存行。求解全程单精度 float(Chaos 同款)。.cpp 中另有 static_assert(std::is_trivially_copyable_v<FAgentPhysicsData>)——以原始字节跨总线安全。

The grid/slot hot record — exactly one 64-byte cache line (enforced by static_assert(sizeof == 64)). Two versions exist per slot (Pre / Current): a substep reads neighbors' Pre and writes its own Current (whole-record copy + new position), so every neighbor read is one contiguous line. The whole solve runs single-precision float (Chaos-style). The .cpp adds static_assert(std::is_trivially_copyable_v<FAgentPhysicsData>) — concurrency-safe to ship as raw bytes.

成员变量 Members

名称 Name类型 Type默认值说明 Description
PosFVector3fZeroVector子步位置(单精度)。Substep position (single precision).
UpFVector3fUpVector碰撞体上向(世界空间,含相对旋转的总旋转)。Collider up (world space, from the total rotation including the relative rotation).
Radiusfloat34.fPBD 半径(已含 FPhysicsShared::ColliderMult 放大)——agent 对接触用。PBD radius (inflated by FPhysicsShared::ColliderMult) — agent-pair contacts.
ShaftHalffloat0.f真实轴半高(绝不放大)。Real shaft half-height (never inflated).
Massfloat1.f质量(对修正的质量加权分摊)。Mass (mass-weighted split of pair corrections).
AngularVelocityfloat0.f角速度(度/秒),供旋转扫掠膨胀计算。Angular velocity (deg/s), feeding the rotational sweep inflation.
Boundfloat68.fShaftHalf + Radius——预算候选界(包围球半径)。ShaftHalf + Radius — precomputed candidacy bound (bounding-sphere radius).
UniqueIDint32-1实体 UID(网格成员排序键来源)。Entity UID (source of the grid member sort key).
bIsDyinguint8 : 10死亡中(旗标发布)。Dying (flag publication).
bImmovableuint8 : 10不可推动——发布式与 GridUpdate 一致:!bRunsMoveSolver || !bEnablePBD || !bCollideWithAgent;命中时对修正的自身份额取满。Immovable — published exactly as GridUpdate does: !bRunsMoveSolver || !bEnablePBD || !bCollideWithAgent; a hit takes the full self-share of the pair correction.
bTouchingObstuint8 : 10帧初网格发布值(触碰障碍);对端命中时自身份额取满。Frame-start grid publication (touching an obstacle); neighbors take the full self-share against it.
bMultiCelluint8 : 10巨型单位——多格注册;查询侧用槽位去重保证只处理一次。Giant — multi-cell registered; query-side slot dedup processes it once.
CollisionGroupuint8 : 30碰撞组 1–8 打包为 0–7。Collision groups 1–8 packed as 0–7.
Pad : 1 + PadToLine[3]uint8 / float[3]0填充位,保持记录恰好一整条缓存行。Padding keeping the record exactly one full cache line.

方法 Methods

FAgentPhysicsData 构造函数(= default)

默认构造,按上表默认值初始化;POD 语义,可平凡拷贝。

Default constructor initializing the defaults above; POD semantics, trivially copyable.

Input

无参数。

No parameters.

Output

无返回值。

No return value.

FPhysicsObstRef 缓存障碍候选

一条缓存障碍候选。数据指针整帧稳定(障碍网格帧初重建、模拟阶段只读);子步对引用重算接触几何,不重扫障碍网格。每帧只采集一次(注册段内 BuildObstacleCacheForSlot 构建进 FPhysicsSlotSelf::ObstRefs)。

One cached obstacle candidate. The data pointer is stable for the whole frame (the obstacle grid is rebuilt at frame start and read-only during the sim stages); substeps recompute contact geometry against the ref without re-walking the obstacle grid. Gathered ONCE per frame (built into FPhysicsSlotSelf::ObstRefs by BuildObstacleCacheForSlot during the register pass).

成员变量 Members

名称 Name类型 Type默认值说明 Description
Srcconst void*nullptr障碍网格数据指针,按 Type 转译为 FCapsuleObstGridData* / FSegmentObstGridData* / FBoxObstGridData*(类型定义于 Fragments/GridData.h)。Obstacle grid data pointer, reinterpret-cast by Type to FCapsuleObstGridData* / FSegmentObstGridData* / FBoxObstGridData* (types from Fragments/GridData.h).
CellLocationFVectorZeroVector障碍所在网格单元位置(double;与相对位置相加得世界位置)。Grid cell location of the obstacle (double; added to its relative location for the world position).
Typeuint800 = 胶囊,1 = 线段墙,2 = 盒。0 capsule, 1 segment, 2 box.
InvOrientationFQuat4fIdentity仅盒使用——缓存构建时预算的逆朝向(变换帧内恒定,子步免重复求逆)。Box only — inverse orientation precomputed at cache build (frame-constant transform; substeps skip re-inverting).

FPhysicsSlotSelf 仅自身求解状态

仅自身可见的求解状态(配置拷贝、门控、交接输入、累积器)。邻居永远不碰它——所以它可以「胖」,不伤对遍历的缓存表现。热记录(FAgentPhysicsData)只保留邻居需要的最小字段,自身专属的一切都在这里。

Self-only solve state (config copies, gates, handoff inputs, accumulators). Neighbors never touch it — so it can be fat without hurting the pair-walk cache profile. The hot record (FAgentPhysicsData) keeps only the minimum neighbors need; everything self-specific lives here.

成员变量 Members

名称 Name类型 Type默认值说明 Description
Entity / UniqueIDFMassEntityHandle / int32— / -1实体句柄与 UID。Entity handle and UID.
RealRadiusfloat34.f真实(未放大)半径——障碍/场景路径使用,与旧路径口径一致。Real (un-inflated) radius — used by the obstacle/scene paths, legacy parity.
SelfTotalHalffloat68.fShaftHalf + RealRadius——障碍包围半径。ShaftHalf + RealRadius — obstacle bounding radius.
SweepInflationfloat0.f旋转扫掠膨胀(轴 XY 投影 × |角速度| × Δt 弧度);竖直胶囊该值≈0。Rotational sweep inflation (shaft XY projection × |angular velocity| × Δt in radians); ≈0 for vertical capsules.
bEnablePBD … bLaunchinguint8 : 1 ×90自身门控位:是否启用 PBD、是否与 agent 碰撞、是否与障碍碰撞、是否与死亡单位碰撞、agent 对是否参与(死亡碰撞延迟的镜像)、Z 轴启用、可飞行、下落中、击飞中。Self gate bits: PBD on, collide-agent, collide-obstacle, collide-dying, agent-pair participation (dying-delay mirror), Z enabled, can fly, falling, launching.
IgnoreCollMaskuint80忽略碰撞组位掩码(按对端 CollisionGroup 位测试)。Ignore-collision-group bitmask (tested per neighbor CollisionGroup bit).
RelaxAgentfloat1.f每子步刚度 k′ = 1−(1−k)^(1/N)(k 为帧级 PositionRelaxation_Agent),保持 N 次应用的帧等效语义。Per-substep stiffness k′ = 1−(1−k)^(1/N) (k = the frame-level PositionRelaxation_Agent), preserving the frame-equivalent meaning across N applications.
VelRelaxAgentfloat1.fagent 位移的速度反馈份额。Velocity-feedback share for agent-pair displacement.
MassDominanceRatiofloat1.f质量优势比(质量优势门控)。Mass dominance ratio (the dominance gate).
StackStrengthfloat0.25f堆叠支撑强度(支撑/滑移判定)。Stacking support strength (support/slip decision).
FrictionStaticLat / FrictionStaticLongfloat0.f库仑静摩擦预算(横向/纵向,帧量——每子步按整帧预算判定)。Coulomb static friction budgets (lateral / longitudinal; frame quantities — each substep tests against the full frame budget).
MoveAccel / MoveDecelfloat1000.f速度反馈的加速/减速限幅(写回段)。Acceleration/deceleration caps for the velocity feedback (writeback).
XYSpeedLimitFVector2f(0, 5000)XY 速度钳制(X=下限,Y=上限;击飞/下落例外保留)。XY speed clamp (X = min, Y = max; launching/falling exception preserved).
TurnSpeedDeg / TurnSpeedRangeMapfloat / FVector4f360 / (0,1,2000,1)转向速度与「速度→转向倍率」映射(扫掠弧占位归一化用)。Turn speed and the speed→turn-multiplier range map (sweep-arc occupancy normalization).
Fwd2D / DesiredVel2D / OldVelFVector2f / FVector2f / FVector3f(1,0) / 零 / 零(1,0) / zero / zero摩擦帧(求解处零保护)、纵向意图判定用的期望速度、构建时旧速度(反馈基准)。Friction frame (zero-guarded at solve), desired velocity for the longitudinal-intent gate, build-time old velocity (feedback baseline).
ScenePlanesTArray<FPlane, TInlineAllocator<4>>空交接输入:场景硬约束平面(拷贝自 FPhysicsHandoffRecord)。Handoff input: hard-constraint scene planes (copied from FPhysicsHandoffRecord).
ObstRefsTArray<FPhysicsObstRef, TInlineAllocator<6>>空每帧障碍缓存(采集趟构建,消费趟只读)。Per-frame obstacle cache (built at gather, read-only while consuming).
PairSlotsTArray<int32, TInlineAllocator<16>>空接触对缓存——采集趟按余量界准入的候选槽位列表;消费趟只迭代此表。Contact-pair cache — candidate slots admitted (margin-bound) at gather substeps; consume substeps iterate only this list.
PairActiveTArray<uint8, TInlineAllocator<16>>空与 PairSlots 对齐——某对产生真实接触时置位(AVBD 对偶更新的行门)。Aligned with PairSlots — set when a pair produces a real contact (the AVBD dual update's row gate).
PairLambdaTArray<float, TInlineAllocator<16>>空AVBD(Solver 1)对偶乘子 λ 的每对镜像——两端从相同输入各自演化副本,镜像同步、力等大反向,零跨体写零原子;每 sweep clamp(ρ·C+λ, 0, ∞) 更新。AVBD (Solver 1) per-pair mirror of the dual multiplier λ — both endpoints evolve their own copy from identical inputs; mirrors stay in sync, forces equal-opposite, with zero cross-body writes and no atomics; updated per sweep as clamp(ρ·C+λ, 0, ∞).
PairPenaltyTArray<float, TInlineAllocator<16>>空AVBD 罚量 ρ 镜像——行处于内域(λ>0)时逐 sweep 按 β·|C| 递增,封顶 ρ_max。AVBD penalty ρ mirror — ramps β·|C| per sweep while the row is interior (λ>0), capped at ρ_max.
PairCContactTArray<float, TInlineAllocator<16>>空sweep 起点重叠缓存——对偶更新使用的(滞后一 sweep 的)C。Sweep-start overlap cache — the (one-sweep-lagged) C consumed by the dual update.
bSteepSlope / DetectObstDistbool / floatfalse / 300.f交接输入:陡坡标志与障碍采集半径(兜底 = RealRadius + 300,与 Move 一致)。Handoff inputs: steep-slope flag and obstacle gather reach (fallback = RealRadius + 300, matching Move).
StartPosFVector3f零帧初构建位置——AVBD 锚项拉力目标与累计漂移度量基准。Frame-start build position — the AVBD anchor target and the cumulative-drift baseline.
TotalAgentDeltaFVector3f零agent 对修正累计(速度反馈的 agent 份额)。Accumulated agent-pair corrections (the agent share of velocity feedback).
AccumSweepOccupancyfloat0.f子步间扫掠弧占位的运行最大值 → 写回为 RotationalResistance。Running MAX of sweep-arc occupancy across substeps → written back as RotationalResistance.
LastObservedOverlapfloat0.f调试:本 body 子步开始时看到的 agent 对总重叠(收敛诊断求和用)。Debug: agent-pair overlap this body saw at substep start (summed for convergence diagnostics).
bCollidedAgent / bCollidedObst / bSceneContact / bSupportedbool ×4false接触结果标志,写回段发布进 FMoving 的缓存字段。Contact-result flags, published into the FMoving cached fields at writeback.

FPhysicsCellEntry 网格注册项

一条网格注册项:cell 坐标 + 槽位索引 + 偏置 UID(巨型单位发多条——多格脚印)。UID 与槽位一起打包进每格的 64 位成员字,因此逐格成员排序即得 UID 序,与槽位编号无关——槽位序本身是票号分配(依赖调度),确定性住在格子里(AgentGrid 的 SortAgentCellsByUniqueID 配方)。

One grid registration entry: cell coordinate + slot index + biased UID (giants emit several — a multi-cell footprint). The UID packs WITH the slot into each cell's 64-bit member word, so the per-cell member sort produces UID rank regardless of slot numbering — slot order itself is ticket-assigned (scheduling-dependent); determinism lives in the cells (the AgentGrid SortAgentCellsByUniqueID recipe).

成员变量 Members

名称 Name类型 Type默认值说明 Description
CellFIntPointZeroValue2D 格坐标。2D cell coordinate.
Slotint320槽位数组下标。Slot-array index.
BiasedUIDuint320uint32(UniqueID ^ 0x80000000)——无符号序排序键。uint32(UniqueID ^ 0x80000000) — the unsigned-order sort key.

方法 Methods

Packed FORCEINLINE uint64

把 BiasedUID 与 Slot 打包为一个 64 位成员字(BiasedUID << 32 | Slot)。逐格成员按此字排序后,高 32 位即 UID 序、低 32 位即槽位——一次排序同时确定遍历顺序与去重键。

Packs BiasedUID and Slot into one 64-bit member word (BiasedUID << 32 | Slot). Sorting each cell's members by this word yields UID rank in the high 32 bits and the slot in the low 32 — one sort fixes both traversal order and the dedup key.

Input

无参数。

No parameters.

Output

返回打包后的 64 位成员字。无副作用。

Returns the packed 64-bit member word. No side effects.

管线角色 Pipeline Role

全部类型只服务于 UMassBattlePhysicsProcessor(及其持久化网格宿主 UMassBattlePhysicsSubsystem),以下关系在发布树 grep 验证:FPhysicsHandoffRecord 由两个 Move 处理器(MassBattleAgentMoveProcessor.cpp 与 MassBattleAgentMoveProcessorFP.cpp)在其尾部经 UMassBattlePhysicsProcessor::PushHandoff 实体索引直写;FAgentPhysicsData / FPhysicsSlotSelf 是处理器的 PreData / CurData / Selves 三条槽位数组元素;FPhysicsObstRef 由处理器的 BuildObstacleCacheForSlot 从 UMassBattleHashGridSubsystem 的障碍网格一次性采集;FPhysicsCellEntry 被 UMassBattlePhysicsSubsystem 消费——注册波内经自旋锁追加 / Pending 收集器进入,串行 flush 后逐格按打包字做 UID 成员排序(MassBattlePhysicsSubsystem.h / .cpp)。fragment 的读写全部隔离在处理器的注册段(读)与写回段(写),求解段只见这些槽位结构。网络通道零参与——绝不进 hash / 快照。

Every type here serves only UMassBattlePhysicsProcessor (and its persistent grid host UMassBattlePhysicsSubsystem); the following relations are grep-verified in the release tree: FPhysicsHandoffRecord is written at the tails of both Move processors (MassBattleAgentMoveProcessor.cpp and MassBattleAgentMoveProcessorFP.cpp) via UMassBattlePhysicsProcessor::PushHandoff with direct entity-indexed writes; FAgentPhysicsData / FPhysicsSlotSelf are the elements of the processor's PreData / CurData / Selves slot arrays; FPhysicsObstRef is gathered once per frame by the processor's BuildObstacleCacheForSlot from the obstacle grid of UMassBattleHashGridSubsystem; FPhysicsCellEntry is consumed by UMassBattlePhysicsSubsystem — spinlock appends / the Pending gatherer during the register wave, then a serial flush and the per-cell UID-packed member sort (MassBattlePhysicsSubsystem.h / .cpp). Fragment reads/writes are fully isolated into the processor's register pass (reads) and writeback pass (writes); the solve pass sees only these slot structures. Zero network-channel involvement — never hashed, never snapshotted.

补充说明 Notes

两条 static_assert 守住硬约束:sizeof(FAgentPhysicsData) == 64(恰好一条缓存行,头文件内)与 std::is_trivially_copyable_v<FAgentPhysicsData>(可平凡复制,PhysicsProcessor.cpp 内)。头文件按引擎小版本条件包含实体句年头(UE ≥ 5.8 走 Mass/EntityHandle.h,否则 MassEntityHandle.h),并包含 Fragments/GridData.h 以取三种障碍网格数据类型。AVBD 求解器状态(PairLambda / PairPenalty / PairCContact)的生命周期 = 对条目的生命周期:采集趟初始化(λ=0、ρ=ρ₀),消费趟演化,重采重置——v1 无跨帧 warmstart;完整设计见引擎源码引用的 references/avbd-solver-design.md。

Two static_asserts guard the hard constraints: sizeof(FAgentPhysicsData) == 64 (exactly one cache line, in the header) and std::is_trivially_copyable_v<FAgentPhysicsData> (trivially copyable, in PhysicsProcessor.cpp). The header conditionally includes the entity-handle header by engine minor version (UE ≥ 5.8 → Mass/EntityHandle.h, else MassEntityHandle.h) and includes Fragments/GridData.h for the three obstacle grid data types. The AVBD solver state (PairLambda / PairPenalty / PairCContact) has the lifecycle of its pair entry: gather inits (λ=0, ρ=ρ₀), consume sweeps evolve, regather resets — no cross-frame warmstart in v1; full design in references/avbd-solver-design.md cited by the engine sources.