CC Package and Physics
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
---
|
||||
id: DR-006
|
||||
title: M5 physics-in-prediction — velocity-driven character on Unity Physics in the predicted loop; planar pin
|
||||
status: accepted
|
||||
date: 2026-06-01
|
||||
tags:
|
||||
- decision
|
||||
- netcode
|
||||
- physics
|
||||
- prediction
|
||||
- m5
|
||||
permalink: gamevault/07-sessions/decisions/dr-006-m5-physics-in-prediction
|
||||
---
|
||||
|
||||
# DR-006 — M5 Physics-in-Prediction (velocity-driven character; planar pin)
|
||||
|
||||
## Context
|
||||
|
||||
M5 ([[Milestones]]) = "home base + physics". The operator scoped this first pass to **physics-in-prediction**: stand up Unity Physics (1.4.6) *inside* Netcode's predicted simulation loop before any base-streaming work, because deterministic, rollback-safe physics is the highest-risk netcode surface in the roadmap. Through M4 the player moved by **teleporting `LocalTransform.Position`** (`PlayerMoveSystem`, kinematic) — no collision, no bodies. `Unity.Physics` was already referenced by `ProjectM.Simulation`, and the netcode-physics integration assemblies (`Unity.NetCode.Physics`, `…Physics.Hybrid`) ship with our stack. Validated against Unity package docs + `unity_reflect` against the installed packages (context7 MCP was unavailable this session — fell back to official docs + reflection). Extends [[DR-005_M4_Connection_Model_Direct_IP]].
|
||||
|
||||
## Decision
|
||||
|
||||
1. **Predicted physics is implicit — no toggle.** With the netcode-physics package present and predicted ghosts carrying physics components, Netcode relocates `PhysicsSystemGroup` into the `PredictedFixedStepSimulationSystemGroup` (a child of `PredictedSimulationSystemGroup`, marked **OrderFirst**). `NetCodePhysicsConfig` has **no `PredictedPhysics` field** — it only tunes lag-comp / run-mode / history. A single `NetCodePhysicsConfig` lives in the `Gameplay` subscene with `PhysicGroupRunMode = LagCompensationEnabledOrAnyPhysicsEntities` (so the predicted physics group runs whenever physics entities exist) and `EnableLagCompensation = false` (our damage is swept-segment + server-side; lag comp deferred).
|
||||
2. **The player is a velocity-driven dynamic body, authored with built-in components.** Unity Physics **1.x bakes built-in `UnityEngine` colliders + `Rigidbody`** — the old `PhysicsBodyAuthoring`/`PhysicsShapeAuthoring` (Physics 0.x) are gone. `Player.prefab` gets a `CapsuleCollider` (r 0.5, h 2) + a `Rigidbody` (`useGravity=false` → planar, `isKinematic=false`, `FreezeRotation`, `interpolation=Interpolate`). These bake to `PhysicsCollider` / `PhysicsVelocity` / `PhysicsMass` / `PhysicsGravityFactor`(0) / `PhysicsGraphicalSmoothing`. `PlayerMoveSystem` now writes `PhysicsVelocity.Linear = dir * MoveSpeed` (Y zeroed, angular zeroed) instead of teleporting; the solver integrates + resolves contacts.
|
||||
3. **`PhysicsVelocity` auto-replicates.** Netcode ships `PhysicsVelocityDefaultVariant` + a generated serializer, so no hand-written `[GhostField]` is needed; `LocalTransform` is already replicated on the player ghost. Owner-predicted player (`DefaultGhostMode=OwnerPredicted`) → owner predicts physics, other clients interpolate (Simulate disabled), server is authoritative.
|
||||
4. **Players are pinned to the movement plane after the step.** `PlayerPlanarConstraintSystem` (`PredictedSimulationSystemGroup`, `[UpdateAfter(PredictedFixedStepSimulationSystemGroup)]`) clamps each predicted player's Y to `PlayerSpawner.SpawnPoint.y` (single source) and zeroes vertical velocity. Required because with gravity off, any vertical contact impulse (a capsule riding a box edge) is **permanent** — without the pin the character climbs over obstacles and floats away.
|
||||
5. **Static world geometry is baked, not ghosted.** Three static box colliders (`Pillar_Center`, `Wall_East`, `Wall_North`, red `M_Dummy`) added to the `Gameplay` subscene — present identically in server + client worlds (deterministic, no replication). Projectiles stay kinematic this pass.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Validated end to end on 6.4.7.** EditMode 51/51 (4 new `PlayerMoveSystemTests`: cardinal map, diagonal unit-clamp, sub-unit proportional, determinism+angular-zero). Runtime: player baked with all physics components on **both** worlds; 4 `PhysicsCollider` bodies; driving +X into `Wall_East` (face x≈5.75) stops the capsule at **x=5.25** (= face − radius) on server **and** client, holds there under continuous push (no tunnel, no creep), Y pinned at 1.00 (no climb-over), and releases to free movement on reverse. **Server == client exactly** — prediction in sync, console clean of all five M2/M4 cascade signatures and Burst errors.
|
||||
- **Ordering: physics runs *before* `PlayerMoveSystem` (1-tick velocity offset).** The fixed-step group is **OrderFirst**, so `[UpdateBefore]` against it is ignored (the attribute was removed to keep the console warning-free). The offset is identical on server + owning client + rollback, so prediction stays consistent and the lag is absorbed by prediction. If same-tick responsiveness is ever needed, move `PlayerMoveSystem` *into* `PredictedFixedStepSimulationSystemGroup` `[UpdateBefore(PhysicsSystemGroup)]` (verified to sort correctly) — at the cost of two cosmetic "invalid UpdateBefore" warnings from the netcode physics relocation.
|
||||
- **`Rigidbody.FreezeRotation` is NOT honored by the DOTS baker** — baked `PhysicsMass.InverseInertia` stayed non-zero. Rotation is instead held by zeroing angular velocity each tick (`PlayerMoveSystem`) + `PlayerAimSystem` writing the facing directly. If precise rotation lock is needed later, set `PhysicsMass.InverseInertia = 0` in a baker/system.
|
||||
- **The known unfocused-editor `Server Tick Batching` artifact persists** (1.25–1.75 ticks/frame) — clears when the Game view is focused / in a build (see [[2026-06-01_M4_LAN_CoOp_And_Classification_Fix]]). It does not desync the physics in-session.
|
||||
- **Determinism caveat (deferred):** Unity Physics is same-binary deterministic, which is what client re-simulation needs; cross-platform float determinism is not guaranteed but the server is authoritative and corrects via snapshots + `PhysicsGraphicalSmoothing`. No lag compensation yet (`EnableLagCompensation=false`).
|
||||
|
||||
Mirrors the server-authoritative + client-prediction pillars from [[Pillars]]; unblocks the base-streaming half of M5.
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: DR-007
|
||||
title: M5b — adopt Unity Character Controller package for the predicted player (replaces the dynamic-Rigidbody mover)
|
||||
status: accepted
|
||||
date: 2026-06-01
|
||||
tags:
|
||||
- decision
|
||||
- netcode
|
||||
- physics
|
||||
- character-controller
|
||||
- prediction
|
||||
- m5
|
||||
permalink: gamevault/07-sessions/decisions/dr-007-m5b-character-controller-package
|
||||
---
|
||||
|
||||
# DR-007 — M5b Unity Character Controller (kinematic, owner-predicted)
|
||||
|
||||
## Context
|
||||
|
||||
The M5 player ([[DR-006_M5_Physics_In_Prediction]]) was a **dynamic Unity Physics Rigidbody** driven by setting `PhysicsVelocity` each tick. It worked, but only with workarounds the kinematic approach avoids by design: a planar-pin system (gravity-off bodies accumulate vertical contact impulses and climb over walls), per-tick angular-zeroing, and `Rigidbody.FreezeRotation` (which the DOTS baker ignores). The operator chose to adopt Unity's **Character Controller package** (`com.unity.charactercontroller`) — a DOTS, collide-and-slide kinematic controller with first-class Netcode-for-Entities prediction — as the player's movement foundation. Researched via a read-only workflow + verified against the installed API with `unity_reflect`. Supersedes the **movement** half of DR-006; the predicted-physics infrastructure DR-006 stood up (predicted physics group, `NetCodePhysicsConfig`, baked static walls) is **kept** and is exactly what the CC character sweeps against.
|
||||
|
||||
## Decision
|
||||
|
||||
1. **CC version: `com.unity.charactercontroller` 1.4.2 — verified to resolve on our stack.** It declares `com.unity.entities@1.3.15` / `com.unity.physics@1.3.15`, but Unity treats these as SemVer **floors**: the resolver kept our **Entities 6.4.0 / Physics 1.4.6 / Netcode 1.13.2 / Collections 6.4.0** with **no downgrade**, and CC 1.4.2 compiled clean against the renumbered Entities 6.x. (Empirical install probe was the gate; the docs' "installation will fail" prediction was wrong.)
|
||||
2. **Kinematic collide-and-slide, owner-predicted.** The player is a `KinematicCharacterBody` (no Rigidbody). `BakeCharacter` adds the CC component/buffer set + bakes the prefab's `CapsuleCollider` into `PhysicsCollider`. Top-down config: `SnapToGround=false`, gravity handled in the processor (fed `float3.zero`), `InterpolateRotation=false` (rotation stays owned by `PlayerAimSystem`), `SimulateDynamicBody=false`.
|
||||
3. **CC API pattern (1.4.2): `IKinematicCharacterProcessor<T>` + `KinematicCharacterDataAccess` + static `KinematicCharacterUtilities.Update_*`.** The legacy `KinematicCharacterAspect` (IAspect, instance `Update_*`) still exists but the static-utilities path is what the 1.4.x samples use — confirmed via reflect. The processor runs the canonical Update sequence (Initialize→ParentMovement→Grounding→[velocity control]→PreventGrounding→GroundPushing→MovementAndDecollisions→…) in `CharacterPhysicsUpdateSystem` (`[UpdateInGroup(KinematicCharacterPhysicsUpdateGroup)]`, which netcode relocates into the predicted fixed-step loop).
|
||||
4. **Data-driven + reuse the existing input.** `PlayerControlSystem` (`PredictedSimulationSystemGroup`, `[UpdateAfter(StatRecomputeSystem)]`) maps the replicated `PlayerInput.Move` × `EffectiveCharacterStats.MoveSpeed` → a non-replicated `CharacterControl.MoveVelocity` (clamp via the unit-tested `CharacterControlMath.DesiredMovement`); the processor lerps `RelativeVelocity` toward it. No second input component; spawn / GhostOwner / co-op ring unchanged.
|
||||
5. **Ghost variants: only `CharacterInterpolation` → PredictedClient-only.** `BakeCharacter` adds `CharacterInterpolation` to every prefab version; a `DefaultVariantSystemBase` + `[GhostComponentVariation(typeof(CharacterInterpolation))] [GhostComponent(PrefabType = GhostPrefabType.PredictedClient)]` strips it from server + interpolated-client prefabs (presentation-only; double-interp on remotes otherwise). **We deliberately do NOT register the CC sample's global `LocalTransform → DontSerializeVariant`** — that is project-wide and would break our non-character ghosts (projectiles/dummies/pickups) that rely on stock `LocalTransform` replication. The character replicates position via the normal owner-predicted `LocalTransform` path.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Validated on 6.4.7.** EditMode 47/47 (the 4 M5 PhysicsVelocity-mapping tests replaced by 4 `CharacterControlMath` tests). Runtime (single in-editor client): player bakes the full CC set on **both** worlds; driving into `Wall_East` (face x≈5.75) stops the capsule at **x=5.24** = face − radius on server **and** client; diagonal input **slides along** the wall and rounds its finite end (true collide-and-slide, not tunnelling); **Y holds at 1.00 with no planar-pin system** (kinematic + no gravity stays on plane); **server == client**; `CharacterInterpolation` confirmed present on the client ghost and **absent on the server** (variant works). Console clean of CC/Burst/cascade errors — only the known unfocused-editor tick-batching artifact.
|
||||
- **Net code simplification.** Deleted `PlayerMoveSystem` + `PlayerPlanarConstraintSystem` (and removed `StatRecomputeSystem`'s `[UpdateBefore(PlayerMoveSystem)]`). The climb-over / rotation-lock / planar-pin workarounds from DR-006 are gone — the kinematic controller handles it natively.
|
||||
- **Asmdefs:** `ProjectM.Simulation` and `ProjectM.Authoring` now reference `Unity.CharacterController` (Authoring also `Unity.Physics` for `BakeCharacter`).
|
||||
- **Stack:** new dependency `com.unity.charactercontroller@1.4.2` in `manifest.json` / lock; core DOTS versions unchanged.
|
||||
|
||||
## Open / deferred
|
||||
|
||||
- **Multi-client co-op interpolation not headlessly validated.** Single owner-predicted client is validated; remote interpolation smoothness (and the predicted-only `CharacterInterpolation` variant's effect on a *real* second client) needs a live two-build / thin-client check. The variant is in place per Unity's documented setup; CC 1.4.2 also guards `CharacterInterpolationSystem` to enabled-`Simulate` entities only.
|
||||
- **Player-vs-player is non-physical** (`SimulateDynamicBody=false`): kinematic characters don't shove each other. Revisit if mutual push is wanted (set true + handle masses in `OverrideDynamicHitMasses`).
|
||||
- **Gravity-free / no floor collider** — planar top-down; if verticality/terrain is added, give the character gravity in the processor + a ground collider and reconsider `SnapToGround`.
|
||||
- **CC 1.4.2 declares older package deps** (entities/physics 1.3.15). Re-check on any CC bump; a CC build explicitly targeting Entities 6.x ("1.5+") would be preferable when available.
|
||||
- **`KinematicCharacterBody` velocity ghost variant** (replicate `RelativeVelocity`/`IsGrounded` for tighter reconciliation) was left at defaults; add if owner-prediction reconciliation looks jittery under latency.
|
||||
|
||||
Mirrors the server-authoritative + client-prediction + small-co-op pillars from [[Pillars]]. Builds on [[DR-006_M5_Physics_In_Prediction]] (kept infra) and [[DR-005_M4_Connection_Model_Direct_IP]] (spawn/co-op).
|
||||
Reference in New Issue
Block a user