はきだめ

UnityやRailsに関するメモを残します。

トランジション付きのシーン遷移を管理するクラスを作ってみた [Unity]

はじめに

他のゲームにも使いまわせそうな超簡易のシーン遷移を管理するクラスを作ってみました。

f:id:kurome-stdio:20180404151141g:plain

クラスの作成に関して以下の記事を参考にさせて頂いています。

qiita.com

またトランジションの作成に関しては以下の記事を参考にさせて頂いています。

tsubakit1.hateblo.jp

使い方

基本的に持っている機能はシーンの遷移の際にいい感じに蓋絵を被せて遷移してくれる機能のみです。またRxは使わずにコルーチンだけで実装しています。手順は以下の通りです。

  • 1.シーンをEnum型のGameScenesに登録する
using UnityEngine;

namespace Model
{
    //BuildSettingで設定しているシーンをここに追加する
    public enum GameScenes
    {
        Title,
        Home,
        Shop,
        GlobalMenu
    }
}
// シーンを一つだけ読み込むとき
SceneLoader.LoadScene(Model.GameScenes.Home);

// 追加で他のシーンを読み込むとき
TransitionManager.StartTransaction(Model.GameScenes.Home, new[] {Model.GameScenes.GlobalMenu});

これだけでどこからでもシーンを遷移するメソッドを呼び出すことができ、さらにシーンを遷移している間はアニメーションで隠したりしてよしなにやってくれます。

事前準備

  • DOTweenの導入
    • トランジションの実装にDOTweenを使っているので、入っていない場合はそちらの導入が必要です。
  • Prefabを作成してResources/Prefabs配下に置く
    • テラシュールさんのブログからFadeCameraをダウンロードして以下のようなPrefabを作ってください

f:id:kurome-stdio:20180403005925p:plain:w500

f:id:kurome-stdio:20180403005934p:plain:w500

f:id:kurome-stdio:20180403005943p:plain:w500

コード紹介

SingletonMonobehaviour

MonoBehaviourの便利メソッドを引き継ぎつつ、シングルトンにしたいクラスにこのクラスを継承させます。

using System;
using UnityEngine;

public abstract class SingletonMonoBehaviour<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                Type t = typeof(T);

                _instance = (T)FindObjectOfType(t);
            }

            return _instance;
        }
    }

    public static bool HasInstance { get { return Instance != null; }}

    virtual protected void Awake ()
    {
        if (this != Instance)
        {
            Destroy(this);
            return;
        }
    }

    protected virtual void OnDestroy()
    {
        if(_instance == this) {
            _instance = null;
        }
    }
}
TransitionManager

実際にシーンの遷移などをおこなうメソッドを持っているクラスです。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

namespace Transition
{
    public class TransitionManager : SingletonMonoBehaviour<TransitionManager>
    {
        Model.GameScenes currentGameScene;

        // シーン遷移処理を実行中であるか
        bool isRunning = false;

        // 蓋絵のアニメーションを実行中か
        bool isFading = false;

        //  アクティブなシーンが切り替わったか
        bool isSwichedScene = false;

        [SerializeField] UICanvas uiCanvas;

        public static UICanvas UI { get { return Instance.uiCanvas; } }
        public Model.GameScenes CurrentGameScene { get { return currentGameScene; } }
        public bool IsRunning { get { return isRunning; } }

        override protected void Awake()
        {
            //このオブジェクトが消えないようにする
            DontDestroyOnLoad(gameObject);

            try
            {
                //現在のシーンを取得する
                currentGameScene = (Model.GameScenes)Enum.Parse(typeof(Model.GameScenes), SceneManager.GetActiveScene().name, false);
            }
            catch
            {
                currentGameScene = Model.GameScenes.Title;
            }

            //アクティブなシーンが切り替わったことに実行するメソッドを登録
            SceneManager.activeSceneChanged += OnActiveSceneChanged;
        }

        void TransitionReset()
        {
            isRunning = false; 
            isFading = false; 
            isSwichedScene = false;
            UI.FadeImage.raycastTarget = false;
        }

        public void StartTransaction(Model.GameScenes nextScene, Model.GameScenes[] additiveLoadScenes)
        {
            if (isRunning) return;
            TransitionReset();
            StartCoroutine(TransitionCoroutine(nextScene, additiveLoadScenes));
        }

