3Dでのカメラ操作について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
sadora3
記事: 175
登録日時: 11年前

3Dでのカメラ操作について

#1

投稿記事 by sadora3 » 9年前

プレイヤーが前に進むとカメラも前に進み、カメラは常にプレイヤーを注視する感じのカメラ操作がしたいです。

言語:C
ライブラリ:DXライブラリ
コンパイラ:Microsoft Visual Studio 2010
OS:Windows7 ←(書くの忘れてました。すみません。追記しました)

現状無理やりそれっぽくしてみたのですが、正しいやり方はどうやるのでしょうか?

コード:

#include "DxLib.h"
#include <math.h>

#define CAMERA_SPEED 10.0f

VECTOR PlayerPos = VGet(1000.0f, 0.0f, 1000.0f);

typedef struct{
	VECTOR Pos;
	float RotV, RotH;
}CameraStatus;

CameraStatus Camera = {VGet(1600.0f, 500.0f, 1600.0f), 30.0f*DX_PI_F/180.0f, -133.0f*DX_PI_F/180.0f};

void MoveCamera();
void DrawSp();
void Information();

int ProcessLoop(){
	if(ProcessMessage() != 0){	return -1;	}
	if(ClearDrawScreen() != 0){	return -1;	}
	return 0;
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
	ChangeWindowMode(TRUE);
	SetGraphMode(640, 480, 16);
	SetBackgroundColor(255,255,255);
	if(DxLib_Init() == -1 || SetDrawScreen(DX_SCREEN_BACK) != 0){	return -1;	}

	while(ProcessLoop() == 0 && !CheckHitKey(KEY_INPUT_ESCAPE)){
		MoveCamera();
		DrawSp();
		Information();
		DrawSphere3D(PlayerPos, 30, 8, GetColor(0,255,255), GetColor(255,255,255), TRUE);
		ScreenFlip();
	}

	DxLib_End();
	return 0;
}

void MoveCamera(){
	VECTOR Move;

	if(CheckHitKey(KEY_INPUT_W) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,1.0f), MGetRotY(Camera.RotH)) ,CAMERA_SPEED);
		Camera.Pos = VAdd(Camera.Pos, Move);
		PlayerPos = VAdd(PlayerPos, Move);
	}
	if(CheckHitKey(KEY_INPUT_S) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,-1.0f), MGetRotY(Camera.RotH)) ,CAMERA_SPEED);
		Camera.Pos = VAdd(Camera.Pos, Move);
		PlayerPos = VAdd(PlayerPos, Move);
	}
	if(CheckHitKey(KEY_INPUT_D) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,-1.0f), MGetRotY(Camera.RotH)) ,CAMERA_SPEED);
		Move = VCross(Move,VGet(0.0f,1.0f,0.0f));
		Camera.Pos = VAdd(Camera.Pos, Move);
		PlayerPos = VAdd(PlayerPos, Move);
	}
	if(CheckHitKey(KEY_INPUT_A) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,1.0f), MGetRotY(Camera.RotH)) ,CAMERA_SPEED);
		Move = VCross(Move,VGet(0.0f,1.0f,0.0f));
		Camera.Pos = VAdd(Camera.Pos, Move);
		PlayerPos = VAdd(PlayerPos, Move);
	}

	if(CheckHitKey(KEY_INPUT_LEFT) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,1.0f), MGetRotY(Camera.RotH)) ,CAMERA_SPEED * 2);
		Move = VCross(Move,VGet(0.0f,1.0f,0.0f));
		Camera.Pos = VAdd(Camera.Pos, Move);
		Camera.RotH += 1.5f * DX_PI_F / 180;
	}
	if(CheckHitKey(KEY_INPUT_RIGHT) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,-1.0f), MGetRotY(Camera.RotH)) ,CAMERA_SPEED * 2);
		Move = VCross(Move,VGet(0.0f,1.0f,0.0f));
		Camera.Pos = VAdd(Camera.Pos, Move);
		Camera.RotH -= 1.5f * DX_PI_F / 180;
	}
	if(CheckHitKey(KEY_INPUT_UP) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,1.0f), MMult(MGetRotX(Camera.RotV - (90.0f * DX_PI_F / 180.0f)), MGetRotY(Camera.RotH))) ,CAMERA_SPEED * 2);
		Camera.Pos = VAdd(Camera.Pos, Move);
		Camera.RotV += 1.5f * DX_PI_F / 180;
	}
	if(CheckHitKey(KEY_INPUT_DOWN) != 0){
		Move = VScale(VTransform(VGet(0.0f,0.0f,1.0f), MMult(MGetRotX(Camera.RotV + (90.0f * DX_PI_F / 180.0f)), MGetRotY(Camera.RotH))) ,CAMERA_SPEED * 2);
		Camera.Pos = VAdd(Camera.Pos, Move);
		Camera.RotV -= 1.5f * DX_PI_F / 180;
	}
	
	SetCameraPositionAndAngle(Camera.Pos, Camera.RotV , Camera.RotH, 0.0f);
}

