C言語、アクションゲームのスクロールの端での処理

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
山田

C言語、アクションゲームのスクロールの端での処理

#1

投稿記事 by 山田 » 9年前

始めまして、山田と申します。

友人とアクションゲームを作ろうということになりまして、僕はプログラミング担当になりました。
週末に少しずつ作っていき、マップの表示、キャラクターの表示と操作ができました。
次にマップのスクロールを作ろうと思っているのですが、うまくいかずここでトピックを立てさせていただきました。

スクロールは右方向で、
・通常時はキャラクターを中央に配置して移動
・マップの端につくと、スクロールがとまりキャラクターだけが移動
というようなものを理想としてますが、どのようにしたらいいかさっぱりわからず困っています。

現時点でのコードを載せます。アドバイスいただけるとありがたいです。

C言語は1ヶ月ほど前から本(猫でもわかるC言語プログラミング)を購入し、基本的な事は理解したつもりです。
わからないことがでてきたら本やインターネットを使い調べるようにしています。
開発環境は、XPSP3、BCC Developer(Borland C++ Compiler)、DXライブラリを使っております。

コード:

#include "DxLib.h"

int MAP[15][40]=
{
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1},
};

char Key[256];

double PlayerX,PlayerY,Speed,JumpSpeed;
int ScrollX,OnGround;

void Map(){
	int StartX,StartY,EndX,EndY; 
	StartX=ScrollX/16; 
	StartY=0; 
	EndX=(ScrollX+320)/16+1; 
	EndY=240/16; 

	for(int y=StartY;y<EndY;y++){ 
		for(int x=StartX;x<EndX;x++){ 
			if(MAP[y][x] == 1)DrawBox(x*16-ScrollX, y*16, x*16-ScrollX+16, y*16+16, GetColor(255,255,255),TRUE);
		} 
	}
}

void Player(){
	int maxspeed = 4.5;
	int x1,x2,y1,y2;
	
	//X入力
	if(Key[KEY_INPUT_LEFT]){
		Speed--;
		if(Speed < -maxspeed) Speed=-maxspeed;
	}else if(Speed<0)Speed++;
	
	if(Key[KEY_INPUT_RIGHT]){
		Speed++;
		if(Speed > maxspeed) Speed=maxspeed;
	}else if(Speed>0)Speed--;
	
	PlayerX+=Speed;
	
	x1=(ScrollX+PlayerX)/16;
	y1=PlayerY/16;
	x2=(ScrollX+PlayerX+15)/16;
	y2=(PlayerY+15)/16;
	if(MAP[y1][x1]!=0 || MAP[y2][x1]!=0){//左判定 
		PlayerX = x2*16; 
		Speed = 0;
	} 
	if(MAP[y1][x2]!=0 || MAP[y2][x2]!=0){//右判定 
		PlayerX = x1*16; 
		Speed = 0; 
	}
	
	//Y入力
	if(Key[KEY_INPUT_SPACE] && OnGround){
		JumpSpeed-=12;
	}
	
	JumpSpeed++;//自動落下
	PlayerY+=JumpSpeed;
		
	x1=(ScrollX+PlayerX)/16;
	y1=PlayerY/16;
	x2=(ScrollX+PlayerX+15)/16;
	y2=(PlayerY+15)/16;
	if(MAP[y1][x1]!=0 || MAP[y1][x2]!=0){//上判定 
		PlayerY=y2*16; 
		JumpSpeed=0; 
	} 
	if(MAP[y2][x1]!=0 || MAP[y2][x2]!=0){//下判定 
		PlayerY=y1*16; 
		JumpSpeed=0; 
		OnGround = TRUE; 
	}else OnGround = FALSE;
	
	//キャラクター描画
	DrawBox(PlayerX, PlayerY, PlayerX+16, PlayerY+16, GetColor(255,255,0), TRUE);
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){
        SetMainWindowText("アクションゲーム");
	ChangeWindowMode(TRUE);
	SetGraphMode(320,240,32);
	
	if( DxLib_Init() == -1 ) return -1;
	
	SetDrawScreen( DX_SCREEN_BACK );
	
	PlayerX = 320/2;
	PlayerY = 220/2;
	
	while( ProcessMessage() == 0 && !ClearDrawScreen() && !GetHitKeyStateAll( Key ) && !Key[KEY_INPUT_ESCAPE]){
		if(Key[KEY_INPUT_1])	ScrollX--;
		if(Key[KEY_INPUT_2])	ScrollX++;
		
		Map();
		Player();
		
		DrawFormatString(0,0,GetColor(255,255,255),"x:%lf",PlayerX);
		DrawFormatString(0,20,GetColor(255,255,255),"y:%lf",PlayerY);
		
		DrawFormatString(150,0,GetColor(255,255,255),"ScrollX:%d",ScrollX);
				
		ScreenFlip();
	}

	DxLib_End();
	return 0;
}

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

