Animation fixes Demon

This commit is contained in:
Szymon Miś
2025-09-09 03:19:38 +02:00
parent 3b0b20a165
commit 9d6c237088
5 changed files with 633 additions and 254 deletions

View File

@@ -1,12 +1,13 @@
using Invector.vCharacterController.AI.FSMBehaviour;
using Lean.Pool;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
namespace DemonBoss.Magic
{
/// <summary>
/// Spawns a meteor behind the BOSS and launches it toward the player's position
/// Similar mechanics to FireballProjectile but coming from above
/// Spawns a meteor behind the BOSS and launches it toward the player's position.
/// </summary>
[CreateAssetMenu(menuName = "Invector/FSM/Actions/DemonBoss/Call Meteor")]
public class SA_CallMeteor : vStateAction
@@ -31,18 +32,37 @@ namespace DemonBoss.Magic
[Tooltip("Tag used to find the target (usually Player)")]
public string targetTag = "Player";
[Header("One-off Overlay Clip (No Animator Params)")]
public AnimationClip overlayClip;
[Tooltip("Playback speed (1 = normal)")] public float overlaySpeed = 1f;
[Tooltip("Blend-in seconds (instant in this minimal impl)")] public float overlayFadeIn = 0.10f;
[Tooltip("Blend-out seconds (instant in this minimal impl)")] public float overlayFadeOut = 0.10f;
[Header("Debug")]
public bool enableDebug = false;
private Transform _boss;
private Transform _target;
// --- Playables runtime ---
private PlayableGraph _overlayGraph;
private AnimationPlayableOutput _overlayOutput;
private AnimationClipPlayable _overlayPlayable;
private bool _overlayPlaying;
private float _overlayStopAtTime;
public override void DoAction(vIFSMBehaviourController fsm, vFSMComponentExecutionType execType = vFSMComponentExecutionType.OnStateUpdate)
{
if (execType == vFSMComponentExecutionType.OnStateEnter)
{
OnEnter(fsm);
}
else if (execType == vFSMComponentExecutionType.OnStateUpdate)
{
if (_overlayPlaying && Time.time >= _overlayStopAtTime) StopOverlayWithFade();
}
}
private void OnEnter(vIFSMBehaviourController fsm)
@@ -58,7 +78,14 @@ namespace DemonBoss.Magic
if (enableDebug) Debug.Log($"[SA_CallMeteor] Boss: {_boss.name}, Target: {_target.name}");
SpawnMeteor();
// Fire overlay clip (no Animator params)
PlayOverlayOnce(_boss);
// Optional: wait for castDelay before spawning (simple timer via Invoke)
if (castDelay > 0f)
_boss.gameObject.AddComponent<DelayedInvoker>().Init(castDelay, SpawnMeteor);
else
SpawnMeteor();
}
private void SpawnMeteor()
@@ -101,5 +128,79 @@ namespace DemonBoss.Magic
if (enableDebug) Debug.LogError("[SA_CallMeteor] Meteor prefab missing MeteorProjectile component!");
}
}
// -------- Playables helpers (no Animator params) --------
private void PlayOverlayOnce(Transform owner)
{
if (overlayClip == null) return;
var animator = owner.GetComponent<Animator>();
if (animator == null) return;
StopOverlayImmediate();
_overlayGraph = PlayableGraph.Create("ActionOverlay(CallMeteor)");
_overlayGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
_overlayPlayable = AnimationClipPlayable.Create(_overlayGraph, overlayClip);
_overlayPlayable.SetApplyFootIK(false);
_overlayPlayable.SetApplyPlayableIK(false);
_overlayPlayable.SetSpeed(Mathf.Max(0.0001f, overlaySpeed));
_overlayOutput = AnimationPlayableOutput.Create(_overlayGraph, "AnimOut", animator);
_overlayOutput.SetSourcePlayable(_overlayPlayable);
_overlayOutput.SetWeight(1f);
_overlayGraph.Play();
_overlayPlaying = true;
float len = overlayClip.length / Mathf.Max(0.0001f, overlaySpeed);
_overlayStopAtTime = Time.time + len;
if (enableDebug) Debug.Log("[SA_CallMeteor] Overlay clip started via Playables");
}
private void StopOverlayImmediate()
{
if (_overlayGraph.IsValid())
{
_overlayGraph.Stop();
_overlayGraph.Destroy();
}
_overlayPlaying = false;
}
private void StopOverlayWithFade()
{
if (!_overlayPlaying) { StopOverlayImmediate(); return; }
if (_overlayOutput.IsOutputNull() == false) _overlayOutput.SetWeight(0f);
StopOverlayImmediate();
if (enableDebug) Debug.Log("[SA_CallMeteor] Overlay clip stopped");
}
/// <summary>
/// Tiny helper MonoBehaviour to delay a callback without coroutines here.
/// </summary>
private sealed class DelayedInvoker : MonoBehaviour
{
private float _timeLeft;
private System.Action _callback;
public void Init(float delay, System.Action callback)
{
_timeLeft = delay;
_callback = callback;
}
private void Update()
{
_timeLeft -= Time.deltaTime;
if (_timeLeft <= 0f)
{
try { _callback?.Invoke(); }
finally { Destroy(this); }
}
}
}
}
}