前回までのおさらい
その3では、フィールドの自動生成を行いました。
その4では、ゲームには必須の画面遷移の方法を記述
続いて、物体同士の当たり判定を作っていきたいと思います。
本題に入る前にちょっと脱線。
過去3回で作ってた、サンプルからちょっと動きを変更しています。
床がグリグリ動くと、見にくくなってきたから、
ユーザのアクションに応じて、プレイヤーボールだけの
重力場を変更するようにしました。
fieldにつけていたスクリプトをプレイヤーボールの方につけかえて、
下に書いたように、transform.rotationをphysics.gravityに変更しました。
// ターゲットの回転を実行
transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * smooth);
// 画面は傾けないで球に与える重力方向を変更
Physics.gravity = new Vector3(tiltAroundZ, 0, tiltAroundX);
これで、フィールドは動かずボールの重力場だけが更新されるので、
ボールが重力に応じて動くようになりました。
今回の本題
今回の目標
ゲームを作る上ではやっぱり絶対に必要になってくると思われる
当たり判定部分を書いていく
やりたい事
・何か、ゴールオブジェクトを作って触れたらクリア!
・特定オブジェクトに触れたらワープするギミック
とかそういうのを作るために、ぶつかった判定を受け取る仕組みを見ていきます。
目標イメージ
ゴールオブジェクトとして、円筒の物体を生成し、
プレイヤー(球)が触れたら、数秒後タイトル画面に戻る。
ワープするギミックとしても参考にできるように、
ゴールオブジェクトに触れた瞬間、画面右下にワープするようにしてみた。
やるべき事 概要
・判定を行う同士のレイヤーを作成する
・どのレイヤーとどのレイヤーがぶつかるのかを設定する
・当たり判定のColliderを作成
・当たった後に、画面遷移などのイベント発行に必要なスクリプト作成
判定を行う同士のレイヤーを作成する
今回は、ゴール用に作成するオブジェクトとプレイヤーの当たり判定を行うので、
goalレイヤーと、playerレイヤーを作成します。
Edit -> Project Settings -> Tags and Layers
inspectorにレイヤー設定画面が出てくるので、
User Layer 8,9にgoalとplayerを追加
どのレイヤーとどのレイヤーがぶつかるのかを設定する
Edit -> Project Settings -> Physics
physicsManagerが表示されます。
この画面で、"どのレイヤー"と"どのレイヤー"が物理演算対象かを設定します。
Playerは球にする予定なので、
壁とgoalオブジェクトに対して当たり判定が必要です。
壁は、レイヤーはDefaultなので、
player -> [Default][goal]にチェックします。
goalは、動かないオブジェクトになるので、
球と当たり判定が分かればいいということで
goal -> [player]にチェックを入れます。
脱線
例えば、playerとDefaultのチェックを外すと
壁の当たり判定がなくなり、素通りできるようになります。
この仕組みを使えば、隠し扉の仕組みも簡単に作れそうです。
当たり判定のColliderを作成
ゴール用のオブジェクトを作ります。
円筒のcylinderを使います。
今回のゴールオブジェクトはIs Triggerにします。
Capsule Collider の Is Triggerにチェック
物理演算処理は行わず、当たり判定のTriggerにだけ使うという記し。
球の方もデフォルトで、Sphere Colliderが入っているので、
そのままで問題ないです。
前に作った、球のPrefabsを選択して、Inspectorを確認
当たった後に、画面遷移などのイベント発行に必要なスクリプト作成
最後に、ぶつかった時のイベントをスクリプトで受け取る方法
3つのオブジェクト間でやり取りを行う方法を記載します。
今回作成のメッセージのやり取り概要
プレイヤーがゴールオブジェクトに衝突
↓
ゴールオブジェクトが衝突イベント取得
↓
ゲーム監視用スクリプトにクリアを通知(ゴールギミック)
↓
プレイヤーを特定の場所へ移動するイベントを通知(ワープギミック)
最初にも書いた、ゴール用とワープをいっぺんに書きます。
ゴールオブジェクトに触れたら、
ゲームクリアとして、一定時間後にタイトルに戻り、かつ
適当な場所にボールを飛ばす
空のGameObjectに設定
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;
}
}
プレイヤーにつけるオブジェクト
重力場を捜査している元々つけてたスクリプトのどっかに
以下のメソッドを追加
ポジションを無理やり、(10,-10)に変えます。
// goalレイヤーの物体と衝突した時
function Goal(){
this.transform.position.x = 10;
this.transform.position.z = -10;
}
ゴールオブジェクトに付けるスクリプト
衝突イベントは以下のイベントで受け取ります。
// 衝突した瞬間
void OnTriggerEnter(Collider other) {
}
// 衝突している間
void OnTriggerStay(Collider other) {
}
// 衝突から離れた瞬間
void OnTriggerExit(Collider other) {
}
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メソッドが呼ばれて、
ポジションが移動する。
こんな感じになりました。
ゴールオブジェクトに触れると、右下に飛ばされて、
ちょっと経過したらタイトルに戻る。
追加
unity入門 その3で作成した、自動フィールド生成のところで、
FileReadTestの中に[g]を作って、
creatStageを拡張する。
Goalの定義に作成したゴールオブジェクトのPrefabsを登録すれば、
ゴールオブジェクトも自動生成に対応。
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;
-- 略 --
}
}
}
}