105 lines
4.4 KiB
C#
105 lines
4.4 KiB
C#
using Unity.Burst;
|
|
using Unity.Burst.Intrinsics;
|
|
using Unity.CharacterController;
|
|
using Unity.Entities;
|
|
using Unity.Physics;
|
|
using Unity.Transforms;
|
|
|
|
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// Fixed-step kinematic character update. Runs in <see cref="KinematicCharacterPhysicsUpdateGroup"/>
|
|
/// (which sits after the physics build); under Netcode-for-Entities the physics groups are relocated into
|
|
/// <see cref="Unity.NetCode.PredictedFixedStepSimulationSystemGroup"/>, so the character integrates inside
|
|
/// the predicted loop — deterministic, rollback-safe, identical on server + owning client. Builds a
|
|
/// <see cref="KinematicCharacterDataAccess"/> per entity and runs the processor's PhysicsUpdate. Filtered
|
|
/// to <see cref="Simulate"/> so only predicted ghosts step.
|
|
/// </summary>
|
|
[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<CharacterComponent, CharacterControl>()
|
|
.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<PhysicsWorldSingleton>();
|
|
}
|
|
|
|
[BurstCompile]
|
|
public void OnUpdate(ref SystemState state)
|
|
{
|
|
m_Context.OnSystemUpdate(ref state);
|
|
m_BaseContext.OnSystemUpdate(ref state, SystemAPI.Time, SystemAPI.GetSingleton<PhysicsWorldSingleton>());
|
|
|
|
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> localTransform,
|
|
RefRW<KinematicCharacterProperties> characterProperties,
|
|
RefRW<KinematicCharacterBody> characterBody,
|
|
RefRW<PhysicsCollider> physicsCollider,
|
|
RefRW<CharacterComponent> characterComponent,
|
|
RefRW<CharacterControl> characterControl,
|
|
DynamicBuffer<KinematicCharacterHit> characterHitsBuffer,
|
|
DynamicBuffer<StatefulKinematicCharacterHit> statefulHitsBuffer,
|
|
DynamicBuffer<KinematicCharacterDeferredImpulse> deferredImpulsesBuffer,
|
|
DynamicBuffer<KinematicVelocityProjectionHit> 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) { }
|
|
}
|
|
}
|
|
}
|