斜め俯瞰2D(クォータービュー)の描画順について

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

斜め俯瞰2D(クォータービュー)の描画順について

#1

投稿記事 by ユーカリ » 11年前

現在クォータービューのゲームを作成中のものです。
前回の質問ではお世話になりました。
どうやらファイル読み込みエラーのメッセージ表示がf<0で間違っていたために読み込みが成功しているにもかかわらずエラーメッセージをprintfDxしていたことに加え、新規追加したAIの挙動プログラムのエラーにより以降の画面描画がストップしていたせいであたかもマップ読み込みの時点でエラーが出ているように見えていたものと思われます。
現在バグなく順調です。

さて、本題ですがただいまオブジェクト(キャラクター、障害物、射出された弾等のマップ上に描画される小物)の描画関数を作っています。
y成分だけを比較して見かけ奥の方(y座標が小さい方)から描画をする簡単な関数は以下のような形で完成しています。
なんだかごちゃごちゃして人様に見せられるような有様ではありませんがご容赦ください……。

コード:

 
struct Charadata
{
	float muki;//変換前、向きラジアン
	int type;//敵の種類
	int graph[8];//各方向グラフィック格納変数
	int time;//生存時間変数
	int act;//アクション把握用変数
	int dashtimer;
	int hantei;//持っている当たり判定の数(最大4個)
	int area[4][3];//判定のエリア(長径)、判定の[1]=x座標 [2]=y座標
	float lockx;
	float locky;
	float x;
	float y;
};

struct ATKdat
{
	int chara;//射出した敵キャラの番号
	int type;//攻撃の種類
	int time;//存在時間
	double x;//中心座標
	double y;//中心座標
	float muki;
	int graph[8];//画像
	int hantei;//判定の個数(最大16個)
	int area[16][3];//判定のエリア(長径)、判定の[1]=x座標 [2]=y座標
};

extern struct Charadata Chara[128];//0=P1 32-62=enemy 63=boss 64-=object
extern struct ATKdat Atk[256];


