我的tilemap与碰撞解决有问题。我有一个球从瓷砖上碰撞出来。碰撞工作正常,除了两个瓷砖之间发生碰撞时。然后我的碰撞分辨率变得很脆弱,球飞向错误的方向。我的球是一个矩形,瓷砖是矩形。
这是一个演示问题的.gif文件:
球应该一次一个地反射出来。
算法的工作原理如下:
我已经读到这种最小位移技术的问题在于,在解决碰撞后,球可能会移动到另一次碰撞。修复应该是,只是第二次运行算法。我试过这个,但没有用。这是我在C ++中的碰撞处理代码:
void PlayState::handleCollisions() {
// Get ball bounding box.
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();
// Find out the nearby tiles.
int leftTile = (int)floor((float)ballBounds.left / level1.TILE_WIDTH);
int rightTile = (int)ceil(((float)(ballBounds.left + ballBounds.width) / level1.TILE_WIDTH)) - 1;
int topTile = (int)floor((float)ballBounds.top / level1.TILE_HEIGHT);
int bottomTile = (int)ceil(((float)(ballBounds.top + ballBounds.height) / level1.TILE_HEIGHT)) - 1;
// For each potentially colliding tile,
for(int y = topTile; y <= bottomTile; ++y) {
for(int x = leftTile; x <= rightTile; ++x) {
// If this tile is collidable,
TileCollision collision = getCollision(x, y);
if(collision == Impassable) {
// Determine collision depth (with direction) and magnitude.
sf::FloatRect tileBounds = getTileBounds(x, y);
sf::Vector2f depth = Collision::getIntersectionDepth(ballBounds, tileBounds);
if(depth != sf::Vector2f(0, 0)) {
float absDepthX = std::abs(depth.x);
float absDepthY = std::abs(depth.y);
// Resolve the collision along the shallow axis.
if(absDepthY < absDepthX) {
// Resolve the collision along the Y axis.
ball.sprite.setPosition(ball.sprite.getPosition().x, ball.sprite.getPosition().y + depth.y);
// Perform further collisions with the new bounds.
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();
// Y-distance to the tile center.
if(distanceY < 0) {
std::cout << "Collided from TOP." << std::endl;
ball.velocity.y = -ball.velocity.y;
}
else {
std::cout << "Collided from BOTTOM." << std::endl;
ball.velocity.y = -ball.velocity.y;
}
}
else {
// Resolve the collision along the X axis.
ball.sprite.setPosition(ball.sprite.getPosition().x + depth.x, ball.sprite.getPosition().y);
// Perform further collisions with the new bounds.
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();
// X-distance to the tile center.
if(distanceX < 0) {
std::cout << "Collided from LEFT." << std::endl;
ball.velocity.x = -ball.velocity.x;
}
else {
std::cout << "Collided from RIGHT." << std::endl;
ball.velocity.x = -ball.velocity.x;
}
}
}
}
}
}
}
我尝试第二次运行算法,如下所示:
void PlayState::update(sf::Time deltaTime) {
ball.update(deltaTime);
handleCollisions();
handleCollisions();
}
我该怎么做才能解决这个问题?
答案 0 :(得分:1)
我已经从头开始在flash游戏中实现了类似的碰撞检测。我发现将球模型化为单点并将其作为矢量的路径更容易,更准确。为了允许所需的球尺寸 - 半径r填充被添加到框的边缘。
该问题变为检测球的线段(ptFrom..ptTo)与附近区块的交点。如果有多个交叉点,请使用最近的ptFrom。使用线段的反射剩余部分重复碰撞检测,直到不再发生碰撞。
答案 1 :(得分:0)
进行碰撞检测的唯一正确方法是在计算中使用速度矢量。
您不仅要测试离散位置,还应测试它们之间的整线。至少你将能够检测到所有碰撞。 E. g。当您仅测试当前位置(标记为1和2)时,无法检测到下图中的一个
" " "|2
" " /<- second 'collision' point
" " /|
___/_|
/^--collision point
1
您应该至少知道当前和之前的坐标(当前减去速度减去delta t)。因此,您必须计算可能的碰撞点的坐标,找到哪一个更接近开始,然后在碰撞点后重新计算路径。
ADD:如何查找和计算碰撞点。
首先,你必须找到你的球穿过的边缘。对于矩形网格,它并不太难:如果前一个和当前坐标位于不同的行(列)中,则行(列)边缘交叉。
| | | | | 1 | | | 1
--+-----+-- --+-----+-- --+-----+-- --+-----+--
| 1 | | | 1 | | | |
| 0 | | 0 | | 0 | | 0 |
--+-----+-- --+-----+-- --+-----+-- --+-----+--
| | | | | | | |
none crossed column row both
然后你应该根据碰撞边缘方程(x = xc - 交叉垂直边坐标)求解球轨迹方程(x0,y0 - 前一点,vx,vy - 速度):
x = x0 + t⋅vx y = y0 + t⋅vy x = xc
和方程x = xc
的垂直墙,找到你需要求解方程的yc = y
(y坐标)和tc = t
(时间)
t = (xc - x0)/vx y = y0 +t⋅vy (time and y coord of collision)
对于水平边缘,同样适用于x和y交换。
当交叉x和y两条边时,选择一条早先交叉的(具有较少的t)
| 1
| /
|/ Here, point a is a row collision
b*<--- b is a column collision
/| t[a] should be less than t[b]
a/ | because is closer to trajectory start
-----*--+-------- xc[row]
/ |
0 |
|
yc[col]
当然,当你计算一次碰撞并更新坐标和速度时,你应该这样做 重新计算碰撞,因为可能有更多的碰撞:
----*--+
/ \ |
0 \|
*
/|
1