278 lines
9.3 KiB
C#
278 lines
9.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using log4net;
|
|
using Managers;
|
|
using Unity.Mathematics;
|
|
using Unity.VisualScripting;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
using static AffectingForcesManager;
|
|
|
|
[ExecuteInEditMode]
|
|
public class NimbleZone : MonoBehaviour
|
|
{
|
|
private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
private static Transform _nimbleZoneTransform;
|
|
public AffectingForcesManager forcesManager;
|
|
|
|
public GameObject renderedZoneObject;
|
|
|
|
[SerializeField]
|
|
private List<GravityColorEntry> NimbleZoneColors = new();
|
|
|
|
private Dictionary<Gravity, Color> _nimbleZoneColors =
|
|
new Dictionary<Gravity, Color>
|
|
{
|
|
{Gravity.DownGravity, Color.green },
|
|
{Gravity.UpGravity, Color.magenta },
|
|
{Gravity.NoGravity, Color.red },
|
|
{Gravity.InwardsGravity, Color.blue },
|
|
{Gravity.OutwardsGravity, Color.yellow },
|
|
};
|
|
|
|
[SerializeField]
|
|
private static float gravityFactor = 30f;
|
|
[SerializeField]
|
|
private Gravity outsideGravityFunction = Gravity.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()
|
|
{
|
|
foreach (GravityColorEntry entry in NimbleZoneColors)
|
|
{
|
|
_nimbleZoneColors[entry.gravity] = entry.color;
|
|
}
|
|
meshRenderer = renderedZoneObject.GetComponent<MeshRenderer>();
|
|
_nimbleZoneTransform = gameObject.transform;
|
|
ApplyZoneColor(meshRenderer);
|
|
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) return;
|
|
#endif
|
|
|
|
material = meshRenderer.material;
|
|
ResetRippleShaderProperties();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Array of the available gravities.
|
|
/// </summary>
|
|
private Func<Transform, Transform, Vector3>[] gravityFunctions =
|
|
{ DownGravity, UpGravity, NoGravity, InwardsGravity, OutwardsGravity };
|
|
|
|
/// <summary>
|
|
/// Function which returns a gravity zero vector.
|
|
/// </summary>
|
|
private static readonly Func<Transform, Transform, Vector3> NoGravity =
|
|
new((gravitySource, target) => new Vector3());
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
private static readonly Func<Transform, Transform, Vector3> DownGravity =
|
|
new((gravitySource, target) =>
|
|
gravitySource.rotation * Vector3.down * gravityFactor);
|
|
|
|
/// <summary>
|
|
/// Function which returns a gravity vector upwards, depending
|
|
/// on the parent transforms rotation.
|
|
/// The parenting transform for a ship is the arena it's in.
|
|
/// </summary>
|
|
private static readonly Func<Transform, Transform, Vector3> UpGravity =
|
|
new((gravitySource, target) =>
|
|
gravitySource.rotation * Vector3.up * gravityFactor);
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
private static readonly Func<Transform, Transform, Vector3> InwardsGravity =
|
|
new((gravitySource, target) =>
|
|
(target.position - gravitySource.position).normalized * -gravityFactor);
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
private static readonly Func<Transform, Transform, Vector3> OutwardsGravity =
|
|
new((gravitySource, target) =>
|
|
(target.position - gravitySource.position).normalized * gravityFactor);
|
|
|
|
public Func<Transform, Transform, Vector3> GetGravityFunction(Gravity 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()
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) return;
|
|
#endif
|
|
material.SetFloat("_ShaderTime", Time.timeSinceLevelLoad);
|
|
}
|
|
|
|
private void ApplyZoneColor(MeshRenderer renderer)
|
|
{
|
|
Color color = _nimbleZoneColors[outsideGravityFunction];
|
|
MaterialPropertyBlock materialPropertyBlock = new();
|
|
materialPropertyBlock.SetColor("_BaseColor", color);
|
|
renderer.SetPropertyBlock(materialPropertyBlock);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the effect which a given velocity has on a ripple property.
|
|
/// </summary>
|
|
/// <param name="duration">Initial value of the ripple property</param>
|
|
/// <param name="velocity">Velocity of the impact</param>
|
|
/// <returns></returns>
|
|
private float ImpactVelocityEffect(float initial, float velocity)
|
|
{
|
|
return math.max(math.smoothstep(0, maxVelocity, velocity), minImpact)
|
|
* impactVelocityModifier * initial;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Spawns ripples on the shader of the nimble zone.
|
|
/// Up to 5 parallel ripples.
|
|
/// </summary>
|
|
/// <param name="collider"></param>
|
|
/// <param name="isOutwardsRipple"></param>
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the ripple shaders exposed properties to 0
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
|
|
} |