Как получить стабильные коллизии с помощью интеграции Verlet?C++

Программы на C++. Форум разработчиков
Ответить Пред. темаСлед. тема
Anonymous
 Как получить стабильные коллизии с помощью интеграции Verlet?

Сообщение Anonymous »

Я некоторое время работал над этой простой физической системой, и изначально я начал с эйлеровой физики, но мне не удалось добиться стабильной работы столкновений, и после перехода на интеграцию верлетов я все еще не могу получить ничего стабильного. Это работает совершенно нормально, когда шаров недостаточно, чтобы их можно было сложить друг на друга, но как только я добавляю больше шаров, и они приземляются друг на друга, они начинают отлетать, и это хаотично. вы можете увидеть полный код и только конкретную функцию столкновения ниже, я использую C++ и sfml.

Код: Выделить всё

    // Collision check
void checkCollisions(std::vector& balls, std::vector& grid, int gridWidth, int gridHeight, int cellSize) {
const float damping = 0.8f;
const float epsilon = 0.001f;
const float separationFactor = 1.01f; // Slight increase in separation
const int collisionIterations = 5; // Number of iterations for collision resolution

int cellX = static_cast(position.x) / cellSize;
int cellY = static_cast(position.y) / cellSize;

for (int iteration = 0; iteration < collisionIterations; ++iteration) {
// Check neighboring cells for potential collisions
for (int x = std::max(0, cellX - 1); x position.x;
float dy = position.y - otherBall->position.y;
float distance = std::sqrt(dx * dx + dy * dy);

if (distance < minDist && distance > epsilon) {
float nx = dx / distance;
float ny = dy / distance;

// Calculate relative velocity
float relativeVelocityX = (position.x - oldPosition.x) - (otherBall->position.x - otherBall->oldPosition.x);
float relativeVelocityY = (position.y - oldPosition.y) - (otherBall->position.y - otherBall->oldPosition.y);
float relativeVelocityDotNormal = relativeVelocityX * nx + relativeVelocityY * ny;

// Only proceed with collision resolution if balls are moving towards each other
if (relativeVelocityDotNormal < 0) {
float overlap = minDist - distance;
float moveDistance = overlap / 2.0f * separationFactor; // Slight increase in separation

// Move balls apart along the collision normal
position.x += nx * moveDistance;
position.y += ny * moveDistance;
otherBall->position.x -= nx * moveDistance;
otherBall->position.y -= ny * moveDistance;

// Calculate velocities
float v1x = position.x - oldPosition.x;
float v1y = position.y - oldPosition.y;
float v2x = otherBall->position.x - otherBall->oldPosition.x;
float v2y = otherBall->position.y - otherBall->oldPosition.y;

// Calculate normal velocity
float vn = (v2x - v1x) * nx + (v2y - v1y) * ny;

// Calculate impulse scalar
float impulse = (2.0f * vn) / (1.0f + 1.0f);  // Assuming equal mass balls

// Apply impulse to change velocities
oldPosition.x -= impulse * nx * damping;
oldPosition.y -= impulse * ny * damping;
otherBall->oldPosition.x += impulse * nx * damping;
otherBall->oldPosition.y += impulse * ny * damping;
}
}
else if (distance position.x -= std::cos(angle) * perturbation / 2.0f;
otherBall->position.y -= std::sin(angle) * perturbation / 2.0f;
}
}
}
}
}
}
и вот полный код на всякий случай:

Код: Выделить всё

#include 
#include 
#include 
#include 
#include  // for rand()
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

// Define the Vector2 class
class Vector2 {
public:
float x;
float y;

// Add a default constructor
Vector2() : x(0), y(0) {}

// Add a constructor that takes x and y as arguments
Vector2(float x, float y) : x(x), y(y) {}

// Define vector operations
Vector2 operator+(const Vector2& other) const {
Vector2 result;
result.x = x + other.x;
result.y = y + other.y;
return result;
}

Vector2 operator-(const Vector2& other) const {
Vector2 result;
result.x = x - other.x;
result.y = y - other.y;
return result;
}

Vector2 operator*(float scalar) const {
Vector2 result;
result.x = x * scalar;
result.y = y * scalar;
return result;
}

Vector2 operator/(float scalar) const {
Vector2 result;
result.x = x / scalar;
result.y = y / scalar;
return result;
}

float length() const {
return std::sqrt(x * x + y * y);
}

Vector2 normalize() const {
float len = length();
if (len > 0) {
return Vector2(x / len, y / len);
}
return *this;
}

float dot(const Vector2& other) const {
return x * other.x + y * other.y;
}
};