void DrawSp(){
	for(float x = 80.0f; x < 5000.0f; x += 80.0f){
		DrawSphere3D(VGet(x,0,0), 30, 8, GetColor(255,0,0), GetColor(255,255,255), FALSE);
	}
	for(float y = 80.0f; y < 5000; y += 80.0f){
		DrawSphere3D(VGet(0,y,0), 30, 8, GetColor(0,255,0), GetColor(255,255,255), FALSE);
	}
	for(float z = 80.0f; z < 5000; z += 80.0f){
		DrawSphere3D(VGet(0,0,z), 30, 8, GetColor(0,0,255), GetColor(255,255,255), FALSE);
	}
}

void Information(){
	DrawFormatString(0, 0, GetColor(0,0,0), "WASDキーで移動");
	DrawFormatString(0, 20, GetColor(0,0,0), "カーソルキーで旋回");
	DrawFormatString(0, 420, GetColor(0,0,0), "水平角度:%f 垂直角度:%f", Camera.RotH / DX_PI_F * 180, Camera.RotV / DX_PI_F * 180);
	DrawFormatString(0, 440, GetColor(0,0,0), "プレイヤー位置(X,Y,Z)=(%.0f,%.0f,%.0f)", PlayerPos.x, PlayerPos.y, PlayerPos.z);
	DrawFormatString(0, 460, GetColor(0,0,0), "カメラ位置(X,Y,Z)=(%.0f,%.0f,%.0f)", Camera.Pos.x, Camera.Pos.y, Camera.Pos.z);

}
あと、DXライブラリ様の3D関数のリファレンスページのMMult関数の解説で少し違和感を覚えた文章がありました。
・一行目
×:引数 In1 と In2 を乗算した行列を結果の行列を戻り値として返す関数です
○:引数 In1 と In2 を乗算した結果の行列を戻り値として返す関数です
・三行目
×:例:拡大行列と平行移動行列と乗算した場合
○:例:拡大行列と平行移動行列を乗算した場合
じゃないでしょうか?
これ直して欲しいのですが、どこへ報告したらいいのでしょうか?

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: 3Dでのカメラ操作について

#2

投稿記事 by h2so5 » 9年前

DXライブラリ管理者へのコンタクトは http://hpcgi2.nifty.com/natupaji/aska.cgi か http://hpcgi2.nifty.com/natupaji/bbs/patio.cgi

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 3Dでのカメラ操作について

#3

投稿記事 by softya(ソフト屋) » 9年前

カメラ位置をプレーヤーからの距離とベクトルで管理して、毎回カメラ位置を計算する様にしないと誤差の累積で、だんだんずれていく恐れがあります。
それにプレーヤー中心にカメラを回転したりズームさせる場合にも不便じゃないでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

sadora3
記事: 175
登録日時: 11年前

Re: 3Dでのカメラ操作について

#4

投稿記事 by sadora3 » 9年前

>>h2so5さん
ありがとうございます。
早速いってきました。

