2D版スクリーン座標変換

概要

最終更新日:2020/02/20

2Dゲームでワールド座標をスクリーン座標に変換する方法を書いた記事です。
この記事は以下の内容を知りたい方に向けて書いています。
  • アクションゲームで広いステージを作成する
  • ステージ上の特定の場所を表示したい

サンプル

サンプルはここからダウンロードでき、環境については以下の内容となっています。

開発環境
VSのバージョン VisualStudio 2019
DirectXのバージョン DirectX9
説明 マウスを左クリックすることで画面がスクロールします。
変換処理はCamera.cppに書いてあります。



スクリーン座標変換とは

スクリーン座標変換とは別の座標軸をスクリーン座標軸に変換することです。
2Dでは広大なステージの中でプレイヤーのいる位置を表示する場合に使用します。
スクリーン座標に変換される座標軸はワールド座標です。

ワールド座標

ワールド座標とは各ステージの世界のそのものを表す座標の事です。
2Dではステージの左上を原点とすることが多く、ギミックや敵キャラなどは
このワールド座標上でどの位置に置くかを考えます。

2d_game_0015

スクリーン座標

スクリーン座標とはウィンドウの左上を原点とした座標の事です。
画面にキャラクターなどを表示する時はこのスクリーン座標で位置を考えます。

2d_game_0016

座標変換の必要性

ワールド座標の内容をそのままスクリーン座標として使用すると意図した内容が
画面描画されないことがあります。
なぜかというと、ワールド座標とスクリーン座標は別の座標だからです。

2d_game_0017

上の絵で赤枠がワールド座標、緑の枠がスクリーン座標です。
どちらも原点からの範囲を囲っており、スクリーン座標の枠は
ワールド座標から見て左上にあります。
この状態で表示される画面はワールド座標上の左上の部分なので、
ロボットなどのキャラクターは描画されません。

この問題を解決するためにワールド座標上で表示したい範囲を指定して
スクリーン座標に変換します。

変換で使用する情報

変換方法は引き算を行うだけなので単純ですが、
計算の中で用意する情報がいくつかあるので、そちらの紹介をします。
  1. 変換されるオブジェクトの座標
  2. カメラ座標
  3. スクリーンのサイズ

変換されるオブジェクトの座標

変換はワールド座標上にあるオブジェクトは全て変換の対象となる為、
各オブジェクトのワールド座標軸の座標が必要です。

カメラ座標

カメラ座標は画面に表示する範囲の位置を示す座標で表示範囲の中心とし、
ワールド座標軸の値で用意します。
ワールド座標上にカメラを配置して、そのカメラに写っている内容が
画面に表示されると考えてください。

スクリーンのサイズ

カメラ座標は表示範囲の中心として扱うことが多いので、
中心から表示範囲の左上(原点)の座標を算出するためにウィンドウのサイズが必要です。

変換方法

変換は以下の流れで行います。
  1. ワールド座標上のスクリーン座標の原点 = カメラ座標 - (ウィンドウサイズ / 2)
  2. 変換座標 = オブジェクトの座標 - スクリーン座標の原点
まずはカメラ座標をスクリーン座標の原点に変換します。
カメラ座標は表示範囲の中心として扱っていますので、
このままの座標で使用すると最終変換後の座標がずれてしまいます。
その為、カメラ座標からウィンドウサイズの半分の値を引いて、
中心から左上の座標に変換し、その位置をスクリーン座標の原点とします。

2d_game_0021
    カメラ座標 - (ウィンドウサイズ / 2)
    (640, 480) - ((640 / 2), (480 / 2)) => (320, 240)
原点の算出が完了したら、オブジェクトの座標からスクリーン座標の原点を引いて、
オブジェクトの座標をワールドからスクリーンに変換します。

2d_game_0022
    オブジェクト座標 - スクリーン座標原点(ワールド座標上)
    (500, 400) - (320, 240) => (180, 160)
(x, y)=(180, 160)がワールド座標からスクリーン座標に変換した座標なので、
この座標を使用してオブジェクトを描画します。

変換の実装

カメラ座標がないとワールド座標上で表示したい範囲の指定ができないので、
まずは、カメラ座標を保存する変数を用意します。

// カメラ
static Vec2 g_CameraPos { FIELD_WIDHT / 2.0f, FIELD_HEIGHT / 2.0f };

次は変換関数を用意して「変換方法」の項目の手順を実装していきます。
最初はカメラ座標からスクリーンで表示する範囲の左上の座標を算出します。

// カメラ座標からスクリーン座標の原点に変換する
Vec2 screen_origin_position =
{
	g_CameraPos.X - GetWindowSize().Width / 2.0f,
	g_CameraPos.Y - GetWindowSize().Height / 2.0f
};

次にオブジェクト座標とスクリーン座標の原点から
スクリーン座標上の位置に変換します。

// ワールド座標からスクリーン座標に変換する
Vec2 screen_position = Vec2(pos.X - screen_origin_position.X, pos.Y - screen_origin_position.Y);

この座標を描画関数に指定したら意図した位置に
オブジェクトを描画することができます。

// 敵描画
Vec2 world_pos = Vec2(500.0f, 400.f);
// ワールド座標をスクリーン座標に変換
Vec2 screen_pos = ConvertPositionWorldToScreen(world_pos);
DrawTexture(screen_pos.X, screen_pos.Y, GetTexture(TextureList::Enemy));

変換はワールド座標上に存在する全オブジェクト一つ一つに対して行います。
UI等の画面に常に描画されているオブジェクトはワールド座標上に配置せず、
最初からスクリーン座標に配置するので、変換する必要はありません。