ユニティちゃんでゲーム制作演習



今から一年半ぐらい前にUnityの勉強を始めようと思っていた時、本屋で見つけとりあえずやってみようと初めて買ったUnity本が日経BP社の「ユニティちゃんでゲームを作ろう」というムックでした。

本に書かれている「UnityChanDash」を見よう見まねで実践したら何とかうまくいって動いてくれて、Unityを使えば思っていたより簡単にゲームらしいものが作れるんだなと感心した半面、プログラミングなど無縁だった自分にとってまるで意味が分からないコードや記号が一杯で、こんなのどうやって使いこなせるようになるのだろうかと絶望感にも似た感情を抱いたのも今でも覚えています。(^^;)

それでもUnityを触り始めて一年以上経って、今ではC#もそれなりに理解できるようになりました。そこで、ここまで自分が学習してきたUnityの知識のまとめとして、「UnityChanDash」にいろいろ機能を追加して一連のゲームアプリとしての形式が成り立つようにアレンジしてみたいと思いました。

まず、追加したいことの確認、

・タイトル画面を設けて、スタートボタンを押すとゲームスタート
・プレーヤーが走り出す前にカメラが前方から後方に回り込む
・カメラが定位置に着てから走り出す
・障害物に当たった時、ゴールした時、カメラがプレーヤーの正面に回り、UI表示する
・障害物に3回ぶつかるとゲームオーバー。その都度UIでスペア数を表示する
・ゲームオーバー、ゴール後にリトライ、タイトル画面にもどる、ゲーム終了の選択メニューを出す
・BGMと効果音を付ける
・Escキーで強制終了、メニューから終了でユニティちゃんライセンスを表示して終了する

というような感じです。

それで完成したので公開したいと思います。



「UnityChanDash~arranged~」
Windows版
(19MB)
MacOS版
(22MB)
WebGL版

- WebGL版URL -
http://initmusic.web.fc2.com/games/UnityChanDash_arranged_WebGL/index.html



スタンドアロン版とWebGL版をビルドしてみました。もしよかったら、試してみてください。Win版とmac版はオンラインストレージからzipをダウンロードして解凍して実行してください。制作とビルドはUnity5.4.1でしています。

WebGL版はWeb上のリンクとなっています。WebGL版はFireFoxでしか動作しないようです。ご使用しているブラウザが違う場合はFireFoxを立ち上げてから下のURLをコピペでお願いします。WebGL版では終了したいときはウィンドウを閉じてください。なお、「Unity WebGL」はスマホ等のモバイル端末には対応しておりません。

いちおうお断りしておきますが、ゲーム自体が面白いかどうかではなく、ゲームアプリとして一連の形式を取らせるということが目的ということなのでその辺りはご了承ください。(わざとミスなどして処理を確認していただけたらと思います)







「↑」キーかマウスの「左クリック」でジャンプ、「↓」キーか「右クリック」でスライディングで障害物を避けます。障害物に3回ぶつかるとゲームオーバーです。ゲーム中にPキーを押すたびにBGMのオンオフを切り替えられます。

WebGL版では影の描写をしないようにしています。余興でユニティちゃんライセンス表示中にBキーを押すとユニティちゃんの「バイバイ」ボイスがなります。

せっかくなので、自分の備忘録としてこのゲームで使った自分が追加した部分のスクリプトの解説を書いておきたいと思います。全部は長くなりすぎるので部分的ということで。

・走り出す処理

public class PlayerAnimationController : MonoBehaviour {

    bool RunStartCheck = false;

    void Update() {
        if (RunStartCheck) {
        GetComponent<Rigidbody>().MovePosition(transform.position + 
       transform.forward * Time.deltaTime * 3);
        }
    }

    public void RunStart() {
        RunStartCheck = true;
    }
以下略

まず、走り出すところですが、初めはインクリメント計算処理で一定時間待機にしてみましたが、そうすると初回とリロード後では微妙にタイミングが変わってしまいました。また、この方法では自分のPCでうまく合わせられても違うPCではタイミングが変わってしまう可能性もあります。ここは厳密に同じタイミングでスタートさせたいところです。

それで、bool型のRunStartCheck変数を用意してfalseにしておいてAnimationの方からユニティちゃんが待機状態からRun状態に移るタイミングで仕込んでいるイベントでRunStart()を実行してRunStartCheckをtrueにすることで前に動き出すようにしました。これだとタイミングはバッチリですね。




・スペア数で分岐等の処理


using UnityEngine;
using UnityEngine.UI;//UnityUIを使うための名前空間
using UnityEngine.SceneManagement;//SceneManagerを使うための名前空間
using System.Collections;

public class GameManagement : MonoBehaviour {

    public GameObject Branch;
    public Text mistakeText;
    public Text gameoverText;
    public Text spareText;
    public static int spare = 2;

