平铺地图碰撞检测

时间:2010-04-05 00:24:28

标签: tiles

有很多这样的主题,但没有具体的答案。我正在以传统的方式绘制一个瓷砖地图(两个用于循环)并保持我的播放器居中,除非到达地图的边缘。我如何创建碰撞检测?我需要知道如何将数组中的tile位置转换为我认为的屏幕坐标。

2 个答案:

答案 0 :(得分:2)

我将为您提供我为point / 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;
}

答案 1 :(得分:1)

取决于型号。

如果您的模型(数据)是网格,则只有当两个不兼容的对象占据相同位置时才会发生冲突。处理此类碰撞的最简单方法是确保将游戏实体移动到哪里“可用”。如果是,则不会发生冲突,更新模型。如果它不是免费的,那么就会发生碰撞。

屏幕只是渲染模型。除了每像素碰撞检测(想想原始的旅鼠或蠕虫)之外,不会将其用于碰撞检测

屏幕/视图只是渲染代理。虽然您可以将模型紧紧地绑在屏幕上(例如,您只需要更新屏幕中已发生变化的部分,例如一块被移动),屏幕不是,也不应该,通常被认为是模型的一部分。但是,凭借现代计算速度,您可以简单地每帧重新渲染整个可见模型。

(是的,我知道我重复了一遍。这是故意的。)

现在,回答标题中未提及的次要问题:

当您开始渲染时,只需向左侧绘制screen_width / cell_width / 2单元格,在播放器右侧绘制screen_width / cell_width / 2单元格(假设玩家采用1x1)。对于上下都做同样的事情。确保不会导致Index-Out-Of-Bounds异常。只要在使用它们之前进行钳位/过滤,就可以运行带有越界值的for循环。如果您希望仅在角色靠近时使角色“推”到边缘,也要跟踪当前的模型到视图参考。