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】3Dユニティちゃんで演出スキップ可能なデモを作った ― ChainRacePattern

0
Last updated at Posted at 2026-04-19

はじめに

ChainRacePattern を使って、3Dユニティちゃんを含むスキップ可能な演出デモを作りました。

2DのUI演出のスキップ例は比較的見かけますが、3Dキャラクターのモーション・ボイス・字幕・入力分岐まで含めて、一つの流れとして扱う実装例はあまり見かけませんでした。

そこで今回は、あえて 3D のキャラクターを使ったデモを作成しました。

なお、このデモで使っている ChainRacePattern の考え方自体は、過去の記事で説明しています。
詳細は以下を参照してください。

  1. 【Unity】演出スキップがつらすぎたので、ChainRacePattern という仕組みを作った
  2. Unityの演出スキップ問題をどう解決するか ― ChainRacePattern

3Dユニティちゃんデモ

UnityChan-1-Main.gif

開始画面のあとにアイリスインが入り、その後演出パターンが順番に再生されます。
各セクションはスキップ可能で、画面タップによって次のフェーズへ即座に遷移します。
スキップの挙動は、実際にデモを触ってもらったほうが分かりやすいと思います。

演出内容は少しフワッとしていて、「なんとなく意味がありそうで、でもよく分からない」感じになっています。

デモの流れ

デモ全体の流れは次のとおりです。

  1. 開始画面(ボタン押下待ち)
  2. アイリスイン
  3. セクション1
  4. セクション2
  5. セクション3
  6. セクション4
  7. セクション5
  8. アイリスアウト
  9. ロゴ表示
  10. 開始画面に戻る

この一連の流れ全体を、ひとつの Chain として表現しています。
実際には、デモ全体は次のような ChainSequence で組み立てています。

chain = new ChainSequence(
    new ChainDelay(0.5f),
    ChainStartButton(),
    new ChainDelay(0.25f),
    ChainOpening(),
    ChainPickRandom(5),
    ChainClosing()
);

ChainOpening()ChainClosing() は固定で、その間にある ChainPickRandom(5) の部分では、用意しておいた演出パターンの中からランダムに 5 個を選んで再生しています。

利用クラスの一覧

今回のデモでは、演出要素を Chain としてカプセル化したクラスを組み合わせています。主に利用したのは次のようなクラスです。

クラス 説明
ChainForward キャラクターを前進させる
ChainRotate キャラクターの向きを変える
ChainPlayVoice ボイスを再生する
ChainDOTween DOTween を Chain として扱う
ChainCrossFade Animator の state を切り替える
ChainCrossFadeWaitState Animator の state 切り替えを待つ(状態遷移が完了したら完了)

実行時の演出切り替え

基本的には、Chain は先に全体の流れを組み立てておきます。
ただし今回のデモでは、ユーザー入力に応じて実行中に分岐先を差し込む例も入れています。

このデモには、途中で画面タップを促し、ユーザー入力によって後続の演出が変わるパターンも入れています。
ChainUnityChan10() では、まず「ぽちっと!」と入力を促す演出を再生し、その後 ChainRace で「ボタンが押される」か「5秒経過する」かを競争させています。
タップされた場合は褒めるモーション、時間切れなら叱るモーションを、branchSequence に後から追加しています。
UnityChanScene.cs

