SdlDotNet平台游戏:碰撞矩形A(玩家)ntersectswith碰撞矩形B(等级)

时间:2017-08-08 18:52:41

标签: c# arrays sdl collision-detection rectangles

我在玩家或敌人的碰撞矩形与水平/墙壁之间的碰撞中挣扎。

级别是一维数组。

这画得很好:一个平台和一个水平的盒子,所以玩家和敌人都不能离开竞技场。

在我的Game_Manager类中(处理游戏,它的更新和碰撞)绘制关卡,玩家和一个敌人。运作良好,但玩家和敌人可以离开该区域,因为碰撞检测不正确:

在我的Game_Manager类中,玩家会检测到敌人,并在碰撞时重置它的位置:

struct
{
    union
    {
        float a;
        int32_t b;
        double c;
    };
    int identifier;
};

再次:效果很好。

但是:我似乎无法与1D阵列(我的级别)发生冲突。 而且我似乎在我的关卡中获得每个块/瓦片的碰撞矩形有问题 - 我的玩家可以离开,我的敌人也可以离开。

敌人是粉红色的,玩家是绿色的。 红色块目前尚未使用: See here what my level looks like and whats happening (.gif)

我试过了:

          if(player.PlayerCollRect.IntersectsWith(enemy.enemyCollRect))
        {
            player.playerPosition = playerStartPosition;
        }

我应该尝试与我的瓷砖碰撞吗?还是我的完整水平?怎么样?

我的Level.cs代码

player.PlayerCollRect.IntersectsWith(enemy.EnemyCollRect)

Level01.cs的代码:

public abstract class Level
{
    protected byte[,] byteArray;
    public Tile[,] tileArray;

    public Rectangle levelTileColl;

    public Level()
    {
        CreateTileArray();
        tileArray = new Tile[byteArray.GetLength(0), byteArray.GetLength(1)];

        CreateWorld();
    }

    protected abstract void CreateTileArray();

    private void CreateWorld()
    {
        for (int r = 0; r < byteArray.GetLength(0); r++)
        {
            for (int c = 0; c < byteArray.GetLength(1); c++)
            {
                if(byteArray[r, c] == 1)
                {
                    tileArray[r, c] = new Tile(new Point(c * 40, r * 40));
                    //tileArray[r, c] = new Tile(new Point(c * tile.TileWidth, r * tile.TileHeight));
                }
            }
        }
    }

    public void Draw(Surface showTiles)
    {
        for (int r = 0; r < byteArray.GetLength(0); r++)
        {
            for (int c = 0; c < byteArray.GetLength(1); c++)
            {
                if (tileArray[r, c] != null)
                    tileArray[r, c].Draw(showTiles);
            }
        }
    }
}

