【初心者Unity】ビューポート座標の使い方(画面の端を取得)
▶
【Unity】3Dアクションゲームを作ろう!#7 ステージの作成(Skybox・落下判定)
▶
【Unity】3Dアクションゲームを作ろう!#8 ステージの作成(スイッチ・扉)
▶
【Unity】3Dアクションゲームを作ろう!#9 プレイヤーのHP管理
▶
【初心者Unity】JsonUtilityクラスでJSONを扱う方法
▶
【初心者Unity】スクリプトからコンポーネントを追加する方法
1.はじめに
本記事では、Unityが提供する座標系の一つであるビューポート座標について解説します。
ビューポート座標を使用することで、以下のことが実現できます。
- シューティングゲーム等でプレイヤーが画面外に移動できないようにする
- 右上のアイテム欄に取得したアイテムを飛ばす
- 画面の真ん中にゲームオブジェクトを出現させる etc…
…こんなのビューポート座標なんて使わなくても実装できるよ、と思いませんでしたか?
確かにTransformのPositionを上手く設定すれば実現できそうですが、それは画面の大きさが固定されている場合のみです。
ビューポート座標の強みは、画面のサイズが変化する場合でもこれらの機能を実現できる、というところにあります。
本記事は、前半でビューポート座標の解説を行い、後半でビューポート座標を使った実装例を紹介します。
是非一緒に手を動かして確認してみてください!
2.ビューポート座標とは
ビューポート座標とはゲーム画面におけるものの位置を比率で表した座標系です。
ゲーム画面の左下を基準点とし、ゲームの縦幅、横幅をそれぞれ1として表現します。
例えば、ゲーム画面の左下は(0, 0)、右上は(1, 1)と表されます。
上でも書きましたが、ビューポート座標の強みは画面の大きさの影響を受けないことにあります。
例えば、以下のようにCubeの位置をTransform > Position で設定しただけでは、ゲームビューの大きさを変えた際に相対的な位置関係が崩れ、画面外に飛び出してしまったりします。
これに対して、ビューポート座標はゲーム画面に対する比率で位置を指定できるので、ゲームビューの大きさを変えたとしても相対的な位置を維持することができます。
そのため、PCやスマートフォンなど様々なサイズの画面でプレイする場合でも、共通の設定で対応が可能になる訳です。
3.ビューポート座標の実装例
それでは実際に、ビューポート座標を使った機能の実装を行ってみましょう。
今回実装するのは、「Cubeが画面外に移動できないようにする」機能です。
まずは、左右の矢印キーでCubeを動かせるようにしましょう。
Cubeに以下のようなスクリプトをアタッチしました。
➡【初心者Unity】矢印キーを使ってオブジェクトを操作しよう!(GetAxis)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Cube : MonoBehaviour { Vector3 position; float speed; // Cubeが動くスピード void Start() { position = new Vector3(0,0,0); speed = 0.3f; } void Update() { // 左右の矢印キーで動きます position.x += Input.GetAxisRaw("Horizontal") * speed; transform.position = position; } }
これでCubeが動くようにはなりましたが、当然画面の外にも移動できてしまいます。
さて、どうしたら移動範囲を画面内に制限できるでしょうか。
ここで登場するのがビューポート座標です。
ビューポート座標の世界においては「画面内」の座標は縦横問わず全て0から1の範囲で表されます。
この性質を利用して、以下のようなロジックを考えてみました。
画面外に移動できないようにする ↓ 画面外に出そうな場合は移動しないようにする ↓ 画面外に出ないときだけ移動する ↓ 移動後のビューポート座標が0から1の範囲のときだけ移動する ↓ if(移動後のビューポート座標が0から1) { // 移動する }
では実際にこれをスクリプトに書き起こしてみましょう、というところで、
まだビューポート座標をスクリプトで扱う方法について解説していませんでしたね。
ビューポート座標はワールド座標と相互に変換することで使用します。
// Cameraクラスのメソッド // Camera.mainと書いていますが、メインカメラでなくても良い // ワールド座標⇒ビューポート座標 Camera.main.WorldToViewportPoint(ワールド座標); // ビューポート座標⇒ワールド座標 Camera.main.ViewportToWorldPoint(ビューポート座標);
ということで、スクリプトは以下のようになります。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Cube : MonoBehaviour { Vector3 position; // 現在の位置 Vector3 nextPosition; // 移動後の位置 float viewX; // ビューポート座標のxの値 float speed; // Cubeが動くスピード void Start() { position = new Vector3(0, 0, 0); nextPosition = position; speed = 0.3f; } void Update() { // 左右の矢印キーで動きます nextPosition.x = position.x + Input.GetAxisRaw("Horizontal") * speed; // 移動後のビューポート座標のxの値を取得 viewX = Camera.main.WorldToViewportPoint(nextPosition).x; // もし移動後のビューポート座標が0から1の範囲ならば if (0 <= viewX && viewX <= 1) { // 移動する transform.position = nextPosition; // positionにnextPositionを代入する(次のUpdateで使う) position = nextPosition; } } }
ゲームを再生して動かしてみましょう。
画面の端でピタッと止まりましたか?
4.その他実装アイデア
冒頭で紹介した使用例について、簡単に解説します。
右上のアイテム欄に取得したアイテムを飛ばす
アイテム欄のようなものは基本的にuGUIを使って実装するかと思いますが、uGUIの座標系はビューポート座標(あるいはスクリーン座標)です。
RectTransformのAnchorは画面内の比率で指定していますよね。
通常のゲームオブジェクトと異なる座標系であるためそのまま連携することはできませんが、ワールド座標と相互に変換することで、例えば取得したコインを右上のコインUIの位置まで飛ばすというような演出も可能になります。
画面の真ん中にゲームオブジェクトを出現させる
これも↑と同じようなアイデアですが、画面の真ん中の座標を指定するのって単純なようで難しいですよね。
例えば横スクロールゲームの場合、かなり横長なステージになりますが、どの座標が画面の真ん中を表すのかは刻一刻と変わっていきますよね。
そんな時にビューポート座標を使えば、(0.5, 0.5)と単純に表せる訳です。
このように、ゲームオブジェクトをUIのように扱うのがビューポート座標の主な使い道になるかと思います。
5.(おまけ)本当にゲーム画面?
はじめに、
“ビューポート座標とはゲーム画面におけるものの位置を比率で表した座標系です”
というような説明をしましたが、実はこの説明は厳密ではありません。
上の実装例で触れましたが、ビューポート座標を扱うメソッドはCameraクラスのメソッドです。
そこからわかる通り、ビューポート座標はカメラが映す範囲の比率で表す座標系、というのが正確な説明になります。
試しにカメラを2台設置し、画面を2分割してみました(左側がMain Camera)。
➡【初心者Unity】複数のカメラの使い方(複数カメラ表示・カメラ切り替え)
この時、CubeはMain Cameraが映す範囲内のみを移動可能になります。
もっとも、画面の端を取得したい場面は、1台のカメラがゲーム画面全体を映しているケースがほとんどでしょうから、実質ゲーム画面の座標系だと考えても良いかも知れませんね。
6.おわりに
ビューポート座標の使い方について簡単に解説しました。
繰り返しになりますが、ビューポート座標は画面の大きさによらず相対的な位置関係を定義できる非常に便利なツールです。
様々なサイズのディスプレイでゲームを操作するのが当たり前の時代ですから、是非とも使いこなせるようになってください!