プロが教えるわが家の防犯対策術!

 はじめまして、C#でジャンプアクションゲームを製作していて、どうしても解決できないことが出来たので質問させてください。
 質問と言うのは、キャラクタとマップとの衝突判定についてです。
 テキストベースのマップデータを二次元配列に入れ、それを元にマップを作成し、キャラクタの現在位置の座標を、接触しているマップチップのサイズで割り、その値をマップデータが入っている二次元配列に要素数として入れると、現在接触しているマップデータの位置が割り出せると言うところまではわかり、やってみるとマップと衝突しているように見えるのですが、このマップをスクロールさせると、衝突判定に利用している二次元配列のマップデータがスクロールしない為か、キャラクタを移動させると、マップの段差部分でキャラクタが宙に浮くと言う問題が発生しています。
 なら、マップデータも一緒にスクロールさせれば言いと思い、ずっとネットや本などで調べたりして、試してはいるのですが、これと言った解決法が見つかりません。
どなたか、わかる方おりましたらよろしくお願いいたします。

A 回答 (6件)

とりあえず、全部の答えを書いてみる事にしました。

これで難しいとかんじたら、もう少し基礎の部分をきっちりやった方が良いでしょう。

(1)訂正。
MoveMyCharでリターン値を忘れてました。
移動したら1を、移動できなかったら0を返してください。
map[40][15]では小さすぎることに気づいたので、修正。map[40][30]が最低サイズ。スクロールするならmap[120][60]でやっと6画面サイズと言う事で、map[120][60]で。

MapHitで定数化を忘れていたので修正。
public int MapHit(int mx,int my) {
int px,py;
px=mx/MapChipSizeX;py=mx/MapChipSizeY;
return map[px][py];//0なら何も無い?
}
このMapHit関数で問題が残っています。それは、自キャラが画面外の高さまでジャンプした場合です。その場合は、どういう対処が必要でしょうか?

(2)許容範囲に収まるように自キャラにあわせて。
こういう感じで処理します。無い部分を埋めてみてください。

const int bx=160;//表示範囲BOXの相対X座標
const int by=120;//表示範囲BOXの相対Y座標
const int bsx=320;//表示範囲BOXのサイズX
const int bsy=240;//表示範囲BOXのサイズY
int cx=100;int cy=100;//キャラクタの初期位置
int mapx=cx-vx/2;//最初のマップ表示座標
int mapy=cy-vy/2;//最初のマップ表示座標

//マップ表示座標の移動関数。mapx,mapyは値を返すため参照渡し。
public void MoveMap( ref int mapx, ref int mapy, int cx, int cy ) {
//キャラが表示範囲BOXの上にはみ出していたら上にスクロールする。
if( (mapy+by) > cy ) {
//はみ出した分だけmapyを上にスクロールする。
mapy = mapy - (cy - (mapy+by));
//mapyがマイナスならスクロールを抑制。
if( mapy < 0 ) {
mapy = 0;
}
}

:
:同様に下、右、左を行う。
:
}

(3)はこういうことですか?
マップチップサイズは、とりあえず16x16との仮定だったで変更。
元のプログラムのままでは、map配列のコピー操作が必要になるのとマップ座標系と表示座標系が混乱しているみたいだったので全面的に修正しました。

const int vx=640;//フォームの横幅
const int vy=480;//フォームの縦幅。←普通は480でした失礼。
const int MapChipSizeX = 16;
const int MapChipSizeY = 16;
int cx=100;int cy=100;//キャラクタの初期位置
int mapx=cx-vx/2;//最初のマップ表示座標
int mapy=cy-vy/2;//最初のマップ表示座標
int map[120][60];//0:当りなし 1:当りあり
int MapParts[120][60];//マップ上に置かれているマップチップのパーツ番号

