Код: Выделить всё
// 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