我正在尝试用 C++ 编写一个简单的物理引擎;到目前为止,一切进展顺利,除了球本身之间的物理作用外,所有功能都按预期工作。在碰撞过程中,球有时会互相穿过。
#include <iostream>
#include <stdio.h>
#include <raylib.h>
#include <raymath.h>
#include <stdlib.h>
#include <time.h>
#include <chrono>
#include <list>
using namespace std;
class Ball {
public:
Vector2 position;
Vector2 velocity;
float radius;
Color color;
};
void updateBall(Ball &ball, float gravity, float frictionCoefficient, float deltaTime, float screenWidth, float screenHeight, float bounciness, std::list<Ball> &balls) {
ball.velocity.y -= gravity;
// Apply friction if the ball is at the top or bottom of the screen
ball.velocity.x *= (ball.position.y >= screenHeight - ball.radius || ball.position.y <= ball.radius) ? frictionCoefficient : 1;
// Update the ball's position based on its velocity
ball.position.x += ball.velocity.x * deltaTime;
ball.position.y += -ball.velocity.y * deltaTime;
// Clamp the ball's position within the screen bounds
ball.position.x = Clamp(ball.position.x, ball.radius, screenWidth - ball.radius);
ball.position.y = Clamp(ball.position.y, ball.radius, screenHeight - ball.radius);
// Check for collisions with screen edges and apply bounciness
ball.velocity.x *= (ball.position.x == ball.radius || ball.position.x == screenWidth - ball.radius) ? -bounciness : 1;
ball.velocity.y *= (ball.position.y == ball.radius || ball.position.y == screenHeight - ball.radius) ? -bounciness : 1;
for (auto &otherBall : balls) {
// Avoid checking the ball against itself
if (&otherBall == &ball) {
continue;
}
// Calculate the distance between the two balls
float distance = Vector2Distance(ball.position, otherBall.position);
// Check if a collision has occurred
if (distance < (ball.radius + otherBall.radius)) {
ball.velocity.x = -(ball.velocity.x);
otherBall.velocity.x = -(otherBall.velocity.x);
ball.velocity.y = -(ball.velocity.y);
otherBall.velocity.y = -(otherBall.velocity.y);
}
}
}
Ball newBall(Vector2 position, Vector2 velocity, float radius, Color color) {
Ball tempBall;
tempBall.position = position;
tempBall.velocity = velocity;
tempBall.radius = radius;
tempBall.color = color;
return tempBall;
}
int main() {
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
int screenWidth = 900;
int screenHeight = 900;
InitWindow(screenWidth, screenHeight, "cppPhysics");
SetTargetFPS(120);
const float gravity = 9.81f;
const float frictionCoefficient = 0.981f;
const float bounciness = 0.7f;
Ball ballOne = newBall({100, 100}, {400, -450}, 60, WHITE);
Ball ballTwo = newBall({50, 200}, {400, 450}, 45, BLUE);
Ball ballThree = newBall({200, 50}, {300, -450}, 20, YELLOW);
std::list<Ball> balls;
balls.push_back(ballOne);
balls.push_back(ballTwo);
balls.push_back(ballThree);
while (!WindowShouldClose()) {
if (IsWindowResized()) {
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
}
float deltaTime = GetFrameTime();
for (auto &ball : balls) {
updateBall(ball, gravity, frictionCoefficient, deltaTime, screenWidth, screenHeight, bounciness, balls);
}
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
for (auto &ball : balls) {
ball.velocity.y += 500;
ball.velocity.x += 500;
}
}
BeginDrawing();
ClearBackground(BLACK);
for (auto &ball : balls) {
DrawCircleV(ball.position, ball.radius, ball.color);
}
EndDrawing();
}
CloseWindow();
return 0;
}
注意:这里的代码被缩短了;我正在制作的实际程序使用鼠标点击和颜色值的随机值。
最理想的情况是,它们会彼此弹开(有点像它们离开地面的方式)。我已经尝试将代码移动到函数的开头,并在检测到碰撞时使函数中断。
你有量子跳跃球:
float deltaTime = GetFrameTime();
updateBall(ball, gravity, frictionCoefficient, deltaTime, screenWidth, screenHeight, bounciness, balls);
ball.position.x += ball.velocity.x * deltaTime;
ball.position.y += -ball.velocity.y * deltaTime;
在深入研究之前,您应该强烈考虑切换到固定增量物理:
accumulatedDelta += GetFrameTime(); // accumulatedDelta is a member value
while(accumulatedDelta > someConstant)
{
updateBall(ball, gravity, frictionCoefficient, someConstant, screenWidth, screenHeight, bounciness, balls);
accumulatedDelta -= someConstant;
}
这会让你摆脱大部分的阶段性。它将使你的物理不再依赖于帧持续时间,如果你进入多人游戏,你的同步和决定论将使我更容易处理。