2013年3月6日水曜日

AI開発&クォータニオン入門~6.カメラの方向を向くようにPlaneを回転

関連記事

例のごとく、Unity Gemsからの翻訳をどうぞ!

----------------

カメラの方向を向くように Plane を回転させる


ここまで何とかしてテキストメッシュにカメラの方向を正しく向かせることには成功した。次は平面をどう扱うか考える番だ。プレイヤーに表示する 2D テクスチャを作成するのに Unity で作成した Plane は適している。画面の方向を常に向く 2D ビルボードを作成しよう。

ここで敵キャラに「ムード」があることにしよう。最初はある一定の機嫌の良さから始めて、時間の変化とともに機嫌が悪くなる。ゲームの中では色んな場面に応用して面白い要素に出来るはずだ。

プレイヤーは敵キャラを正面から見据えたときにこのムードが判別出来るようにしたい。敵キャラの頭上に機嫌を表すスマイルマークを表示して、色での工夫も重ねて機嫌がハッピーな時は緑色、怒りの時は赤色と、反映する。

当然、これにより必要なスクリプトは新たにいくつか必要となる。

最初に敵キャラを正面から見据える方法から考えて見よう。まずプレイヤーにスクリプトをアタッチして目の前のものに対して反応する処理を試す。
using UnityEngine;
using System.Collections;

public class LookTrigger : MonoBehaviour {

    Transform _transform;

    // Use this for initialization
    void Start () {
        _transform = transform;
    }

    // Update is called once per frame
    void Update () {
        RaycastHit hit;
        if(Physics.SphereCast(_transform.position + _transform.forward * 0.5f, 3f, _transform.forward, out hit, 400))
        {
            hit.collider.SendMessage("LookedAt", SendMessageOptions.DontRequireReceiver);
        }
    }
}
プレイヤーの正面の位置で SphereCast を実行してそれが何かと当たり判定があれば "LookedAt" されたとみなして、オブジェクトに対してその旨のメッセージ送信する。

ここで SphereCast を使用する理由は距離が離れたものについて多少のズレは許容する適度な正確さで扱ってくれるためである。

さて、ここまで簡単だったが次に敵キャラにムードを持たせる番だ。EnemyMood という新規のスクリプトの準備を進めていこう。
using UnityEngine;
using System.Collections;

public class EnemyMood : MonoBehaviour {

    public MoodIndicator moodIndicatorPrefab;
    public float _mood;

    Transform _transform;
    MoodIndicator _currentIndicator;

    void Start () {
        _transform = transform;
        mood = Random.Range(30,99);
    }

    void Update () {
        mood -= Time.deltaTime/2;
    }

    void LookedAt()
    {
        if(_currentIndicator)
            return;

        _currentIndicator = Instantiate(moodIndicatorPrefab, _transform.position + Vector3.up * 3.5f,
            Quaternion.identity) as MoodIndicator;

        _currentIndicator.enemy = this;
    }

}
初めに mood 変数を 30 と 99 の間の値で初期化する。次にこれを Update コールの中で 徐々にデクリメントさせる。

プレハブ変数を Plane に追加してムードを表示したうえ、プレハブに MoodIndicator スクリプトを与えるので、後ほど詳細に中身を検証していきたい。

EnemyMood は LookedAt メッセージを受け取り、すでに 既存の MoodIndicator があるかチェックし、あれば return で関数を終了する。

Unity は MoodIndicator が破棄されたときは自動的にこの変数の値として null/false を戻す。

もし既存の MoodIndicator がなかった場合、新たに作成して敵キャラの上に配置する。次に indicator に対して敵キャラのムードが現在どの状態であるかを通知する。

MoodIndicator は Plane の表示をハンドリングして適切な画像の表示を行う。
using UnityEngine;
using System.Collections;

public class MoodIndicator : MonoBehaviour {

    public Texture2D[] moodIndicators;
    public Color happyColor = Color.green;
    public Color angryColor = Color.red;
    public float fadeTime = 4f;
    public EnemyMood enemy;

    Transform _transform;
    Transform _cameraTransform;
    Material _material;
    Color _color;
    Quaternion _pointUpAtForward;