//地形描画関数
public void DrawMap( int mapx, int mapy ) {
//マップチップ単位の座標系を求める。
int x = mapx / MapChipSizeX;
int y = mapy / MapChipSizeY;
//表示座標系とマップチップ単位の補正値を求める。
int ofx= mapx % MapChipSizeX;
int ofy= mapy % MapChipSizeY;

//マップチップ単位のXループ(vx/MapChipSizeX)+1回。
for(int lx=0 ; lx<((vx/MapChipSizeX)+1) ; lx++ ) {
//マップチップ単位のYループ(vy/MapChipSizeY)+1回。
for(int ly=0 ; ly<((vy/MapChipSizeY)+1) ; ly++ ) {
//マップデータの1の部分のときはマップチップを描画する。
if( map[x+lx][y+ly] == 1 ) {
//マップチップ描画する関数。表示座標と表示するパーツの番号
DrawMapChip((x+lx)*MapChipSizeX-ofx,
(y+ly)*MapChipSizeY-ofy,
MapParts[x+lx][y+ly] );
}
}
}
}

問題1。
ループが縦横1回余分なのですが、これはどうしてでしょう?

問題2。
実は画面の隅で表示が化けてしまうバグのある処理になっています。
それは何故でしょうか?

質問
マップデータの1の部分のときはマップチップを描画するとありますが、0の時は背景色で良いのでしょうか?


(4)はマップ描画を開始する座標から 自キャラの位置を計算すると言うことですか。
そうです。マップ座標系から表示座標系への変換を行います。

const int vx=640;//フォームの横幅
const int vy=480;//フォームの縦幅。←普通は480でした失礼。
int cx=100;int cy=100;//キャラクタの初期位置
int mapx=cx-vx/2;//最初のマップ表示座標
int mapy=cy-vy/2;//最初のマップ表示座標

//自キャラ描画関数
public void DrawMyChar( int mapx, int mapy, int cx, int cy ) {
//表示する画面座標を求める。
int x = cx - mapx;
int y = cy - mapy;
//自キャラを描画する。
DrawMyChar( x, y );
}
    • good
    • 0
この回答へのお礼

回答と問題ありがとうございます。
後は、自分なりに試してみます。
質問と解答をやり取りしている間にも、このままでは、無理やりにでも解答をzwiさんから聞き出そうとしているのではないかと思い、この後は、頂いたヒントを元に自分なりに解答を見つける方が良いと判断し、
質問を締め切ることに致しました。
本当に今まで有難うございました。

お礼日時:2007/07/08 21:33

いくつか問題がありますので、添削してみました。


C++言語派なので、間違いがあってもご容赦を。
しかし、コードを見ているとC言語風の使い方で、オブジェクト指向じゃないは良いんでしょうか?今回のテーマと無関係なので、そのまま書きますが。
一部参照渡しにするためにrefを書きました。使い方あってますよね?
方針として、プログラムの中では、基本的にマップ座標を使い極力画面座標は使いません。

(1)の部分。
//初期化
const int vx=640;//フォームの横幅
const int vy=480;//フォームの縦幅。←普通は480でした失礼。
const int MapChipSizeX = 16;
const int MapChipSizeY = 16;
int cx=100;int cy=100;//キャラクタの初期位置
int mapx=cx-vx/2;//最初のマップ表示座標
int mapy=cy-vy/2;//最初のマップ表示座標
int G=0;//重力加速度。←垂直移動速度が0なら良いが重力加速度は一定なので意味的に変。
int map[40][15];

//自キャラ移動関数
public int MoveMyChar( ref int cx, ref int cy ) {//参照渡しなのは、変更した値を呼び元に返すためです。
//仮の座標位置の変数を作ります。
int ccx = cx;
int ccy = cy;
//キー入力で自キャラを移動させます(仮の座標位置を変更)。
MoveKey( ccx, ccy ); //キー入力方向でで自キャラを動かす。記述は省略。
//ジャンプや落下などの処理
MoveVirtical( ccx, ccy ); //重力加速度処理。記述は省略。
//移動した先が地形に刺さっていないかチェックします。
if( MapHit(ccx,ccy)==0 ) {//0は何も無いんですよね?
//移動に成功。位置を実際に変更する。
cx = ccx;
cy = ccy;
} else {
//移動を無かったことに。
ccx = cx;
ccy = cy;
//移動に失敗したので、垂直方向の処理だけを行う。
MoveVirtical( ccx, ccy ); //重力加速度処理。記述は省略。
//移動した先が地形に刺さっていないかチェックします。
if( MapHit(ccx,ccy)==0 ) {//0は何も無いんですよね?
//移動に成功。位置を実際に変更する。
cx = ccx;
cy = ccy;
}
}
}