    void Start() {
        spareText.text = spare.ToString(); //スペア数の表示、更新
    }

    //障害物にぶつかったらSendMessageを受信、スペアの数で処理の分岐
    void spareNum() {
        if(spare >= 1) {
            Invoke("mistake", 0.5f);
        }
        else if (spare == 0) {
            Invoke("gameover", 0.5f);
        }
    }
    //ミスしたとき、テキスト「MISTAKE」を表示させる処理
    void mistake() {
        mistakeText.gameObject.SetActive(true);
        Invoke("OnLoad", 6);
    }
    //ゲームオーバー時、テキスト「GAME OVER」を表示させる処理
    void gameover() {
        gameoverText.gameObject.SetActive(true);
        Invoke("TimeLag", 4);
    }
    //呼び出されたら、4秒後にReGameBranch()を実行
    void TimeLag() {
        Invoke("ReGameBranch", 4);
    }
    //ゲーム後に3つの選択肢を表示させる
    void ReGameBranch() {
        Branch.gameObject.SetActive(true);
    }
    //ミスしたらスペアを一人減らす、シーンを読み直す
    void OnLoad() {
        spare -= 1;
        SceneManager.LoadScene("UnityChanDash2016");
    }
    //選択肢1が押されたら、スペアを2に戻す、ゲームを始めからやり直す
    public void PushTryAgain() {
        spare = 2;
        SceneManager.LoadScene("UnityChanDash2016");
    }
    //選択肢2が押されたら、スペアを2に戻す、BGMを廃棄、タイトルに戻る
    public void PushReTitle() {
        spare = 2;
        bgmloop.DestroyOn();
        SceneManager.LoadScene("UCD_Title");
    }
    //選択肢3が押されたら、ゲームを終了、ライセンス表示シーンに移行
    public void GameQuit() {
        SceneManager.LoadScene("LicenseLogo");
    }
}

スペアの数のspare変数はシーンを読み直しても維持されるようにpublic staticで宣言しておきます。時間差が欲しいとき便利なInvoke()メソッドを多用しましたが、今思えばAnimationのイベントを使えばもう少しスッキリ出来たかもしれません。もう直す気はありませんが。(^^;)

・BGMをシーンをまたいでループで流す

単にBGMを付けたいとき、一曲であればAudioSourceコンポーネントを追加してAudioClipに指定して「Play On Awake」をオンにしておけばノンコーディングでシーン開始時から流すことが出来ます。しかし、この場合はシーンを読み直すと始めから再生されます。



今回の場合、ゲームプレイ中はリプレイしても途切れないでずっとループさせた方が良いと考えました。そうすると、シーンをまたいで再生し続ける必要があります。色々試してみてうまくいった方法を書いておきたいと思います。

まず、空のゲームオブジェクト(名前はMusicBox)にBGM用のAudioSourceコンポーネントを追加して、DontDestroyOnLoadを使ってシーンをまたいでもこのゲームオブジェクトが破棄されないようにしてみました。でも、これだけだとシーンが呼び出されるたびにBGMが重なっていってしまいます。

それで、このMusicBoxをインスタンス化してprefabとしてスクリプトからResources.Loadを使って呼び出せるようにして、BGMを初回のみ再生させ次回以降は再生させないようにして、タイトルに戻るときはDestroy()でゲームオブジェクトを破棄するという方法を取ることでうまくいくことができました。

public class GameManagement : MonoBehaviour {

    private GameObject BGMobj;
    private BGM_Loop bgmloop;
    public static bool musicOn = true;

    void Start() {
        Instantiate(Resources.Load("Prefabs/MusicBox"), new Vector3(0, 0, 0),
        Quaternion.identity);

        BGMobj = GameObject.Find("MusicBox(Clone)");
        bgmloop = BGMobj.GetComponent<BGM_Loop>();
    }
以下略

GameManagementクラスのBGM関連の部分です。Start()で
"Resources/Prefabs/"フォルダに入れておいたMusicBoxを取り出します。そして、GameObject.Find()でBGMobj変数に格納します。ちなみに、このとき呼び出したMusicBoxには(Clone)が付いているのでそれを含めて名前にしておかないと見つけてくれません。

これは、MusicBoxにアタッチしているBGM_Loopスクリプトです。

public class BGM_Loop : MonoBehaviour {

    public AudioSource audioSource;
    public static bool shokaiCheck = true;

    void Start() {

        if (shokaiCheck) {
        shokaiCheck = false;
        DontDestroyOnLoad(gameObject);

        audioSource = GetComponent<AudioSource>();
        audioSource.Play();
        }
    }