>>ソフト屋さん
「カメラ位置をプレーヤーからの距離とベクトルで管理して、」とありますが、距離とベクトルって同じものかと思っていたのですが、違うのでしょうか?
また、ベクトルはVECTORで定義された変数(変数というか構造体?)ですよね。じゃあ距離は一体なんでしょうか。

あと、「毎回カメラ位置を計算する様にしないと」とありますが、一体計算はどのようにすればいいのでしょうか・・・。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 3Dでのカメラ操作について

#5

投稿記事 by softya(ソフト屋) » 9年前

あぁ、すいません。ボケたこと書いてますね。
プレーヤーからの距離と方向(角度)でカメラの位置を管理してくださいって事です。
ベクトルは距離+方向です。失礼しました。方向ベクトルって書いたつもりでしたが、それでも分かりづらいです。

角度で単位ベクトルを回転をさせて、それに距離を乗算して貰うとカメラのプレーヤーからの相対座標ベクトルが出ます。
プレーヤーの座標に相対座標ベクトルを加算すれば、カメラの座標が出ます。
これをSetCameraPositionAndTarget_UpVecY()でカメラ座標と、プレーヤー座標を設定するだけです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

sadora3
記事: 175
登録日時: 11年前

Re: 3Dでのカメラ操作について

#6

投稿記事 by sadora3 » 9年前

出来ました!ありがとうございます。
返信に時間がかかって申し訳ありませんでした。色々忙しかったもので・・・。
ソフト屋さんにはいつもお世話になっています。本当に感謝しています。
また何かあれば、よろしくお願いします。

コード:

#include "DxLib.h"

#define PLAYER_MOVE_SPEED 15.0f					//プレイヤーの移動速度
#define RELATIVE_POSITION 800.0f				//プレイヤーからのカメラの位置
#define ROTATE_SPEED (2.0f * DX_PI_F / 180.0f)	//カメラの旋回速度

typedef struct{
	VECTOR Pos;
}PlayerStatus;

typedef struct{
	VECTOR Pos;
	float RotV, RotH;
}CameraStatus;

PlayerStatus Player = {VGet(0.0f, 0.0f, 0.0f)};
CameraStatus Camera = {VGet(0.0f, 0.0f, 0.0f), -20.0f * DX_PI_F / 180.0f, 0.0f};

void MovePlayer();
void RotateCamera();
void DrawSp();
void Information();

int ProcessLoop(){
	if(ProcessMessage() != 0){	return -1;	}
	if(ClearDrawScreen() != 0){	return -1;	}
	return 0;
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
	ChangeWindowMode(TRUE);
	SetGraphMode(640, 480, 16);
	SetBackgroundColor(255,255,255);
	if(DxLib_Init() == -1 || SetDrawScreen(DX_SCREEN_BACK) != 0){	return -1;	}

	while(ProcessLoop() == 0 && !CheckHitKey(KEY_INPUT_ESCAPE)){
		MovePlayer();
		RotateCamera();
		DrawSp();
		Information();
		ScreenFlip();
	}

	DxLib_End();
	return 0;
}

