アイテムを設置しよう のバックアップソース(No.3)

#contents
*はじめに [#v3a58097]
ステージにアイテム・ギミック・ゴールを設置していきます。
今回は
 ・拾うと移動スピードが変化するりんご
 ・エレベーター
 ・ゴール
の3つを実装します。
#br
*りんごの設置 [#m089a5bb]
まずアイテム用の画像を Assets に追加します。
#br
#download(apple_red.png)
#download(apple_blue.png)
#br
追加できたら Assets > それぞれの画像を選択し、inspector から Pixels Per Unit を 32 にそれぞれ変更に変更しておきます。
実装したいリンゴの仕様
 ・ステージの各所に同じ機能のりんごを複数設置する
 ・赤いリンゴを拾うと一定時間速度が上がる
 ・青いリンゴを拾うと一定時間速度が落ちる
 ・速度変化の効果は重ね掛けできる
 ・それぞれの色の速度変化と継続時間は inspector から調整できるようにする
#br
「拾う」という動作は、ひつじに当たったときに効果を発動してりんごを消す
という処理で表現しようと思います。
**プレハブについて [#m7a4dc60]
調べてみるとUnityで同じものを複製して使用するときはプレハブ(Prefab)が便利なようです。
プレハブはゲームオブジェクトから生成するので、まずはりんごのゲームオブジェクトを完成させます。
#br
**りんごのゲームオブジェクトの準備 [#tc80cf62]
Assets から apple_red.png を Scene にドラッグ・ドロップし、ゲームオブジェクトを作ります。
作ったゲームオブジェクトにコンポーネントを追加します。
必要になりそうなコンポーネントは
・当たり判定用のCollider
です。
りんごの画像がほぼ円に近いので、今回は Circle Collider 2D でよさそうです。
inspector > Add Component から追加します。
#br
追加できたらinspectorからコンポーネントの設定を少し変更します。
まず、りんごにひつじが反発するなどの物理演算は不要なので、isTrigger にチェックを入れて物理演算を無効にします。
次に、Radius の数値を調整し、当たり判定の大きさをりんごの画像に合わせます。0.4 くらいがちょうどよさそうです。
#br
**りんごにスクリプトを追加 [#v952b5b2]
Assets > Add Component > New Script から新しいスクリプトをりんごに追加します。
スクリプト名は Apple としておきます。
毎フレーム行いたい処理はないので Update メソッドは削除して大丈夫です。
まずは必要な変数を作っておきます。
速度の変化率 を float型で rate
継続時間を float型で duration としておきます。
#geshi(csharp,number){{
using System.Collections;
using UnityEngine;

public class Apple : MonoBehaviour
{
    public float duration;
    public float rate;
}
}}
rate と duration は public にしておけば inspector から編集できます。
テスト用に rate は 1.5, duration は 3.0 にしておきます。
#br
***速度変化の実装 [#wff4b8b3]
次に速度変化を作ります。
当たり判定のイベントを受け取り、当たったオブジェクトがひつじだった場合にひつじの速度を変化させる処理で表現できそうです。
調べてみると isTrriger が入った Collider の当たり判定を受け取るイベントには OnTriggerEnter, OnTriggerStay, OnTriggerExit などがありましたが、今回は当たった時に一度だけ呼ばれればよいので OnTriggerEnter を選択しました。
#geshi(csharp,number){{
using System.Collections;
using UnityEngine;

public class Apple : MonoBehaviour
{
    // 略

    void OnTriggerEnter2D(Collider2D col)
    {
        if (col.name == "hitsuji_dot") {
            MoveSheep moveSheep = col.GetComponent<MoveSheep>();
            moveSheep.speed *= rate;
        }
    }
}
}}
#br
OnTriggerEnterの引数 Collider2D 型の中にはnameという名前で当たったオブジェクトの名前が入っているので、それを使って当たったオブジェクトがひつじかどうかを判定しています。
当たったオブジェクトがひつじだった場合には、そのオブジェクトから GetComponent で MoveSheepコンポーネント を取得し、
speed にりんごの速度変化率 rate でかけて代入します。
#br
***継続時間の実装 [#v028d6a9]
次に、継続時間を実装します。
ひつじがりんごに触れて速度を変化させた後、一定時間後に元の速度に戻すことで表現できそうです。
調べてみると、Unity で 数秒後に何か処理を行いたいときには コルーチン (coroutine)が便利なようです。
コルーチンはIEnumerator 型を戻り値とする関数として書き、StartCoroutine関数でその関数を呼び出すことができます。
以下はコルーチンの基本的な書き方です。
#geshi(csharp,number){{
public class Apple : MonoBehaviour {
    void Start () {
        // コルーチンを呼び出し  
        StartCoroutine ("Test");
        // メッセージをconsoleに出力
        Debug.Log("コルーチンを呼び出しました");
    }

    // コルーチン  
    private IEnumerator Test() {
        // 一秒待つ
        yield return new WaitForSeconds (1.0f);
        // メッセージをconsoleに出力
        Debug.Log("コルーチンの処理が終わりました");
    }
}}
このように書くと、Unityの console では
'''コルーチンを呼び出しました'''
と出力された1秒後に
'''コルーチンの処理が終わりました'''
と出力されます。
#br
これを使って、
ひつじがリンゴに触れたらコルーチンを呼び、
コルーチンのなかで速度を変化させた後、一定時間後に元の速度に戻す
ようにスクリプトを改良します。
まずは、コルーチンを作ります。コルーチン名は ChangeSpeed としておきます。
コルーチンのなかでひつじの速度を変えたいので、MoveSheep を引数で受け取れるようにしておきます。
#geshi(csharp,number){{
public class Apple : MonoBehaviour {

    //略

    // コルーチン  
    private IEnumerator ChangeSpeed(MoveSheep moveSheep) {
        moveSheep.speed *= rate;
        yield return new WaitForSeconds(duration);
        moveSheep.speed /= rate;
    }
}
}}
'''*=''' で変化させたので、 '''/='''すればもとの速度に戻ります。 
元の速度を変数で保管しておく方法もありそうですが、りんごが複数あった時にスピード変化の効果を重ね掛けできるようにしたいのでこのような処理を考えました。
#br
コルーチンができたら、ひつじと接触したときに子のコルーチンを呼び出すようにします。
コルーチンに引数を渡したいときは
#geshi(csharp,number){{
StartCoroutine(コルーチン(引数));
}}
のように書くことができます。

