移動の基本

概要

ゲームでは様々なオブジェクトが描画されていますがこれらのオブジェクトは
その場にとどまっていることは少なく、プレイヤーや敵などは様々な方向に移動します。
移動処理の結論を書くと移動とは「現在の座標と違う座標に変更する」ことです。

	gmpg_0203
	gmpg_0203

座標を変更することで、私たちが見ている画面上ではオブジェクトが1フレーム前とは
違う位置に描画されるので私たちにはオブジェクト移動しているように見えます。
移動処理の方法には「別の座標を代入する」「ベクトルを加算する」等があります。

前提知識

このページでは以下の項目の知識があることを前提に書いていますので確認をお願いします。

	座標
	描画の基本

サンプル

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

開発環境
VSのバージョン DirectXのバージョン
VisualStudio 2017 DirectX9(June 2010)

別の座標を代入する

別の座標を代入する方法は今の座標とは全く関係のない新しい座標を代入することで
オブジェクトを移動させる方法です。
この方法はオブジェクトを画面上に出現させる時(座標の初期化)やワープなどで使用します。

	gmpg_0205
	gmpg_0205

以下のコードは量を減らした内容で処理を書いてます。

struct ObjectData
{
	int m_TextureId;
	float m_PosX;
	float m_PosY;
};

void main()
{
	ObjectData player =
	{
		-1,
		200,
		400,
	};

	srand((unsinged)time(NULL));
	int timer;

	while (true)
	{
		timer++;

		if (timer > 60)
		{
			timer = 0;
			// 代入による移動
			player.m_PosX = rand() % WINDOW_WIDTH;
			player.m_PosY = rand() % WINDOW_HEIGHT;
		}

		Draw(player.m_TextureId, player.m_PosX, player.m_PosY);
	}
}

ベクトルを加算する

移動量を加算する方法は今の座標に対してベクトルを加算して移動処理を行います。
一般的な移動処理はこちらを指します。
ゲームの仕様次第でオブジェクトは様々な方向に移動することになります。

例えばシューティングゲームの自機の移動なら
「上下左右キーを押すことで、押したキーの方向に自機が移動する」
アクションゲームの敵なら「プレイヤーの方向に向かっていく」などです。

これらの移動処理はベクトルという移動量を用意して、
その移動量を今の位置に加算して描画位置を更新しています。

ベクトル

ベクトルには「方向と大きさ」が含まれています。
PLの作成してくれる移動関係の仕様にもその情報が含まれているはずです。
	gmpg_0204
	gmpg_0204

以下の例はプレイヤーの移動に関する挙動の仕様です。

ユーザーが押した十字キーの方向にスピードの分だけ移動する
この仕様のベクトルの情報を置き換えると以下のようになります。
ベクトル情報への置き換え
仕様情報 ベクトル情報
押された十字キーの方向 方向
スピード 大きさ

ベクトル意識の有無

ベクトルは仕様によってきちんと意識して使わないとPLの意図した挙動とは
異なった動作になります。

意識しなくていいケース

ベクトルを意識しなくていいケースはオブジェクトが斜め移動しない場合です。

例えば、敵の挙動で「一定の間隔で左右にスピードの値の分だけ移動する」という
仕様だった場合、X軸に対してのみ移動する仕様ということがわかります。
当然、Y軸やZ軸に対して移動することがないのでベクトルを意識することなく
X軸に対してスピードを加算、減算しても問題なく動作します。

意識をする必要があるケース

ベクトルを意識するケースはオブジェクトが斜め移動をする場合です。

先ほどのベクトルの例でも使ったプレイヤーの挙動では、
上下左右キーをユーザーが押した方向にスピードの分だけ移動する仕様になっています。
この時、上下キーと左右キーが同時に押された場合は斜め方向に移動するとしたら、
ベクトルを意識する必要があります。

プレイヤーの移動速度を2として、ベクトルを意識せずに実装した場合の
コードは以下のようになります。

float speed = 2.0f;

if (GetKey(UP_ARROW) == true)
{
	player.m_PosY -= speed;
}
else if (GetKey(DOWN_ARROW) == true)
{
	player.m_PosY += speed;
}

if (GetKey(LEFT_ARROW) == true)
{
	player.m_PosX -= speed;
}
else if(GetKey(RIGHT_ARROW) == true)
{
	player.m_PosX += speed;
}

			
このコードで下キーと右キーを同時に押した場合、プレイヤーは斜めに移動するので
方向としては問題ありませんが大きさに問題が生じます。
斜め移動した場合のベクトルの大きさ(移動量)は以下のようになります。

	gmpg_0201
	gmpg_0201

√のままだとわかりづらいので、√をはずします。

	gmpg_0202
	gmpg_0202

大きさの値が仕様書の移動量(スピード)の2という値を超えており、
予定よりも大きく移動してしまいます。

	gmpg_0207
	gmpg_0207

指定された移動量にするには単位ベクトルにして方向のみを抽出し、
その情報に大きさ(移動量)を反映させます。

float speed = 2.0f;
float vec_x = 0.0f;
float vec_y = 0.0f;
float length = 0.0f;

if (GetKey(UP_KEY) == true)
{
	vec_y = -speed;
}
else if (GetKey(DOWN_KEY) == true)
{
	vec_y = speed;
}

if (GetKey(LEFT_KEY) == true)
{
	vec_x = -speed;
}
else if(GetKey(RIGHT_KEY) == true)
{
	vec_x = speed;
}

if (vec_x != 0.0f || vec_y != 0.0f)
{
	length = sqrt(vec_x * vec_x + vec_y * vec_y);

	float normal_x = vec_x / length;
	float normal_y = vec_y / length;

	normal_x *= speed;
	normal_y *= speed;

	player.m_PosX += normal_x;
	player.m_PosY += normal_y;
}

				
これで、PLが望んだ移動量とほぼ同じ値で移動できます。

ベクトルを意識しないケースの意識しない理由

ベクトルを意識しないケースはX、Y、Z軸の1軸に対してのみ向きを指定します。
このベクトルは単位ベクトル * 移動量の計算で出たベクトルと同じ結果になります。
なので、わざわざベクトルの長さを出して、それを単位ベクトル変換して、
移動量をかけるという方法をとらなくてもよくなっています。