当两个实体之间的碰撞导致与第三个实体的第二次碰撞时,我不知道如何处理我的碰撞响应。
蓝色箭头是最右边实体的速度,下面的数字是单个碰撞响应。
单次碰撞
在开始时,最左边和中间的实体不会相互碰撞,但是在最右边的实体与中间实体碰撞之后,速度中间实体获得现在使它与最左边的实体碰撞
最终结果应如何
或者如果最左边的实体是墙 - > 0,0,-v
当前实施:检查冲突,直到完成所有操作
碰撞处理前的速度
如果实体速度因碰撞而发生变化,请执行新的检查
1.碰撞检查后:
碰撞检查后:
3.碰撞检查后:
4.碰撞检查后:
第n次碰撞检查后:
问题:正如我们清楚地看到的那样,通过这种方式进行过多的碰撞检查以解决简单的碰撞问题。我该如何改进呢?
使用sfml编写的示例代码:
我的测试用例中有3种类型的实体:
实体,速度和boundingRect:
碰撞响应:
示例代码中的控件
#include <SFML/Graphics.hpp>
#include <iostream>
const sf::Vector2f entitySize(16.f, 16.f);
bool isMovingUp = false;
bool isMovingDown = false;
bool isMovingRight = false;
bool isMovingLeft = false;
enum Category
{
wall,
pushable,
passable,
};
class Entity
{
public:
Entity(sf::Vector2f position, sf::Color color, Category type) : position(position), type(type)
{
shape.setSize(entitySize);
shape.setFillColor(sf::Color::Transparent);
shape.setOutlineColor(color);
shape.setOutlineThickness(1.f);
}
sf::Vector2f position;
sf::Vector2f velocity;
sf::Vector2f adjustedVelocity;
sf::Vector2f defaultVelocity;
sf::RectangleShape shape;
Category type;
};
void DrawEntity(sf::RenderWindow& window, Entity& entity)
{
entity.shape.setPosition(entity.position - entity.shape.getSize() / 2.f);
window.draw(entity.shape);
}
sf::FloatRect GetBoundingRect(const Entity& entity)
{
return sf::FloatRect(entity.position+entity.velocity - entitySize / 2.f, entitySize);
}
void HandleCollision(std::vector<Entity>& entities)
{
bool allCollisionsChecked = false;
while(!allCollisionsChecked)
{
allCollisionsChecked = true;
// Pair all possible combinations, but only once per pair
for (auto first = entities.begin(); first != entities.end(); ++first)
{
for (auto second = std::next(first); second != entities.end(); ++second)
{
if (first->type == Category::passable || second->type == Category::passable || first->type == Category::wall && second->type == Category::wall)
continue;
sf::FloatRect intersection;
if (GetBoundingRect(*first).intersects(GetBoundingRect(*second), intersection))
{
if (second->position == first->position)
second->position.x += entitySize.x;
sf::Vector2f direction = second->position - first->position;
sf::Vector2f offset;
// X collision
if (abs(direction.x) > abs(direction.y))
offset.x = ((direction.x<0)?-1:1)*intersection.width;
// Y collision
if (abs(direction.x) < abs(direction.y))
offset.y = ((direction.y<0)?-1:1)*intersection.height;
if(first->type == Category::pushable && second->type == Category::pushable)
{
first->velocity -= offset / 2.f;
second->velocity += offset / 2.f;
allCollisionsChecked = false;
}
else if(first->type == Category::pushable)
{
first->velocity -= offset;
allCollisionsChecked = false;
}
else if(second->type == Category::pushable)
{
second->velocity += offset;
allCollisionsChecked = false;
}
}
}
}
}
}
void handlePlayerInput(sf::Keyboard::Key key, bool isPressed)
{
switch(key)
{
case sf::Keyboard::Up:
isMovingUp = isPressed;
break;
case sf::Keyboard::Down:
isMovingDown = isPressed;
break;
case sf::Keyboard::Left:
isMovingLeft = isPressed;
break;
case sf::Keyboard::Right:
isMovingRight = isPressed;
break;
}
}
int main()
{
sf::RenderWindow window(sf::VideoMode(1280, 720), "SFML Application");
window.setVerticalSyncEnabled(true);
std::vector<Entity> entities;
Entity player(sf::Vector2f(1280/2, 720/2), sf::Color::Green, Category::pushable);
entities.push_back(player);
size_t cols = 1280/int(entitySize.x);
size_t rows = 720/int(entitySize.y);
for (size_t i=0; i < cols*rows; ++i)
if (i%cols == rows/5 && i/cols > rows/6 && i/cols < rows*5/6 || i%cols >= rows/5 && i%cols <= rows*4/5 && (i/cols == rows/6 || i/cols == rows*5/6))
entities.push_back(Entity(sf::Vector2f(entitySize.x*(i%cols)+entitySize.x/2, entitySize.y*(i/cols)+entitySize.y/2), sf::Color::Yellow, Category::wall));
//else
//entities.push_back(Entity(sf::Vector2f(entitySize.x*(i%cols)+entitySize.x/2, entitySize.y*(i/cols)+entitySize.y/2), sf::Color::Transparent, Category::passable));
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::MouseButtonPressed)
{
sf::Vector2i pixel(event.mouseButton.x, event.mouseButton.y);
sf::Vector2f coord = window.mapPixelToCoords(pixel);
if (event.mouseButton.button == sf::Mouse::Left)
{
Entity pushable(coord, sf::Color::Blue, Category::pushable);
entities.push_back(pushable);
}
else if (event.mouseButton.button == sf::Mouse::Right)
{
Entity wall(coord, sf::Color::Yellow, Category::wall);
entities.push_back(wall);
}
else if (event.mouseButton.button == sf::Mouse::Middle)
{
Entity mover(coord, sf::Color::Magenta, Category::pushable);
mover.defaultVelocity.x = -1.f;
entities.push_back(mover);
}
}
switch (event.type)
{
case sf::Event::KeyPressed:
handlePlayerInput(event.key.code, true);
break;
case sf::Event::KeyReleased:
handlePlayerInput(event.key.code, false);
break;
case sf::Event::Closed:
window.close();
break;
}
}
if (isMovingUp)
entities[0].velocity.y -= 5.f;
if (isMovingDown)
entities[0].velocity.y += 5.f;
if (isMovingLeft)
entities[0].velocity.x -= 5.f;
if (isMovingRight)
entities[0].velocity.x += 5.f;
if (entities[0].velocity.x != 0.f && entities[0].velocity.y != 0.f)
{
entities[0].velocity.x /= std::sqrt(2.f);
entities[0].velocity.y /= std::sqrt(2.f);
}
HandleCollision(entities);
// Apply and reset velocities
for (Entity& e : entities)
{
e.position += e.velocity;
e.velocity = e.defaultVelocity;
}
// Draw
window.clear();
for (Entity& e : entities)
DrawEntity(window, e);
window.display();
}
}
示例结果
洋红色实体以恒定速度向左移动,黄色实体在墙上移动。
没有检查其他碰撞造成的碰撞
期望的结果:
检查碰撞时的相同情况,直到所有问题都解决了
我怎么能实现这个目标?