Help us understand the problem. What is going on with this article?

unity入門 その5  当たり判定処理

More than 3 years have passed since last update.

前回までのおさらい

その3では、フィールドの自動生成を行いました。

test.gif

その4では、ゲームには必須の画面遷移の方法を記述

続いて、物体同士の当たり判定を作っていきたいと思います。

本題に入る前にちょっと脱線。

過去3回で作ってた、サンプルからちょっと動きを変更しています。
床がグリグリ動くと、見にくくなってきたから、
ユーザのアクションに応じて、プレイヤーボールだけの
重力場を変更するようにしました。

こんな感じ。
test.gif

fieldにつけていたスクリプトをプレイヤーボールの方につけかえて、
下に書いたように、transform.rotationをphysics.gravityに変更しました。

fieldControl.js
    // ターゲットの回転を実行
    transform.rotation = Quaternion.Slerp(transform.rotation, target,  Time.deltaTime * smooth);
boalControl.js
// 画面は傾けないで球に与える重力方向を変更
    Physics.gravity = new Vector3(tiltAroundZ, 0, tiltAroundX);

これで、フィールドは動かずボールの重力場だけが更新されるので、
ボールが重力に応じて動くようになりました。

今回の本題

今回の目標

 ゲームを作る上ではやっぱり絶対に必要になってくると思われる
 当たり判定部分を書いていく

 やりたい事
・何か、ゴールオブジェクトを作って触れたらクリア!
・特定オブジェクトに触れたらワープするギミック
 
とかそういうのを作るために、ぶつかった判定を受け取る仕組みを見ていきます。


目標イメージ
ゴールオブジェクトとして、円筒の物体を生成し、
プレイヤー(球)が触れたら、数秒後タイトル画面に戻る。
ワープするギミックとしても参考にできるように、
ゴールオブジェクトに触れた瞬間、画面右下にワープするようにしてみた。

test.gif


やるべき事 概要

・判定を行う同士のレイヤーを作成する
・どのレイヤーとどのレイヤーがぶつかるのかを設定する
・当たり判定のColliderを作成
・当たった後に、画面遷移などのイベント発行に必要なスクリプト作成

判定を行う同士のレイヤーを作成する

今回は、ゴール用に作成するオブジェクトとプレイヤーの当たり判定を行うので、
 goalレイヤーと、playerレイヤーを作成します。

Edit -> Project Settings -> Tags and Layers
スクリーンショット 2016-02-20 16.15.20.png

inspectorにレイヤー設定画面が出てくるので、
User Layer 8,9にgoalとplayerを追加
スクリーンショット 2016-02-20 16.16.12.png

どのレイヤーとどのレイヤーがぶつかるのかを設定する

Edit -> Project Settings -> Physics
スクリーンショット 2016-02-20 16.32.46.png

physicsManagerが表示されます。
この画面で、"どのレイヤー"と"どのレイヤー"が物理演算対象かを設定します。
Playerは球にする予定なので、
壁とgoalオブジェクトに対して当たり判定が必要です。
壁は、レイヤーはDefaultなので、
player -> [Default][goal]にチェックします。

goalは、動かないオブジェクトになるので、
球と当たり判定が分かればいいということで
goal -> [player]にチェックを入れます。

スクリーンショット 2016-02-20 16.33.15.png


脱線
例えば、playerとDefaultのチェックを外すと
壁の当たり判定がなくなり、素通りできるようになります。
この仕組みを使えば、隠し扉の仕組みも簡単に作れそうです。

壁がなかったことになりました。
test.gif


当たり判定のColliderを作成

ゴール用のオブジェクトを作ります。
円筒のcylinderを使います。
スクリーンショット 2016-02-20 16.43.42.png

オブジェクト名を[goal]に変更(任意)
スクリーンショット 2016-02-20 16.44.41.png

goalオブジェクトのレイヤーをgoalにします。
スクリーンショット 2016-02-20 16.52.32.png

今回のゴールオブジェクトはIs Triggerにします。

スクリーンショット 2016-02-20 16.54.50.png
Capsule Collider の Is Triggerにチェック
物理演算処理は行わず、当たり判定のTriggerにだけ使うという記し。

goalをPrefab化してください。
スクリーンショット 2016-02-20 17.02.36.png

球の方もデフォルトで、Sphere Colliderが入っているので、
そのままで問題ないです。

スクリーンショット 2016-02-20 17.04.03.png
前に作った、球のPrefabsを選択して、Inspectorを確認

当たった後に、画面遷移などのイベント発行に必要なスクリプト作成

最後に、ぶつかった時のイベントをスクリプトで受け取る方法
3つのオブジェクト間でやり取りを行う方法を記載します。

今回作成のメッセージのやり取り概要


プレイヤーがゴールオブジェクトに衝突

ゴールオブジェクトが衝突イベント取得

ゲーム監視用スクリプトにクリアを通知(ゴールギミック)

プレイヤーを特定の場所へ移動するイベントを通知(ワープギミック)


