using System; using System.Collections.Generic; using RebootKit.Engine.Main; using RebootKit.Engine.Network; using RebootKit.Engine.Simulation; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using Logger = RebootKit.Engine.Foundation.Logger; namespace RebootReality.jelycho.Feedbacks { struct CameraShakeFeedback { public Vector3 center; public float radius; public float intensity; public float timer; public static int GetMaxBytes() { return sizeof(float) * 3 + sizeof(float) * 3; } } public class FeedbacksManagerActor : Actor { static readonly Logger s_Logger = new Logger(nameof(FeedbacksManagerActor)); [SerializeField] Volume m_Volume; [SerializeField] float m_QuickAttackIndicatorDuration = 0.3f; [SerializeField] float m_QuickAttackIndicatorPaniniAppearSpeed = 2.0f; [SerializeField] float m_QuickAttackIndicatorChromaticAppearSpeed = 2.0f; [SerializeField] float m_QuickAttackIndicatorPaniniDisappearSpeed = 2.0f; [SerializeField] float m_QuickAttackIndicatorChromaticDisappearSpeed = 2.0f; [SerializeField] float m_QuickAttackIndicatorPaniniIntensity = 0.1f; [SerializeField] float m_QuickAttackIndicatorChromaticIntensity = 1.0f; PaniniProjection m_PaniniProjection; ChromaticAberration m_ChromaticAberration; bool m_IsChargeReady; float m_QuickAttackTimer; List m_ActiveCameraShakes = new List(); void Awake() { if (!m_Volume.profile.TryGet(out m_PaniniProjection)) { s_Logger.Error($"Failed to find PaniniProjection on volume: {m_Volume.name}"); } if (!m_Volume.profile.TryGet(out m_ChromaticAberration)) { s_Logger.Error($"Failed to find ChromaticAberration on volume: {m_Volume.name}"); } } // // @MARK: Camera shake // public void ShakeCamera(Vector3 center, float radius, float intensity, float duration) { if (!RR.IsServer()) { s_Logger.Error("ShakeCamera can only be called on the server."); return; } FeedbacksCameraShakeEvent ev = new FeedbacksCameraShakeEvent { Feedback = new CameraShakeFeedback { center = center, radius = radius, intensity = intensity, timer = duration } }; SendActorEvent((byte) FeedbacksManagerActorEvents.CameraShake, ref ev); } public float GetShakeIntensityForPosition(Vector3 position) { if (m_ActiveCameraShakes.Count == 0) { return 0.0f; } float intensity = 0.0f; foreach (CameraShakeFeedback feedback in m_ActiveCameraShakes) { if (feedback.radius <= 0.0f) { continue; } float distSquared = math.distancesq(feedback.center, position); float radiusSquared = feedback.radius * feedback.radius; if (distSquared > radiusSquared) { continue; } float feedbackIntensity = Mathf.Lerp(0.0f, feedback.intensity, 1.0f - (distSquared / radiusSquared)); intensity = Mathf.Max(feedbackIntensity, intensity); } return intensity; } // // @MARK: Local only // public void ShowQuickAttackIndicator() { m_QuickAttackTimer = m_QuickAttackIndicatorDuration; } public void ShowChargeReadyIndicator() { m_IsChargeReady = true; } public void HideChargeReadyIndicator() { m_IsChargeReady = false; } // // @MARK: Actor // protected override IActorData CreateActorData() { return new NoActorData(); } public override void OnClientTick(float deltaTime) { for (int i = m_ActiveCameraShakes.Count - 1; i >= 0; i--) { CameraShakeFeedback feedback = m_ActiveCameraShakes[i]; feedback.timer -= deltaTime; if (feedback.timer <= 0.0f) { m_ActiveCameraShakes.RemoveAt(i); continue; } m_ActiveCameraShakes[i] = feedback; } m_QuickAttackTimer -= Time.deltaTime; if (m_QuickAttackTimer > 0.0f || m_IsChargeReady) { float chromaticIntensity = m_ChromaticAberration.intensity.value; chromaticIntensity = Mathf.MoveTowards(chromaticIntensity, m_QuickAttackIndicatorChromaticIntensity, deltaTime * m_QuickAttackIndicatorChromaticAppearSpeed); m_ChromaticAberration.intensity.value = chromaticIntensity; float paniniIntensity = m_PaniniProjection.distance.value; paniniIntensity = Mathf.MoveTowards(paniniIntensity, m_QuickAttackIndicatorPaniniIntensity, deltaTime * m_QuickAttackIndicatorPaniniAppearSpeed); m_PaniniProjection.distance.value = paniniIntensity; } else { float chromaticIntensity = m_ChromaticAberration.intensity.value; chromaticIntensity = Mathf.MoveTowards(chromaticIntensity, 0.0f, deltaTime * m_QuickAttackIndicatorChromaticDisappearSpeed); m_ChromaticAberration.intensity.value = chromaticIntensity; float paniniIntensity = m_PaniniProjection.distance.value; paniniIntensity = Mathf.MoveTowards(paniniIntensity, 0.0f, deltaTime * m_QuickAttackIndicatorPaniniDisappearSpeed); m_PaniniProjection.distance.value = paniniIntensity; } } protected override void OnActorEventClient(ActorEvent actorEvent) { FeedbacksManagerActorEvents feedbackEvent = (FeedbacksManagerActorEvents) actorEvent.EventID; switch (feedbackEvent) { case FeedbacksManagerActorEvents.CameraShake: { FeedbacksCameraShakeEvent ev = new FeedbacksCameraShakeEvent(); DataSerializationUtils.Deserialize(actorEvent.Data, ref ev); if (ev.Feedback.timer > 0.0f) { m_ActiveCameraShakes.Add(ev.Feedback); } break; } } } } enum FeedbacksManagerActorEvents : byte { None = 0x00, CameraShake = 0x01, } struct FeedbacksCameraShakeEvent : IActorData { public CameraShakeFeedback Feedback; public int GetMaxBytes() { return CameraShakeFeedback.GetMaxBytes(); } public void Serialize(NetworkBufferWriter writer) { writer.Write(Feedback.center); writer.Write(Feedback.radius); writer.Write(Feedback.intensity); writer.Write(Feedback.timer); } public void Deserialize(NetworkBufferReader reader) { reader.Read(out Feedback.center); reader.Read(out Feedback.radius); reader.Read(out Feedback.intensity); reader.Read(out Feedback.timer); } } }