void MovePlayer(){
	DrawSphere3D(Player.Pos, 30, 8, GetColor(0,255,255), GetColor(255,255,255), TRUE);

	if(CheckHitKey(KEY_INPUT_S) != 0){
		VECTOR Move = VScale(VTransform(VGet(0.0f,0.0f,1.0f), MGetRotY(Camera.RotH)) ,PLAYER_MOVE_SPEED);
		Camera.Pos = VAdd(Camera.Pos, Move);
		Player.Pos = VAdd(Player.Pos, Move);
	}
	if(CheckHitKey(KEY_INPUT_W) != 0){
		VECTOR Move = VScale(VTransform(VGet(0.0f,0.0f,-1.0f), MGetRotY(Camera.RotH)) ,PLAYER_MOVE_SPEED);
		Camera.Pos = VAdd(Camera.Pos, Move);
		Player.Pos = VAdd(Player.Pos, Move);
	}
	if(CheckHitKey(KEY_INPUT_A) != 0){
		VECTOR Move = VScale(VTransform(VGet(0.0f,0.0f,-1.0f), MGetRotY(Camera.RotH)) ,PLAYER_MOVE_SPEED);
		Move = VCross(Move,VGet(0.0f,1.0f,0.0f));
		Camera.Pos = VAdd(Camera.Pos, Move);
		Player.Pos = VAdd(Player.Pos, Move);
	}
	if(CheckHitKey(KEY_INPUT_D) != 0){
		VECTOR Move = VScale(VTransform(VGet(0.0f,0.0f,1.0f), MGetRotY(Camera.RotH)) ,PLAYER_MOVE_SPEED);
		Move = VCross(Move,VGet(0.0f,1.0f,0.0f));
		Camera.Pos = VAdd(Camera.Pos, Move);
		Player.Pos = VAdd(Player.Pos, Move);
	}
}

void RotateCamera(){
	if(CheckHitKey(KEY_INPUT_RIGHT) != 0){
		Camera.RotH += 1.0f * DX_PI_F / 180;
	}
	if(CheckHitKey(KEY_INPUT_LEFT) != 0){
		Camera.RotH -= 1.0f * DX_PI_F / 180;
	}
	if(CheckHitKey(KEY_INPUT_DOWN) != 0 && Camera.RotV > -45.0f * DX_PI_F / 180){
		Camera.RotV -= 1.0f * DX_PI_F / 180;
	}
	if(CheckHitKey(KEY_INPUT_UP) != 0 && Camera.RotV < 0.0f * DX_PI_F / 180){
		Camera.RotV += 1.0f * DX_PI_F / 180;
	}

	Camera.Pos = VAdd(VScale(VTransform(VGet(0.0f, 0.0f, 1.0f), MMult(MGetRotX(Camera.RotV), MGetRotY(Camera.RotH))), RELATIVE_POSITION), Player.Pos);

	if(Camera.RotH > 360 * DX_PI_F / 180){
		Camera.RotH -= 360 * DX_PI_F / 180;
	}
	else if(Camera.RotH < 0.0f){
		Camera.RotH += 360 * DX_PI_F / 180;
	}

	SetCameraPositionAndTarget_UpVecY(Camera.Pos, Player.Pos);
}

void DrawSp(){
	for(float x = 80.0f; x < 5000.0f; x += 80.0f){
		DrawSphere3D(VGet(x,0,0), 30, 8, GetColor(255,0,0), GetColor(255,255,255), FALSE);
	}
	for(float y = 80.0f; y < 5000; y += 80.0f){
		DrawSphere3D(VGet(0,y,0), 30, 8, GetColor(0,255,0), GetColor(255,255,255), FALSE);
	}
	for(float z = 80.0f; z < 5000; z += 80.0f){
		DrawSphere3D(VGet(0,0,z), 30, 8, GetColor(0,0,255), GetColor(255,255,255), FALSE);
	}
}

void Information(){
	DrawFormatString(0, 0, GetColor(0,0,0), "WASDキーで移動");
	DrawFormatString(0, 20, GetColor(0,0,0), "カーソルキーで旋回");
	DrawFormatString(0, 40, GetColor(0,0,0), "ESCキーで終了");
	DrawFormatString(0, 420, GetColor(0,0,0), "水平角度:%f 垂直角度:%f", Camera.RotH / DX_PI_F * 180, Camera.RotV / DX_PI_F * 180);
	DrawFormatString(0, 440, GetColor(0,0,0), "プレイヤー位置(X,Y,Z)=(%.0f,%.0f,%.0f)", Player.Pos.x, Player.Pos.y, Player.Pos.z);
	DrawFormatString(0, 460, GetColor(0,0,0), "カメラ位置(X,Y,Z)=(%.0f,%.0f,%.0f)", Camera.Pos.x, Camera.Pos.y, Camera.Pos.z);
}


閉鎖

“C言語何でも質問掲示板” へ戻る