2D TOOLKIT で横スクロールアクション

20090818-1

ハロウインのチョコレート風呂

先日「なにわの湯」で日替わり湯が「ブドウジュース」「ラムネ」「チョコレート」になる!というハリガミがあったのです。チョコレート風呂だと!? いや多分チョコ色にチョコの香りのするお風呂なんでしょうがこれは是非いかなければ!

脱線終わり。

メガファイト(目がファイト)

というわけでマップ内をジャンプアクションで移動するシーンを作ってみました。挙動がまだおかしいので色々修正の余地ありです。操作方法はカーソルの左右で横移動、スペースキーでジャンプです。移動するだけなので特にゴールもなければ敵もいません。

新規プロジェクト作成

まずは Unity を立ち上げて新規プロジェクトを作成しましょう。

File → New Project… をクリックして新規プロジェクトのダイアログを開き、Browse… をクリックしてフォルダ指定ダイアログを開きます。ここで新規プロジェクトのフォルダを作成して フォルダーの選択 をクリックしてフォルダ指定ダイアログを閉じ、最後に Create をクリックしてプロジェクトの作成を確定します。 作業手順は下記画面写真を参考にして下さい。
2dtk20131027-001

2D TOOLKIT のインポート

2D TOOLKIT の購入方法がこの時点で購入完了している事が前提の説明です。2D TOOLKIT の購入方法は「2D TOOLKIT を使ってみようず(前半)」を参考にしてください。

2D TOOLKIT をインポートします。アセットストアからインポートする場合、Window → Asset Store でアセットストアウインドウを開き、写真で赤丸したアイコンをクリックすると、今まで購入したアセット一覧が表示されます。2D TOOLKIT を一覧から探し、Import をクリックします。

インポートダイアログではそのまま Import をクリックしてください。
2dtk20131027-002

すでにダウンロード済で直接ファイルからインポートしたい場合はこちらの手順を参考にしてください。
2dtk20131027-003

サンプルプロジェクトのインポート

サンプルプロジェクトパッケージのダウンロード : MegaFight20131027_UnityPackage

上記ファイルをダウンロードし、Zipファイルを解凍して MegaFight20131027.unitypackage をダブルクリックするとインポートダイアログが表示されます。そのまま Import をクリックしてください。2D TOOLKIT のプロジェクトインポート後は 2D Toolkit → Rebuild Index をクリックしてください。これでプロジェクトの準備完了です。
2dtk20131027-004

サンプルシーンのオープン

Assets / sample008 を選択し、sample008.unity をダブルクリックしてシーンを開きます。
2dtk20131027-005

シーンを構成するファイルの説明

BGPhysicsMaterial

物理演算の演算結果に影響するマテリアルです。Bounciness を大きくすると着地時に少しバウンドするようになります。その他パラメータはまた把握できたら書いてみます。
2dtk20131027-006

MegaminController

キャラクター制御用の作成したスクリプトです。2D Toolkit のアニメーション切り替えやタイルマップ背景との当たり判定処理を行っています。
2dtk20131027-007

sample008SA, sample008SC, sample008SC Data

今回サンプルに使用したスプライトアニメーション、スプライトコレクション及び、スプライトコレクションの実体データのフォルダです。
2dtk20131027-008

スプライトの当たり判定の内容を確認する場合、sample008SC を選択して Open Editor… をクリックしてスプライトコレクションを開きます。次に Sprites の中から閲覧したいスプライトを選択し、スプライトコレクションウインドウ下部の Collider をクリックすると選択したスプライトの当たり判定が確認できます。 当たり判定が設定されていない場合はウインドウ下部に Collider は表示されません。

当たり判定は Box Custom タイプなので矩形指定ですがポリゴンによる指定も可能です。(後述します)

Settings の内容も確認しておきましょう。ここでは 1 メートルあたり 48 ドット、当たり判定は厚さ 1 メートルにしています。
2dtk20131027-009

