CC Package and Physics
This commit is contained in:
@@ -10,7 +10,7 @@ namespace ProjectM.Simulation
|
||||
/// AbilityRef / CharacterStatsRef) with its replicated StatModifier buffer into the
|
||||
/// EffectiveAbilityStats / EffectiveCharacterStats components - every predicted tick, on both worlds.
|
||||
///
|
||||
/// Runs at the head of the predicted group (UpdateBefore PlayerAimSystem and PlayerMoveSystem;
|
||||
/// Runs at the head of the predicted group (UpdateBefore PlayerAimSystem;
|
||||
/// AbilityFireSystem runs after PlayerAimSystem, so it sees fresh values too). Recompute is
|
||||
/// unconditional every tick: it is a pure function of (blob base + replicated buffer), both of which
|
||||
/// are restored on rollback, so predicted and server results always agree. A dirty-flag / change
|
||||
@@ -19,7 +19,6 @@ namespace ProjectM.Simulation
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
|
||||
[UpdateBefore(typeof(PlayerAimSystem))]
|
||||
[UpdateBefore(typeof(PlayerMoveSystem))]
|
||||
[BurstCompile]
|
||||
public partial struct StatRecomputeSystem : ISystem
|
||||
{
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using Unity.CharacterController;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Tunables for the top-down kinematic character (M5b: Unity Character Controller package). Twin-stick /
|
||||
/// planar: NO jump, NO air control, NO gravity, NO view pitch. The per-tick target speed comes from the
|
||||
/// data-driven <see cref="EffectiveCharacterStats.MoveSpeed"/> via <see cref="PlayerControlSystem"/>; this
|
||||
/// component only carries the smoothing sharpness + step/slope handling the CC processor needs. Authored
|
||||
/// on the player prefab by <c>PlayerCharacterAuthoring</c>. Not a ghost — movement replicates through the
|
||||
/// owner-predicted LocalTransform (re-simulated on the owner, interpolated on remotes).
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct CharacterComponent : IComponentData
|
||||
{
|
||||
/// <summary>How quickly RelativeVelocity is lerped toward the target velocity on the ground.</summary>
|
||||
public float GroundedMovementSharpness;
|
||||
|
||||
/// <summary>Step/slope handling params (defaults; planar ground so mostly inert).</summary>
|
||||
public BasicStepAndSlopeHandlingParameters StepAndSlopeHandling;
|
||||
|
||||
public static CharacterComponent GetDefault()
|
||||
{
|
||||
return new CharacterComponent
|
||||
{
|
||||
GroundedMovementSharpness = 15f,
|
||||
StepAndSlopeHandling = BasicStepAndSlopeHandlingParameters.GetDefault(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-tick, NON-replicated control for the CC processor, derived each predicted tick from the replicated
|
||||
/// <see cref="PlayerInput"/> by <see cref="PlayerControlSystem"/>. Derived (not authored, not a ghost): a
|
||||
/// pure function of replicated input + recomputed stats, so it reproduces identically on server, owning
|
||||
/// client, and across rollback re-simulation.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct CharacterControl : IComponentData
|
||||
{
|
||||
/// <summary>World-space desired velocity (already scaled by MoveSpeed, Y == 0). The processor lerps
|
||||
/// RelativeVelocity toward this each tick.</summary>
|
||||
public float3 MoveVelocity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69a9d5a5573f2804d987602849f43554
|
||||
@@ -0,0 +1,24 @@
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Pure, Burst-friendly math for top-down character control, factored out for EditMode unit testing
|
||||
/// (no World/system needed). Maps a twin-stick move axis + speed to a planar world velocity.
|
||||
/// </summary>
|
||||
public static class CharacterControlMath
|
||||
{
|
||||
/// <summary>
|
||||
/// Twin-stick move (x,y) on the XZ plane scaled by <paramref name="speed"/>. Input longer than unit
|
||||
/// length is clamped (so diagonals aren't faster than cardinals); shorter is left proportional
|
||||
/// (analog sticks). Returns a world velocity with Y == 0.
|
||||
/// </summary>
|
||||
public static float3 DesiredMovement(float2 move, float speed)
|
||||
{
|
||||
float lenSq = math.lengthsq(move);
|
||||
if (lenSq > 1f)
|
||||
move = math.normalize(move);
|
||||
return new float3(move.x, 0f, move.y) * speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ada5ffb04316ba48851d44f5b3340f2
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.CharacterController;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Ghost-variant registration for the kinematic character (M5b). <see cref="CharacterInterpolation"/> is
|
||||
/// presentation-only fixed-step smoothing that must exist ONLY on predicted clients: on the server it
|
||||
/// would interfere with LocalToWorld, and on interpolated remote ghosts it would double up with netcode's
|
||||
/// own snapshot interpolation. <c>BakeCharacter</c> adds it to every prefab version, so we force it
|
||||
/// predicted-client-only here.
|
||||
/// <para>
|
||||
/// We deliberately do NOT override the default variants for <c>LocalTransform</c> or
|
||||
/// <c>PhysicsVelocity</c> (the CC sample's DontSerializeVariant on LocalTransform is global and would
|
||||
/// break the non-character ghosts in this project — projectiles, dummies, pickups — which rely on stock
|
||||
/// LocalTransform replication). The character's position therefore replicates via the normal
|
||||
/// owner-predicted LocalTransform path, like every other ghost.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed partial class CharacterGhostVariantsSystem : DefaultVariantSystemBase
|
||||
{
|
||||
protected override void RegisterDefaultVariants(Dictionary<ComponentType, Rule> defaultVariants)
|
||||
{
|
||||
defaultVariants.Add(typeof(CharacterInterpolation), Rule.ForAll(typeof(CharacterInterpolation_GhostVariant)));
|
||||
}
|
||||
}
|
||||
|
||||
[GhostComponentVariation(typeof(CharacterInterpolation))]
|
||||
[GhostComponent(PrefabType = GhostPrefabType.PredictedClient)]
|
||||
public struct CharacterInterpolation_GhostVariant
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6383b28a0d1f44649b514ece99ee4915
|
||||
@@ -0,0 +1,104 @@
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70d14da3eeeaec24896586d71fd22fdc
|
||||
@@ -0,0 +1,213 @@
|
||||
using Unity.CharacterController;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Physics;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public struct CharacterUpdateContext
|
||||
{
|
||||
public void OnSystemCreate(ref SystemState state) { }
|
||||
public void OnSystemUpdate(ref SystemState state) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Top-down kinematic character processor (CC 1.4.2 pattern: <see cref="IKinematicCharacterProcessor{T}"/>
|
||||
/// + a <see cref="KinematicCharacterDataAccess"/> built in the job, driving the static
|
||||
/// <see cref="KinematicCharacterUtilities"/> Update_* sequence). Stripped of all FPS features: no jump,
|
||||
/// no air movement, no gravity, no view. <see cref="PhysicsUpdate"/> runs the canonical CC update
|
||||
/// sequence; <see cref="HandleVelocityControl"/> lerps RelativeVelocity toward the desired planar
|
||||
/// velocity <see cref="PlayerControlSystem"/> wrote. Rotation/facing is owned by <see cref="PlayerAimSystem"/>.
|
||||
/// </summary>
|
||||
public struct CharacterProcessor : IKinematicCharacterProcessor<CharacterUpdateContext>
|
||||
{
|
||||
public KinematicCharacterDataAccess CharacterDataAccess;
|
||||
public RefRW<CharacterComponent> CharacterComponent;
|
||||
public RefRW<CharacterControl> 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<KinematicVelocityProjectionHit> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2bcdc39be82b4c4dbc85b799da194f3
|
||||
@@ -0,0 +1,34 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using Unity.NetCode;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Bridges replicated <see cref="PlayerInput"/> + data-driven <see cref="EffectiveCharacterStats"/> into
|
||||
/// the CC processor's per-tick <see cref="CharacterControl"/>. Replaces the M5 <c>PlayerMoveSystem</c>:
|
||||
/// instead of writing PhysicsVelocity, it writes the desired world velocity that
|
||||
/// <see cref="CharacterProcessor"/> lerps toward inside the fixed-step character update. Runs in
|
||||
/// <see cref="PredictedSimulationSystemGroup"/> after <see cref="StatRecomputeSystem"/> (fresh MoveSpeed)
|
||||
/// and before the predicted fixed-step group (where the character physics steps). Derived purely from
|
||||
/// replicated input + recomputed stats → identical on server, owning client, and across rollback.
|
||||
/// Filtered to <see cref="Simulate"/> for predicted ghosts only.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
|
||||
[UpdateAfter(typeof(StatRecomputeSystem))]
|
||||
[BurstCompile]
|
||||
public partial struct PlayerControlSystem : ISystem
|
||||
{
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState state)
|
||||
{
|
||||
foreach (var (control, input, stats) in
|
||||
SystemAPI.Query<RefRW<CharacterControl>, RefRO<PlayerInput>, RefRO<EffectiveCharacterStats>>()
|
||||
.WithAll<Simulate>())
|
||||
{
|
||||
control.ValueRW.MoveVelocity =
|
||||
CharacterControlMath.DesiredMovement(input.ValueRO.Move, stats.ValueRO.MoveSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d70cdf70e3ae53440aeb7274bdb27793
|
||||
@@ -1,41 +0,0 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.NetCode;
|
||||
using Unity.Transforms;
|
||||
|
||||
namespace ProjectM.Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Canonical predicted system: advances each player's planar (XZ) position from twin-stick Move
|
||||
/// input. Runs inside the prediction loop on the owning client (re-simulated on rollback) and
|
||||
/// once per tick on the server; filtered to <see cref="Simulate"/> so only predicted ghosts move.
|
||||
/// Deterministic by construction: uses <c>SystemAPI.Time.DeltaTime</c> (the fixed tick step)
|
||||
/// only — no wall-clock, no <c>System.Random</c>. Move is clamped to unit length so diagonal
|
||||
/// keyboard movement is not faster than cardinal. Move speed is the data-driven
|
||||
/// <see cref="EffectiveCharacterStats.MoveSpeed"/> (authored base + active modifiers), recomputed
|
||||
/// each tick by <see cref="StatRecomputeSystem"/> which runs before this system.
|
||||
/// </summary>
|
||||
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
|
||||
[BurstCompile]
|
||||
public partial struct PlayerMoveSystem : ISystem
|
||||
{
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState state)
|
||||
{
|
||||
float dt = SystemAPI.Time.DeltaTime;
|
||||
|
||||
foreach (var (transform, input, stats) in
|
||||
SystemAPI.Query<RefRW<LocalTransform>, RefRO<PlayerInput>, RefRO<EffectiveCharacterStats>>()
|
||||
.WithAll<Simulate>())
|
||||
{
|
||||
float2 move = input.ValueRO.Move;
|
||||
if (math.lengthsq(move) > 1f)
|
||||
move = math.normalize(move);
|
||||
|
||||
float3 delta = new float3(move.x, 0f, move.y) * stats.ValueRO.MoveSpeed * dt;
|
||||
transform.ValueRW.Position += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35cd3eacccc2f4172b557b2807a6df22
|
||||
@@ -8,7 +8,8 @@
|
||||
"Unity.Mathematics",
|
||||
"Unity.Burst",
|
||||
"Unity.Physics",
|
||||
"Unity.NetCode"
|
||||
"Unity.NetCode",
|
||||
"Unity.CharacterController"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
Reference in New Issue
Block a user