//衝突したときのマップデータを返すメソッド(関数)
//ただし、キャラの当たり判定が点の場合、ある程度のサイズがあるなら範囲処理が必要。
public int MapHit(int mx,int my) {
int px,py;
px=mx/16;py=mx/16;
return map[px][py];//0なら何も無い?
}

基本的にキャラ処理が理解できていない気がするので、画面スクロールする前に、ちゃんとしたキャラ処理を作りませんか?という事で、まず(1)だけです。これがちゃんと動いてから次に進んだほうが良いでしょう。
    • good
    • 0
この回答へのお礼

たびたびのご回答と添削大変ありがとうございます。
>コードを見ているとC言語風の使い方で、オブジェクト指向じゃないは良いんでしょうか?今回のテーマと無関係なので、そのまま書きますが。
すみません。C#の方があまり詳しくないとおっしゃられたので、C言語風にしてしまいました。refの使い方はあっております。C#であれば、二次元配列は、int [,]map=new int[40,15];で、int map[40][15];じゃないです。誤解を与えてしまいました。
>int G=0; //重力加速度。←垂直移動速度が0なら良いが重力加速度は一定なので意味的に変。
これ確かに違いますね。例えで1にするつもりが0にしてしまったと思います。

確かにキャラ処理の理解が不完全であると思います。とりあえず、この先は自分で解決してみたいと思います。スクロールの方も先程買った本の中に書いていたみたいなのでそれを参考にしてやってみようと思います。もし、また行き詰ったら、この場にて質問させていただきます。
今までどうもありがとうございました。

お礼日時:2007/07/08 15:56

ごめんなさい。

ちょっと省略して書きすぎました。
書いた例は、画面のど真ん中で固定している場合です。
ですので、VX,VYは固定値です。例えば、640x400の画面なら320、200という位置だと想定してください。
複雑な仕様で説明すると混乱するかなと思いましたので、簡略化しました。

実際には、スーパーマリオだと自キャラは画面中心に固定されているわけではなく、画面スクロールしない状態で上下左右にある程度自由に動けますが、ある範囲を超えようとすると画面がスクロールして自キャラに追従してくるわけです。
ですので、ちゃんとこのシステムを構築しようとすれば、自キャラ位置はマップ全体の座標系で管理。地形表示の座標系もマップ全体で管理する必要があります。処理の流れとしては以下のようになります。
(1)自キャラを移動する。マップデータで地形当り判定をして、自キャラの行ってはいけない位置にならないように補正する。
(2)現在表示している地形座標で、自キャラの位置が許容範囲外に出ていないかをチェックする。はみ出していた場合は、地形座標を許容範囲に収まるように自キャラに合わせて補正する。
(3)地形座標を基点として、マップデータを参照しながら地形の描画を行う。
(4)自キャラ座標と地形座標の相対位置から画面上に表示する描画X,Yを計算して自キャラを描画する。同様に敵キャラ等の描画も行う。
まだ簡略化してますが、こんな感じですかね。

この回答への補足

毎度のご回答ありがとうございます。
(1)を参考にすると、
例えば、
int vx=640;//フォームの横幅
int vy=400;//フォームの縦幅
int cx=100;int cy=100;//キャラクタの位置
int G=0;//重力加速度
int mx=cx-vx/2;int my=cy-vy/2;
int map[40][15];
if(MapHit(mx,my)==1)G=0;//マップデータの1の部分に接触してると衝突と判定
//衝突したときのマップデータを返すメソッド(関数)
public int MapHit(int mx,int my){
int px,py;
px=mx/16;py=mx/16;
return map[px][py];
}
こういう感じになりましたが、違うでしょうか?
(2)許容範囲外に収まるように自キャラにあわせてという部分が少し、わかりません。
(3)はこういうことですか?
int vx=640;int vy=400;//フォームの幅
int tipsize=32;
int cx=100;int cy=100;//キャラクタの位置
int map[vx/tipsize][vy/tipsize];
int my=cy-vy/2;
int mx=cx-vx/2;
for(int i=my/16;i<vy/tipsize;i++){
for(int j=mx/16;j<vx/tipsize;j++){
//マップデータの1の部分のときはマップチップを描画する。
if(map[j][i]==1){
draw(tiletopixel(j),tiletopixel(i));//この関数は例えです。
}
}
}
static int tiletopixel(int tile){
return tile*tipsize;
}
(4)はマップ描画を開始する座標から 自キャラの位置を計算すると言うことですか。
たびたびですみませんが、よろしくお願いいたします。