void Drawfunc()
{
	float y[384][3];//[個数][3つの要素(x座標:y座標:存在(-1=存在しない))]
	float min;
	int muki=0;
	int count=0;
	//キャラクターの存在によって初期化の値を変更する
	for(int i=0;i<128;i++){
		if(Chara[i].type!=0){
			y[i][0]=Chara[i].x;
			y[i][1]=Chara[i].y;
			y[i][2]=1;
		}
		if(Chara[i].type==0){y[i][2]=0;}
	}
	//Atkも同様に初期化
	for(int i=0;i<256;i++){
		if(Atk[i].type!=0){
			y[i+128][0]=Atk[i].x;
			y[i+128][1]=Atk[i].y;
			y[i+128][2]=1;
		}
		if(Atk[i].type==0){y[i+128][2]=0;}
	}
	float g_x=Chara[0].x+Xdis(50,UtoQ(Chara[0].muki));
	float g_y=Chara[0].y-Ydis(50,UtoQ(Chara[0].muki));
	DrawGraph(320-g_x,240-g_y,UseMisData.graph,TRUE);//背景描画
	//以下キャラ、オブジェクト、弾の描画
	while(1){
		min=10000000000;
		count=0;
		for(int i=0;i<384;i++){
			if(y[i][1]<=min && y[i][2]==1){
				min=y[i][1];
			}
		}
		for(int i=0;i<384;i++){
			if(y[i][1]<=min && y[i][2]!=0){
				y[i][2]=0;
				if(i<128){//キャラクター構造体
					if(Chara[i].muki<=0+PAI/8 || Chara[i].muki>PAI/4*7+PAI/8){muki=0;}
					if(Chara[i].muki>=PAI/4-PAI/8 && Chara[i].muki<PAI/4+PAI/8){muki=1;}
					if(Chara[i].muki>=PAI/4*2-PAI/8 && Chara[i].muki<PAI/4*2+PAI/8){muki=2;}
					if(Chara[i].muki>=PAI/4*3-PAI/8 && Chara[i].muki<PAI/4*3+PAI/8){muki=3;}
					if(Chara[i].muki>=PAI/4*4-PAI/8 && Chara[i].muki<PAI/4*4+PAI/8){muki=4;}
					if(Chara[i].muki>=PAI/4*5-PAI/8 && Chara[i].muki<PAI/4*5+PAI/8){muki=5;}
					if(Chara[i].muki>=PAI/4*6-PAI/8 && Chara[i].muki<PAI/4*6+PAI/8){muki=6;}
					if(Chara[i].muki>=PAI/4*7-PAI/8 && Chara[i].muki<PAI/4*7+PAI/8){muki=7;}
					DrawRotaGraph(320-(g_x-Chara[i].x),240-(g_y-Chara[i].y),1,0,Chara[i].graph[muki],TRUE,FALSE);
				}
				if(i>=128){//攻撃構造体
					if(Atk[i-128].muki<=0+PAI/8 || Atk[i-128].muki>PAI/4*7+PAI/8){muki=0;}
					if(Atk[i-128].muki>=PAI/4-PAI/8 && Atk[i-128].muki<PAI/4+PAI/8){muki=1;}
					if(Atk[i-128].muki>=PAI/4*2-PAI/8 && Atk[i-128].muki<PAI/4*2+PAI/8){muki=2;}
					if(Atk[i-128].muki>=PAI/4*3-PAI/8 && Atk[i-128].muki<PAI/4*3+PAI/8){muki=3;}
					if(Atk[i-128].muki>=PAI/4*4-PAI/8 && Atk[i-128].muki<PAI/4*4+PAI/8){muki=4;}
					if(Atk[i-128].muki>=PAI/4*5-PAI/8 && Atk[i-128].muki<PAI/4*5+PAI/8){muki=5;}
					if(Atk[i-128].muki>=PAI/4*6-PAI/8 && Atk[i-128].muki<PAI/4*6+PAI/8){muki=6;}
					if(Atk[i-128].muki>=PAI/4*7-PAI/8 && Atk[i-128].muki<PAI/4*7+PAI/8){muki=7;}					
					DrawRotaGraph(320-(g_x-Atk[i-128].x),240-(g_y-Atk[i-128].y),1,Atk[i-128].muki,Atk[i-128].graph[muki],TRUE,FALSE);
				}
			}
		}
		for(int i=0;i<384;i++){
			if(y[i][2]==1){count=1;}
		}
		if(count==0){break;}
	}
}


 この関数は各オブジェクトの画像描画時の中心座標同士を比較させて描画順を決定しているのですが、これだけだとレーザーなどある一方向に長い(もっと言うならy座標に幅があるユニークな形状の)オブジェクトを描画した際、奥行きによる重なりを無視した描画をしてしまいます。
 例えば、レーザー上のある一点では柱より手前にレーザーが来ているように見えても、レーザーを延ばしていった先では柱の後ろに隠れてしまうかもしれませんよね。
 そうなると私の場合リストで一つ一つのオブジェクトの大きさ形を見極めてソートしていくような面倒くさいプログラムしか思いつかないのですが、何か良い方法はないものでしょうか。

アバター
ookami
記事: 214
登録日時: 13年前
住所: 東京都

Re: 斜め俯瞰2D(クォータービュー)の描画順について

#2

投稿記事 by ookami » 11年前

こんにちは、ookamiです。

「柱の中心座標」と「レーザー(直線と見立てる)」を比較するなら何とかなるかもしれませんね。

柱の中心座標を x1,y1 、
レーザーの直線の式を y=a*x+b とすると、

y1>a*x1+b なら柱が手前、そうでないなら奥、(逆か...?) という判定になると思います。


柱を貫通する場合は考慮していません。
レーザー以外の何か特殊な形のものが出てくる場合とか、もっと厳密にやろうとしたら、
DxLibの3D機能をあたかも2D表示っぽく使って、前後関係はZバッファに任せる、ということになるかなと。

ユーカリ

Re: 斜め俯瞰2D(クォータービュー)の描画順について

#3

投稿記事 by ユーカリ » 11年前

>ookamiさん
やはり一次関数が無難でしょうか。

なんだか無駄に長いソースになってしまいましたが、レーザーの起点、向きを使って一次関数を出し、それを比較の対象にすることにしました。
構造体とか纏めればもう少し見栄えも良くなるかなと思います……。あと変なコメント消したり。
考えながら行き当たりばったりにやってるものでごちゃごちゃします。どうにかしないといけませんね。


