見出し画像

【Unity】アニメーションするボタンとウィンドウを作る

UIがアニメーションするとかっこいいので、記事を参考に導入してみましたが、スクロールウィンドウが思ったより手こずってしまいました。

1.ボタン

下の動画のような押すとアニメーションするボタンを作りました。

画像

コードは以下のようになりますが、これはAnimancer Proというアセットを使用しています。

 [Header("Animancer")][SerializeField]AnimancerComponent animancer;

 /////////////////中略///////////////

private void AnimePlay() {
        if (animancer == null) return;
        var state = animancer.Play(pressed, 0.1f);
        state.Events.OnEnd = () => animancer.Play(notPressed, 0.1f);
    }

Animancer Proを使用しない場合は、デフォルトのボタンコンポーネントのボタンアニメーションを使用することになると思います(こちらのほうが簡単かつ手軽)。
そのやり方は以下の記事を参考にしてください。
ねこじゃらシティさんの記事が特にわかりやすいです。

2.ウィンドウ(ダイアログ)

(1)非スクロールウィンドウ

以下のようなアニメーションして表示されるウィンドウを作成しました。

画像

以下のねこじゃらシティさんの記事を参考に、UniTaskとAnimancer Proを使用したスクリプトを作成しました。

このスクリプトはウィンドウではなく、ボタンのほうにアタッチしてください(ウィンドウは始め、非アクティブだと思いますので)。
また、アニメーションクリップは「ループする」のチェックを必ず外してください。

using System.Threading;
using UnityEngine;
using UnityEngine.UI;
using Animancer;
using Cysharp.Threading.Tasks;

public class AnimatedDialog : MonoBehaviour {
    [SerializeField] private AnimancerComponent _animancer;
    [SerializeField] private AnimationClip _openClip;
    [SerializeField] private AnimationClip _closeClip;
    [SerializeField] private Button [] _openButton;
    [SerializeField] private Button [] _closeButton;
    [SerializeField] private GameObject _dialog;
    private CancellationTokenSource _cts;
    // アニメーション中かどうか
    private bool IsTransition;
    // ダイアログは開いているかどうか
    public bool IsOpen => _dialog.activeSelf;
    
    private void Awake() {
        // キャンセルトークンを生成
        _cts = new CancellationTokenSource();
        // ボタンにリスナーを追加
        foreach (var button in _openButton)
            button.onClick.AddListener(() => OpenAsync().Forget());
        foreach (var button in _closeButton)
            button.onClick.AddListener(() => CloseAsync().Forget());
    }

    private void OnDestroy() {
        _cts.Cancel();// キャンセルトークンをキャンセル
        _cts.Dispose();// キャンセルトークンを破棄
        // ボタンのリスナーを削除
        foreach (var button in _openButton)
            button.onClick.RemoveAllListeners();
        foreach (var button in _closeButton)
            button.onClick.RemoveAllListeners();
    }

    public async UniTask OpenAsync() {
        if (IsOpen || IsTransition) return;
        IsTransition = true;
        _dialog.SetActive(true);
        //ボタンを押せなくする
        foreach (var button in _openButton)
            button.enabled = false;

        if (_animancer == null) return;
        
        // 開くアニメーションの再生、アニメーションのループは外す
        _animancer.Play(_openClip);
        
        // 開くアニメーションが終了したらIdleに戻る
        IsTransition = false;

        //ボタンを押せるようにする
        foreach (var button in _openButton)
            button.enabled = true;
    }

    public async UniTask CloseAsync() {
        if (!IsOpen || IsTransition) return;
        IsTransition = true;
        //ボタンを押せなくする
        foreach (var button in _closeButton)
            button.enabled = false;

        if (_animancer == null) return;

        _animancer.Play(_closeClip);
        
        _dialog.SetActive(false); // アニメーションが終了した後にダイアログを非表示
        IsTransition = false;

        //ボタンを押せるようにする
        foreach (var button in _closeButton)
            button.enabled = true;
    }
}

(2)スクロールビュー

スクロールビューも同じように、アニメーションさせて表示させようと思ったのですが…できませんでした。
理由はScrollResetterと絶望的に相性が悪かったからです。

↓ScrollResetterの内容についてはこちら

スクロールビューのScrollRectを一番上に戻すスクリプトです。(1)と同じ方法で、スクロールビューを表示させると、ScrollRectが中途半端な位置で止まってしまいました。
どうやらスクロールビュー全体のスケールの値をいじって、表示させているのに原因がありそうでしたが、上手く解消できませんでした。

ただ、表示時にアニメーションはさせたかったので、TimeLineを使用し、上から移動してくるようにしました。

画像

クレジットボタンをクリックしたときにScrollResetterとConveniButtonのDelayPlayTimelineを実行しています。

↓ConveniButtonについてはこちら

2028.8.26追記

以下のScrollResetterを以下のように改良し、アニメーションするダイアログでも問題なく、スクロールビューが一番上に戻るようになりました。

using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;

public class ScrollResetter : MonoBehaviour{
    [Header("位置をリセットするツマミを登録")] public ScrollRect scrollRect;
    //クリックするとスクロールビューが開くボタン等にセット。
    //スクロールビューが開くと同時にSetActiveAndResetScrollを実行。
    private Button button;

    //このスクリプトが有効になったときにボタンを取得
    private void OnEnable() {
        button = GetComponent<Button>();
        // ボタンがあればイベントリスナーを追加
        if (button != null){
            button.onClick.AddListener(SetActiveAndResetScroll);
        }
    }

    private async void ResetScrollToTop() {
        Canvas.ForceUpdateCanvases();  // すべてのキャンバスの更新を強制
        await UniTask.Yield();  // 1フレーム待機してレイアウト更新を待つ
        // ScrollRectのcontentの位置をリセット
        if (scrollRect != null) {
            scrollRect.verticalNormalizedPosition = 1.0f;
        }
    }
    
    public async void SetActiveAndResetScroll() {
        // 親オブジェクトのスケールを変更するアニメーションを開始
        var parentTransform = scrollRect.transform.parent;
        if (parentTransform != null) {
            await ScaleParentObjectAsync(parentTransform);
        }
        
        // スケール変更後にスクロール位置をリセット
        ResetScrollToTop();
    }

    private async UniTask ScaleParentObjectAsync(Transform parentTransform) {
        Vector3 originalScale = parentTransform.localScale;
        Vector3 targetScale = new Vector3(1.0f, 1.0f, 1.0f);  // 目標のスケールを設定

        float duration = 0.1f;  // アニメーションの期間
        float elapsedTime = 0f;

        while (elapsedTime < duration) {
            elapsedTime += Time.deltaTime;
            float t = Mathf.Clamp01(elapsedTime / duration);
            parentTransform.localScale = Vector3.Lerp(originalScale, targetScale, t);
            await UniTask.Yield();  // フレームをまたいで処理
        }

        parentTransform.localScale = targetScale;  // 最終的なスケールを設定
    }
}

この記事が気に入ったらサポートをしてみませんか?

ピックアップされています

オススメnoteソラップ選考 ゲームクリエイター向け

  • 106本

コメント

ログイン または 会員登録 するとコメントできます。
プロ野球とゲームと猫とペンギンが好きな見た目は大人、中身は子供のダメ社会人。 Unityでゲーム開発→挫折を何回かしているので、2022年は本気で取り組みたい。 最近、Pixivでゆっくり愛での画像を描いたり、ゆっくり解説動画の投稿もやってる。 株も興味あり。スタローンは心の師。
【Unity】アニメーションするボタンとウィンドウを作る|Yamasho[GamingGentoo][ゆっくり学ぶチャンネル]
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1