0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】AnimatorのTriggerを自動でResetする拡張メソッド(SetTrigger、ResetTrigger)

Last updated at Posted at 2024-05-20

概要

AnimatorステートマシンのTriggerはUnity標準機能のはずなのにReset周りがとてもややこしいです。
ざっくり言えば、攻撃中に攻撃ボタンを押すと、攻撃が終わったあとに攻撃を始めます。

Unity Technologies Japan様の動画では「攻撃の意思が残留する」と表現しています。
AnimatorControllerを使ってコンボ攻撃する方法 - Animation Tips #2

動画内で行われているように、StateMachineBehaviourをアタッチするのも手段の一つです。
しかし、ジャンプ中に攻撃ボタンを押すなどすると、ジャンプ終了着地後に攻撃を始めるかもしれないので、結局あらゆるTriggerをResetするしかありません。
そして、AnimatorController本体に手を入れるのも正直抵抗があります。

そのため、できるだけ通常のSetTriggerと同じ感覚で使用でき、適切なタイミングでResetを行うようにしてほしいです。

対応

下記記事では1フレームでリセットする手法が紹介されていました。

LIGHT11様
【Unity】【Animator Controller】即座に遷移できなかったら無効になるトリガーを作る

今回はこちらの記事を参考に、以下の改善を行いました。

  • LateUpdateのSetTriggerにも対応
AnimatorExtension.cs
using UnityEngine;
using UniRx;
using UniRx.Triggers;
using System.Threading;
using Cysharp.Threading.Tasks;

public static class AnimatorExtension
{
	public static void SetTriggerOneShot(this Animator self, string name)
	{
		var cts = new CancellationTokenSource();

		self.SetTrigger(name);
		self.OnAnimatorMoveAsObservable()
			.Subscribe(_ => { self.ResetTrigger(name); cts.Cancel(); })
			.AddTo(cts.Token)
			.AddTo(self.GetCancellationTokenOnDestroy());
	}

	public static void SetTriggerOneShot(this Animator self, int id)
	{
		var cts = new CancellationTokenSource();

		self.SetTrigger(id);
		self.OnAnimatorMoveAsObservable()
			.Subscribe(_ => { self.ResetTrigger(id); cts.Cancel(); })
			.AddTo(cts.Token)
			.AddTo(self.GetCancellationTokenOnDestroy());
	}
}

注意点としては、1フレームでリセットされるため
実際にStateが再生され終わるのを待って遷移させたい場合は
通常のSetTriggerを使うようにすることくらいでしょうか。

つまり、コンボ攻撃などで攻撃A中に攻撃ボタンを押したとき、攻撃Aが所定のフレームに達したあとに攻撃Bが出る(いわゆる「先行入力」)が想定の仕様の場合です。

誰か助けて

本当はUniTaskだけで完結させたかったですが、OnAnimatoMove直後に処理を実行するやり方がわかりませんでした。

AnimatorExtensionUniTask.cs
using UnityEngine;
using Cysharp.Threading.Tasks;

public static class AnimatorExtension
{
	public static void SetTriggerOneShot(this Animator self, string name)
	{
		self.SetTrigger(name);
		AutoResetTrigger(self, name).Forget();
	}

	public static void SetTriggerOneShot(this Animator self, int id)
	{
		self.SetTrigger(id);
		AutoResetTrigger(self, id).Forget();
	}

	private static async UniTaskVoid AutoResetTrigger(Animator animator, string name)
	{
		// Update,OnAnimatorMove,PreLateUpdate,LateUpdate だと思ったのに…….
		await UniTask.Yield(PlayerLoopTiming.PreLateUpdate, animator.GetCancellationTokenOnDestroy());

		if (animator == null)
		{
			return;
		}

		animator.ResetTrigger(name);
	}

	private static async UniTaskVoid AutoResetTrigger(Animator animator, int id)
	{
		// Update,OnAnimatorMove,PreLateUpdate,LateUpdate だと思ったのに…….
		await UniTask.Yield(PlayerLoopTiming.PreLateUpdate, animator.GetCancellationTokenOnDestroy());

		if (animator == null)
		{
			return;
		}

		animator.ResetTrigger(id);
	}
}

結論

Triggerは本記事のような拡張メソッドで1フレーム、それ以外はBoolという運用でいい気がする。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?