From 01f38a19a1da772ad010c923a84e60b5b23f2c47 Mon Sep 17 00:00:00 2001 From: Marius Date: Mon, 3 Feb 2025 20:30:57 +0100 Subject: [PATCH] Progress character movement --- .../Scripts/FirstPersonCharacterComponent.cs | 3 +- .../Scripts/FirstPersonCharacterSystems.cs | 6 +-- .../FirstPerson/Scripts/FirstPersonPlayer.cs | 14 +++-- .../Scripts/FirstPersonPlayerAuthoring.cs | 1 + .../Scripts/FirstPersonPlayerSystems.cs | 50 ++++++++++-------- Assets/Scripts/Common/GhostVariants.cs | 52 +++++++++++++++++++ Assets/Scripts/Common/GhostVariants.cs.meta | 2 + Assets/Scripts/Common/InputDeltaUtilities.cs | 46 ++++++++++++++++ .../Common/InputDeltaUtilities.cs.meta | 3 ++ ...niversalRenderPipelineGlobalSettings.asset | 12 ++++- 10 files changed, 160 insertions(+), 29 deletions(-) create mode 100644 Assets/Scripts/Common/GhostVariants.cs create mode 100644 Assets/Scripts/Common/GhostVariants.cs.meta create mode 100644 Assets/Scripts/Common/InputDeltaUtilities.cs create mode 100644 Assets/Scripts/Common/InputDeltaUtilities.cs.meta diff --git a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterComponent.cs b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterComponent.cs index 08ab8f9..589cfcd 100644 --- a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterComponent.cs +++ b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterComponent.cs @@ -4,6 +4,7 @@ using Unity.Entities; using Unity.Mathematics; using UnityEngine; using Unity.CharacterController; +using Unity.NetCode; [Serializable] public struct FirstPersonCharacterComponent : IComponentData @@ -22,7 +23,7 @@ public struct FirstPersonCharacterComponent : IComponentData public float MaxViewAngle; public Entity ViewEntity; - public float ViewPitchDegrees; + [GhostField] public float ViewPitchDegrees; public quaternion ViewLocalRotation; } diff --git a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterSystems.cs b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterSystems.cs index 0352be0..a05eb7d 100644 --- a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterSystems.cs +++ b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonCharacterSystems.cs @@ -7,6 +7,7 @@ using Unity.Physics; using Unity.Transforms; using Unity.CharacterController; using Unity.Burst.Intrinsics; +using Unity.NetCode; [UpdateInGroup(typeof(KinematicCharacterPhysicsUpdateGroup))] [BurstCompile] @@ -72,10 +73,9 @@ public partial struct FirstPersonCharacterPhysicsUpdateSystem : ISystem } } -[UpdateInGroup(typeof(SimulationSystemGroup))] -[UpdateAfter(typeof(FixedStepSimulationSystemGroup))] +[UpdateInGroup(typeof(PredictedSimulationSystemGroup))] +[UpdateAfter(typeof(PredictedFixedStepSimulationSystemGroup))] [UpdateAfter(typeof(FirstPersonPlayerVariableStepControlSystem))] -[UpdateBefore(typeof(TransformSystemGroup))] [BurstCompile] public partial struct FirstPersonCharacterVariableUpdateSystem : ISystem { diff --git a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayer.cs b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayer.cs index 84738f7..7495bed 100644 --- a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayer.cs +++ b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayer.cs @@ -1,18 +1,26 @@ using System; -using Unity.Collections; using Unity.Entities; using Unity.Mathematics; +using Unity.NetCode; [Serializable] public struct FirstPersonPlayer : IComponentData { - public Entity ControlledCharacter; + [GhostField] public Entity ControlledCharacter; } [Serializable] -public struct FirstPersonPlayerInputs : IComponentData +public struct FirstPersonPlayerInputs : IInputComponentData { public float2 MoveInput; public float2 LookInput; public FixedInputEvent JumpPressed; +} + +[Serializable] +[GhostComponent(SendTypeOptimization = GhostSendType.OnlyPredictedClients)] +public struct FirstPersonPlayerNetworkInput : IComponentData +{ + [GhostField()] + public float2 LastProcessedLookInput; } \ No newline at end of file diff --git a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerAuthoring.cs b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerAuthoring.cs index 7dc78fc..4a737d5 100644 --- a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerAuthoring.cs +++ b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerAuthoring.cs @@ -16,6 +16,7 @@ public class FirstPersonPlayerAuthoring : MonoBehaviour ControlledCharacter = GetEntity(authoring.ControlledCharacter, TransformUsageFlags.Dynamic), }); AddComponent(entity); + AddComponent(entity); } } } \ No newline at end of file diff --git a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerSystems.cs b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerSystems.cs index fa5ac91..c9638e0 100644 --- a/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerSystems.cs +++ b/Assets/Samples/Character Controller/1.2.4/Standard Characters/FirstPerson/Scripts/FirstPersonPlayerSystems.cs @@ -1,26 +1,26 @@ using Unity.Burst; -using Unity.Collections; using Unity.Entities; -using Unity.Jobs; using Unity.Mathematics; using Unity.Transforms; using UnityEngine; using Unity.CharacterController; +using Unity.NetCode; -[UpdateInGroup(typeof(InitializationSystemGroup))] +[UpdateInGroup(typeof(GhostInputSystemGroup))] +[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)] public partial class FirstPersonPlayerInputsSystem : SystemBase { protected override void OnCreate() { - RequireForUpdate(); + RequireForUpdate(); RequireForUpdate(SystemAPI.QueryBuilder().WithAll().Build()); } protected override void OnUpdate() { - uint tick = SystemAPI.GetSingleton().Tick; - - foreach (var (playerInputs, player) in SystemAPI.Query, FirstPersonPlayer>()) + var time = SystemAPI.GetSingleton(); + + foreach (var (playerInputs, player) in SystemAPI.Query, FirstPersonPlayer>().WithAll()) { playerInputs.ValueRW.MoveInput = new float2 { @@ -28,11 +28,13 @@ public partial class FirstPersonPlayerInputsSystem : SystemBase y = (Input.GetKey(KeyCode.W) ? 1f : 0f) + (Input.GetKey(KeyCode.S) ? -1f : 0f), }; - playerInputs.ValueRW.LookInput = new float2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")); - + InputDeltaUtilities.AddInputDelta(ref playerInputs.ValueRW.LookInput.x, Input.GetAxis("Mouse X")); + InputDeltaUtilities.AddInputDelta(ref playerInputs.ValueRW.LookInput.y, Input.GetAxis("Mouse Y")); + + playerInputs.ValueRW.JumpPressed = default; if (Input.GetKeyDown(KeyCode.Space)) { - playerInputs.ValueRW.JumpPressed.Set(tick); + playerInputs.ValueRW.JumpPressed.Set(time.Tick); } } } @@ -41,28 +43,33 @@ public partial class FirstPersonPlayerInputsSystem : SystemBase /// /// Apply inputs that need to be read at a variable rate /// -[UpdateInGroup(typeof(SimulationSystemGroup))] -[UpdateAfter(typeof(FixedStepSimulationSystemGroup))] +[UpdateInGroup(typeof(PredictedSimulationSystemGroup))] +[UpdateAfter(typeof(PredictedFixedStepSimulationSystemGroup))] [BurstCompile] public partial struct FirstPersonPlayerVariableStepControlSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { + state.RequireForUpdate(); state.RequireForUpdate(SystemAPI.QueryBuilder().WithAll().Build()); } [BurstCompile] public void OnUpdate(ref SystemState state) { - foreach (var (playerInputs, player) in SystemAPI.Query().WithAll()) + foreach (var (playerInputs, playerNetworkInput, player) in SystemAPI.Query, FirstPersonPlayer>().WithAll()) { + // Compute input deltas, compared to last known values + float2 lookInputDelta = InputDeltaUtilities.GetInputDelta( + playerInputs.LookInput, + playerNetworkInput.ValueRO.LastProcessedLookInput); + playerNetworkInput.ValueRW.LastProcessedLookInput = playerInputs.LookInput; + if (SystemAPI.HasComponent(player.ControlledCharacter)) { FirstPersonCharacterControl characterControl = SystemAPI.GetComponent(player.ControlledCharacter); - - characterControl.LookDegreesDelta = playerInputs.LookInput; - + characterControl.LookDegreesDelta = lookInputDelta; SystemAPI.SetComponent(player.ControlledCharacter, characterControl); } } @@ -73,22 +80,23 @@ public partial struct FirstPersonPlayerVariableStepControlSystem : ISystem /// Apply inputs that need to be read at a fixed rate. /// It is necessary to handle this as part of the fixed step group, in case your framerate is lower than the fixed step rate. /// -[UpdateInGroup(typeof(FixedStepSimulationSystemGroup), OrderFirst = true)] +[UpdateInGroup(typeof(PredictedFixedStepSimulationSystemGroup), OrderFirst = true)] [BurstCompile] public partial struct FirstPersonPlayerFixedStepControlSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { - state.RequireForUpdate(); + + state.RequireForUpdate(); state.RequireForUpdate(SystemAPI.QueryBuilder().WithAll().Build()); } [BurstCompile] public void OnUpdate(ref SystemState state) { - uint tick = SystemAPI.GetSingleton().Tick; - + var time = SystemAPI.GetSingleton(); + foreach (var (playerInputs, player) in SystemAPI.Query().WithAll()) { if (SystemAPI.HasComponent(player.ControlledCharacter)) @@ -104,7 +112,7 @@ public partial struct FirstPersonPlayerFixedStepControlSystem : ISystem characterControl.MoveVector = MathUtilities.ClampToMaxLength(characterControl.MoveVector, 1f); // Jump - characterControl.Jump = playerInputs.JumpPressed.IsSet(tick); + characterControl.Jump = playerInputs.JumpPressed.IsSet(time.Tick); SystemAPI.SetComponent(player.ControlledCharacter, characterControl); } diff --git a/Assets/Scripts/Common/GhostVariants.cs b/Assets/Scripts/Common/GhostVariants.cs new file mode 100644 index 0000000..6229afb --- /dev/null +++ b/Assets/Scripts/Common/GhostVariants.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using Unity.Entities; +using Unity.Mathematics; +using Unity.NetCode; +using Unity.CharacterController; + +public partial class DefaultVariantSystem : DefaultVariantSystemBase +{ + protected override void RegisterDefaultVariants(Dictionary defaultVariants) + { + defaultVariants.Add(typeof(KinematicCharacterBody), Rule.ForAll(typeof(KinematicCharacterBody_DefaultVariant))); + defaultVariants.Add(typeof(CharacterInterpolation), Rule.ForAll(typeof(CharacterInterpolation_GhostVariant))); + defaultVariants.Add(typeof(TrackedTransform), Rule.ForAll(typeof(TrackedTransform_DefaultVariant))); + } +} + +[GhostComponentVariation(typeof(KinematicCharacterBody))] +[GhostComponent()] +public struct KinematicCharacterBody_DefaultVariant +{ + // These two fields represent the basic synchronized state data that all networked characters will need. + [GhostField()] + public float3 RelativeVelocity; + [GhostField()] + public bool IsGrounded; + + // The following fields are only needed for characters that need to support parent entities (stand on moving platforms). + // You can safely omit these from ghost sync if your game does not make use of character parent entities (any entities that have a TrackedTransform component). + [GhostField()] + public Entity ParentEntity; + [GhostField()] + public float3 ParentLocalAnchorPoint; + [GhostField()] + public float3 ParentVelocity; +} + +// Character interpolation must only exist on predicted clients: +// - for remote interpolated ghost characters, interpolation is handled by netcode. +// - for server, interpolation is superfluous. +[GhostComponentVariation(typeof(CharacterInterpolation))] +[GhostComponent(PrefabType = GhostPrefabType.PredictedClient)] +public struct CharacterInterpolation_GhostVariant +{ +} + +[GhostComponentVariation(typeof(TrackedTransform))] +[GhostComponent()] +public struct TrackedTransform_DefaultVariant +{ + [GhostField()] + public RigidTransform CurrentFixedRateTransform; +} \ No newline at end of file diff --git a/Assets/Scripts/Common/GhostVariants.cs.meta b/Assets/Scripts/Common/GhostVariants.cs.meta new file mode 100644 index 0000000..1928912 --- /dev/null +++ b/Assets/Scripts/Common/GhostVariants.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ad968c3972f1b5d45a01ff13d76c6073 \ No newline at end of file diff --git a/Assets/Scripts/Common/InputDeltaUtilities.cs b/Assets/Scripts/Common/InputDeltaUtilities.cs new file mode 100644 index 0000000..470d109 --- /dev/null +++ b/Assets/Scripts/Common/InputDeltaUtilities.cs @@ -0,0 +1,46 @@ +using Unity.Mathematics; + +public static class InputDeltaUtilities +{ + public const float InputWrapAroundValue = 2000f; + + public static void AddInputDelta(ref float input, float addedDelta) + { + input = math.fmod(input + addedDelta, InputWrapAroundValue); + } + + public static void AddInputDelta(ref float2 input, float2 addedDelta) + { + input = math.fmod(input + addedDelta, InputWrapAroundValue); + } + + public static float GetInputDelta(float currentValue, float previousValue) + { + float delta = currentValue - previousValue; + + // When delta is very large, consider that the input has wrapped around + if(math.abs(delta) > (InputWrapAroundValue * 0.5f)) + { + delta += (math.sign(previousValue - currentValue) * InputWrapAroundValue); + } + + return delta; + } + + public static float2 GetInputDelta(float2 currentValue, float2 previousValue) + { + float2 delta = currentValue - previousValue; + + // When delta is very large, consider that the input has wrapped around + if(math.abs(delta.x) > (InputWrapAroundValue * 0.5f)) + { + delta.x += (math.sign(previousValue.x - currentValue.x) * InputWrapAroundValue); + } + if(math.abs(delta.y) > (InputWrapAroundValue * 0.5f)) + { + delta.y += (math.sign(previousValue.y - currentValue.y) * InputWrapAroundValue); + } + + return delta; + } +} \ No newline at end of file diff --git a/Assets/Scripts/Common/InputDeltaUtilities.cs.meta b/Assets/Scripts/Common/InputDeltaUtilities.cs.meta new file mode 100644 index 0000000..2993fe2 --- /dev/null +++ b/Assets/Scripts/Common/InputDeltaUtilities.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: edf2b03f9a1d4b22b732a2d347673c5a +timeCreated: 1738609617 \ No newline at end of file diff --git a/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset b/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset index 244d7f3..76f4036 100644 --- a/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset +++ b/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset @@ -56,7 +56,17 @@ MonoBehaviour: - rid: 7930710930656067584 - rid: 4055230942537842688 m_RuntimeSettings: - m_List: [] + m_List: + - rid: 6852985685364965378 + - rid: 6852985685364965379 + - rid: 6852985685364965380 + - rid: 6852985685364965381 + - rid: 6852985685364965384 + - rid: 6852985685364965385 + - rid: 6852985685364965392 + - rid: 6852985685364965394 + - rid: 8712630790384254976 + - rid: 7930710930656067584 m_AssetVersion: 8 m_ObsoleteDefaultVolumeProfile: {fileID: 0} m_RenderingLayerNames: