219 lines
7.3 KiB
C#
219 lines
7.3 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Unheimlich.VFX;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
public class ScarecrowFire : MonoBehaviour
|
|
{
|
|
[System.Serializable]
|
|
public class FireElement
|
|
{
|
|
public GameObject VFX;
|
|
[HideInInspector] public Vector3 OriginalScale;
|
|
}
|
|
|
|
[Header("Fire VFX Elements")]
|
|
[SerializeField] private FireElement _fireBase;
|
|
[SerializeField] private FireElement _fireTorso;
|
|
[SerializeField] private FireElement _fireArmLeft;
|
|
[SerializeField] private FireElement _fireArmLeftEnd;
|
|
[SerializeField] private FireElement _fireArmRight;
|
|
[SerializeField] private FireElement _fireArmRightEnd;
|
|
[SerializeField] private FireElement _fireHead;
|
|
[SerializeField] private FireElement _fireBig;
|
|
|
|
[Header("Propagation Timings")]
|
|
[SerializeField] private float _growDuration = 3f;
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float _overlapFactor = 0.4f;
|
|
[SerializeField] private AnimationCurve _growCurve = new AnimationCurve(
|
|
new Keyframe(0f, 0f, 0f, 0f),
|
|
new Keyframe(0.3f, 0.1f, 0f, 2f),
|
|
new Keyframe(0.7f, 0.8f, 2f, 0.5f),
|
|
new Keyframe(1f, 1f, 0.2f, 0f)
|
|
);
|
|
|
|
[Header("Post Process")]
|
|
[SerializeField] private Volume _heatVolume;
|
|
[SerializeField] private AnimationCurve _heatCurve = new AnimationCurve(
|
|
new Keyframe(0f, 0f, 0f, 0.5f),
|
|
new Keyframe(0.5f, 0.4f, 1f, 1f),
|
|
new Keyframe(1f, 1f, 0.5f, 0f)
|
|
);
|
|
|
|
[Header("Heat Shader")]
|
|
[SerializeField] private Material _heatMaterial;
|
|
|
|
[Header("Lights")]
|
|
[SerializeField] private List<LightEntry> _lights = new List<LightEntry>();
|
|
|
|
[System.Serializable]
|
|
public class LightEntry
|
|
{
|
|
public LightFlicker Flicker;
|
|
public float TargetIntensity = 3f;
|
|
}
|
|
|
|
private bool _fireSequenceComplete = false;
|
|
private float _totalSequenceDuration = 0f;
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
void Awake()
|
|
{
|
|
InitElement(_fireBase);
|
|
InitElement(_fireTorso);
|
|
InitElement(_fireArmLeft);
|
|
InitElement(_fireArmLeftEnd);
|
|
InitElement(_fireArmRight);
|
|
InitElement(_fireArmRightEnd);
|
|
InitElement(_fireHead);
|
|
InitElement(_fireBig);
|
|
|
|
float overlapDelay = _growDuration * (1f - _overlapFactor);
|
|
_totalSequenceDuration = _growDuration + (7f * overlapDelay);
|
|
|
|
SetHeatShaderWeight(0f);
|
|
|
|
foreach (var entry in _lights)
|
|
if (entry.Flicker) entry.Flicker.SetBaseIntensity(0f);
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
TriggerFire();
|
|
}
|
|
|
|
private void InitElement(FireElement el)
|
|
{
|
|
if (!el.VFX) return;
|
|
|
|
el.OriginalScale = el.VFX.transform.localScale;
|
|
el.VFX.transform.localScale = new Vector3(
|
|
el.OriginalScale.x * 0.2f,
|
|
el.OriginalScale.y * 0.1f,
|
|
el.OriginalScale.z * 0.2f
|
|
);
|
|
el.VFX.SetActive(false);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
public void TriggerFire()
|
|
{
|
|
_fireSequenceComplete = false;
|
|
StartCoroutine(FireSequence());
|
|
}
|
|
|
|
private IEnumerator FireSequence()
|
|
{
|
|
float overlapDelay = _growDuration * (1f - _overlapFactor);
|
|
|
|
StartCoroutine(DriveHeatEffects());
|
|
|
|
StartCoroutine(GrowFire(_fireBase));
|
|
yield return new WaitForSeconds(overlapDelay);
|
|
|
|
StartCoroutine(GrowFire(_fireTorso));
|
|
yield return new WaitForSeconds(overlapDelay);
|
|
|
|
StartCoroutine(PropagateArm(_fireArmLeft, _fireArmLeftEnd, overlapDelay));
|
|
StartCoroutine(PropagateArm(_fireArmRight, _fireArmRightEnd, overlapDelay));
|
|
yield return new WaitForSeconds(overlapDelay * 2f);
|
|
|
|
StartCoroutine(GrowFire(_fireHead));
|
|
yield return new WaitForSeconds(overlapDelay);
|
|
|
|
yield return StartCoroutine(GrowFire(_fireBig));
|
|
|
|
_fireSequenceComplete = true;
|
|
}
|
|
|
|
private IEnumerator PropagateArm(FireElement shoulder, FireElement tip, float overlapDelay)
|
|
{
|
|
StartCoroutine(GrowFire(shoulder));
|
|
yield return new WaitForSeconds(overlapDelay);
|
|
yield return StartCoroutine(GrowFire(tip));
|
|
}
|
|
|
|
private IEnumerator GrowFire(FireElement el)
|
|
{
|
|
if (!el.VFX) yield break;
|
|
|
|
el.VFX.SetActive(true);
|
|
|
|
Vector3 startScale = Vector3.zero;
|
|
Vector3 targetScale = el.OriginalScale;
|
|
float elapsed = 0f;
|
|
|
|
while (elapsed < _growDuration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = Mathf.Clamp01(elapsed / _growDuration);
|
|
float curveT = _growCurve.Evaluate(t);
|
|
|
|
el.VFX.transform.localScale = Vector3.Lerp(startScale, targetScale, curveT);
|
|
|
|
yield return null;
|
|
}
|
|
|
|
el.VFX.transform.localScale = targetScale;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
private IEnumerator DriveHeatEffects()
|
|
{
|
|
float elapsed = 0f;
|
|
float lastWeight = 0f;
|
|
|
|
// Phase 1 : monte avec la heatCurve jusqu'à ce que la séquence feu soit finie.
|
|
// On snapshote lastWeight à chaque frame pour partir de la vraie valeur courante
|
|
// en phase 2, évitant tout creux si la courbe redescend après t=1.
|
|
while (!_fireSequenceComplete)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = elapsed / _totalSequenceDuration;
|
|
lastWeight = _heatCurve.Evaluate(Mathf.Clamp01(t));
|
|
|
|
ApplyHeatWeight(lastWeight);
|
|
yield return null;
|
|
}
|
|
|
|
// Phase 2 : lerp propre de la valeur courante vers 1, sans jamais redescendre.
|
|
float lerpElapsed = 0f;
|
|
float lerpDuration = 8f;
|
|
float startWeight = lastWeight; // ← valeur réelle au moment du flag, pas heatCurve(0.99)
|
|
|
|
while (lerpElapsed < lerpDuration)
|
|
{
|
|
lerpElapsed += Time.deltaTime;
|
|
float weight = Mathf.Lerp(startWeight, 1f, lerpElapsed / lerpDuration);
|
|
|
|
ApplyHeatWeight(weight);
|
|
yield return null;
|
|
}
|
|
|
|
// Phase 3 : maintien à 1 à l'infini.
|
|
ApplyHeatWeight(1f);
|
|
}
|
|
|
|
/// <summary>Applique un weight normalisé à tous les effets de chaleur + lumière.</summary>
|
|
private void ApplyHeatWeight(float weight)
|
|
{
|
|
if (_heatVolume) _heatVolume.weight = weight;
|
|
SetHeatShaderWeight(weight);
|
|
|
|
// ScarecrowFire pilote la base — LightFlicker applique le flicker par-dessus
|
|
// sans écrire directement sur targetLight.intensity.
|
|
foreach (var entry in _lights)
|
|
if (entry.Flicker) entry.Flicker.SetBaseIntensity(entry.TargetIntensity * weight);
|
|
}
|
|
|
|
private void SetHeatShaderWeight(float weight)
|
|
{
|
|
if (!_heatMaterial) return;
|
|
_heatMaterial.SetFloat("_Weight", weight);
|
|
}
|
|
} |