Re: C言語、アクションゲームのスクロールの端での処理

#2

投稿記事 by h2so5 » 9年前

キー操作では、プレイヤーの座標 PlayerX, Y のみを増減させて
マップの座標は描画する時にプレイヤーの座標から計算します。(ScreenX = PlayerX - 320/2;)

計算の結果マップが画面内に収まるのなら(マップの端でないなら)プレイヤーを中心にしてその位置でマップ描画、
マップが画面内に収まらない場合、その差分だけプレイヤーを中心からずらした後、画面内に収まる位置でマップを描画。

うまく説明できていませんが、私ならこうします。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: C言語、アクションゲームのスクロールの端での処理

#3

投稿記事 by MoNoQLoREATOR » 9年前

①キー操作に応じてキャラクターの座標を増減。(どれだけ増減したか記録しておく)
②マップが移動できる範囲で、キャラクター増減分だけ逆向きにマップとキャラクターを移動させる。

これでOK
どこまでマップが移動できるのかを判断する方法はお任せします。

山田

Re: C言語、アクションゲームのスクロールの端での処理

#4

投稿記事 by 山田 » 9年前

お二人ともありがとうございます。
時間が取れたのでDXライブラリのサンプルも参考にして書き換えてみました。
キャラクターのX座標をマップ全体と表示用に分けました。
そこでまた問題が出てしまって・・
左側の時はうまく動作するのですが右のほうに移動するとどこかに飛んでいってしまったりしてうまく動きません。
これは当たり判定の問題なのでしょうか?

コード:

#include "DxLib.h"

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define CHIP_SIZE 16
#define MAP_PIXEL_WIDTH 640
#define MAP_PIXEL_HEIGHT 240
#define MAP_WIDTH (MAP_PIXEL_WIDTH/CHIP_SIZE)
#define MAP_HEIGHT (MAP_PIXEL_HEIGHT/CHIP_SIZE)

int MAP[15][40]=
{
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,1,1,1, 0,0,0,0,0,1,1,1,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,0,0,0,0,0,0,1,1,1, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1},
	{1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1},
};

char Key[256];

double PlayerX,PlayerViewX,PlayerY,Speed,JumpSpeed;
int CameraX,OnGround;

