はじめに
前回に続きまして、
Blockを破壊して元に戻せるようにしてみます。
ロードランナーといえば、左右の足元のブロックをビーム?みたいなので削って、何故かしばらくすると復活するというのが最大の特徴かと思います。アニメーション的な効果はちょっとおいておいて、そうなるように作ってみます。
ビームは左右に出せるので、左=Z
、右=X
を押した時にビームが出ることにします。
今回のポイント
- 破壊できるBlockを特定する
- Physics.Raycastを使う
- Debug.DrawRay でRaycastを確認する
- Layerを変更して余計なものにHitしないようにする
- Blockを非表示にして、しばらくしたら元に戻す
破壊できるBlockを特定する
どのブロックが破壊できるか、を判定する方法はいくつかありそうですが、
せっかく?なのでUnityの中の当たり判定を最大限に使うことにします。
Raycastというのは、Ray(光線)をCast(投げかける)という意味合いなんだと思いますが、
一点から直線を引いてぶつかったObjectや地点を調べてくれる機能です。
大体の流れは以下のようになります。
- ビームが出せるのは、「落ちていない」かつ「
Z
かX
を押しているとき」 - Unityちゃんの頭上から斜め下45度にRaycast
- HitしたのがBlockならば、
HitGun
メッセージをBlockに送る
以下の様なコードを、UnityChanDemo1.cs
のUpdate()
メソッドの下のほうに追加します。
// Gun
int gun = Input.GetKey(KeyCode.Z) ? -1 : Input.GetKey(KeyCode.X) ? 1 : 0;
if (gun != 0 && !isFalling) {
RaycastHit hit;
Vector3 fromPos = transform.position+Vector3.up;
Vector3 direction = new Vector3(gun, -1, 0);
float length = 1.8f;
Debug.DrawRay(fromPos, direction.normalized * length, Color.green, 1, false);
if (Physics.Raycast(fromPos, direction, out hit, length)) {
if (canDestroyBlock(hit.transform)) {
hit.transform.gameObject.SendMessage("HitGun");
}
}
}
同様にcanDestroyBlock()を追加します。
private bool canDestroyBlock(Transform block) {
return
block.tag == BLOCK &&
Mathf.Abs(block.position.x - transform.position.x) < 1.2f &&
transform.position.y - block.position.y > 0.3;
}
ちなみに UnityChanDemo1.cs の全体はこうなっています。
Physics.Raycast を使う
Physics.Raycast(始点、方向、Hit時にセットされるObject、判定する距離)
という感じに使います。
何かにHitしたら、戻り値がTrueになって、hitオブジェクトに値が入ります。
基本的には、このObjectのTagがBlock
ならば壊しても良いわけです。
ただ、高さの制約や、2つ向こうの側のBlockを消さないかという細かい条件もあるので、
実際に破壊できるかどうかは、canDestoryBlock()
というメソッドで精査することにします。
壊せると判断すれば、そのObjectに足して SendMessage
を使って HitGun
メッセージを送ります。
Debug.DrawRay でRaycastを確認する
作っている最中におもったように判定がされない原因を調べたり、Raycastの微調整などをするときに実際のRaycast光線が見えると便利です。DrawRayを使うとそれが簡単にできます。
Debug.DrawRay(始点、方向、色、表示時間、depthTest [カメラから見えない場合に非表示にするか]))
というように使います。
ただ一つ注意があって、このDrawRayはGame Viewでは通常は表示されません。
Game Viewで表示するようにするには、 Gizmos
というのを有効にする必要があります(これが最初わからなかった・・・)。下図の右上を押しておきましょう。
Layerを変更して余計なものにHitしないようにする
今回Unityちゃんの頭上から斜め下にRaycastをしているので、
実はBlockに当たる前にUnityちゃん自身にRaycastがぶつかってしまいます。
これだと本当に見つけたいBlockがわかりません。また、「はしご」「バー」に当たってしまうのも問題です。
なので、「Unityちゃん」「はしご」「バー」の3つのLayerを Ignore Raycast
というLayerに割り当てます。
Raycastをするときは基本このLayerについて無視するようで今回は好都合です。
もし複数のRaycastを管理するときは、Raycastの5番目の引数にもLayerMaskというのがあるのでこれが使えそうです(今回は使いませんが)。
Prefabに修正を適用する
ちょっと説明が遅れましたが、
PrefabからGameObjectに実体化したものの属性を変更(Layerの変更のような)した場合、InspectorのApply
ボタンを押すとそれが大本のPrefabに反映され、その他全てのGameObjectに適用されます。便利ですね。
逆にRevert
を押すと、GameObjectに修正が元のPrefabの属性で上書きされます。Select
を押すと元となっているPrefabが表示されます。
Blockを非表示にして、しばらくしたら元に戻す
全てが上手くいっていれば、Block オブジェクトにHitGun
メッセージが送られています。
ただ、今はそれを処理するメソッドがないので、Consoleにはエラーが表示されます。
Blockにスクリプトを追加
BlockControl.cs というC# スクリプトを作り、Blockの 子供のCube に関連付けます。
実際の当たり判定(Collider)はこのCubeが持っているので、SendMessageの対象はこのCubeになります。
以下のような処理を追加します。
-
HitGun
メッセージを受け取ったら、Timer をセットする。今回は180Frame(約3秒)とします。 -
Update()
では、 Timerの値を一つずつ減らし、0ではない場合はcollider
(衝突判定),renderer
(描画)を無効にする
とりあえずこれで意図通り動きました。
using UnityEngine;
using System.Collections;
public class BlockControl : MonoBehaviour {
private const int DISAPPEAR_TIMER_COUNT = 60*3;
private int disappearTimer = 0;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (disappearTimer > 0) {
disappearTimer--;
}
renderer.enabled = collider.enabled = disappearTimer == 0;
}
void HitGun() {
if (disappearTimer > 0) {
return;
}
disappearTimer = DISAPPEAR_TIMER_COUNT;
}
}
さいごに
一応上手く?やるとUnityちゃんがBlockに挟まって動けなくなりますw
Blockが壊れる・元に戻るというのもアニメーションさせたいですね。
アニメーションの課題は山積みです。。。
今回のものはここからWebPlayerで見られます。
ソースコードはGithubに公開しています。