Tile.cs的代码:

    public class Level01 : Level
{
    protected override void CreateTileArray()
    {
        byteArray = new Byte[,]
        {
            { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        };
    }
}

- 编辑 - 好的,让我以不同的方式说明: 如何检查我的玩家是否在我的关卡中的tileArray中触摸/碰撞/与瓷砖相交? 我有一个tileArray,我想检查碰撞与任何1的瓷砖(见上文),如果有碰撞。

3 个答案:

答案 0 :(得分:0)

假设等级没有移动,只有玩家移动,你实际上可以通过玩家X和Y坐标进行碰撞。这样就可以制作一个基本上无限大小的级别,而不会因为必须为每个单独的区块测试碰撞而导致滞后。

您所做的只是在与玩家紧邻的拼贴中测试碰撞。由于你的玩家和每个街区一样大,所以它会给出九个牌来测试碰撞(三个在玩家之上,三个与玩家在同一级别,三个在玩家之下)。

我不知道你的玩家X和Y是如何存储的,但是让我们说它是在xPos和yPos下的Player类中。对于所有图块,图块宽度为40,图块高度为40,但是如果您想要更改它,请将allWidth声明为40,将allHeight声明为40.现在让我们设置碰撞测试:

for (int x = Math.Floor(player.xPos/allWidth) - 1; x < Math.Floor(player.xPos/allWidth) + 2; x++){
    for (int y = Math.Floor(player.yPos/allHeight) - 1; y < Math.Floor(player.xPos/allHeight) + 2; y++){
        if (byteArray[x, y] > 0)
            player.playerPosition = player.StartPosition;
    }
}

那我刚刚在那里做了什么?

Math.Floor(player.xPos / allWidth):让我们说你的玩家的X是62. Math.Floor(player.xPos / allWidth)= Math.Floor(62/40)=因此,使用for循环,这意味着我们正在查看byteArray [0,y],byteArray [1,y]和byteArray [2,y]。现在让我们说你的玩家Y是152.然后Math.Floor(player.yPos / allHeight)= Math.Floor(152/40)= 3.现在有了循环,我们只是想与byteArray发生冲突[0,2],byteArray [0,3],byteArray [0,4],byteArray [1,2],byteArray [1,3],byteArray [1,4],byteArray [2,2],byteArray [ 2,3]和byteArray [2,4]。

基本上,你的角色的X和Y在byteArray上被转换为X和Y位置,通过向下舍入并除以图块大小:0到39之间的玩家X是0,玩家X在40到40之间79是1,80到119之间的玩家X是2,依此类推。然后你的播放器周围的九个瓦片只不过是数字 - 只需检查相应的byteArray [x,y]&gt; 0,如果是,那么你的玩家需要回到他们的起始位置。

我希望这个解释得足够好!

答案 1 :(得分:0)

在你的经理课中应该有一个循环,constanlty更新游戏,这意味着关卡,玩家,敌人,射弹......,任何在比赛期间都可能发生任何变化的事情。我知道你使用你使用的库中的Thick事件。

不是在管理器中创建和更新所有内容,而是最好在那里执行属于该级别的所有内容。例如,在您的经理中,而不是:

Level level = new Level();
Sprite player = new Player();
Sprite enemy = new Enemy();
Projectile[] projectiles =  new... (and then filled with projectiles)

private void Events_Tick(object sender, TickEventArgs e){
    player.update();
    enemy.update();
    projectiles.update();
    ...
}

由于精灵(让我们留下射弹)必须与关卡中的瓦片互动,让它们以这种方式进行交互可能会有些麻烦。为了使这更简单和更干净,您应该在存储tileArray的级别类中执行此操作。这样可以轻松地与其进行交互并更新属于该级别的所有内容。

所以在你的关卡课程中:

public abstract class Level
{
    protected byte[,] byteArray;
    public Tile[,] tileArray;

    Sprite player = new Player();
    Sprite enemy = new Enemy();

    public Level()
    {
        CreateTileArray();
        tileArray = new Tile[byteArray.GetLength(0), byteArray.GetLength(1)];

        CreateWorld();
    }

    protected abstract void CreateTileArray();
    private void CreateWorld() {...}
    public void Draw(Surface showTiles) {...}

