提问人:metror 提问时间:10/29/2023 更新时间:10/30/2023 访问量:54
球在物理引擎中相互相位
Balls phasing through each other in physics engine
问:
我正在尝试用 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;
}
注意:此处的代码已缩短;我正在制作的实际程序使用 MouseClick 和 Color 值的随机值。
理想情况下,它们会相互反弹(有点像它们在地面上的表现)。我已经尝试将代码移动到函数的开头,并在检测到冲突时使函数中断。
答:
1赞
Jeffrey
10/29/2023
#1
你有量子跳跃球:
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;
}
这将使您摆脱大多数阶段。这将使你的物理特性对帧持续时间的依赖性降低,如果你进入多人游戏,你的同步和确定性将更容易处理。
您需要小心挑选。太小,你会做太多的物理工作。太大,你会得到不会更新每一帧的相位和/或球。我喜欢百分之一秒,除非我的帧速率非常高。someConstant
编辑:还有第二个问题。更大的一个:-)
if (distance < (ball.radius + otherBall.radius))
{
// flip direction
一旦两个球接触,在你翻转方向后,它们可能仍然接触。然后,在下一帧,您将再次翻转方向,使球继续。在翻转球 A 的方向之前,您需要检查球 A 是否与球 B 相交并且球 A 的速度是否朝向球 B。
我没有时间运行和调试您的代码,但是,大约是这样的:
// 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))
{
distance2 = Vector2Distance(ball.position - ball.velocity, otherBall.position); // hopefully you overloaded Vector2 operator+ and - ?
if (distance2 > distance) // if the velocity flipped would make us further
{
ball.velocity.x = -(ball.velocity.x);
otherBall.velocity.x = -(otherBall.velocity.x);
ball.velocity.y = -(ball.velocity.y);
otherBall.velocity.y = -(otherBall.velocity.y);
}
}
评论
0赞
metror
10/29/2023
嘿!感谢您的回复;我实现了这一点,但它实际上只是让我的程序做同样的事情(并使它看起来有点断断续续:()。新代码在 pastebin.com/J6gT74fv ;我做错了吗?别的?
0赞
Jeffrey
10/30/2023
呃,是的,看到更新。错过了那部分
评论