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