所以,我现在通过尝试为我的游戏引擎制作一个简单的物理引擎来重新发明轮子(并且学到了很多东西)。我一直在互联网上搜索,试图(失败)解决我目前的问题。在这个问题上有很多资源,但我所发现的似乎都不适用于我的情况。
简单地说,问题:当两个矩形发生碰撞时,碰撞分辨率在某些角上不起作用。失败的原因取决于矩形的尺寸。我正在寻找的是一个“最短的重叠”类型的解决冲突或另一个相当简单的解决方案(我是开放的建议!)。(滚动下来,以获得更好的解释和插图)。
警告:下面的代码可能不是很有效.
首先,这是我的物理回路。它只需遍历所有游戏实体,并检查它们是否与任何其他游戏实体发生冲突。它不是有效的(n^2和所有这些),但它现在起作用了。
updatePhysics: function(step) {
// Loop through entities and update positions based on velocities
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled) {
switch (entity.entityType) {
case VroomEntity.KINEMATIC:
entity.pos.x += entity.vel.x * step;
entity.pos.y += entity.vel.y * step;
break;
case VroomEntity.DYNAMIC:
// Dynamic stuff
break;
}
}
}
// Loop through entities and detect collisions. Resolve collisions as they are detected.
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled && entity.entityType !== VroomEntity.STATIC) {
for (var targetID in Vroom.entityList) {
if (targetID !== entityID) {
var target = Vroom.entityList[targetID];
if (target.physicsEnabled) {
// Check if current entity and target is colliding
if (Vroom.collideEntity(entity, target)) {
switch (entity.collisionType) {
case VroomEntity.DISPLACE:
Vroom.resolveTestTest(entity, target);
break;
}
}
}
}
}
}
}
},这是实际碰撞检测的代码。这似乎也没问题。
collideEntity: function(entity, target) {
if (entity.getBottom() < target.getTop() || entity.getTop() > target.getBottom() || entity.getRight() < target.getLeft() || entity.getLeft() > target.getRight()) {
return false;
}
return true;
},这里是问题开始出现的地方。我希望实体被“推”出目标实体,并将速度设置为0。只要实体和目标都是完美的正方形,就可以很好地工作。如果假设实体(gif中的玩家图形)是一个矩形,那么当与目标(正方形)碰撞最长的边(X轴)时,碰撞将“滑动”。如果我交换播放器的尺寸,使它是短和宽,那么同样的问题出现在Y轴代替。
resolveTestTest: function(entity, target) {
var normalizedX = (target.getMidX() - entity.getMidX());
var normalizedY = (target.getMidY() - entity.getMidY());
var absoluteNormalizedX = Math.abs(normalizedX);
var absoluteNormalizedY = Math.abs(normalizedY);
console.log(absoluteNormalizedX, absoluteNormalizedY);
// The collision is comming from the left or right
if (absoluteNormalizedX > absoluteNormalizedY) {
if (normalizedX < 0) {
entity.pos.x = target.getRight();
} else {
entity.pos.x = target.getLeft() - entity.dim.width;
}
// Set velocity to 0
entity.vel.x = 0;
// The collision is comming from the top or bottom
} else {
if (normalizedY < 0) {
entity.pos.y = target.getBottom();
} else {
entity.pos.y = target.getTop() - entity.dim.height;
}
// Set velocity to 0
entity.vel.y = 0;
}
},Y轴上的碰撞与这些形状的一起工作

X轴滑块上的碰撞与这些形状

我能做些什么来解决这个滑倒的问题?在过去的5天里,我一直对此大发雷霆,所以如果有人能帮助我朝着正确的方向前进,我将不胜感激!
谢谢您:)
--编辑:--
如果只沿左或右向一个方向移动,则滑动也会发生。

