Files
jelito/Assets/jelycho/Code/Feedbacks/FeedbacksManagerActor.cs
2025-08-08 23:38:03 +02:00

211 lines
7.7 KiB
C#

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<CameraShakeFeedback> m_ActiveCameraShakes = new List<CameraShakeFeedback>();
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);
}
}
}