// Ball class
class Ball {
private:
sf::CircleShape shape;
Vector2 oldPosition;
Vector2 acceleration;
Vector2 gravity = Vector2(0, 980);
float radius;

public:
Vector2 position;

Ball(float radius, sf::Color color, float x, float y)
: radius(radius), position(x, y), oldPosition(x, y), acceleration(0, 0) {
shape.setRadius(radius);
shape.setFillColor(color);
shape.setPosition(x - radius, y - radius);
}

void updatePhysics(float dt) {
gravity.y = 980;
Vector2 velocity = (position - oldPosition) / dt; // Calculate velocity
oldPosition = position;
position = position + velocity * dt + acceleration * (0.5f * dt * dt);
acceleration = gravity; // Apply gravity

// Threshold values
const float restThreshold = 0.1f; // Lower threshold for resting
const float minMovement = 0.01f; // Minimum movement threshold

// Prevent jittering when at rest at bottom of window
if (std::abs(velocity.y) < restThreshold && position.y + radius >= 600) {
position.y = 600 - radius;
acceleration.y = 0;
gravity.y = 0;
oldPosition.y = position.y;
}

// Bounce off the top of the window
if (position.y - radius < 0) {
position.y = radius;
velocity.y = -velocity.y * 0.9f;  // Dampen the bounce and invert the velocity
oldPosition.y = position.y - velocity.y * dt;
}

// Bounce off the bottom of the window
if (position.y + radius > 600) {
position.y = 600 - radius;
velocity.y = -velocity.y * 0.9f; // Dampen the bounce and invert the velocity
oldPosition.y = position.y - velocity.y * dt;
}

// Bounce off the sides of the window
if (position.x - radius < 0) {
position.x = radius;
velocity.x = -velocity.x * 0.7f; // Dampen the bounce and invert the velocity
oldPosition.x = position.x - velocity.x * dt;
}
else if (position.x + radius > 1000) {
position.x = 1000 - radius;
velocity.x = -velocity.x * 0.7f; // Dampen the bounce and invert the velocity
oldPosition.x = position.x - velocity.x * dt;
}

shape.setPosition(position.x - radius, position.y - radius);
}

// Collision check
void checkCollisions(std::vector& balls, std::vector& grid, int gridWidth, int gridHeight, int cellSize) {
const float damping = 0.8f;
const float epsilon = 0.001f;
const float separationFactor = 1.01f; // Slight increase in separation
const int collisionIterations = 5; // Number of iterations for collision resolution

int cellX = static_cast(position.x) / cellSize;
int cellY = static_cast(position.y) / cellSize;

for (int iteration = 0; iteration < collisionIterations; ++iteration) {
// Check neighboring cells for potential collisions
for (int x = std::max(0, cellX - 1); x position.x;
float dy = position.y - otherBall->position.y;
float distance = std::sqrt(dx * dx + dy * dy);

if (distance < minDist && distance > epsilon) {
float nx = dx / distance;
float ny = dy / distance;

// Calculate relative velocity
float relativeVelocityX = (position.x - oldPosition.x) - (otherBall->position.x - otherBall->oldPosition.x);
float relativeVelocityY = (position.y - oldPosition.y) - (otherBall->position.y - otherBall->oldPosition.y);
float relativeVelocityDotNormal = relativeVelocityX * nx + relativeVelocityY * ny;

// Only proceed with collision resolution if balls are moving towards each other
if (relativeVelocityDotNormal < 0) {
float overlap = minDist - distance;
float moveDistance = overlap / 2.0f * separationFactor; // Slight increase in separation

// Move balls apart along the collision normal
position.x += nx * moveDistance;
position.y += ny * moveDistance;
otherBall->position.x -= nx * moveDistance;
otherBall->position.y -= ny * moveDistance;

// Calculate velocities
float v1x = position.x - oldPosition.x;
float v1y = position.y - oldPosition.y;
float v2x = otherBall->position.x - otherBall->oldPosition.x;
float v2y = otherBall->position.y - otherBall->oldPosition.y;

// Calculate normal velocity
float vn = (v2x - v1x) * nx + (v2y - v1y) * ny;

// Calculate impulse scalar
float impulse = (2.0f * vn) / (1.0f + 1.0f);  // Assuming equal mass balls

// Apply impulse to change velocities
oldPosition.x -= impulse * nx * damping;
oldPosition.y -= impulse * ny * damping;
otherBall->oldPosition.x += impulse * nx * damping;
otherBall->oldPosition.y += impulse * ny * damping;
}
}
else if (distance position.x -= std::cos(angle) * perturbation / 2.0f;
otherBall->position.y -= std::sin(angle) * perturbation / 2.0f;
}
}
}
}
}
}