#geshi(csharp,number){{
public class Apple : MonoBehaviour {

    // 略

    void OnTriggerEnter2D(Collider2D col)
    {
        if (col.name == "hitsuji_dot") {
            MoveSheep moveSheep =  col.GetComponent<MoveSheep>();
            StartCoroutine(ChangeSpeed(moveSheep));
        }
    }

    //略

}
}}
#br
***ひつじを消す [#y1139f88]
最後にひつじを消す処理を作ります。
オブジェクトの削除は Destory メソッドで実装できます。
#geshi(csharp,number){{
Destroy(消したいゲームオブジェクト, 消すまでの時間)
}}
消すまでの時間は省略できます。
#geshi(csharp,number){{
public class Apple : MonoBehaviour {

    // 略

    void OnTriggerEnter2D(Collider2D col)
    {
        if (col.name == "hitsuji_dot") {
            MoveSheep moveSheep =  col.GetComponent<MoveSheep>();
            StartCoroutine(ChangeSpeed(moveSheep));
            Destroy(this.gameObject);
        }
    }

    //略

}
}}
これで動きそうですが、これを実行してみるとひつじの速度が変わらなくなります。
調べてみると、Destroyは実行中のCoroutineもオブジェクトと一緒に消してしまうようです。
そこで、オブジェクト自体は消さずに、画像を見えなくして当たり判定を無効にする処理にかえようとおもいます。
オブジェクトの画像をUnityで表示している SpriteRenderer と CircleCollider2D をそれぞれ無効にすれば再現できそうです。
#geshi(csharp,number){{
public class Apple : MonoBehaviour {

    // 略

    void OnTriggerEnter2D(Collider2D col)
    {
        if (col.name == "hitsuji_dot") {
            MoveSheep moveSheep =  col.GetComponent<MoveSheep>();
            spriteRenderer = this.GetComponent<SpriteRenderer>();
            circleCollider2D = this.GetComponent<CircleCollider2D>();

            StartCoroutine(ChangeSpeed(moveSheep));
            spriteRenderer.enabled = false;
            circleCollider2D.enabled = false;
        }
    }

    //略

}
}}
enebled を false にすることでコンポーネントを簡単に無効化できます。
これでりんごが消えたように見えます。
#br
**りんごのプレハブ化 [#ef9f3281]
完成したリンゴをプレハブにします。
その前に、プレハブ保管用のフォルダを Assets のなかに作っておきます。
フォルダ名は Prefabs としておきます。
フォルダを作ったら Hierarchy から先ほど作ったりんごをフォルダにドラッグ・ドロップします。
これだけでプレハブの完成です。
プレハブは Hierarchy 上で水色のキューブとして表示されるようになります。
#br
**りんごをステージの各所に設置 [#n7fd35e9]
作ったプレハブを Scene にドラッグ・ドロップすると簡単にプレハブのインスタンスを生成できます。
ソフトウェアの分野でインスタンスとは「実体」を指し、設計図をもとにして実際に作った物などとよく表現されています。
ここではプレハブが「設計図」になっているわけです。純粋な「コピー」とは少し異なる概念です。
ステージの適当な位置にりんごのインスタンスを作って設置しておきます。
#br
**青りんごの実装 [#b35ea27a]
青リンゴも赤リンゴと同じように作ります。スクリプトは Apple.cs を流用し、rate を1/1.5にするだけです。
長くなるので省略します。