補足日時:2007/07/08 07:24
    • good
    • 0

#1です。


画面に表示されているマップのCGデータとドット単位の当たり判定が無い限り、配列上のマップデータと衝突判定すればすれば良い事だとおもいます。
どういう参考書やネットの情報を参考にされたかは分かりませんが、たぶん用途が違う場合じゃないかと思われます。

計算方法:
すべて原点座標は、左上を0,0としています。
マップ原点からの自キャラ位置X,Y座標を持ちます。移動と地形との当たり判定は、この座標で行います。仮にCX,CYとしましょう。
自キャラの画面上の表示位置X,YをVX,VYとします。
マップの表示開始座標をMX,MYとします。マップチップのサイズが16x16ドットとして、配列にアクセスするX,Yを求めてみましょう。
C#は詳しくないので、Cで書くと。
MX=CX-VX;
MY=CY-VY;
X=MX/16;
Y=MX/16;
このX,Yを画面に表示する個数分ループで回してやれば画面が描画できます。

この回答への補足

たびたびのご回答ありがとうございます。
>マップ原点からの自キャラ位置X,Y座標を持ちます。移動と地形との当たり判定は、この座標で行います。仮にCX,CYとしましょう。
自キャラの画面上の表示位置X,YをVX,VYとします。

自キャラの位置CX,CYは自キャラの画面上の表示位置VX,VXと一緒と思ったのですが、そうではなく、VX,VYは自キャラの移動量と考えるのでしょうか。

たびたびですみませんが、お教えいただけないでしょうか。

補足日時:2007/07/07 07:39
    • good
    • 0

マップを移動させるのではなくキャラクタの方を移動後の位置で演算する方が一般的。


画面上に見えていて固定しているのはキャラクタなのでマップを動かせば、と思う事もあるだろうけど座標の取り出しはマップが固定である方が楽である場合が多い。

といってもその考え方、随分古いものだからもしかしたらマップを動かした方が早いのかも・・・


衝突判定は移動し終わったら行うのではなく移動前に行う。
移動後に行うのは「動きすぎに対する補正」程度。

この回答への補足

質問へのご回答ありがとうございます。
>キャラクタの方を移動後の位置で演算する方が一般的。
RPGではこの方法を用いられているようですが。

>といってもその考え方、随分古いものだからもしかしたらマップを動かした方が早いのかも・・・
いいえ。古いとは思いません。現在の考え方のベースになっていると思います。

>衝突判定は移動し終わったら行うのではなく移動前に行う。
マップやキャラクタを描画した後に衝突判定を行えばよろしいのですか?

補足日時:2007/07/06 09:12
    • good
    • 0

マップデータとの位置関係を出すのに画面上のX、Y座標を使っていませんか?


スクロールと無関係に、マップチップのサイズで割っていないマップ上の位置座標を持っていれば解決する問題です。
基本座標はすべてこのマップ上の座標で管理して、画面上のX,Yをマップ上の座標から計算して求めるようにすれば、解決すると思います。

この回答への補足

質問へのご回答ありがとうございます。
>マップデータとの位置関係を出すのに画面上のX、Y座標を使っていませんか?
はい。使っています。調べてみるとそのように判定すると参考書やネットにありましたので。

>マップ上の位置座標を持っていれば解決する問題です。
それは、キャラクタがマップチップがある座標に接触しているときと考えればよろしいのでしょうか?

>基本座標はすべてこのマップ上の座標で管理して、画面上のX,Yをマップ上の座標から計算して求めるようにすれば、解決すると思います。
X,Yをどのように計算すれば求めることが出来ますか?
お教えいただければありがたいのですが。

補足日時:2007/07/06 08:40
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!