--编辑2种工作代码:--关于工作代码的示例,参见下面的答案!
发布于 2017-09-13 11:31:20
您所犯的重要逻辑错误是这一行:
if (absoluteNormalizedX > absoluteNormalizedY) {
这只在两个实体都是正方形的情况下才能工作。
为你的X-滑动例子考虑一个近乎极端的例子:如果它们几乎碰到了拐角处:

虽然图表有点夸张,但在这种情况下,您可以看到absoluteNormalizedX < absoluteNormalizedY --您的实现将继续解决垂直冲突,而不是预期的水平冲突。
另一个错误是,无论碰撞发生在哪一边,总是将相应的速度分量设置为零:如果该分量是在与正常碰撞方向相反的方向,则只能对该分量进行零化,否则您将无法从表面移动。
克服这一问题的一个好方法是,在进行碰撞检测时也要记录碰撞脸:
collideEntity: function(entity, target) {
// adjust this parameter to your liking
var eps = 1e-3;
// no collision
var coll_X = entity.getRight() > target.getLeft() && entity.getLeft() < target.getRight();
var coll_Y = entity.getBottom() > target.getTop() && entity.getTop() < target.getBottom();
if (!(coll_X && coll_Y)) return 0;
// calculate bias flag in each direction
var bias_X = entity.targetX() < target.getMidX();
var bias_Y = entity.targetY() < target.getMidY();
// calculate penetration depths in each direction
var pen_X = bias_X ? (entity.getRight() - target.getLeft())
: (target.getRight() - entity.getLeft());
var pen_Y = bias_Y ? (entity.getBottom() - target.getUp())
: (target.getBottom() - entity.getUp());
var diff = pen_X - pen_Y;
// X penetration greater
if (diff > eps)
return (1 << (bias_Y ? 0 : 1));
// Y pentration greater
else if (diff < -eps)
return (1 << (bias_X ? 2 : 3));
// both penetrations are approximately equal -> treat as corner collision
else
return (1 << (bias_Y ? 0 : 1)) | (1 << (bias_X ? 2 : 3));
},
updatePhysics: function(step) {
// ...
// pass collision flag to resolver function
var result = Vroom.collideEntity(entity, target);
if (result > 0) {
switch (entity.collisionType) {
case VroomEntity.DISPLACE:
Vroom.resolveTestTest(entity, target, result);
break;
}
}
// ...
}为了提高效率,使用位标志而不是布尔数组。然后,可以将解析器函数重写为:
resolveTestTest: function(entity, target, flags) {
if (!!(flags & (1 << 0))) { // collision with upper surface
entity.pos.y = target.getTop() - entity.dim.height;
if (entity.vel.y > 0) // travelling downwards
entity.vel.y = 0;
}
else
if (!!(flags & (1 << 1))) { // collision with lower surface
entity.pos.y = target.getBottom();
if (entity.vel.y < 0) // travelling upwards
entity.vel.y = 0;
}
if (!!(flags & (1 << 2))) { // collision with left surface
entity.pos.x = target.getLeft() - entity.dim.width;
if (entity.vel.x > 0) // travelling rightwards
entity.vel.x = 0;
}
else
if (!!(flags & (1 << 3))) { // collision with right surface
entity.pos.x = target.getRight();
if (entity.vel.x < 0) // travelling leftwards
entity.vel.x = 0;
}
},请注意,与您的原始代码不同,上面的代码还允许角碰撞-即速度和位置沿两个轴被解析。
发布于 2017-09-19 18:29:58
我的工作代码
因此,在这只神奇的@meowgoesthedog狗的帮助和指导下,我终于走上了正确的轨道,找到了我想要的东西。问题(正如@meowgoesthedog所指出的)是,我的代码实际上只适用于正方形。解决方案是检查碰撞物体的交点,并以最短交点为基础进行求解。注:如果你需要精确的物理和快速移动的物体,这可能不是一个合适的解决方案。,求交深度的代码是基于这个:https://github.com/kg/PlatformerStarterKit/blob/0e2fafb8dbc845279fe4116c37b6f2cdd3e636d6/RectangleExtensions.cs,与这个项目相关的:https://msdn.microsoft.com/en-us/library/dd254916(v=xnagamestudio.31).aspx。
这里是我的工作代码:
除了一些函数的更好的名称之外,我的物理循环没有太多的变化。
updatePhysics: function(step) {
// Loop through entities and update positions based on velocities
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled) {
switch (entity.entityType) {
case VroomEntity.KINEMATIC:
entity.pos.x += entity.vel.x * step;
entity.pos.y += entity.vel.y * step;
break;
case VroomEntity.DYNAMIC:
// Dynamic stuff
break;
}
}
}
// Loop through entities and detect collisions. Resolve collisions as they are detected.
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled && entity.entityType !== VroomEntity.STATIC) {
for (var targetID in Vroom.entityList) {
if (targetID !== entityID) {
var target = Vroom.entityList[targetID];
if (target.physicsEnabled) {
// Check if current entity and target is colliding
if (Vroom.collideEntity(entity, target)) {
switch (entity.collisionType) {
case VroomEntity.DISPLACE:
Vroom.resolveDisplace(entity, target);
break;
}
}
}
}
}
}
}
},碰撞检测也保持不变。
collideEntity: function(entity, target) {
if (entity.getBottom() < target.getTop() || entity.getTop() > target.getBottom() || entity.getRight() < target.getLeft() || entity.getLeft() > target.getRight()) {
return false;
}
return true;
},下面是基本解决问题的代码。代码中的注释应该很好地解释它所做的事情。
getIntersectionDepth: function(entity, target) {
// Calculate current and minimum-non-intersecting distances between centers.
var distanceX = entity.getMidX() - target.getMidX();
var distanceY = entity.getMidY() - target.getMidY();
var minDistanceX = entity.halfDim.width + target.halfDim.width;
var minDistanceY = entity.halfDim.height + target.halfDim.height;
// If we are not intersecting at all, return 0.
if (Math.abs(distanceX) >= minDistanceX || Math.abs(distanceY) >= minDistanceY) {
return {
x: 0,
y: 0,
};
}
// Calculate and return intersection depths.
var depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
var depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
return {
x: depthX,
y: depthY,
};
},以下是更新的解析函数。在确定碰撞轴时,先将交点深度计算在内,然后在确定碰撞轴的方向时,用交点深度的符号表示碰撞轴的相交深度。
resolveDisplace: function(entity, target) {
var intersection = Vroom.getIntersectionDepth(entity, target);
if (intersection.x !== 0 && intersection.y !== 0) {
if (Math.abs(intersection.x) < Math.abs(intersection.y)) {
// Collision on the X axis
if (Math.sign(intersection.x) < 0) {
// Collision on entity right
entity.pos.x = target.getLeft() - entity.dim.width;
} else {
// Collision on entity left
entity.pos.x = target.getRight();
}
entity.vel.x = 0;
} else if (Math.abs(intersection.x) > Math.abs(intersection.y)) {
// Collision on the Y axis
if (Math.sign(intersection.y) < 0) {
// Collision on entity bottom
entity.pos.y = target.getTop() - entity.dim.height;
} else {
// Collision on entity top
entity.pos.y = target.getBottom();
}
entity.vel.y = 0;
}
}
},谢谢大家的帮助!
发布于 2017-09-12 09:51:41
问题可能是,您正在根据相同的位置纠正X和Y冲突:
X位置被纠正:球员被移动到左边。Y位置被修正:球员被向上移动。你可能需要再次“得到”球员的位置,在检查之间。
https://stackoverflow.com/questions/46172953
复制相似问题