using Unity.Burst; using Unity.Burst.Intrinsics; using Unity.CharacterController; using Unity.Entities; using Unity.Physics; using Unity.Transforms; namespace ProjectM.Simulation { /// /// Fixed-step kinematic character update. Runs in /// (which sits after the physics build); under Netcode-for-Entities the physics groups are relocated into /// , so the character integrates inside /// the predicted loop — deterministic, rollback-safe, identical on server + owning client. Builds a /// per entity and runs the processor's PhysicsUpdate. Filtered /// to so only predicted ghosts step. /// [UpdateInGroup(typeof(KinematicCharacterPhysicsUpdateGroup))] [BurstCompile] public partial struct CharacterPhysicsUpdateSystem : ISystem { EntityQuery m_CharacterQuery; CharacterUpdateContext m_Context; KinematicCharacterUpdateContext m_BaseContext; [BurstCompile] public void OnCreate(ref SystemState state) { m_CharacterQuery = KinematicCharacterUtilities.GetBaseCharacterQueryBuilder() .WithAll() .Build(ref state); m_Context = new CharacterUpdateContext(); m_Context.OnSystemCreate(ref state); m_BaseContext = new KinematicCharacterUpdateContext(); m_BaseContext.OnSystemCreate(ref state); state.RequireForUpdate(m_CharacterQuery); state.RequireForUpdate(); } [BurstCompile] public void OnUpdate(ref SystemState state) { m_Context.OnSystemUpdate(ref state); m_BaseContext.OnSystemUpdate(ref state, SystemAPI.Time, SystemAPI.GetSingleton()); var job = new CharacterPhysicsUpdateJob { Context = m_Context, BaseContext = m_BaseContext, }; state.Dependency = job.Schedule(state.Dependency); } [BurstCompile] [WithAll(typeof(Simulate))] public partial struct CharacterPhysicsUpdateJob : IJobEntity, IJobEntityChunkBeginEnd { public CharacterUpdateContext Context; public KinematicCharacterUpdateContext BaseContext; void Execute( Entity entity, RefRW localTransform, RefRW characterProperties, RefRW characterBody, RefRW physicsCollider, RefRW characterComponent, RefRW characterControl, DynamicBuffer characterHitsBuffer, DynamicBuffer statefulHitsBuffer, DynamicBuffer deferredImpulsesBuffer, DynamicBuffer velocityProjectionHits) { var processor = new CharacterProcessor { CharacterDataAccess = new KinematicCharacterDataAccess( entity, localTransform, characterProperties, characterBody, physicsCollider, characterHitsBuffer, statefulHitsBuffer, deferredImpulsesBuffer, velocityProjectionHits), CharacterComponent = characterComponent, CharacterControl = characterControl, }; processor.PhysicsUpdate(ref Context, ref BaseContext); } public bool OnChunkBegin(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { BaseContext.EnsureCreationOfTmpCollections(); return true; } public void OnChunkEnd(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask, bool chunkWasExecuted) { } } } }