    public void Update() {
        player.Update();
        enemy.Update();
    }
}

从这一点来看,精灵更容易与关卡及其中的所有内容进行交互。然后在你的经理课上你可以这样做,并保持清洁:

Level level = new Level();

private void Events_Tick(object sender, TickEventArgs e){
    level.Update();
}

因此,为了检查碰撞,我们必须将精灵的coördinates与tile的coördinates进行比较。在这种情况下,由于您使用库中的函数:'InteractWith',您必须创建碰撞矩形,因为它不适用于您的自定义对象'Tile'。这意味着我们必须从'tileArray'中的所有图块中获取coördinates并将它们放在矩形中。让我们调用包含矩形'tileCollisionArray'的新数组。

在您的关卡类中这样:

public Tile[,] tileArray;
public Rectangle[,] tileCollisionArray;

public Level()
    {
        CreateTileArray();
        tileArray = new Tile[byteArray.GetLength(0), byteArray.GetLength(1)];
        tileCollisionArray = new Rectangle[tileArray.GetLength(0), tileArray.GetLength(1)];

        CreateWorld();

        //here I convert the coördinates from the tiles to the collision rectangles
        for (int x = 0; x < tileArray.GetLength(0); x++) {
            for (int y = 0; y < tileArray.GetLength(1); y++) {
                tileCollisionArray[x,y] = new Rectangle(tileArray[x,y].GetPosX, tileArray[x,y].GetPosY, tileArray[x,y].GetTileWidth, tileArray[x,y].GetTileHeight);
        }
    }

!!!确保你在Tile类中制作getter,这样我们就可以访问这些coördinates和尺寸。!!!

所以现在我们可以使用这个InteractWith函数,这使碰撞变得更容易。接下来我们必须确保精灵可以与瓷砖碰撞,因此它们需要以某种方式链接。

还有多种方法可以做到这一点,但是因为我们可能在级别中有多个可以与tile碰撞的sprite,所以最好的做法是检查sprite本身的碰撞。根据DRY,玩家和敌人继承的抽象类Sprite是很好的。因此,当我在抽象超类中的方法中检查碰撞时,所有子类也将具有检查冲突的能力。

这样:

public abstract class Sprite {
    public void CheckCollisionSurrounding(Rectangle[,] collisionObjects) { ... }
}

public class Player: Sprite { ... }
public class Enemy: Sprite { ... }

这样我们就可以在关卡中做下一步了:

public abstract class Level {
    ...
    public void Update() {
        player.Update();
        player.CheckCollisionSurrounding(tileCollisionArray);
        enemy.Update();
        enemy.CheckCollisionSurrounding(tileCollisionArray);
    }
    ...
}

这就是sprite可以访问碰撞对象的方法,接下来是如何检查碰撞是否为真。我们已经在Sprite类中创建了函数CheckCollisionSurrounding,所以我们将检查实际的碰撞。

(更好的做法是使这个方法受到保护并将其放在精灵本身的Update函数中,但是你需要通过注入或构造函数将tileCollisionArray传递给精灵。但如果上面这个工作,这个步骤应该很容易。)

接下来,最后一步,我们可以做到:

public abstract class Sprite {

    public void CheckCollisionSurrounding(Rectangle[,] collisionObjects) {

        for (int x = 0; x < collisionObjects.GetLength(0); x++)
        {
            for (int y = 0; y < collisionObjects.GetLength(1); y++)
            {
                if (collisionObjects[x, y] != null) //saves us from possible null reference exceptions
                {
                 //so now we iterate through every collision object so see if the sprite collides with it and call an action to it if it does collide.

                    if(PlayerCollRect.IntersectsWithTop(collisionObjects[x,y].tileCollRect)) {
                        //whatever you want to happen, but let's say you want the sprite at the position of the object when colliding with it:
                        playerPosition.y = collisionObjects[x,y].GetPosY;
                    }
                    //and so on for InteractWithBottom, -Right and -Left
                    //also keep in to account the dimensions of your own sprite which I haven't here. If the sprites hits left side of tile with it's right side, you have to add the width of the sprite to the calculation.
                }
            }
        }

    }

}

最后一段代码不是很干净而且非常繁忙,所以如果以这种方式工作,那么做一些重构是很好的。

通常这是它应该如何工作并保持代码清洁和易懂。我希望它有所帮助。

编辑:我现在在编辑时看到你的问题。您已经处理仅与包含1的索引进行交互。它位于您的tileArray中;如果byteArray在该索引上为1,则向tileArray添加一个tile。因此,所有包含0的字节都不再用于此处。

答案 2 :(得分:0)

感谢您的投入。我使用了Rectangle1.InteractsWith(Reactangle2),它运行正常。 此外,我的播放器正在下降,当它与Rectangle2交互时,它会获得Position.Y坐标,因此会进入矩形。

谢谢!