using Unity.CharacterController; using Unity.Entities; using Unity.Mathematics; using Unity.Physics; namespace ProjectM.Simulation { /// /// Per-update "global" data the character processor needs (lookups, singletons). Empty for the minimal /// top-down character — kept so the CC update signature is satisfied and future lookups have a home. /// public struct CharacterUpdateContext { public void OnSystemCreate(ref SystemState state) { } public void OnSystemUpdate(ref SystemState state) { } } /// /// Top-down kinematic character processor (CC 1.4.2 pattern: /// + a built in the job, driving the static /// Update_* sequence). Stripped of all FPS features: no jump, /// no air movement, no gravity, no view. runs the canonical CC update /// sequence; lerps RelativeVelocity toward the desired planar /// velocity wrote. Rotation/facing is owned by . /// public struct CharacterProcessor : IKinematicCharacterProcessor { public KinematicCharacterDataAccess CharacterDataAccess; public RefRW CharacterComponent; public RefRW CharacterControl; public void PhysicsUpdate(ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext) { ref CharacterComponent characterComponent = ref CharacterComponent.ValueRW; ref KinematicCharacterBody characterBody = ref CharacterDataAccess.CharacterBody.ValueRW; ref float3 characterPosition = ref CharacterDataAccess.LocalTransform.ValueRW.Position; float planeY = characterPosition.y; // authored movement plane — restored after the update (see below) KinematicCharacterUtilities.Update_Initialize( in this, ref context, ref baseContext, ref characterBody, CharacterDataAccess.CharacterHitsBuffer, CharacterDataAccess.DeferredImpulsesBuffer, CharacterDataAccess.VelocityProjectionHits, baseContext.Time.DeltaTime); KinematicCharacterUtilities.Update_ParentMovement( in this, ref context, ref baseContext, CharacterDataAccess.CharacterEntity, ref characterBody, CharacterDataAccess.CharacterProperties.ValueRO, CharacterDataAccess.PhysicsCollider.ValueRO, CharacterDataAccess.LocalTransform.ValueRO, ref characterPosition, characterBody.WasGroundedBeforeCharacterUpdate); KinematicCharacterUtilities.Update_Grounding( in this, ref context, ref baseContext, ref characterBody, CharacterDataAccess.CharacterEntity, CharacterDataAccess.CharacterProperties.ValueRO, CharacterDataAccess.PhysicsCollider.ValueRO, CharacterDataAccess.LocalTransform.ValueRO, CharacterDataAccess.VelocityProjectionHits, CharacterDataAccess.CharacterHitsBuffer, ref characterPosition); HandleVelocityControl(ref context, ref baseContext); KinematicCharacterUtilities.Update_PreventGroundingFromFutureSlopeChange( in this, ref context, ref baseContext, CharacterDataAccess.CharacterEntity, ref characterBody, CharacterDataAccess.CharacterProperties.ValueRO, CharacterDataAccess.PhysicsCollider.ValueRO, in characterComponent.StepAndSlopeHandling); KinematicCharacterUtilities.Update_GroundPushing( in this, ref context, ref baseContext, ref characterBody, CharacterDataAccess.CharacterProperties.ValueRO, CharacterDataAccess.LocalTransform.ValueRO, CharacterDataAccess.DeferredImpulsesBuffer, float3.zero); KinematicCharacterUtilities.Update_MovementAndDecollisions( in this, ref context, ref baseContext, CharacterDataAccess.CharacterEntity, ref characterBody, CharacterDataAccess.CharacterProperties.ValueRO, CharacterDataAccess.PhysicsCollider.ValueRO, CharacterDataAccess.LocalTransform.ValueRO, CharacterDataAccess.VelocityProjectionHits, CharacterDataAccess.CharacterHitsBuffer, CharacterDataAccess.DeferredImpulsesBuffer, ref characterPosition); KinematicCharacterUtilities.Update_MovingPlatformDetection(ref baseContext, ref characterBody); KinematicCharacterUtilities.Update_ParentMomentum(ref baseContext, ref characterBody, CharacterDataAccess.LocalTransform.ValueRO.Position); KinematicCharacterUtilities.Update_ProcessStatefulCharacterHits( CharacterDataAccess.CharacterHitsBuffer, CharacterDataAccess.StatefulHitsBuffer); // Top-down planar lock: the world is one flat plane and gravity is zero, so any Y gained by // step-handling/velocity-projection up a sloped collider (convex rock hulls etc.) would persist // FOREVER — the character ends up floating and its shadow drifts away (07-09 field report). // Restoring the pre-update Y forbids the CC update itself from ever changing the plane while // spawn/teleport writes (made outside this update) stay honored. Deterministic + idempotent. characterPosition.y = planeY; characterBody.RelativeVelocity.y = 0f; } void HandleVelocityControl(ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext) { float deltaTime = baseContext.Time.DeltaTime; ref KinematicCharacterBody characterBody = ref CharacterDataAccess.CharacterBody.ValueRW; ref CharacterComponent characterComponent = ref CharacterComponent.ValueRW; CharacterControl characterControl = CharacterControl.ValueRO; // Planar twin-stick: smoothly approach the desired world velocity (already speed-scaled, Y==0). float3 targetVelocity = characterControl.MoveVelocity; CharacterControlUtilities.StandardGroundMove_Interpolated( ref characterBody.RelativeVelocity, targetVelocity, characterComponent.GroundedMovementSharpness, deltaTime, math.up(), math.up()); } public void VariableUpdate(ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext) { } public void UpdateGroundingUp(ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext) { ref KinematicCharacterBody characterBody = ref CharacterDataAccess.CharacterBody.ValueRW; KinematicCharacterUtilities.Default_UpdateGroundingUp( ref characterBody, CharacterDataAccess.LocalTransform.ValueRO.Rotation); } public bool CanCollideWithHit(ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext, in BasicHit hit) { return PhysicsUtilities.IsCollidable(hit.Material); } public bool IsGroundedOnHit(ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext, in BasicHit hit, int groundingEvaluationType) { CharacterComponent characterComponent = CharacterComponent.ValueRO; return KinematicCharacterUtilities.Default_IsGroundedOnHit( in this, ref context, ref baseContext, CharacterDataAccess.CharacterEntity, CharacterDataAccess.PhysicsCollider.ValueRO, CharacterDataAccess.CharacterBody.ValueRO, CharacterDataAccess.CharacterProperties.ValueRO, in hit, in characterComponent.StepAndSlopeHandling, groundingEvaluationType); } public void OnMovementHit( ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext, ref KinematicCharacterHit hit, ref float3 remainingMovementDirection, ref float remainingMovementLength, float3 originalVelocityDirection, float hitDistance) { ref KinematicCharacterBody characterBody = ref CharacterDataAccess.CharacterBody.ValueRW; ref float3 characterPosition = ref CharacterDataAccess.LocalTransform.ValueRW.Position; CharacterComponent characterComponent = CharacterComponent.ValueRO; KinematicCharacterUtilities.Default_OnMovementHit( in this, ref context, ref baseContext, ref characterBody, CharacterDataAccess.CharacterEntity, CharacterDataAccess.CharacterProperties.ValueRO, CharacterDataAccess.PhysicsCollider.ValueRO, CharacterDataAccess.LocalTransform.ValueRO, ref characterPosition, CharacterDataAccess.VelocityProjectionHits, ref hit, ref remainingMovementDirection, ref remainingMovementLength, originalVelocityDirection, hitDistance, characterComponent.StepAndSlopeHandling.StepHandling, characterComponent.StepAndSlopeHandling.MaxStepHeight, characterComponent.StepAndSlopeHandling.CharacterWidthForStepGroundingCheck); } public void OverrideDynamicHitMasses( ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext, ref PhysicsMass characterMass, ref PhysicsMass otherMass, BasicHit hit) { } public void ProjectVelocityOnHits( ref CharacterUpdateContext context, ref KinematicCharacterUpdateContext baseContext, ref float3 velocity, ref bool characterIsGrounded, ref BasicHit characterGroundHit, in DynamicBuffer velocityProjectionHits, float3 originalVelocityDirection) { CharacterComponent characterComponent = CharacterComponent.ValueRO; KinematicCharacterUtilities.Default_ProjectVelocityOnHits( ref velocity, ref characterIsGrounded, ref characterGroundHit, in velocityProjectionHits, originalVelocityDirection, characterComponent.StepAndSlopeHandling.ConstrainVelocityToGroundPlane, in CharacterDataAccess.CharacterBody.ValueRO); } } }