这样的话题有很多,但没有一个给出具体的答案。我正在以传统方式(两个 for 循环)绘制瓦片地图,并保持我的玩家居中,除非到达地图的边缘。我将如何创建碰撞检测?我需要知道如何将数组中的图块位置转换为我认为的屏幕坐标。
2 回答
我会给你我为点/tilemap碰撞检测编写的代码。该代码假定您在 (xfrom, yfrom) 处有一个点,并且您想将其移动到 (xto, yto) 并想查看是否与 tilemap map[Y][X] 中的块发生碰撞。我假设一个方法 isSolid(tileId) 如果瓷砖是实心的,它将返回 true。
/**
* 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;
}
这取决于型号。
如果您的模型(数据)是一个网格,那么当两个不兼容的对象占据相同的位置时就会发生碰撞。处理这种类型的碰撞最简单的方法就是确保您尝试将游戏实体移动到的位置是“可用的”。如果是,则没有碰撞,并更新模型。如果它不是免费的,那么就会发生碰撞。
屏幕只是渲染模型。除了每像素碰撞检测(想想原来的旅鼠或蠕虫),不要将它用于碰撞检测。
屏幕/视图只是渲染代理。虽然您可以将模型紧紧地绑在屏幕上(例如,您只需要更新屏幕中发生变化的部分,例如移动一块时),但屏幕通常不是也不应该被视为该模型。但是,以现代计算速度,您不妨简单地在每一帧重新渲染整个可见模型。
(是的,我知道我重复了自己。这是故意的。)
现在,回答标题中未提及的第二个问题:
开始渲染时,只需在播放器左侧绘制 screen_width/cell_width/2 个单元格,在播放器右侧绘制 screen_width/cell_width/2 个单元格(假定播放器占用 1x1)。对上下做同样的事情。确保不会导致 Index-Out-Of-Bounds 异常。您可以使用超出范围的值运行 for 循环,只要在使用它们之前进行钳制/过滤即可。如果您希望仅在角色靠近时“推动”边缘,请同时跟踪当前的模型到视图参考。