    void Start () {

        _pointUpAtForward = Quaternion.FromToRotation(Vector3.up, Vector3.forward);

        _material = renderer.material;

        // 画像のセット
        _material.mainTexture = moodIndicators[
            Mathf.Clamp(
                Mathf.RoundToInt( enemy.mood/(100/moodIndicators.Length)),
                0,
                moodIndicators.Length-1)
            ];

        // 色の計算
        var moodRatio = enemy.mood/100;
        _material.color = _color = new Color(
            angryColor.r * (1 - moodRatio) + happyColor.r * moodRatio,
            angryColor.g * (1 - moodRatio) + happyColor.g * moodRatio,
            angryColor.b * (1 - moodRatio) + happyColor.b * moodRatio
        );

        Update();
    }

    void Awake()
    {
        _transform = transform;
        _cameraTransform = Camera.main.transform;
    }

    public void Update () {

        // カメラに対して Plane を表示
        _transform.rotation = Quaternion.LookRotation(-_cameraTransform.forward, Vector3.up)
            * _pointUpAtForward;

        // 画像のフェードアウト処理
        _color.a -= Time.deltaTime/fadeTime;
        _material.color = _color;
    }
}
重要なスクリプトの初登場なのでいくつかの部分に分けて見ていこう。
public Texture2D[] moodIndicators;
public Color happyColor = Color.green;
public Color angryColor = Color.red;
public float fadeTime = 4f;
public EnemyMood enemy;
パブリック変数は画像イメージの配列であり、ムードのインジケータとして使用して、悲しみ、喜び、等々を表す。サンプルプロジェクトには4種類含まれる。ハッピーな色と怒りの色があり、後は時間の経過とともにフェードアウトさせて最終的に全てが透明になる。最後に、どの敵キャラのものかの紐付け情報がある。これら全て Start 実行開始前までにセットされる。
void Start () {

    _pointUpAtForward = Quaternion.FromToRotation(Vector3.up, Vector3.forward);

    _material = renderer.material;

    //Set the graphic
    _material.mainTexture = moodIndicators[
        Mathf.Clamp(
            Mathf.RoundToInt( enemy.mood/(100/moodIndicators.Length)),
            0,
            moodIndicators.Length-1)
        ];

    // 色の計算
    var moodRatio = enemy.mood/100;
    _material.color = _color = new Color(
        angryColor.r * (1 - moodRatio) + happyColor.r * moodRatio,
        angryColor.g * (1 - moodRatio) + happyColor.g * moodRatio,
        angryColor.b * (1 - moodRatio) + happyColor.b * moodRatio
    );

    Update();
}
最初に行うべきことはキャッシュされたクォータニオンを作成して、その up ベクトルが forward ベクトルを向くようにする。これは Plane にカメラに向けるのと全く同じ方法の復習だ。

次にムードを表すテクスチャを選択するために、敵である怪物のムード(0 から 98 の間の数字)を一定の係数で割り算して、提供されているテクスチャの範囲に収まるようにして、必ずテクスチャが有効となるように clamp を行う。

次にスマイルマークの色を判断させるためにムードにもとづいて怒りおよびハッピーマークの一部を使用する。

最後に Update を呼び出して回転のコードが直ちに呼び出しされるようにする。

Update関数は次のようになる:
public void Update () {

        // Plane をカメラに向ける
        _transform.rotation = Quaternion.LookRotation(-_cameraTransform.forward, Vector3.up)
            * _pointUpAtForward;

        // 画像のフェードアウト
        _color.a -= Time.deltaTime/fadeTime;
        _material.color = _color;
    }

最初にカメラに向く通常のオブジェクトの回転を作成するために Quaternion.LookRotation 関数を使用してカメラの -transform.forward を渡して、これを、オブジェクトの up を forward を向けてキャッシュした回転と組み合わせる。これで Plane の設定は大丈夫だ。

回転の二つ目の部分は画像のアルファ値を事前に定めた時間でフェードアウトさせるだけの処理だ。

これで準備完了だ。実行して試すと敵キャラを画面の中央に持ってきた時にインジケータがポップアップする。敵キャラの機嫌もあっという間に怒りモードに変化する。
------
次に続くぜ!


関連記事


0 件のコメント:

コメントを投稿

ブックマークに追加

このエントリーをはてなブックマークに追加

自己紹介

自分の写真
Unity3D公式マニュアル翻訳やってる人がスマホ(iPhone, Android)のゲーム開発しています。気軽に面白く初心者が遊べる内容がモットー。Blogでは開発情報をひたすら、Twitterではゲーム作成の過程で参考にしている情報を中心につぶやきます

ページビューの合計

過去7日間の人気投稿