スプライトの中心座標を確認する場合はスプライトコレクションウインドウ下部の Anchor をクリックします。図では足元が中心となっています。(今後中心位置変えるかもです)
2dtk20131027-010

ちなみに、スプライトの元画像はこのようなデータです。スプライトコレクションにまずスプライトシートとして登録してから 64 x 64 のスプライトとして登録しています。
2dtk20131027-011

スプライトアニメの確認方法は、sample008SA を選択し、Open Editor… をクリックし、スプライトアニメーションウインドウを開きます。次にスプライトアニメのリストとありますが、クリップですね。クリップ名をクリックするとアニメーションの確認が行えます。

C# からアニメーションの切り替えを行う場合、クリップ名の指定で切り替えができます。
2dtk20131027-012

sample008TMSC, sample008TMSC Data, sample008TMD, sample008TMED

背景に使用したタイルマップのスプライトコレクション、スプライトコレクション実体データ、タイルマップデータ及び、タイルマップエディットデータです。
2dtk20131027-012a

タイルマップの当たり判定もスプライトコレクションで行えます。sample008TMSC を選択し、Open Editor… をクリックしてスプライトコレクションウインドウを開き、BackGround512/16 をクリックし、ウインドウ下部の Collider をクリックして当たり判定を表示させます。 このタイルマップのように一部分しか絵がない場合はこのようにポリゴンで当たり判定を指定します。その他の殆どのタイルマップは矩形タイプです。

Settings の内容も確認しておきましょう。ここでは 1 メートルあたり 48 ドット、当たり判定は厚さ 1 メートルにしています。
2dtk20131027-013

先ほども厚さ 1 メートルとありましたが、厚さをつけると下図のように当たり判定の厚みを調整できます。

2D スプライトは微妙に前後に Z 座標をずらす事で表示優先順位を調整したりしますのである程度ズレても大丈夫な厚みを指定しておくと良いと思います。
2dtk20131027-014

次にヒエラルキのタイルマップを確認してみます。sample008TM を選択し、インスペクタで Edit をクリックして編集モードに切り替え、Settings をクリックで詳細な設定情報が確認できます。ここでは Layers を開いてみてください。

2D Toolkit では自由にレイヤーを最大 32 枚まで追加できます。レイヤーの用途は開発者次第で好みに設定可能です。今回のプロジェクトでは衝突するとジャンプするが可能な Ground レイヤーと衝突してもジャンプできない Wall レイヤーにわけています。

どのレイヤーに衝突したかは C# のスクリプトから検知できますので衝突したレイヤー名で判定しています。

また、上で説明した BGPhysicsMaterial はこのタイルマップレイヤーに設定されています。(Wall は別マテリアルにして跳ねやすい材質にしてもいいかもですね)
2dtk20131027-015

タイルマップの元画像はこのようなデータです。ほんとはもっとタイルマップ用のパーツ描きたかったですが気がついたらこれだけで1日費やしそうな勢いだったので適当に描くのを切り上げました。
2dtk20131027-016

スクリプト

using UnityEngine;
using System.Collections;

public class MegaminController : MonoBehaviour {

    Camera oMainCamera = null;          // カメラ
    Vector2 v2AxisMove = Vector2.zero;  // パッド入力
    Vector3 v3Speed = Vector3.zero;     // めがみん速度
    tk2dSprite oSprite = null;          // めがみんスプライト
    tk2dSpriteAnimator oAnime = null;   // めがみんアニメ
    Rigidbody oRigidBody = null;        // 物理演算オブジェクト
    bool isGround = false;              // 接地フラグ
    int iIgnoreJumpCount = 0;           // ジャンプ不可タイム
    int iIgnoreJumpCountMax = 5;        // 最大ジャンプ不可タイム