    public void DestroyOn() {
        shokaiCheck = true;
        Destroy(gameObject);
    }
    public void BGM_Play() {
        audioSource.Play();
    }
    public void BGM_Stop() {
        audioSource.Stop();
    }
}

bool型で初回チェック用のshokaiCheck変数をpublic staticで宣言しておきます。こうすることによってその状態はシーンをまたいでも保たれます。「Play On Awake」はオフにしておきます。

shokaiCheckは始めはtrueなのでif文の中に入ります。そこでshokaiCheckをfalseに変えておき、"MusicBox(clone)"をDontDestroyOnLoadにします。そして、ここでBGMを再生させます。

これでシーンが呼び直されても次回からはshokaiCheckはfalseなのでBGMは重ねて再生されることはなくなります。タイトルに戻るときはDestroyOn()でオブジェクトを破棄するとBGMは止まります。破棄する前にshokaiCheckはtrueに戻しておきます。こうしておくことで、タイトルに戻って再びスタートボタンを押したときにBGMがまた始めに再生される処理に入ります。BGM_Play()、BGM_Stop()はPキーを押した時用です。

ユニティちゃんの掛け声ですが、同時に再生することはないので一つのAudioSourceに各音声をAudioClip型の配列にそれぞれ格納しておいて、各Animationのイベントからその都度入れ替えて再生させています。



public class PlayerAnimationController : MonoBehaviour {

    public AudioClip[] voice;
    private AudioSource audiosource;

    void Start() {
        audiosource = GetComponent<AudioSource>();
    }

    void slideVoice() {
        audiosource.clip = voice[0];
        audiosource.Play();
    }
    void jumpVoice() {
        audiosource.clip = voice[1];
        audiosource.Play();
    }
    void damageVoice() {
        audiosource.clip = voice[2];
        audiosource.Play();
    }
    void loseVoice() {
        audiosource.clip = voice[3];
        audiosource.Play();
    }
    void goalVoice() {
        audiosource.clip = voice[4];
        audiosource.Play();
    }
    void ufufuVoice() {
        audiosource.clip = voice[5];
        audiosource.Play();
    }

ちなみに、BGMからユニティちゃんボイスもすべてwav形式だったのでmp3に変換して7MBほど軽量化したのですが、ビルドした総サイズを比べると1MBぐらいしか差がありませんでした。それでも、mp3の方が少しは軽くなりますね。

これは最後のライセンス表示シーンで使っているスクリプトです。

using UnityEngine;
using System.Collections;

public class LicenseLogo : MonoBehaviour {

    public AudioSource audioSource;
    private GameObject BGMobj;
    private BGM_Loop bgmloop;
    public static bool musicOn2;

    void Start() {
        musicOn2 = GameManagement.musicOn;
        BGMobj = GameObject.Find("MusicBox(Clone)");
        bgmloop = BGMobj.GetComponent<BGM_Loop>();
        audioSource = GetComponent<AudioSource>();
        audioSource.Play(); //ユニティちゃん「バイバイ」音声の再生
    }

    void Update() {
    //Pキーが押されるたびにBGMの再生と停止を交互させる
        if (Input.GetKeyDown(KeyCode.P)) {
           if (musicOn2) {
               bgmloop.audioSource.Stop();
               musicOn2 = false;
               return;
            }
            if (!musicOn2) {
               bgmloop.audioSource.Play();
               musicOn2 = true;
               return;
            }
        }
    //Bキーが押されたらユニティちゃん「バイバイ」音声の再生
        if (Input.GetKeyDown(KeyCode.B)) {
            audioSource.Play();
        }
    }
    //アプリケーションを閉じる
    void QuitLogo() {
        Application.Quit();
    }
}

ゲーム中のBGMをオンオフ出来るようにしてしまったため、このアプリを終了するだけのシーンのためにスクリプトをこれだけ書く事になってしまいました。本来ならば最後のQuitLogo()だけあればいいところなのですが(^^;)。ちなみに、ロゴがフェイドアウトしていくタイミングでAnimationのイベントからQuitLogo()を実行するようにしています(WebGLでは機能はしません)。

musicOnはGameManagementクラスでpublic staticのbool型で宣言してある変数でBGMの再生状態を格納してあります。それをこのクラスでも引き継がせないと都合が悪いことが分かったのでmusicOn2に代入しておきました。public staticの変数はシーンをまたいで他のクラスに値を渡せるのですね。

という感じで、部分的な解説なのですが、これでもものすごく長くなってしまいましたね(^^;)。しかし、解るようになるとプログラミングは面白いですね。もし、Unityを始めてなかったら自分がプログラミングなど理解できるようになることはなかったと思います。プログラミングの面白さを知ることが出来たのもUnityのおかげです。

来年こそはオリジナルキャラを使ってなにか作品をつくれたらいいなぁと考えています。しかし、そのためにはやることがいっぱいで時間がいくらあっても足りないぐらいですが、めげずに来年こそは!(笑)





スポンサーサイト
テーマ: CG ジャンル: 日記

コメント

コメントの投稿


非公開コメント

カテゴリ
ini-T MUSIC SKY
プロフィール