我在玩家或敌人的碰撞矩形与水平/墙壁之间的碰撞中挣扎。
级别是一维数组。
这画得很好:一个平台和一个水平的盒子,所以玩家和敌人都不能离开竞技场。
在我的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的瓷砖(见上文),如果有碰撞。
答案 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
坐标,因此会进入矩形。
谢谢!