There are many topics like this, but none with concrete answers. I am drawing a tile-map in the traditional way (two for loops) and keeping my player centered except when the edges of the map is reached. How would I create collision detection? I need to know how to translate tile location in the array to screen coordinates I think.
2 に答える
ポイント/タイルマップの衝突検出のために私が書いたコードを提供します。このコードは、(xfrom, yfrom) にポイントがあり、それを (xto, yto) に移動し、タイルマップ map[Y][X] 内のブロックとの衝突があるかどうかを確認したいと想定しています。タイルがソリッドの場合に true を返すメソッド isSolid(tileId) を想定しています。
/**
* This method returns true if there is a collision between a point and a 2D tilemap map[Y][X].
* The isSolid method must be implemented to indicate if a tile is solid or not
* Assumes the tilemap starts at (0,0) and TILEWIDTH and TILEHEIGHT hold the size of a tile (in pixels)
* @param xfrom the original x-coordinate of the point
* @param yfrom the original y-coordinate of the point
* @param xto the destination x-coordinate of the point
* @param yto the destination y-coordinate of the point
* @param outCollisionPoint output the location where the collision occurs
* @return true if a collision is found
*/
public boolean collisionDetection(int xfrom, int yfrom, int xto, int yto, Point outCollisionPoint){
//Ref: A fast voxel traversal algorithm J.Amanatides, A. Woo
float tMaxX, tMaxY, tDeltaX, tDeltaY, collisionLength;
int X, Y, stepX, stepY, endX, endY, blkX, blkY;
//Calculate direction vector
float dirX = (xto - xfrom);
float dirY = (yto - yfrom);
float length = (float) Math.sqrt(dirX * dirX + dirY * dirY);
//Normalize direction vector
dirX /= length;
dirY /= length;
//tDeltaX: distance in terms of vector(dirX,dirY) between two consecutive vertical lines
tDeltaX = TILEWIDTH / Math.abs(dirX);
tDeltaY = TILEHEIGHT / Math.abs(dirY);
//Determine cell where we originally are
X = xfrom / TILEWIDTH;
Y = yfrom / TILEHEIGHT;
endX = xto / TILEWIDTH;
endY = yto / TILEHEIGHT;
//stepX: Determine in what way do we move between cells
//tMaxX: the distance in terms of vector(dirX,dirY) to the next vertical line
if (xto > xfrom){
blkX = 0;
stepX = 1;
tMaxX = ((X+1) * TILEWIDTH - xfrom) / dirX;
}else{
blkX = 1;
stepX = -1;
tMaxX = (X * TILEWIDTH - xfrom) / dirX;
}
if (yto > yfrom){
blkY = 0;
stepY = 1;
tMaxY = ((Y+1) * TILEHEIGHT - yfrom) / dirY;
}else{
blkY = 1;
stepY = -1;
tMaxY = (Y * TILEHEIGHT - yfrom) / dirY;
}
if (isSolid(map[Y][X])) {
//point already collides
outCollisionPoint = new Point(xfrom, yfrom);
return true;
}
//Scan the cells along the line between 'from' and 'to'
while (X != endX || Y !=endY){
if(tMaxX < tMaxY){
tMaxX += tDeltaX;
X += stepX;
if (isSolid(map[Y][X])) {
collisionLength = ((X + blkX) * TILEWIDTH - xfrom) / dirX;
outCollisionPoint = new Point((int)(xfrom + dirX * collisionLength), (int)(yfrom + dirY * collisionLength));
return true;
}
}else{
tMaxY += tDeltaY;
Y += stepY;
if (isSolid(map[Y][X])) {
collisionLength= ((Y + blkY) * TILEHEIGHT - yfrom) / dirY;
outCollisionPoint = new Point((int)(xfrom + dirX * collisionLength), (int)(yfrom + dirY * collisionLength));
return true;
}
}
}
return false;
}
モデルによって異なります。
モデル (データ) がグリッドの場合、互換性のない 2 つのオブジェクトが同じ場所を占有するだけで衝突が発生します。このタイプの衝突を処理する最も簡単な方法は、ゲーム エンティティを移動しようとしている場所が「利用可能」であることを確認することです。そうであれば、衝突はなく、モデルを更新します。それが自由でなければ、衝突がありました。
画面は単にモデルをレンダリングします。ピクセルごとの衝突検出 (元のレミングやワームを考えてください) のようなものを除いて、衝突検出には使用しないでください。
画面/ビューは単なるレンダリング エージェントです。モデルを画面にしっかりと結び付けることができますが (たとえば、ピースが移動したときなど、画面の一部が変更された場合にのみ更新する必要があります)、画面は通常、モデルの一部とは見なされません。モデル。ただし、最新のコンピューティング速度では、フレームごとに表示されているモデル全体を単純に再レンダリングすることもできます。
(はい、私は自分自身を繰り返したことを知っています。それは意図的でした。)
さて、タイトルに記載されていない二次的な質問に答えるために:
レンダリングを開始するときは、プレイヤーの左に screen_width/cell_width/2 セル、右に screen_width/cell_width/2 セルを描画するだけです (プレイヤーは 1x1 を想定しています)。上下も同様に行います。Index-Out-Of-Bounds 例外が発生しないようにしてください。それらを使用する前にクランプ/フィルタリングする限り、範囲外の値で for ループを実行できます。キャラクターが近づいたときにのみエッジを「押す」ようにしたい場合は、現在のモデルからビューへの参照も追跡します。