        private IEnumerator TransitionCoroutine(Model.GameScenes nextScene, Model.GameScenes[] additiveLoadScenes)
        {
            isRunning = true;
            UI.FadeImage.raycastTarget = true;

            //蓋絵で画面を隠す
            isFading = true;
            UI.Fade.FadeIn(1f, () => isFading = false);

            //トランジションアニメーションが終了するのを待つ
            while(isFading){ yield return null;}

            //メインとなるシーンを読み込む
            var main = SceneManager.LoadSceneAsync(nextScene.ToString());

            //追加シーンがある場合は一緒に読み込む
            if (additiveLoadScenes != null)
            {
                foreach(var item in additiveLoadScenes)
                {
                    SceneManager.LoadSceneAsync(item.ToString(), LoadSceneMode.Additive);
                }
            }

            Resources.UnloadUnusedAssets();
            GC.Collect();
            yield return null;

            //現在シーンを設定
            currentGameScene = nextScene;

            while(!isSwichedScene){ yield return null; }

            //蓋絵を開くアニメーションを開始
            isFading = true;
            UI.Fade.FadeOut(1f, () => isFading = false);

            //蓋絵が開ききるのを待つ
            while(isFading){ yield return null; }
            TransitionReset(); 
        }

        //アクティブなシーンが切り替わったときに呼ばれる
        void OnActiveSceneChanged(Scene prevScene, Scene nextScene)
        {
            isSwichedScene = true;
        }
    }
}
SceneLoader

TransitionManagerのメソッドをStaticで公開されているSceneLoader経由で叩くようにしています。またシーン内にTransitionManagerが存在しない場合はInstanciateしてシーン内に置くようにしているのでシーン内に一々TransitionManagerを設置する必要が無くなります。

using UnityEngine;

namespace Transition
{
    public static class SceneLoader
    {
        private static TransitionManager _transitionManager;
        private static TransitionManager TransitionManager
        {
            get
            {
                if (_transitionManager != null)
                {
                    return _transitionManager;
                }

                Initialize();
                return _transitionManager;
            }
        }

        public static void Initialize()
        {
            if (TransitionManager.Instance == null)
            {
                var resource = Resources.Load("Prefabs/TransitionManager");
                Object.Instantiate(resource);
            }
            _transitionManager = TransitionManager.Instance;
        }

        public static bool IsTransitionRunning
        {
            get { return TransitionManager.IsRunning; }
        }

        public static void LoadScene(Model.GameScenes scene, Model.GameScenes[] additiveLoadScenes = null)
        {
            TransitionManager.StartTransaction(scene, additiveLoadScenes);
        }
    }
}
Fade

画像のアニメーションをいい感じにやってくれるクラスです。シーンの遷移行わずにアニメーションを行うのであれば、テラシュールブログさんのFadeをそのまま使えるのですが、シーンの遷移を行うとTime.timeSinceLevelLoad(最後にロードしたシーンからの経過時間)が変わってしまい上手くアニメーションが出来ないので少し弄っています。

using UnityEngine;
using System.Collections;
using DG.Tweening;

public class Fade : MonoBehaviour
{
    IFade fade;

    float cutoutRange;
    bool isfading = false;

    void Awake ()
    {
        Init ();
        fade.Range = cutoutRange;
    }

    void Init ()
    {
        fade = GetComponent<IFade> ();
    }

    void OnValidate ()
    {
        Init ();
        fade.Range = cutoutRange;
    }

    IEnumerator FadeoutCoroutine (float time, System.Action action)
    {
        isfading = true;
        DOTween.To(
            () => fade.Range,
            cutoutRange => fade.Range = cutoutRange,
            0f,
            1f
        ).OnComplete(() => isfading = false);

        while(isfading){ yield return null; }
        cutoutRange = 0;
        fade.Range = cutoutRange;

        if (action != null) {
            action ();
        }
    }

    IEnumerator FadeinCoroutine (float time, System.Action action)
    {
        isfading = true;
        DOTween.To(
            () => fade.Range,
            cutoutRange => fade.Range = cutoutRange,
            1f,
            1f
        ).OnComplete(() => isfading = false);

        while(isfading){ yield return null; }    
        cutoutRange = 1;
        fade.Range = cutoutRange;

        if (action != null) {
            action ();
        }
    }

    public Coroutine FadeOut (float time, System.Action action = null)
    {
        StopAllCoroutines ();
        return StartCoroutine (FadeoutCoroutine (time, action));
    }

    public Coroutine FadeIn (float time, System.Action action = null)
    {
        StopAllCoroutines ();
        return StartCoroutine (FadeinCoroutine (time, action));
    }
}
UICanvas

Canvas配下のオブジェクトにアクセス出来るクラスです。もしアニメーションをした上で「Now Loading」といった文字列を表示させる場合はここにシリアライズしてプロパティで公開すればTransitionManager経由でどこからでもアクセスすることが出来ます。

using UnityEngine;

