You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改点说明

  1. 重构粒子结构:用SingleParticle存储单个粒子的完整属性,搭配vector动态管理,比原数组结构更灵活易维护。
  2. 修复运动逻辑:新增速度变量vx/vy,严格遵循“加速度→速度→位置”的物理更新顺序,解决原逻辑中直接用加速度改位置的错误。
  3. 修正引力公式:加速度计算时除以受力粒子的质量,同时将距离向量归一化(dx/dist),确保力的方向和大小匹配物理规律。
  4. 初始化修正:给TicksCounter赋初始值,把srand移到循环外,避免随机数重复和初始DeltaTime异常。
  5. 添加边界保护:设置粒子数量上限和最小距离阈值,防止粒子过多或近距离导致的异常。
  6. 修复窗口参数:原代码窗口高度误用ScreenWidth,改为ScreenHeight,确保窗口尺寸正确。

内容的提问来源于stack exchange,提问作者riyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:59:57