SDL2+C++粒子重力模拟异常:粒子被吸至屏幕右上角求助
SDL2粒子重力模拟:粒子被吸引至右上角的问题修复
核心问题原因
- 加速度与速度概念混淆:你直接把加速度(AX/AY)加到粒子位置上,正确逻辑应该是加速度改变速度,再由速度驱动位置变化。当前写法等于把加速度当成速度用,完全违背物理运动规律。
- 引力公式的质量项错误:计算加速度时,应该除以受力粒子的质量(比如粒子y的质量),而不是施力粒子的质量(粒子x的质量)。正确的加速度公式是
a = F / m_受力,其中F是粒子受到的引力。 - DeltaTime初始化缺失:
TicksCounter一开始没赋值,第一次循环时DeltaTime会是SDL启动到当前的总时长,导致初始加速度直接爆炸。 - 随机数种子重复初始化:
srand(time(0))放在主循环里,每次循环都重置种子,导致rand()生成的随机数几乎完全一样,粒子质量没有真正的随机性。 - 引力方向计算不规范:没有将距离向量归一化,直接用原始距离差计算力的方向,容易导致力的大小和方向不匹配。
修正后的完整代码
#include "SDL.h" #undef main #include <iostream> #include <vector> #include <cmath> #include <ctime> double Gravity = 0.1; // 调低G值,配合正确运动逻辑避免速度过快 const int MaxParticles = 100; bool isRunning = true; int MouseX = 0; int MouseY = 0; int ScreenHeight = 700; int ScreenWidth = 700; Uint32 TicksCounter; double DeltaTime; // 单个粒子的属性,结构更清晰 struct SingleParticle { double mass; double ax, ay; // 加速度 double vx, vy; // 速度 SDL_Rect rect; }; std::vector<SingleParticle> particles; int main() { // 创建窗口与渲染器 SDL_Window* GameWindow = SDL_CreateWindow("Gravity Simulation.", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, ScreenWidth, ScreenHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Renderer* Renderer = SDL_CreateRenderer(GameWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); SDL_Event Event; // 初始化时间计数器与随机数种子 TicksCounter = SDL_GetTicks(); srand(time(nullptr)); // 主循环 while (isRunning) { // 计算DeltaTime(秒) Uint32 currentTicks = SDL_GetTicks(); DeltaTime = (currentTicks - TicksCounter) / 1000.0; TicksCounter = currentTicks; // 事件处理 SDL_GetMouseState(&MouseX, &MouseY); while (SDL_PollEvent(&Event)) { if (Event.type == SDL_QUIT) { isRunning = false; } if (Event.type == SDL_MOUSEBUTTONDOWN && particles.size() < MaxParticles) { SingleParticle newPart; newPart.rect.x = MouseX; newPart.rect.y = MouseY; newPart.ax = 0; newPart.ay = 0; newPart.vx = 0; newPart.vy = 0; if (Event.button.button == SDL_BUTTON_LEFT) { newPart.rect.w = 5; newPart.rect.h = 5; newPart.mass = 10 + (rand() % 10); // 生成10-19的随机质量 } else if (Event.button.button == SDL_BUTTON_RIGHT) { newPart.rect.w = 25; newPart.rect.h = 25; newPart.mass = 1000; } particles.push_back(newPart); } } // 重置所有粒子的加速度(每帧重新计算) for (auto& p : particles) { p.ax = 0; p.ay = 0; } // 计算两两粒子间的引力与加速度 for (size_t i = 0; i < particles.size(); i++) { auto& p1 = particles[i]; for (size_t j = 0; j < particles.size(); j++) { if (i == j) continue; auto& p2 = particles[j]; // 计算距离向量 double dx = p2.rect.x - p1.rect.x; double dy = p2.rect.y - p1.rect.y; double distSq = dx*dx + dy*dy; // 避免近距离引力过大,设置最小距离平方 if (distSq < 100) distSq = 100; double dist = sqrt(distSq); // 计算引力大小 double force = Gravity * (p1.mass * p2.mass) / distSq; // 计算p1受到p2的加速度(方向指向p2) p1.ax += (force * dx / dist) / p1.mass * DeltaTime; p1.ay += (force * dy / dist) / p1.mass * DeltaTime; } } // 更新速度与位置 for (auto& p : particles) { p.vx += p.ax; p.vy += p.ay; p.rect.x += static_cast<int>(p.vx); p.rect.y += static_cast<int>(p.vy); } // 渲染 SDL_SetRenderDrawColor(Renderer, 255, 255, 255, 255); SDL_RenderClear(Renderer); SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255); for (auto& p : particles) { SDL_RenderFillRect(Renderer, &p.rect); } SDL_RenderPresent(Renderer); } // 资源清理 SDL_DestroyRenderer(Renderer); SDL_DestroyWindow(GameWindow); SDL_Quit(); return 0; }
关键修改点说明
- 重构粒子结构:用
SingleParticle存储单个粒子的完整属性,搭配vector动态管理,比原数组结构更灵活易维护。 - 修复运动逻辑:新增速度变量
vx/vy,严格遵循“加速度→速度→位置”的物理更新顺序,解决原逻辑中直接用加速度改位置的错误。 - 修正引力公式:加速度计算时除以受力粒子的质量,同时将距离向量归一化(
dx/dist),确保力的方向和大小匹配物理规律。 - 初始化修正:给
TicksCounter赋初始值,把srand移到循环外,避免随机数重复和初始DeltaTime异常。 - 添加边界保护:设置粒子数量上限和最小距离阈值,防止粒子过多或近距离导致的异常。
- 修复窗口参数:原代码窗口高度误用
ScreenWidth,改为ScreenHeight,确保窗口尺寸正确。
内容的提问来源于stack exchange,提问作者riyan
相关产品推荐
相关产品推荐