レーザー実装の為に書き直したのにそもそものレーザーを発生させる部分のソースが出来てないのでレーザー描画の部分についてこれで合ってるかが不明ですが、キャラクター描画順に関してはとりあえず無問題でしたので、たぶんレーザーも大丈夫かなーと。根拠はないのですが。
本当ならばしっかり実装してから色々報告したかったのですが、一応これで解決とさせていただきます。

予定はありませんがもっと複雑になったら3D機能のことも調べつつ実装してみようと思います。
ありがとうございました。

コード:


void Drawfunc()
{
	struct Graph *top=(struct Graph*)calloc(1,sizeof(struct Graph));
	struct Graph *newptr=top;
	struct Graph *ptr;
	for(int i=0;i<256;i++){//レーザーのグラフ順位を最初に入力しなければ後に描画順序が狂う
		if(Atk[i].type>=1000){
			if(top->flag==0){//最初のレーザーは無条件
				newptr->number=i+128;
				newptr->flag=1;
				newptr->next=(struct Graph*)calloc(1,sizeof(struct Graph));
			}
			else{
				newptr=top;
				while(1){
					if(newptr!=NULL && -tan(Atk[i].muki)*100+(Atk[i].y)+tan(Atk[i].muki)*Atk[i].x <=-tan(Atk[(newptr->number)-128].muki)*100+(Atk[(newptr->number)-128].y)+tan(Atk[(newptr->number)-128].muki)*Atk[(newptr->number)-128].x ){//あるx座標(何でもいいので無作為に100で)でのy座標を比べる
						//もし現在入力待ちのデータが現在調べている対象よりy座標が小さければ入力
						ptr=(struct Graph*)calloc(1,sizeof(struct Graph));
						ptr->flag=1;
						ptr->next=newptr->next;
						ptr->number=newptr->number;
						newptr->number=i+128;
						newptr->flag=1;
						newptr->next=ptr;
						break;
					}
					if(newptr->next==NULL)
					{
						newptr->next=(struct Graph*)calloc(1,sizeof(struct Graph));
						newptr->next->flag=1;
						newptr->next->number=i+128;
						break;
					}
					newptr=newptr->next;
				}
			}
		}
	}
	for(int i=0;i<256;i++){//通常の弾の入力
		if(Atk[i].type>0 && Atk[i].type<1000){
			if(top->flag==0){//最初のは無条件
				newptr->number=i+128;
				newptr->flag=1;
				newptr->next=(struct Graph*)calloc(1,sizeof(struct Graph));
			}
			else{
				newptr=top;
				while(1){
					if(newptr!=NULL && Atk[(newptr->number)-128].type<1000 && Atk[(newptr->number)-128].type>0){
						if(Atk[i].y <=Atk[(newptr->number)-128].y ){//Atk[i]のx座標()でのy座標を比べる
							//もし現在入力待ちのデータが現在調べている対象よりy座標が小さければ入力
							ptr=(struct Graph*)calloc(1,sizeof(struct Graph));
							ptr->flag=1;
							ptr->next=newptr->next;
							ptr->number=newptr->number;
							newptr->number=i+128;
							newptr->flag=1;
							newptr->next=ptr;
							break;
						}
					}
					if(newptr!=NULL &&Atk[(newptr->number)-128].type>=1000){
						if(Atk[i].y <=-tan(Atk[(newptr->number)-128].muki)*Atk[i].x+(Atk[(newptr->number)-128].y)+tan(Atk[(newptr->number)-128].muki)*Atk[(newptr->number)-128].x ){//Atk[i]のx座標()でのy座標を比べる
							//もし現在入力待ちのデータが現在調べている対象よりy座標が小さければ入力
							ptr=(struct Graph*)calloc(1,sizeof(struct Graph));
							ptr->flag=1;
							ptr->next=newptr->next;
							ptr->number=newptr->number;
							newptr->number=i+128;
							newptr->flag=1;
							newptr->next=ptr;
							break;
						}
					}
					if(newptr->next==NULL)
					{
						newptr->next=(struct Graph*)calloc(1,sizeof(struct Graph));
						newptr->next->flag=1;
						newptr->next->number=i+128;
						break;
					}
					newptr=newptr->next;
				}
			}
		
		}
	}
	for(int i=0;i<128;i++){//キャラクターの入力
		if(Chara[i].type>0){
			if(top->flag==0){//最初のは無条件
				newptr->number=i;
				newptr->flag=1;
				newptr->next=(struct Graph*)calloc(1,sizeof(struct Graph));
			}
			else{
				newptr=top;
				while(1){
					if(newptr!=NULL &&newptr->number>=128){//対象が攻撃オブジェクトのばあい
						if(Atk[(newptr->number)-128].type<1000 && Atk[(newptr->number)-128].type>0){//対象が弾の場合
							if(Chara[i].y <=Atk[(newptr->number)-128].y ){//Atk[i]のx座標()でのy座標を比べる
								//もし現在入力待ちのデータが現在調べている対象よりy座標が小さければ入力
								ptr=(struct Graph*)calloc(1,sizeof(struct Graph));
								ptr->flag=1;
								ptr->next=newptr->next;
								ptr->number=newptr->number;
								newptr->number=i;
								newptr->flag=1;
								newptr->next=ptr;
								break;
							}
						}
						if(Atk[(newptr->number)-128].type>=1000){//対象がレーザーの場合
							if(Chara[i].y <=-tan(Atk[(newptr->number)-128].muki)*Chara[i].x+(Atk[(newptr->number)-128].y)+tan(Atk[(newptr->number)-128].muki)*Atk[(newptr->number)-128].x ){//Atk[i]のx座標()でのy座標を比べる
								//もし現在入力待ちのデータが現在調べている対象よりy座標が小さければ入力
								ptr=(struct Graph*)calloc(1,sizeof(struct Graph));
								ptr->flag=1;
								ptr->next=newptr->next;
								ptr->number=newptr->number;
								newptr->number=i;
								newptr->flag=1;
								newptr->next=ptr;
								break;
							}
						}
					}
					if(newptr!=NULL &&newptr->number<128){//対象がキャラクターの場合
						if(Chara[i].y <=Chara[newptr->number].y ){//Atk[i]のx座標()でのy座標を比べる
							//もし現在入力待ちのデータが現在調べている対象よりy座標が小さければ入力
								ptr=(struct Graph*)calloc(1,sizeof(struct Graph));
								ptr->flag=1;
								ptr->next=newptr->next;
								ptr->number=newptr->number;
								newptr->number=i;
								newptr->flag=1;
								newptr->next=ptr;
								break;
							}
					}
					if(newptr->next==NULL)
					{
						newptr->next=(struct Graph*)calloc(1,sizeof(struct Graph));
						newptr->next->flag=1;
						newptr->next->number=i;
						break;
					}
					newptr=newptr->next;	
				}
			}
		}
		
	}
	//以下は入力されたデータをもとに描画する
	float g_x=Chara[0].x+Xdis(50,UtoQ(Chara[0].muki));
	float g_y=Chara[0].y-Ydis(50,UtoQ(Chara[0].muki));
	DrawGraph(320.0-g_x,240-g_y,UseMisData.graph,TRUE);//背景描画
	//以下キャラ、オブジェクト、弾の描画
	int muki;
	int count;
	newptr=top;
	while(1){
		count=0;
		if(newptr->number<128){//キャラクター構造体
			if(Chara[newptr->number].muki<=0+PAI/8 || Chara[newptr->number].muki>PAI/4*7+PAI/8){muki=0;}
			if(Chara[newptr->number].muki>=PAI/4-PAI/8 && Chara[newptr->number].muki<PAI/4+PAI/8){muki=1;}
			if(Chara[newptr->number].muki>=PAI/4*2-PAI/8 && Chara[newptr->number].muki<PAI/4*2+PAI/8){muki=2;}
			if(Chara[newptr->number].muki>=PAI/4*3-PAI/8 && Chara[newptr->number].muki<PAI/4*3+PAI/8){muki=3;}
			if(Chara[newptr->number].muki>=PAI/4*4-PAI/8 && Chara[newptr->number].muki<PAI/4*4+PAI/8){muki=4;}
			if(Chara[newptr->number].muki>=PAI/4*5-PAI/8 && Chara[newptr->number].muki<PAI/4*5+PAI/8){muki=5;}
			if(Chara[newptr->number].muki>=PAI/4*6-PAI/8 && Chara[newptr->number].muki<PAI/4*6+PAI/8){muki=6;}
			if(Chara[newptr->number].muki>=PAI/4*7-PAI/8 && Chara[newptr->number].muki<PAI/4*7+PAI/8){muki=7;}
			DrawRotaGraph(320-(g_x-Chara[newptr->number].x),240-(g_y-Chara[newptr->number].y),1,0,Chara[newptr->number].graph[muki],TRUE,FALSE);
		}
		if(newptr->number>=128 && Atk[(newptr->number)-128].type<1000){//普通の攻撃構造体
			if(Atk[(newptr->number)-128].muki<=0+PAI/8 || Atk[(newptr->number)-128].muki>PAI/4*7+PAI/8){muki=0;}
			if(Atk[(newptr->number)-128].muki>=PAI/4-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4+PAI/8){muki=1;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*2-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*2+PAI/8){muki=2;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*3-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*3+PAI/8){muki=3;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*4-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*4+PAI/8){muki=4;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*5-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*5+PAI/8){muki=5;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*6-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*6+PAI/8){muki=6;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*7-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*7+PAI/8){muki=7;}					
//			DrawRotaGraph(320-(g_x-Atk[(newptr->number)-128].x),240-(g_y-Atk[(newptr->number)-128].y),1,Atk[(newptr->number)-128].muki,Atk[(newptr->number)-128].graph[muki],TRUE,FALSE);
			DrawRotaGraph(320-(g_x-Atk[(newptr->number)-128].x),240-(g_y-Atk[(newptr->number)-128].y),1,Atk[(newptr->number)-128].muki,Atk[(newptr->number)-128].graph[0],TRUE,FALSE);
		}
		if(newptr->number>=128 && Atk[(newptr->number)-128].type>=1000){//レーザーの攻撃構造体
			if(Atk[(newptr->number)-128].muki<=0+PAI/8 || Atk[(newptr->number)-128].muki>PAI/4*7+PAI/8){muki=0;}
			if(Atk[(newptr->number)-128].muki>=PAI/4-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4+PAI/8){muki=1;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*2-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*2+PAI/8){muki=2;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*3-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*3+PAI/8){muki=3;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*4-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*4+PAI/8){muki=4;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*5-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*5+PAI/8){muki=5;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*6-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*6+PAI/8){muki=6;}
			if(Atk[(newptr->number)-128].muki>=PAI/4*7-PAI/8 && Atk[(newptr->number)-128].muki<PAI/4*7+PAI/8){muki=7;}					
			while(1){
				DrawRotaGraph(320-(g_x-Atk[(newptr->number)-128].x)+cos(Atk[(newptr->number)-128].muki)*40*count,240-(g_y-Atk[(newptr->number)-128].y)-sin(Atk[(newptr->number)-128].muki)*40*count,1,Atk[(newptr->number)-128].muki,Atk[(newptr->number)-128].graph[muki],TRUE,FALSE);
				count++;
				if(Atk[(newptr->number)-128].length<sqrt((cos(Atk[(newptr->number)-128].muki)*40*count)*(cos(Atk[(newptr->number)-128].muki)*40*count)+(sin(Atk[(newptr->number)-128].muki)*40*count)*(sin(Atk[(newptr->number)-128].muki)*40*count))){
					if(count==1){break;}
					DrawRotaGraph(320-(g_x-Atk[(newptr->number)-128].x)+cos(Atk[(newptr->number)-128].muki)*(Atk[(newptr->number)-128].length-40),240-(g_y-Atk[(newptr->number)-128].y)-sin(Atk[(newptr->number)-128].muki)*(Atk[(newptr->number)-128].length-40),1,Atk[(newptr->number)-128].muki,Atk[(newptr->number)-128].graph[muki],TRUE,FALSE);//終端を描く
					break;
				}
			}
		}
		if(newptr->next==NULL){break;}
		else{newptr=newptr->next;}
	}
}




閉鎖

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