    // オブジェクト初期化
    // Find 系は重たいので主に Start 等の中で使用する
	void Start () {
        // あらかじめオブジェクトに割り当てられたコンポーネントを取得しておく
        oMainCamera = GameObject.Find("MainCamera").GetComponent(); // カメラ取得
        oSprite = GetComponent(); // スプライト取得(左右反転のために取得)
        oAnime = GetComponent(); // アニメーター取得(これでクリップ切り替え)
        oRigidBody = GetComponent(); // 挙動制御のために取得
	}

	// オブジェクト更新処理
	void Update () {
        // カメラをめがみんにあわせる
        oMainCamera.transform.localPosition = new Vector3(
            transform.position.x + 0.75f,
            transform.position.y + 0.5f,
            oMainCamera.transform.position.z);
        // パッド入力処理
        v2AxisMove = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
        // 接地中のみ左右に加速する
        v3Speed.x = isGround ? (v2AxisMove.x * 20f) : 0f;
        v3Speed.y = (Input.GetAxis("Jump") > 0f && isGround && iIgnoreJumpCount == 0) ? 400f : 0f;
        // パッド入力方向によってめがみんを左右反転させる処理
        if (v2AxisMove.x > 0f) {
            oSprite.scale = Vector3.one;
        } else if (v2AxisMove.x < 0f) {             oSprite.scale = new Vector3(-1f, 1f, 1f);         }         // アニメーションコントロール         if (oAnime != null) {             if (isGround) {                 if (Mathf.Abs(v3Speed.x) > 0.0f) {
                    oAnime.Play("MegaminRun"); // クリップ名をここで指定します
                } else {
                    oAnime.Play("MegaminNutral"); // クリップ名をここで指定します
                }
            } else {
                oAnime.Play("MegaminJump"); // クリップ名をここで指定します
            }
        }
        // ジャンプ不可カウントを更新する
        iIgnoreJumpCount += (iIgnoreJumpCount > 0) ? -1 : 0;
    }

    // 物理演算用更新処理
    void FixedUpdate() {
        // 接地してない時は縦方向の加速度をキャンセルする
        if (!isGround) {
            v3Speed.y = 0f;
        }
        // 加速する
        oRigidBody.AddForce(v3Speed);
        // 移動速度補正処理
        Vector3 v3Velocity = oRigidBody.velocity;
        // X方向の移動速度の絶対値 10 に丸める
        if (Mathf.Abs(v3Velocity.x) > 10f) {
            v3Velocity.x = Mathf.Sign(oRigidBody.velocity.x) * 10f;
        }
        oRigidBody.velocity = v3Velocity;
        // ジャンプ中はジャンプ不可カウントを設定する(接地誤判定による空中ジャンプ抑制)
        if (v3Speed.y != 0) {
            v3Speed.y = 0f;
            iIgnoreJumpCount = iIgnoreJumpCountMax;
        }
    }

    // 衝突判定(何かに衝突した)
    void OnCollisionEnter(Collision oCollision) {
        // Ground レイヤーに衝突した場合、接地フラグをONにする
        isGround = (oCollision.gameObject.transform.parent.gameObject.name.Equals("Ground"));
    }

    // 衝突判定(何かに衝突中)
    void OnCollisionStay(Collision oCollision) {
        // Ground レイヤーに衝突した場合、接地フラグをONにする
        isGround = (oCollision.gameObject.transform.parent.gameObject.name.Equals("Ground"));
    }

    // 衝突判定(何かから離れた)
    void OnCollisionExit(Collision oCollision) {
        // 接地フラグをクリア
        isGround = false;
    }
}

当たり判定が以外と肝?

最初は Raycast を使用して当たり判定を取ろうとしたのですが、なんかうまくいかず。色々調べて OnCollision 系で当たり判定が取れるようになりました。タイルマップに衝突しているが接地している。の判定がなんか微妙ですね。もっと良い方法がないものか、FPS 系のソースコードみたらいいヒントがあるかも。

※ なるほどそうか、キャラクタコントローラ使ったらよかったんや。

悲報

さて、連休も終わりました。これより月曜日がはじまります。(なんつってな)

「シェアする」

ツイートツイート