using System; using System.Reflection; using log4net; using Managers; using Unity.Mathematics; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UIElements; using static AffectingForcesManager; public class NimbleZoneDetection : MonoBehaviour { private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static Transform _nimbleZoneTransform; public AffectingForcesManager forcesManager; public GameObject renderedZoneObject; public enum Gravities { DownGravity, NoGravity, InwardsGravity, OutwardsGravity } [SerializeField] private static float gravityFactor = 30f; [SerializeField] private Gravities outsideGravityFunction = Gravities.NoGravity; [SerializeField] private Zone zone = Zone.NimbleZone; // Ripple properties // These influence the shader on the nimble zone [SerializeField] private float rippleFrequency = 3f; [SerializeField] private float rippleDensity = 30f; [SerializeField] private float rippleAmplitude = 0.1f; [SerializeField] private float rippleRadius = 1f; [SerializeField] private float rippleDuration = 1f; [SerializeField] private float impactVelocityModifier = 1f; [SerializeField, Tooltip("Minimum ripple effect intensity.")] [Range(0, 1)] private float minImpact = 0.2f; [SerializeField, Tooltip("Velocity which makes the highest/most intense ripples.")] private float maxVelocity = 45f; private int maxRippleAmount = 5; private MeshRenderer meshRenderer; private Material material; void Awake() { meshRenderer = renderedZoneObject.GetComponent(); material = meshRenderer.material; _nimbleZoneTransform = gameObject.transform; ResetRippleShaderProperties(); } /// /// Array of the available gravities. /// private Func[] gravityFunctions = { DownGravity, NoGravity, InwardsGravity, OutwardsGravity }; /// /// Function which returns a gravity zero vector. /// private static readonly Func NoGravity = new((gravitySource, target) => new Vector3()); /// /// Function which returns a gravity vector downwards, depending /// on the parent transforms rotation. /// The parenting transform for a ship is the arena it's in. /// private static readonly Func DownGravity = new((gravitySource, target) => gravitySource.rotation * Vector3.down * gravityFactor); /// /// Function which returns a gravity vector towards the center of the parenting transform. /// The parenting transform for a ship is the arena it's in. /// private static readonly Func InwardsGravity = new((gravitySource, target) => (target.position - gravitySource.position).normalized * -gravityFactor); /// /// Function which returns a gravity vector outwards from the center of the parenting transform. /// The parenting transform for a ship is the arena it's in. /// private static readonly Func OutwardsGravity = new((gravitySource, target) => (target.position - gravitySource.position).normalized * gravityFactor); public Func GetGravityFunction(Gravities gravity) { return gravityFunctions[(int)gravity]; } private void OnTriggerEnter(Collider collider) { int instanceID = collider.gameObject.GetInstanceID(); if (collider.tag == "Spike" && collider.attachedRigidbody.velocity.magnitude > 1) { SpawnRipple(collider, false); return; } if (collider.tag == "Ship") { forcesManager.SetGravityForInstance(instanceID, NoGravity, transform); forcesManager.SetZoneForInstance(instanceID, zone); } } private void OnTriggerExit(Collider collider) { int instanceID = collider.gameObject.GetInstanceID(); if (collider.tag == "Spike") { SpawnRipple(collider, true); return; } if (collider.tag == "Ship") { forcesManager.SetGravityForInstance(instanceID, GetGravityFunction(outsideGravityFunction), transform); forcesManager.SetZoneForInstance(instanceID, Zone.OutsideZone); } } private void Update() { material.SetFloat("_ShaderTime", Time.timeSinceLevelLoad); } /// /// Calculates the effect which a given velocity has on a ripple property. /// /// Initial value of the ripple property /// Velocity of the impact /// private float ImpactVelocityEffect(float initial, float velocity) { return math.max(math.smoothstep(0, maxVelocity, velocity), minImpact) * impactVelocityModifier * initial; } /// /// Spawns ripples on the shader of the nimble zone. /// Up to 5 parallel ripples. /// /// /// private void SpawnRipple(Collider collider, bool isOutwardsRipple) { Rigidbody body = collider.attachedRigidbody; GameObject gameObject = collider.gameObject; float velocity = body.velocity.magnitude; Vector3 position = gameObject.transform.position - transform.position; position = transform.InverseTransformDirection(position).normalized; Vector4[] rippleOrigins = material.GetVectorArray("_RippleOrigins"); float currentTime = Time.timeSinceLevelLoad; float[] startedTimes = material.GetFloatArray("_RippleStartTimes"); float[] startedRippleDurations = material.GetFloatArray("_RippleDurations"); float[] rippleAmplitudes = material.GetFloatArray("_RippleAmplitudes"); float[] rippleFrequencies = material.GetFloatArray("_RippleFrequencies"); float[] rippleDensities = material.GetFloatArray("_RippleDensities"); float[] rippleRadii = material.GetFloatArray("_RippleRadii"); if (startedTimes == null) { Log.Warn("Ripple shader properties are null. Reseting shader"); ResetRippleShaderProperties(); return; } for (int i = 0; i < startedTimes.Length; ++i) { if (startedTimes[i] + startedRippleDurations[i] < currentTime) { rippleOrigins[i] = new Vector4(position.x, position.y, position.z, 0); material.SetVectorArray("_RippleOrigins", rippleOrigins); startedTimes[i] = currentTime; material.SetFloatArray("_RippleStartTimes", startedTimes); float amplitude = isOutwardsRipple ? rippleAmplitude : -rippleAmplitude; rippleAmplitudes[i] = ImpactVelocityEffect(amplitude, velocity); rippleFrequencies[i] = ImpactVelocityEffect(rippleFrequency, velocity); rippleDensities[i] = ImpactVelocityEffect(rippleDensity, velocity); startedRippleDurations[i] = ImpactVelocityEffect(rippleDuration, velocity); rippleRadii[i] = ImpactVelocityEffect(rippleRadius, velocity); material.SetFloatArray("_RippleAmplitudes", rippleAmplitudes); material.SetFloatArray("_RippleFrequencies", rippleFrequencies); material.SetFloatArray("_RippleDensities", rippleDensities); material.SetFloatArray("_RippleAmplitudes", rippleAmplitudes); material.SetFloatArray("_RippleDurations", startedRippleDurations); material.SetFloatArray("_RippleRadii", rippleRadii); break; } } } /// /// Resets the ripple shaders exposed properties to 0 /// private void ResetRippleShaderProperties() { Vector4[] rippleOrigins = new Vector4[maxRippleAmount]; float[] startedTimes = new float[maxRippleAmount]; float[] rippleAmplitudes = new float[maxRippleAmount]; float[] rippleFrequencies = new float[maxRippleAmount]; float[] rippleDensities = new float[maxRippleAmount]; float[] rippleDurations = new float[maxRippleAmount]; float[] rippleRadii = new float[maxRippleAmount]; // Initialize Ripple Shader Properties material.SetVectorArray("_RippleOrigins", rippleOrigins); material.SetFloatArray("_RippleStartTimes", startedTimes); material.SetFloatArray("_RippleAmplitudes", rippleAmplitudes); material.SetFloatArray("_RippleFrequencies", rippleFrequencies); material.SetFloatArray("_RippleDensities", rippleDensities); material.SetFloatArray("_RippleDurations", rippleDurations); material.SetFloatArray("_RippleRadii", rippleRadii); } }