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) { }
}
}
}