最初にも書いた、ゴール用とワープをいっぺんに書きます。
ゴールオブジェクトに触れたら、
ゲームクリアとして、一定時間後にタイトルに戻り、かつ
適当な場所にボールを飛ばす

空のGameObjectに設定

GameCtrl.cs
using UnityEngine;
using System.Collections;

public class GameCtrl : MonoBehaviour {
    // 20秒以内にゴールにつけない場合ゲームオーバー
    public float deadlinetime = 20.0f * 60.0f;
    // ゲームオーバーフラグ
    public bool gameOver = false;
    // ゲームクリア
    public bool gameClear = false;
    //
    public float sceneDelay = 3.0f;

    void Update()
    {
        // ゲーム終了条件成立後、シーン遷移
        if( gameOver || gameClear ){
            sceneDelay -= Time.deltaTime;
            if( sceneDelay <= 0.0f ){
                Application.LoadLevel("TitleScene");
            }
            return;
        }
        //前回フレームからの時間差を取得
        deadlinetime -= Time.deltaTime;
        // 残り時間が無くなったらゲームオーバー
        if(deadlinetime<= 0.0f ){
            GameOver();
        }

    }

    // ゲームオーバー
    public void GameOver()
    {
        gameOver = true;
    }

    // ゲームクリア
    // Goalオブジェクトからイベントメッセージとして
    // 呼び出してもらうためのメソッド
    public void GameClear()
    {
        gameClear = true;
    }
}

スクリーンショット 2016-02-20 17.21.02.png

プレイヤーにつけるオブジェクト
重力場を捜査している元々つけてたスクリプトのどっかに
以下のメソッドを追加
ポジションを無理やり、(10,-10)に変えます。

boalControl.js
// goalレイヤーの物体と衝突した時
function Goal(){
    this.transform.position.x = 10;
    this.transform.position.z = -10;
}

ゴールオブジェクトに付けるスクリプト
衝突イベントは以下のイベントで受け取ります。

Csharp
// 衝突した瞬間
void OnTriggerEnter(Collider other) {
}
// 衝突している間
void OnTriggerStay(Collider other) {
}
// 衝突から離れた瞬間
void OnTriggerExit(Collider other) {
}
goalScript.cs
using UnityEngine;
using System.Collections;

public class goalScript : MonoBehaviour {
    GameCtrl gameRuleCtrl;

    // 衝突した瞬間に呼ばれる
    void OnTriggerEnter(Collider other) {
        // GameCtrlクラスを探して、取得する
        gameRuleCtrl = GameObject.FindObjectOfType(typeof(GameCtrl)) as GameCtrl;
        // GameCtrlクラスのGameClearメソッドを呼ぶ
        gameRuleCtrl.GameClear ();

        // 衝突してきた物体のGoalメソッドを呼ぶ
        // boalControl.jsに追加
        other.SendMessage ("Goal");
    }
}

今更だけど、
C#に統一すればよかったです。。。

コードベースの動作の概要

Layer(player)がLayer(goal)に当たった時
goalScript:: OnTriggerEnter が発行される。

その中で、以下の2つの処理を実行
- 「GameCtrl」クラスを探索して、そのクラスのGameClearメソッドを呼ぶ
- ゴールオブジェクトに衝突したオブジェクトが持っているGoal()を呼ぶ

GameCtrlはGameclearの状態が一定期間続いた場合、
TitleSceneへ画面遷移
衝突した球にAddされているboalControl.jsのGoalメソッドが呼ばれて、
ポジションが移動する。

test.gif

こんな感じになりました。
ゴールオブジェクトに触れると、右下に飛ばされて、
ちょっと経過したらタイトルに戻る。


追加
unity入門 その3で作成した、自動フィールド生成のところで、
FileReadTestの中に[g]を作って、
creatStageを拡張する。
スクリーンショット 2016-02-20 19.50.41.png
Goalの定義に作成したゴールオブジェクトのPrefabsを登録すれば、
ゴールオブジェクトも自動生成に対応。

creatStage.cs
public class creatStage : MonoBehaviour {

    -- 略 --
    // ゴールオブジェクトの定義
    public GameObject goal;
    -- 略 --

void CreateStage(Vector3 pos){
Vector3 originPos = pos;
        string stageTextData = textAsset.text;
        pos.y = 0;

        foreach(char c in stageTextData){

            GameObject obj = null;

            if(c == '#'){
                obj = Instantiate(block, pos, Quaternion.identity) as GameObject;
                obj.name = block.name;
                pos.x += obj.transform.lossyScale.x;

                obj.transform.parent = parents.transform;

            -- 略 --
            }else if(c == 'g'){
                // [g]の時はゴールオブジェクトを生成
                obj = Instantiate(goal, pos, Quaternion.identity) as GameObject;
                obj.name = goal.name;
                pos.x += spaceScale.x;

                obj.transform.parent = parents.transform;
            -- 略 --
            }
        }
    }
}



_Nanagin
某一般業務系受託会社に勤めるサラリーマン よく忘れてしまうことをメモ代わりに書きなぐり [今の流行り] unityが面白いと言う噂を聞きつけ 地道に自己学習中。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした