Aetheria/Assets/Scripts/Gameplay/FieldDriver.cs
2026-06-21 09:37:54 +01:00

156 lines
4.9 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
public class FieldDriver : MonoBehaviour
{
public Camera Camera;
public Vector2 Push;
public float FrontTwist;
public float RearTwist;
public float TestMagnitude;
public float FlowSpeed;
public float FlowSpeedThrottleExponent;
public float WaveThrottleExponent;
public int MaxHits;
public ExponentialCurve MagnitudeTimeScaling;
public float MeleeRange;
public float MeleeRangeExponent;
public float MeleeFlatness;
public float MeleeShaping;
public float MeleeDuration;
public float MeleeAngle;
public float MeleeAngleExponent;
public RectTransform.Axis RefractionAxis = RectTransform.Axis.Horizontal;
private Material _field;
private ComputeBuffer _hitBuffer;
private List<FieldHit> _hits = new List<FieldHit>();
private float _waveOffset;
private float _meleeTime;
private bool _meleeActive;
private struct FieldHit
{
public Vector3 Position;
public Vector3 Direction;
public float Magnitude;
public float Time;
}
void Start()
{
_field = GetComponent<MeshRenderer>().material;
var clickableCollider = GetComponent<ClickableCollider>();
if(clickableCollider!=null)
{
clickableCollider.OnClick += (_, _, ray, hit) =>
{
AddHit(hit.Point, ray.direction, TestMagnitude);
};
}
_hitBuffer = new ComputeBuffer(MaxHits, 32);
}
public void AddHit(Vector3 position, Vector3 direction, float magnitude)
{
if (_hits.Count >= MaxHits) return;
var hit = new FieldHit
{
Position = Vector3.Scale(transform.InverseTransformPoint(position).normalized, transform.localScale),
Direction = (transform.rotation * direction).normalized,
Magnitude = magnitude,
Time = 0
};
//Debug.Log($"Received hit: Position={hit.Position}, Direction={hit.Direction}");
_hits.Add(hit);
}
public int HitCount => _hits.Count;
public void Melee()
{
_meleeActive = true;
_meleeTime = 0;
}
void Update()
{
var pushMag = Mathf.Min(Push.magnitude, 1);
_waveOffset = Mathf.Repeat(
_waveOffset + Time.deltaTime * FlowSpeed *
Mathf.Pow(Mathf.Max(pushMag, Mathf.Max(Mathf.Abs(FrontTwist), Mathf.Abs(RearTwist))), FlowSpeedThrottleExponent),
1);
var refractionRotation = Matrix4x4.Rotate(Camera.transform.rotation).inverse;
if (RefractionAxis == RectTransform.Axis.Vertical) refractionRotation =
Matrix4x4.Rotate(Quaternion.Euler(90, 0, 0)) * refractionRotation;
_field.SetFloat("_WaveOffset", _waveOffset * Mathf.PI * 2);
_field.SetFloat("_Push", pushMag);
_field.SetVector("_PushDirection", new Vector4(-Push.x, 0, -Push.y));
_field.SetFloat("_TwistFront", FrontTwist);
_field.SetFloat("_TwistRear", RearTwist);
_field.SetVector("_InverseScale", new Vector4(1/transform.localScale.x,1/transform.localScale.y,1/transform.localScale.z));
_field.SetMatrix("_ReflRotate", refractionRotation);
for (int i = 0; i < _hits.Count; i++)
{
var hit = _hits[i];
hit.Time += Time.deltaTime / MagnitudeTimeScaling.Evaluate(_hits[i].Magnitude);
_hits[i] = hit;
if (hit.Time > 1)
{
_hits.RemoveAt(i);
i--;
}
}
_hitBuffer.SetData(_hits);
_field.SetBuffer("_Hits", _hitBuffer);
_field.SetInt("_HitCount", _hits.Count);
if (_meleeActive)
{
_meleeTime += Time.deltaTime / MeleeDuration;
if (_meleeTime < 1)
{
_field.SetVector("_MeleeDirection", Quaternion.AngleAxis(Mathf.Sign(_meleeTime * 2 - 1) * MeleeAngle * Mathf.Pow(Mathf.Abs(_meleeTime * 2 - 1), MeleeAngleExponent), Vector3.up) * -Vector3.forward);
_field.SetFloat("_MeleeDisplacement", Mathf.Pow(1 - 2 * Mathf.Abs(_meleeTime - .5f), MeleeRangeExponent) * MeleeRange);
_field.SetFloat("_MeleeShape", MeleeShaping);
_field.SetFloat("_MeleeFlattening", MeleeFlatness);
}
else
{
_meleeActive = false;
_field.SetFloat("_MeleeDisplacement", 0);
}
}
}
private float almostIdentity( float x )
{
return x*x*(2.0f-x);
}
private float smooth(float t)
{
return t * t * (3.0f - 2.0f * t);
}
private float SmoothStep(float edge0, float edge1, float x)
{
var t = Mathf.Clamp01((x - edge0) / (edge1 - edge0));
return t * t * (3.0f - 2.0f * t);
}
private void OnDestroy()
{
_hitBuffer.Dispose();
}
}