using System; using Unity.Netcode; using UnityEngine; public struct PlayerImpactState : INetworkSerializable, IEquatable { public uint Sequence; public Vector3 Impulse; public float ShakeAmplitude; public float ShakeDuration; public float ShakeFrequency; public float ShakeSeed; public PlayerImpactState( uint sequence, Vector3 impulse, float shakeAmplitude, float shakeDuration, float shakeFrequency, float shakeSeed) { Sequence = sequence; Impulse = impulse; ShakeAmplitude = shakeAmplitude; ShakeDuration = shakeDuration; ShakeFrequency = shakeFrequency; ShakeSeed = shakeSeed; } public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref Sequence); serializer.SerializeValue(ref Impulse); serializer.SerializeValue(ref ShakeAmplitude); serializer.SerializeValue(ref ShakeDuration); serializer.SerializeValue(ref ShakeFrequency); serializer.SerializeValue(ref ShakeSeed); } public bool Equals(PlayerImpactState other) => Sequence == other.Sequence && Impulse == other.Impulse && ShakeAmplitude.Equals(other.ShakeAmplitude) && ShakeDuration.Equals(other.ShakeDuration) && ShakeFrequency.Equals(other.ShakeFrequency) && ShakeSeed.Equals(other.ShakeSeed); } [DisallowMultipleComponent] public sealed class PlayerImpactController : NetworkBehaviour, IPlayerSystem { private readonly NetworkVariable impactState = new( default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server); private PlayerController movement; private PlayerCameraShakeController cameraShake; public uint LastImpactSequence => impactState.Value.Sequence; public event Action ImpactApplied; public void Initialize(PlayerFacade facade) { movement = GetComponent(); cameraShake = GetComponent(); } public override void OnNetworkSpawn() { impactState.OnValueChanged += HandleImpactChanged; } public override void OnNetworkDespawn() { impactState.OnValueChanged -= HandleImpactChanged; } public bool ApplyImpactOnServer(Vector3 impulse, ShockwaveZone zone) { if (!IsServer || !IsSpawned) return false; impactState.Value = new PlayerImpactState( impactState.Value.Sequence + 1, impulse, zone.CameraShakeAmplitude, zone.CameraShakeDuration, zone.CameraShakeFrequency, UnityEngine.Random.Range(0f, 1000f)); return true; } private void HandleImpactChanged(PlayerImpactState previous, PlayerImpactState current) { if (current.Sequence == 0 || current.Sequence == previous.Sequence) return; ImpactApplied?.Invoke(current); if (!IsOwner) return; movement?.AddImpulse(current.Impulse); cameraShake?.PlayShake( current.ShakeAmplitude, current.ShakeDuration, current.ShakeFrequency, current.ShakeSeed); } }