一週間ゲームジャムが始まったようです。
【拡散希望】Unityを使って1週間でゲームを作るイベント、「Unity 1週間ゲームジャム」はじめます。初回は来週月曜4/24日!
— naichi@びはんとマルの森 (@naichilab) 2017年4月21日
初開催で参加者0は泣いちゃうので気軽に参加お願いします!!
#unity1week https://t.co/2uhr9rdqDr
もちろん参加しますとも。
どんなゲームを作るのかは平日の間になんとなく考えついたんだんですが、ちゃんとゲームになるだろうか……。
プロジェクトを立ち上げる前に、ゲームに使う技術の検証をしておきましょう。
今回検証するのは、マウスポインタのある位置に3D空間のオブジェクトを移動させるという、ググれば山ほど出てくる技術の検証です。
この記事にはUnity5.6.0f3を使用しています。
マウスの座標を3D空間のワールド座標に変換してみましょう。
マウスの座標はInput.mousePosition
で取得できます。
そして、ゲームで使用しているカメラのScreenToWorldPoint()
を使用して、ワールド座標に変換します。
カメラの行列を使って云々計算しなくても、関数呼出し一発で出来るので、学のない僕にだってできちゃいます。
using UnityEngine; public class TestComponent : MonoBehaviour { [SerializeField] private GameObject m_object = null; private void Update() { Vector2 touchScreenPosition = Input.mousePosition; Camera gameCamera = Camera.main; Vector3 touchWorldPosition = gameCamera.ScreenToWorldPoint( touchScreenPosition ); m_object.transform.position = touchWorldPosition; } } // class TestComponent
ゲーム画面に表示されてない!
やはり学のない僕には無理なのか……。
シーンビューも確認してみましょう。
あった!
カメラにぴったりくっついている。
なぜこうなるのでしょうか。コードを確認してみましょう。
ああ。なるほど。ScreenToWorldPoint()
に渡す座標のZ値に何もいれてない(0.0f)からですね。
奥行の値が0.0fなら、そりゃカメラにくっつくさ。
とりあえず適当に10.0f
ぐらいを設定してみましょうか。
private void Update() { Vector3 touchScreenPosition = Input.mousePosition; // 10.0fに深い意味は無い。画面に表示したいので適当な値を入れてカメラから離そうとしているだけ. touchScreenPosition.z = 10.0f; Camera gameCamera = Camera.main; Vector3 touchWorldPosition = gameCamera.ScreenToWorldPoint( touchScreenPosition ); m_object.transform.position = touchWorldPosition; }
わーい出た。
ただ、シーンビューを見る限り、ゲーム画面外にマウスの座標まで取得していますね。
あまりよろしくないので、一応クランプしておきます。
private void Update() { Vector3 touchScreenPosition = Input.mousePosition; touchScreenPosition.x = Mathf.Clamp( touchScreenPosition.x, 0.0f, Screen.width ); touchScreenPosition.y = Mathf.Clamp( touchScreenPosition.y, 0.0f, Screen.height ); // 10.0fに深い意味は無い。画面に表示したいので適当な値を入れてカメラから離そうとしているだけ. touchScreenPosition.z = 10.0f; Camera gameCamera = Camera.main; Vector3 touchWorldPosition = gameCamera.ScreenToWorldPoint( touchScreenPosition ); m_object.transform.position = touchWorldPosition; }
画面外にはみでなくなりました。
さて、マウス座標のある場所にオブジェクトを設置することができました。
ただ、実際ゲーム上でこの機能を使う場合は、床オブジェクトの上に設置するように作りたいのです。
少し改良を加えていきましょう。
では、床オブジェクトを作成します。
この床の上に球オブジェクトが乗っている状態にしたいです。
無論、マウスポインターのある位置に。
どうしよう……。
そうだ、レイを使おう!
カメラには、ScreenToWorldPoint()
だけでなく、レイを飛ばすScreenPointToRay()
もあります。
便利な世の中になったものです。
private void Update() { Vector2 touchScreenPosition = Input.mousePosition; touchScreenPosition.x = Mathf.Clamp( touchScreenPosition.x, 0.0f, Screen.width ); touchScreenPosition.y = Mathf.Clamp( touchScreenPosition.y, 0.0f, Screen.height ); Camera gameCamera = Camera.main; Ray touchPointToRay = gameCamera.ScreenPointToRay( touchScreenPosition ); // デバッグ機能を利用して、スクリーンビューでレイが出ているか見てみよう。 Debug.DrawRay( touchPointToRay.origin, touchPointToRay.direction * 1000.0f ); }
すごい、かっこいい!
まあ実際のゲーム画面ではレイは見えませんけどね。
このレイと当たり判定を取ります。
まず、床オブジェクトにコリジョンが付いていることを確認しましょう。
コリジョンが付いているようなので、Physics.Raycast()
を使って当たり判定を取ります。
private void Update() { Vector2 touchScreenPosition = Input.mousePosition; touchScreenPosition.x = Mathf.Clamp( touchScreenPosition.x, 0.0f, Screen.width ); touchScreenPosition.y = Mathf.Clamp( touchScreenPosition.y, 0.0f, Screen.height ); Camera gameCamera = Camera.main; Ray touchPointToRay = gameCamera.ScreenPointToRay( touchScreenPosition ); RaycastHit hitInfo = new RaycastHit(); if( Physics.Raycast( touchPointToRay, out hitInfo ) ) { m_object.transform.position = hitInfo.point; } // デバッグ機能を利用して、スクリーンビューでレイが出ているか見てみよう。 Debug.DrawRay( touchPointToRay.origin, touchPointToRay.direction * 1000.0f ); }
思ってたのと違う!
なにこれ、何が起きたの。
球のオブジェクトのプロパティを見てみる。
お前もコリジョンもっとるのかい!
球もCollider
を持っているのでそれにレイが当たってしまうようですね。
Collider
を外してもいいんですが、レイヤーを変えることで対応しましょう。
Physics.Raycast()
に当たるレイヤーを指定することが出来ます。
ただ、わざわざレイヤーを追加するもの面倒くさいです。
そんな場合は、Unity側でレイとの当たり判定を行わないレイヤーが既に用意されています。
それがIgone Raycastです。
これを使いましょう。
上手くいったようですね。
これで今回のゲームジャム用に考えていたアイディアが生かせるゲームが作れそうです。