2017-08-08 120 views
0

我正在努力与我的玩家或敌人的碰撞矩形和水平/墙壁之间的碰撞。SdlDotNet平台游戏:碰撞矩形A(玩家)碰撞矩形B(等级)

该级别是一维数组。

这样很好:屏幕上有一个平台和一个围绕着关卡的盒子,所以玩家和敌人都无法离开舞台。

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

在我Game_Manager类玩家侦察到敌人,它的位置是复位,当他们碰撞:

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

再次:运作良好。

但:我似乎无法得到与1D阵列(我的水平)的碰撞权利。 而且我似乎遇到了在我的关卡中获得每个块/瓦片的碰撞矩形的问题 - 我的玩家可以出去,我的敌人也可以出去。

敌人是粉红色,玩家绿色。 红块,不使用现在: See here what my level looks like and whats happening (.gif)

我已经试过:

player.PlayerCollRect.IntersectsWith(enemy.EnemyCollRect) 

我应该尝试与我的瓷砖碰撞?或者我的完整水平?如何?

我对Level.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); 
      } 
     } 
    } 
} 

代码Level01.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 }, 
     }; 
    } 
} 

代码Tile.cs:

public class Tile 
{ 
    private Surface tileImage; 
    private Point tilePosition; 

    public Rectangle tileColl; 

    private int tileWidth = 40; 
    private int tileHeight = 40; 

    public int TileWidth 
    { 
     get { return tileWidth; } 
     set { tileWidth = value; } 
    } 

    public int TileHeight 
    { 
     get { return tileHeight; } 
    } 

    public Rectangle TileColl 
    { 
     get { return tileColl; } 
     set { tileColl = value; } 
    } 

    public Tile(Point position) 
    { 
     tileImage = new Surface("tile.png"); 
     tilePosition = position; 
     tileColl = new Rectangle(tilePosition.X, tilePosition.Y, tileWidth, tileHeight); 
    } 

    public void Draw(Surface showTiles) 
    { 
     showTiles.Blit(tileImage, tilePosition); 
    } 
} 

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

回答

0

在您的经理级别中,应该有一个循环来持续更新游戏,这意味着关卡,玩家,敌人,抛射物......任何可以在游戏过程中发生任何变化的东西。我知道你使用你使用的库中的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(); 
} 

所以,我们必须精灵的坐标与那些瓦片。在这种情况下,由于您使用库中的函数:'InteractWith',所以您必须创建碰撞矩形,因为它不适用于您的自定义对象'瓦片'。这意味着我们必须从“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); 
     } 
    } 

!!!请务必使干将在瓷砖类,所以我们有机会获得这些坐标和维度!!!

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

还有多种方法可以做到这一点,但由于我们可能有多个可以与拼贴相撞的精灵,所以最好的做法是检查精灵本身的碰撞。根据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类中创建了函数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添加一个图块。因此,所有包含0的字节都不再使用。

0

假设水平不移动,只有玩家做,你可以实际上由玩家X和Y坐标进行碰撞。这使得基本无限大的水平成为可能,而不会由于必须为每个瓦片测试碰撞而造成滞后。

你所做的只是在与玩家直接相邻的瓷砖上进行测试碰撞。由于你的玩家和每个街区一样大,所以它给出了9个牌来测试碰撞(玩家上面三个,玩家三个,玩家下面三个)。

我不知道你的播放器X和Y是如何存储的,但我们假设它在xPos和yPos下的播放器类中。对于所有的瓷砖,瓷砖的宽度为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)= 1.所以,使用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,40和79之间的玩家X为1 ,80到119之间的玩家X是2,依此类推。然后围绕你的玩家的九个瓦片只不过是数字 - 只要检查相应的byteArray [x,y]> 0,如果是的话,那么你的玩家需要回到他们的起始位置。

我希望对此有足够的解释!

+0

这不符合我的要求。这个想法看起来不错,我看到了逻辑,但奇怪的是我不想要的检测碰撞: 当我跳跃时,我可以在没有任何位置重置的情况下穿过任何平台。 当我稍后跳起并进入一个空的空间(没有平台,没有碰撞)时,碰撞被检测到并且我的球员的位置被重置。 –

0

感谢您的意见。我用Rectangle1.InteractsWith(Reactangle2),它工作正常。 另外我的玩家正在下降,当它与Rectangle2互动时,它会得到它的坐标并因此进入矩形。

谢谢!