/// <summary>
/// 手を振ってぽちっと促し、タップで勝利ポーズ、タイムアウトで跳び箱
/// </summary>
private Chain ChainUnityChan10()
{
    ChainSequence branchSequence = new ChainSequence();
    return new ChainSequence(
        ChainMoveToTarget(new Vector3(0, 0, 1)),
        ChainLookAtCamera(),                
        new ChainCrossFadeWaitState(unityChanAnimator, "WAIT03", 0.25f), // 手を振る ステートに入るのを待つ
        ChainVoiceAndSubtitle("univ1032"), // ぽちっと!
        new ChainAction(() =>
        {
            touchTheScreenButton.gameObject.SetActive(true);
            touchTheScreenButton.GetComponent<CanvasGroup>().alpha = 0.0f;
        }),
        new ChainDOTween(() => touchTheScreenButton.GetComponent<CanvasGroup>().DOFade(1.0f, 0.25f)),
        new ChainRace(
            new ChainSequence(
                new ChainButton(touchTheScreenButton),
                new ChainAction(ff =>
                {
                    if (!ff)
                    {
                        // タップした
                        branchSequence.Add(new ChainParallel(
                            new ChainCrossFade(unityChanAnimator, "JUMP00", 0.25f, 0.25f), // 大ジャンプ
                            ChainVoiceAndSubtitle("univ1020") // エクセレントっ!すっごーいっ!
                        ));
                    }
                })
            ),
            new ChainSequence(
                new ChainDelay(5.0f),
                new ChainAction(ff =>
                {
                    if (!ff)
                    {
                        // タイムアウト
                        branchSequence.Add(new ChainParallel(
                            new ChainCrossFade(unityChanAnimator, "UMATOBI00", 0.25f, 0.25f), // 跳び箱
                            ChainVoiceAndSubtitle("univ1121") // むぅーっ!遅ぉーーーーいっ!
                        ));
                    }
                })
            )
        ),
        new ChainDOTween(() => touchTheScreenButton.GetComponent<CanvasGroup>().DOFade(0.0f, 0.25f)),
        new ChainAction(() => touchTheScreenButton.gameObject.SetActive(false)),
        branchSequence,
        new ChainCrossFadeWaitState(unityChanAnimator, "Idle", 0.25f)
    );
}

このコードで面白いのは、ChainRace を入力待ちとタイムアウト分岐にそのまま使っていることと、branchSequence をあらかじめ置いておいて、実行中にそこへ後続演出を追加していることです。

こうした実行時分岐は少し技巧的ではありますが、ユーザー入力によって演出内容が変わる例としては面白いと思います。

Chainの進行デバッグ用機能

今回はデモ本体だけでなく、Chain の進行状況を可視化するデバッグ用ウィンドウも作成しました。

下の画像の右側に表示されている Chain Debug がその画面です。
現在どの Chain が実行中なのか、どの Chain がまだ待機状態なのか、どこまで完了したのかを、ツリー構造のまま確認できます。

UnityChan-2-DebugWindow.gif

この例では、ChainSequence の中に ChainRaceChainButton がぶら下がっており、入力待ちを含む分岐がどのように進行しているかが見えるようになっています。

ランダム演出やスキップが入るデモでは、実行時に「今どこが動いているのか」を追いにくくなりがちです。
こうした可視化を入れておくと、Chain の消費状況を確認しやすく、デバッグもしやすくなります。

まとめ

3Dキャラクターの演出では、モーションや状態遷移を自然につなぐ必要があり、2D中心の演出とは少し異なる難しさがあります。
それでも、演出要素に適切に Chain の役割を与えていけば、3Dキャラクターを含むフローも一貫した形で扱えます。
さらに、オープニングとクロージングを固定しつつ、中間だけをランダムに差し替える構成にしているため、演出パターンの追加や拡張もしやすくなっています。
Chain Debug のような可視化を加えれば、ランダム演出やスキップを含むフローでも実行状況を追いやすくなります。

ライセンスについて

このデモでは Unity-Chan 関連アセットを利用しており、利用条件は Unity-Chan License(UCL 2.02)に従っています。
また、本デモのオリジナルコード部分は MIT License です。
そのほか使用しているライブラリやアセットの詳細は、リポジトリ内の README にまとめています。

リンク

リポジトリの内容を詳しく追いたい場合は、DeepWiki もあわせて見るのがおすすめです。構成や各スクリプトのつながりを追いやすく、全体像もつかみやすいと思います。

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?