void GameMain(){
	int maxspeed = 4.5;
	int x1,x2,y1,y2;
	
	//X入力
	if(Key[KEY_INPUT_LEFT]){
		Speed--;
		if(Speed < -maxspeed) Speed=-maxspeed;
	}else if(Speed<0)Speed++;
	
	if(Key[KEY_INPUT_RIGHT]){
		Speed++;
		if(Speed > maxspeed) Speed=maxspeed;
	}else if(Speed>0)Speed--;
	
	PlayerX += Speed;
	
	x1=(CameraX+PlayerX)/16;
	y1=PlayerY/16;
	x2=(CameraX+PlayerX+15)/16;
	y2=(PlayerY+15)/16;
	if(MAP[y1][x1]!=0 || MAP[y2][x1]!=0){//左判定 
		PlayerX = x2*16; 
		Speed = 0;
	} 
	if(MAP[y1][x2]!=0 || MAP[y2][x2]!=0){//右判定 
		PlayerX = x1*16; 
		Speed = 0; 
	}
	
	CameraX = PlayerX - SCREEN_WIDTH/2;
	
	if(CameraX < 0) CameraX = 0;
	if(CameraX > (MAP_PIXEL_WIDTH-SCREEN_WIDTH)) CameraX = (MAP_PIXEL_WIDTH-SCREEN_WIDTH);
	
	PlayerViewX = PlayerX - CameraX;
	
	//Y入力
	if(Key[KEY_INPUT_SPACE] && OnGround){
		JumpSpeed-=12.5;
	}
	
	JumpSpeed++;//自動落下
	PlayerY+=JumpSpeed;
		
	x1=(CameraX+PlayerX)/16;
	y1=PlayerY/16;
	x2=(CameraX+PlayerX+15)/16;
	y2=(PlayerY+15)/16;
	if(MAP[y1][x1]!=0 || MAP[y1][x2]!=0){//上判定 
		PlayerY=y2*16; 
		JumpSpeed=0; 
	} 
	if(MAP[y2][x1]!=0 || MAP[y2][x2]!=0){//下判定 
		PlayerY=y1*16; 
		JumpSpeed=0; 
		OnGround = TRUE; 
	}else OnGround = FALSE;
	
	//マップ描画
	for(int y=0;y<MAP_HEIGHT;y++){ 
		for(int x=0;x<MAP_WIDTH;x++){ 
			if(MAP[y][x] == 1)DrawBox(x*CHIP_SIZE-CameraX, y*CHIP_SIZE,
				 x*CHIP_SIZE-CameraX+CHIP_SIZE, y*CHIP_SIZE+CHIP_SIZE, GetColor(255,255,255),TRUE);
		} 
	}
	
	//キャラクター描画
	DrawBox(PlayerViewX, PlayerY, PlayerViewX+16, PlayerY+16, GetColor(255,255,0), TRUE);
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){
        SetMainWindowText("アクションゲーム");
	ChangeWindowMode(TRUE);
	SetGraphMode(320,240,32);
	
	if( DxLib_Init() == -1 ) return -1;
	
	SetDrawScreen( DX_SCREEN_BACK );
	
	PlayerX = 320/2;
	PlayerY = 240/2;
	
	while( ProcessMessage() == 0 && !ClearDrawScreen() && !GetHitKeyStateAll( Key ) && !Key[KEY_INPUT_ESCAPE]){
		
		GameMain();
				
		DrawFormatString(16,0,GetColor(255,255,255),"x:%lf",PlayerX);
		DrawFormatString(16,20,GetColor(255,255,255),"vx:%lf",PlayerViewX);
		
		DrawFormatString(150,0,GetColor(255,255,255),"CameraX:%d",CameraX);
				
		ScreenFlip();
	}

	DxLib_End();
	return 0;
}

アバター
MNS
記事: 35
登録日時: 9年前

Re: C言語、アクションゲームのスクロールの端での処理

#5

投稿記事 by MNS » 9年前

お察しのとおり、当たり判定の問題です。
PlayerX,Yはプレイヤーの絶対座標、PlayerViewX,Yは画面上でのプレイヤーの相対座標、
そして、CameraX,YはいわばPlayerX,YとPlayerViewX,Yとの差分ですよね?

当たり判定での、
x1=(CameraX+PlayerX)/16;
y1=PlayerY/16;
x2=(CameraX+PlayerX+15)/16;
y2=(PlayerY+15)/16;

で、x1、x2について、CameraX+PlayerXとしているのは何故でしょうか?
当たり判定は、絶対座標で行われるべきですから、わざわざCameraXを足す必要はありません。
CameraX+PlayerX=(PlayerX)×2 - PlayerViewX ということになり、この値は特に意味をなさないと思います。

よって、シンプルにこんな感じでいいのでは?
x1=PlayerX/16;
y1=PlayerY/16;
x2=(PlayerX+15)/16;
y2=(PlayerY+15)/16;

アクションゲーム 作成をしています

Re: C言語、アクションゲームのスクロールの端での処理

#6

投稿記事 by アクションゲーム 作成をしています » 9年前

チップのあたり判定はPlayerの座標のみ知っていれば問題ないのでは、ないでしょうか?
理由はPlayerが一番左に行っている場合はCameraXは0になるので必要ないとは思います。
右判定の場合では、プレイヤーが中央に向かうとCameraXが足されて、値が入るため,一番右に行く前に判定が入るためどこかに飛ぶのではないのでしょうか?

山田

Re: C言語、アクションゲームのスクロールの端での処理

#7

投稿記事 by 山田 » 9年前

MNS さん
その通りでした。書き換えたところうまく動きました。
わかりやすい解説もしてくださりありがとうございます。

アクションゲーム 作成をしています さん
そうだったようです・・自分でもまだ理解しきれていないところがあるので、ちゃんと理解してから次に進むことにしますね。。

これで最初に書いた理想通り動くようになりましたので、解決とさせていただきます。
皆さん、ありがとうございました。

閉鎖

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