namespace Transition
{
    public class UICanvas : MonoBehaviour 
    {
        [SerializeField] Fade fade;
        [SerializeField] FadeImage image;
        public Fade Fade { get { return fade; } }
        public FadeImage FadeImage { get { return image; } }
    }
}

おわりに

現状トランジションのアニメーションが一つしかないので引数をもう一つ増やして複数パターンのトランジションを選べる仕組みを作ろうかなぁって思ってます。あとシーン間のデータの受け渡しをいい感じにやってくれる機能も作りたいです。

uGUIの画像をスライドショーさせるやつをつくってみた

Unityで画像を切り替えて簡単なスライドショーさせるスクリプトを作ったのでメモしておきます。主にコルーチンとDOTweenを使っています。なのでDOTweenが入っていない場合は公式からインポートしてセットアップする必要があります。

assetstore.unity.com

続きを読む

まだprintfデバッグで消耗してるの? ~gdb入門~

はじめに

この記事はみす 52nd Advent Calendar 2017の18日目の記事です。前回の記事はこちらです。

torino-k.hatenablog.com

今回はgdbというデバッガについて紹介しようと思います。

続きを読む

Unity開発で参考になりそうな情報源をまとめました

はじめに

この記事はみす 52nd Advent Calendar 2017の3日目の記事です。
前回の記事はこちらです。

momokunsan.hatenadiary.jp


初めはUnityの機能とかを紹介しようと思ったのですが最近あまりUnityを触っていないのと、ここで紹介するよりも公式や他のブログを参照した方が100倍分かりやすいと思ったので、この記事ではUnityを勉強する上で有用な情報源などをまとめてみました。以下に挙げたブログやサイトは自分が普段お世話になっているところだったり、Unityの勉強会などで教えて貰ったものです。もしかしたらUnityの開発経験が長い人だと「そんなもん全部知っとるわ!」ってなるかも知れないです。

続きを読む

コマンドラインでPDFにかかった鍵をサクッと外す

はじめに

鍵のかかったPDFは毎回パスワードを打つ必要がありとても面倒です。Chromeで開いて「ファイル」→「印刷」→ 「PDFとして保存」というやり方でも鍵を外せますが、どうせならコマンドでサクッとPDFの鍵を外せないかと思い調べたところPDFtkというものを使うとコマンドラインで鍵を外せるみたいなのでメモしておきます。

PDFtkの導入

せっかくなのでbrewで入れました。本家にいってダウンロードしてインストールするのももちろん可能です。

続きを読む

ドワンゴのサマーインターンシップに行ってきました

はじめに

4週間続いたドワンゴでのインターンを先日終えました。参加したのは「プロダクト開発インターン」の「niconicoコース」です。せっかくなので感想を書こうと思います。

自己紹介(2017年9月地点)

今年の2月ぐらいにエンジニアバイト(長期インターン)を始めたのでRails歴は7ヶ月ぐらいです。それまではUnityでゲームを作ったりとかしてました。業務の内容は主にViewの修正だったり、エラーを直したり、SQLをちょこっと書いてたりしてました。

続きを読む

Twitterクライアント開発 タイムラインの表示&ツイート機能の実装

先日、ずっと読み進めていたRailsチュートリアルを読み終えました。次は何をしようか考えていたところ、twitterというgemを使うと簡単にAPIを叩くことが出来るということを聞いたので、ずっと作りたいと思っていたオリジナルのtwitterクライアントを作ることにしました。とりあえず、タイムラインの表示、ツイート機能などは出来たので開発過程を記します。

Oauth認証

# gemfile
gem 'omniauth'
gem 'omniauth-twitter'
gem 'twitter'
gem 'settingslogic'
gem 'dotenv-rails', require: 'dotenv/rails-now'
gem 'honoka-rails'

上記のgemを入れてbundleインストールします。Railstwitterクライアントを作る上で上の3つは必須です。settingslogicは定数を一元管理、dotenvは環境変数を設定、honokaはいい感じにスタイルを当てるために入れました。

続きを読む

3Dアクションゲーム開発記

今回の内容は昔作った3Dアクションゲームの開発記です。先日1年ぶりにunityに触ったときに3Dの知識が吹っ飛んでいて苦労したので、またすぐに忘れないように開発の記録をさらっと自分用にまとめようと思います。

ちなみにそのゲームはこれです(宣伝)
https://unityroom.com/games/monster-island

以下の本を参考にしました

Unityゲーム開発 オンライン3Dアクションゲームの作り方

Unityゲーム開発 オンライン3Dアクションゲームの作り方

ゲーム内のキャラクターのモデルとかアニメーションとかサウンドなどは本を買ったときについてきたものを使用しました。基本的には参考書に沿ってゲームを作った上で少し改造しました。

続きを読む