void draw(sf::RenderWindow& window) {
window.draw(shape);
}
};

int main() {
sf::RenderWindow window(sf::VideoMode(1000, 600), "Verlet Integration");
std::vector balls;
const int ballNum = 20;
const int Maxradius = 20;
const int Minradius = 20;

balls.reserve(ballNum);

// Initialize balls
for (int i = 0; i < ballNum; i++) {
balls.emplace_back(rand() % Maxradius + Minradius, sf::Color(rand() % 255, rand() % 255, rand() % 255), rand() % 1000, rand() % 600);
}

// Grid parameters
int gridWidth = 1000 / (Maxradius * 2);
int gridHeight = 600 / (Maxradius * 2);
int cellSize = 2 * Maxradius;

std::vector grid(gridWidth, std::vector(gridHeight));

sf::Clock displayClock;
sf::Clock physicsClock;
float physicsDt = 1.0f / 240.0f;
float physicsAccumulator = 0.0f;

while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
}

float frameTime = displayClock.restart().asSeconds();
physicsAccumulator += frameTime;

// Physics update loop
while (physicsAccumulator >= physicsDt) {
// Clear grid
for (auto& column : grid) {
for (auto& cell : column) {
cell.clear();
}
}

// Update ball positions
for (auto& ball : balls) {
ball.updatePhysics(physicsDt);

int cellX = static_cast(ball.position.x) / cellSize;
int cellY = static_cast(ball.position.y) / cellSize;
if (cellX >= 0 && cellX < gridWidth && cellY >= 0 && cellY < gridHeight) {
grid[cellX][cellY].push_back(&ball);
}
}

// Resolve collisions
for (auto& ball : balls) {
ball.checkCollisions(balls, grid, gridWidth, gridHeight, cellSize);
}

physicsAccumulator -= physicsDt;
}

// Clear window
window.clear(sf::Color::White);

// Draw balls
for (auto& ball : balls) {
ball.draw(window);
}

// Display frame
window.display();

// Ensure consistent frame rate
sf::sleep(sf::seconds(1.0f / 60.0f) - displayClock.getElapsedTime());
}

return 0;
}

Я попробовал несколько настроек демпфирования, и это, кажется, работает лучше всего, но в остальном я не уверен, что еще я могу сделать.

Подробнее здесь: https://stackoverflow.com/questions/786 ... tergration
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • MD -моделирование с использованием Velocity Verlet в Python
    Anonymous » » в форуме Python
    0 Ответы
    37 Просмотры
    Последнее сообщение Anonymous
  • MD -моделирование с использованием Velocity Verlet в Python
    Anonymous » » в форуме Python
    0 Ответы
    31 Просмотры
    Последнее сообщение Anonymous
  • MD -моделирование с использованием Velocity Verlet в Python
    Anonymous » » в форуме Python
    0 Ответы
    31 Просмотры
    Последнее сообщение Anonymous
  • Не уверен, почему квадратичное зондирование не поможет разрешить коллизии при хешировании.
    Anonymous » » в форуме C++
    0 Ответы
    18 Просмотры
    Последнее сообщение Anonymous
  • Коллизии верхних и нижних колонтитулов
    Anonymous » » в форуме CSS
    0 Ответы
    17 Просмотры
    Последнее сообщение Anonymous

Вернуться в «C++»