如何才能使碰撞更加牢固?
我正在使用 XNA 用 C# 开发一款游戏,并且借助 Nick Gravelyn 的教程,我一直在学习 C# 程序,但我遇到了障碍。当我使用尼克的碰撞系统时,我没有使用他的玩家代码。我正在使用一个基于 Fatso784 的教程并经过修改的教程。因此,我无法使我的修改版本的碰撞系统正常工作。我已经做到了将玩家推出某些图块的程度,但我需要它更坚固,因为玩家仍然可以偶尔穿过墙壁。我很确定我处理碰撞的方式是错误的,但可能碰撞有点糊状。所以这是我的玩家类的相关代码,移动代码:
public void Move()
{
pos.X = bounds.X;
pos.Y = bounds.Y;
offsetPos.X = bounds.Width;
offsetPos.Y = bounds.Height;
if (frameCount % delay == 0)
{
switch (direction)
{
case "stand":
if (sideCollide == "none")
{
Equalize(2);
}
else if (sideCollide == "left")
{
speed += 1f;
}
else if (sideCollide == "right")
{
speed -= 1f;
}
bounds.X += (int)speed;
if (frameCount / delay >= 8)
frameCount = 0;
srcBounds = new Rectangle(frameCount / delay * 64, 0, 64, 64);
break;
case "left":
if (sideCollide != "left")
{
if (speed > -maxspeed)
{
speed -= acceleration;
}
else if (speed < -maxspeed)
{
speed -= acceleration;
speed += drag;
Equalize(2);
}
speed += friction;
}
bounds.X += (int)speed;
if (frameCount / delay >= 4)
frameCount = 0;
srcBounds = new Rectangle(frameCount / delay * 64, 64, 64, 64);
break;
case "right":
if (sideCollide != "right")
{
if (speed < maxspeed)
{
speed += acceleration;
}
else if (speed > maxspeed)
{
speed += acceleration;
speed -= drag;
Equalize(2);
}
speed -= friction;
}
bounds.X += (int)speed;
if (frameCount / delay >= 4)
frameCount = 0;
srcBounds = new Rectangle(frameCount / delay * 64, 64, 64, 64);
break;
case "up":
if (speed > -4 && speed < 4)
srcBounds.Y = 128;
else
srcBounds.Y = 64;
if (srcBounds.Y == 0 || srcBounds.Y == 128)
{
if (jumpCount < 2)
{
if (frameCount / delay >= 9)
frameCount = 0;
}
else if (jumpCount > 2 && jumpCount <= 10)
{
if (frameCount / delay > 3)
frameCount = 2 * delay;
}
else if (jumpCount > 10 && jumpCount <= 18)
{
if (frameCount / delay > 5)
frameCount = 4 * delay;
}
else if (jumpCount > 18)
{
if (frameCount / delay >= 9)
frameCount = 0;
}
srcBounds = new Rectangle(frameCount / delay * 64, 128, 64, 64);
}
else if (srcBounds.Y == 64)
{
if (frameCount / delay >= 4)
frameCount = 0;
if (jumpCount <= 10)
srcBounds = new Rectangle((frameCount / delay) / 2 * 64, 64, 64, 64);
else
srcBounds = new Rectangle(frameCount / delay * 64, 64, 64, 64);
}
if (jumpCount == 0)
startY = bounds.Y;
bounds = new Rectangle(bounds.X + (int)speed,
(jumpCount - 10) * (jumpCount - 10) - 100 + startY, 64, 64);
jumpCount++;
if (bounds.Y > startY)
{
bounds.Y = startY;
direction = "stand";
jumpCount = 0;
}
break;
}
}
frameCount++;
}
和碰撞代码:
public void CollideOutside(TileMap tilemap)
{
Point cell = Engine.PointCell(PlayerCenter);
Point? upLeft = null, Up = null, upRight = null, Right = null, downRight = null, Down = null, downLeft = null, Left = null;
if (cell.Y > 0)
{
Up = new Point(cell.X, cell.Y - 1);
}
if (cell.Y < tilemap.collisionMap.HeightinPixels)
{
Down = new Point(cell.X, cell.Y + 1);
}
if (cell.X > 0)
{
Left = new Point(cell.X - 1, cell.Y);
}
if (cell.X < tilemap.collisionMap.WidthinPixels)
{
Right = new Point(cell.X + 1, cell.Y);
}
if (cell.X > 0 && cell.Y > 0)
{
upLeft = new Point(cell.X - 1, cell.Y - 1);
}
if (cell.X < tilemap.collisionMap.WidthinPixels - 1 && cell.Y > 0)
{
upRight = new Point(cell.X + 1, cell.Y - 1);
}
if (cell.X > 0 && cell.Y < tilemap.collisionMap.HeightinPixels - 1)
{
downLeft = new Point(cell.X - 1, cell.Y + 1);
}
if (cell.X < tilemap.collisionMap.WidthinPixels - 1 && cell.Y < tilemap.collisionMap.Height - 1)
{
downRight = new Point(cell.X + 1, cell.Y + 1);
}
if (Up != null && tilemap.collisionMap.GetCellIndex(Up.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Up.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (Down != null && tilemap.collisionMap.GetCellIndex(Down.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Down.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (Right != null && tilemap.collisionMap.GetCellIndex(Right.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Right.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = -1f;
sideCollide = "right";
}
else
{
sideCollide = "none";
}
}
if (Left != null && tilemap.collisionMap.GetCellIndex(Left.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Left.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = 1f;
sideCollide = "left";
}
else
{
sideCollide = "none";
}
}
if (upLeft != null && tilemap.collisionMap.GetCellIndex(upLeft.Value) == 1)
{
Rectangle rect = Engine.CreateCell(upLeft.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (upRight != null && tilemap.collisionMap.GetCellIndex(upRight.Value) == 1)
{
Rectangle rect = Engine.CreateCell(upRight.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (downLeft != null && Left != null && tilemap.collisionMap.GetCellIndex(downLeft.Value) == 1)
{
Rectangle rect = Engine.CreateCell(downLeft.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = 1f;
sideCollide = "left";
}
}
if (downRight != null && Right != null && tilemap.collisionMap.GetCellIndex(downRight.Value) == 1)
{
Rectangle rect = Engine.CreateCell(downRight.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = -1f;
sideCollide = "right";
}
}
if (Right == null && Left == null)
{
sideCollide = "none";
}
}
public Rectangle Boundary
{
get
{
Rectangle rect = bounds;
rect.X = (int)pos.X;
rect.Y = (int)pos.Y;
return rect;
}
}
那么我如何改进碰撞?
I'm working on a game in C# with XNA and I've been learning program in C# thanks to Nick Gravelyn's tutorials, but I've hit a snag. While I'm using Nick's collision system, I'm not using his player code. I'm using one that's based on a tutorial by Fatso784 that I've modified. So, as a result, I'm having trouble making my modified version of the collision system work properly. I've got it to the point that it pushes the player out of certain tiles, but I need it to be more solid because the player is still able walk through walls occasionally. I'm pretty sure I'm handling the collision wrong, but it could be that the collision is a little mushy. So here's the relevant code from my player class, the move code:
public void Move()
{
pos.X = bounds.X;
pos.Y = bounds.Y;
offsetPos.X = bounds.Width;
offsetPos.Y = bounds.Height;
if (frameCount % delay == 0)
{
switch (direction)
{
case "stand":
if (sideCollide == "none")
{
Equalize(2);
}
else if (sideCollide == "left")
{
speed += 1f;
}
else if (sideCollide == "right")
{
speed -= 1f;
}
bounds.X += (int)speed;
if (frameCount / delay >= 8)
frameCount = 0;
srcBounds = new Rectangle(frameCount / delay * 64, 0, 64, 64);
break;
case "left":
if (sideCollide != "left")
{
if (speed > -maxspeed)
{
speed -= acceleration;
}
else if (speed < -maxspeed)
{
speed -= acceleration;
speed += drag;
Equalize(2);
}
speed += friction;
}
bounds.X += (int)speed;
if (frameCount / delay >= 4)
frameCount = 0;
srcBounds = new Rectangle(frameCount / delay * 64, 64, 64, 64);
break;
case "right":
if (sideCollide != "right")
{
if (speed < maxspeed)
{
speed += acceleration;
}
else if (speed > maxspeed)
{
speed += acceleration;
speed -= drag;
Equalize(2);
}
speed -= friction;
}
bounds.X += (int)speed;
if (frameCount / delay >= 4)
frameCount = 0;
srcBounds = new Rectangle(frameCount / delay * 64, 64, 64, 64);
break;
case "up":
if (speed > -4 && speed < 4)
srcBounds.Y = 128;
else
srcBounds.Y = 64;
if (srcBounds.Y == 0 || srcBounds.Y == 128)
{
if (jumpCount < 2)
{
if (frameCount / delay >= 9)
frameCount = 0;
}
else if (jumpCount > 2 && jumpCount <= 10)
{
if (frameCount / delay > 3)
frameCount = 2 * delay;
}
else if (jumpCount > 10 && jumpCount <= 18)
{
if (frameCount / delay > 5)
frameCount = 4 * delay;
}
else if (jumpCount > 18)
{
if (frameCount / delay >= 9)
frameCount = 0;
}
srcBounds = new Rectangle(frameCount / delay * 64, 128, 64, 64);
}
else if (srcBounds.Y == 64)
{
if (frameCount / delay >= 4)
frameCount = 0;
if (jumpCount <= 10)
srcBounds = new Rectangle((frameCount / delay) / 2 * 64, 64, 64, 64);
else
srcBounds = new Rectangle(frameCount / delay * 64, 64, 64, 64);
}
if (jumpCount == 0)
startY = bounds.Y;
bounds = new Rectangle(bounds.X + (int)speed,
(jumpCount - 10) * (jumpCount - 10) - 100 + startY, 64, 64);
jumpCount++;
if (bounds.Y > startY)
{
bounds.Y = startY;
direction = "stand";
jumpCount = 0;
}
break;
}
}
frameCount++;
}
And the collision code:
public void CollideOutside(TileMap tilemap)
{
Point cell = Engine.PointCell(PlayerCenter);
Point? upLeft = null, Up = null, upRight = null, Right = null, downRight = null, Down = null, downLeft = null, Left = null;
if (cell.Y > 0)
{
Up = new Point(cell.X, cell.Y - 1);
}
if (cell.Y < tilemap.collisionMap.HeightinPixels)
{
Down = new Point(cell.X, cell.Y + 1);
}
if (cell.X > 0)
{
Left = new Point(cell.X - 1, cell.Y);
}
if (cell.X < tilemap.collisionMap.WidthinPixels)
{
Right = new Point(cell.X + 1, cell.Y);
}
if (cell.X > 0 && cell.Y > 0)
{
upLeft = new Point(cell.X - 1, cell.Y - 1);
}
if (cell.X < tilemap.collisionMap.WidthinPixels - 1 && cell.Y > 0)
{
upRight = new Point(cell.X + 1, cell.Y - 1);
}
if (cell.X > 0 && cell.Y < tilemap.collisionMap.HeightinPixels - 1)
{
downLeft = new Point(cell.X - 1, cell.Y + 1);
}
if (cell.X < tilemap.collisionMap.WidthinPixels - 1 && cell.Y < tilemap.collisionMap.Height - 1)
{
downRight = new Point(cell.X + 1, cell.Y + 1);
}
if (Up != null && tilemap.collisionMap.GetCellIndex(Up.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Up.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (Down != null && tilemap.collisionMap.GetCellIndex(Down.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Down.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (Right != null && tilemap.collisionMap.GetCellIndex(Right.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Right.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = -1f;
sideCollide = "right";
}
else
{
sideCollide = "none";
}
}
if (Left != null && tilemap.collisionMap.GetCellIndex(Left.Value) == 1)
{
Rectangle rect = Engine.CreateCell(Left.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = 1f;
sideCollide = "left";
}
else
{
sideCollide = "none";
}
}
if (upLeft != null && tilemap.collisionMap.GetCellIndex(upLeft.Value) == 1)
{
Rectangle rect = Engine.CreateCell(upLeft.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (upRight != null && tilemap.collisionMap.GetCellIndex(upRight.Value) == 1)
{
Rectangle rect = Engine.CreateCell(upRight.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
}
}
if (downLeft != null && Left != null && tilemap.collisionMap.GetCellIndex(downLeft.Value) == 1)
{
Rectangle rect = Engine.CreateCell(downLeft.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = 1f;
sideCollide = "left";
}
}
if (downRight != null && Right != null && tilemap.collisionMap.GetCellIndex(downRight.Value) == 1)
{
Rectangle rect = Engine.CreateCell(downRight.Value);
Rectangle playerCell = Boundary;
if (rect.Intersects(playerCell))
{
speed = -1f;
sideCollide = "right";
}
}
if (Right == null && Left == null)
{
sideCollide = "none";
}
}
public Rectangle Boundary
{
get
{
Rectangle rect = bounds;
rect.X = (int)pos.X;
rect.Y = (int)pos.Y;
return rect;
}
}
So how can I improve the collision?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这个答案主要是对蒂姆的回答的回应 - 因为他给出了非常错误的方法。 (你的问题是一个非常大的代码转储,我真的无法用那么多代码来找出错误。)
碰撞检测的技巧 - “真正的”物理引擎的方式 - 总是 将你的物体视为实体。您总是在每一帧中检查对象是否相互渗透,然后如果它们相互渗透则分离它们。
如果您仅测试移动对象的边界,您将会错过碰撞。这包括尝试通过捕捉到曲面来预测和避免碰撞的任何方法。如果这样做,您只是要求浮点精度错误,以便让您滑入对象内部。
编辑:当然,您的代码似乎都是基于整数的(
Point
和矩形
)。所以至少浮点精度不应该成为问题。但也许你有一个<
,你应该有一个<=
或类似的东西?(此外,您在很多应该使用枚举的地方都使用了字符串。)
This answer is mostly in response to Tim's answer - because he's given very much the wrong approach. (Your question is a very large code dump, I can't really play spot-the-error with that much code.)
The trick with collision detection - the way the "real" physics engines do it - is to always treat your objects as solids. You always - each frame - check objects for interpenetration, and then separate them if they interpenetrate.
If you only test for moving across the boundary of an object you are going to miss collisions. This includes any approach where you attempt to predict and avoid a collision by snapping onto the surface. If you do this, you are just asking for floating-point precision errors to let you slip inside objects.
EDIT: of course, your code all seems to be integer-based (
Point
andRectangle
). So at least floating-point precision should not be an issue. But maybe you have a<
where you should have a<=
or something to that effect?(Also you're using strings in a lot of places where you very much should be using enumerations.)