移動の基本

■サンプル

●環境
	VisualStudio 2017

	DirectX9(June 2010)

●リンク
	サンプル

■前提知識

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

	座標
	描画の基本

■概要

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

	gmpg_0203

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

■別の座標を代入する

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

	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

	・例
		プレイヤーの挙動仕様
			ユーザーが押した十字キーの方向にスピードの分だけ移動する

		上の仕様をベクトルの情報に置き換えると以下のようになります。
				
			押された十字キーの方向 => 方向
			スピード => 大きさ

●ベクトル意識の有無
	ベクトルは仕様によってきちんと意識して使わないと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_0202

			大きさの値が仕様書の移動量(スピード)の2という値を超えており、
			予定よりも大きく移動してしまいます。
			
				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軸に対してのみ向きを指定します。
		このベクトルは単位ベクトル * 移動量の計算で出たベクトルと同じ結果になります。
		なので、わざわざベクトルの長さを出して、それを単位ベクトルにし、
		単位ベクトルに移動量をかけるという方法をとらなくてもよくなっています。