Verlet 集成冲突求解器不工作 C++ SFML

Verlet Integration Collision Solver Not working C++ SFML

提问人:Dreadward07 提问时间:11/9/2023 最后编辑:Dreadward07 更新时间:11/11/2023 访问量:48

问:

我一直在用SFML编写一个简单的物理引擎,并且一直在使用Verlet Integration。在编写一个好的碰撞检测器时,我遇到了一个相当大的障碍。我一直在使用这个 github 作为大多数碰撞代码的来源。由于我的代码设置方式,我不得不进行一些调整,但在大多数情况下,想法是相同的。然而,每当两个球体碰撞时,它们都不会像预期的那样弹跳和滚动,它们只是相互粘附,根本不像实际的球体那样行动。

这是一个简短的视频,演示了我的问题。

下面是与实际碰撞检查相关的代码部分

void updateCollisions()
{
    // Runs through all objects and pushes them apart if they're to close together
for (int i = 0; i < physBalls.size(); i++)
{
    PhysicsBall& physball_1 = physBalls[i];
    for (int k{ i + 1 }; k < physBalls.size(); k++)
    {
        PhysicsBall& physball_2 = physBalls[k];
        
        sf::Vector2f collision_axis = physball_1.getPosition() - physball_2.getPosition();
        const float dist2 = (collision_axis.x * collision_axis.x) + (collision_axis.y * collision_axis.y);
        const float mindist = physball_1.getRadius() + physball_2.getRadius();

        if (dist2 < mindist * mindist)
        {
            const float dist = sqrt(dist2);
            const sf::Vector2f n = collision_axis / dist;
            const float delta = 0.5f * (dist - mindist);

            physball_1.setPosition((physball_1.getPosition() - n * delta * 0.5f));
            physball_2.setPosition((physball_2.getPosition() + n * delta  * 0.5f));
        }
    }
}
}

下面是与 Physicsball 类和 Simulation 类相关的所有代码:

#pragma once

#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/Audio.hpp>

#include <iostream>

class PhysicsBall
{
private:
    sf::CircleShape physball;

    float radius;
    sf::Vector2f position;
    sf::Vector2f prevposition;
    sf::Vector2f accel;

    //Creates our Physics Ball
    void initPhysBall(sf::Vector2f startPos, sf::Vector2f prevPos, float rad)
    {
        this->radius = rad;
        this->position = startPos;
        this->prevposition = prevPos;
        this->physball.setOrigin(sf::Vector2f(radius, radius));
        this->physball.setPosition(startPos);
        
        this->physball.setRadius(this->radius);
        this->physball.setFillColor(sf::Color(255, 255, 255, 255));
        
    }
public:
    //Constructors and Deconstructors
    PhysicsBall(sf::Vector2f startPos, sf::Vector2f prevPos,float rad)
    {
        initPhysBall(startPos, prevPos, rad);
    }

    ~PhysicsBall()
    {

    }

    //Accessors 
    sf::Vector2f getPosition()
    {
        return this->physball.getPosition();
    }
    float getRadius()
    {
        return this->physball.getRadius();
    }
    sf::Vector2f getVelocity()
    {
        return this->position - this->prevposition;
    }
    void setPosition(sf::Vector2f pos)
    {
        this->physball.setPosition(pos);
        this->position = pos;
    }

    //Update Functions

    //Moves the ball
    void updatePhysBall(float dt)
    {
        this->physball.setPosition(position);
        const sf::Vector2f velocity = this->position - this->prevposition;
        this->prevposition = this->position; 
        this->position = this->position + velocity + this->accel * dt * dt; 
    }
    // Accelerators
    void accelerate(sf::Vector2f acc)
    {
        this->accel += acc;
    }

    //Render Function
    void render(sf::RenderTarget & target)
    {
        target.draw(this->physball);
    }
};

class Simulation
{
private:

    sf::RenderWindow* window;
    sf::VideoMode videoMode;
    sf::Event event;

    sf::Clock deltaclock;
    float dt;

    sf::Vector2i mousePosWindow;
    sf::Vector2f mousePosView;
    sf::Vector2f mouseStartPos;
    sf::Vector2f mousePrevPos;
    bool mouseHeldDown;

    std::vector<PhysicsBall> physBalls;
    sf::Vector2f gravity = sf::Vector2f(0.f, 1000.f);

    //Initialize Functions
    void initVariables()
    {
        this->window = nullptr;
    }

    //Creates Window
    void initWindow()
    {
        this->videoMode.width = 640;
        this->videoMode.height = 480;
        this->window = new sf::RenderWindow(this->videoMode, "Verlet Integration", sf::Style::Close);
        this->window->setFramerateLimit(60);
    }

public:

    //Vector math functions
    float Vec2fDist(sf::Vector2f vec1)
    {
        return std::sqrt(
            (vec1.x * vec1.x) + (vec1.y * vec1.y)
        );
    }

    //Update functions
    float calculateDeltaTime()
    {
        return this->deltaclock.getElapsedTime().asSeconds();
    }

    //Gets Input
    void pollevent()
    {
        while (this->window->pollEvent(this->event))
        {
            switch (this->event.type)
            {
            case sf::Event::Closed:
                this->window->close();
                break;
            case sf::Event::KeyPressed:
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
                {
                    this->window->close();
                    break;
                }
            case sf::Event::MouseButtonPressed:
                this->mousePrevPos = mousePosView;
                break;
            case sf::Event::MouseButtonReleased:
                this->mouseStartPos = mousePosView;
                spawnPhysBall();
                break;
            }
        }
    }
    //Creates a physics ball and adds it to a vector
    void spawnPhysBall()
    {
        if (sf::Event::MouseButtonReleased)
        {
            PhysicsBall physball(mouseStartPos, ((mouseStartPos - mousePrevPos) * 0.01f) + mousePrevPos, 50.f);
            physBalls.push_back(physball);
        }
    }

    void updateMousePosistion()
    {
        //Updates mouse posistion as a Vector2

        this->mousePosWindow = sf::Mouse::getPosition(*this->window);
        this->mousePosView = this->window->mapPixelToCoords(this->mousePosWindow);
    }

    //Preforms all the update functions.
    void update()
    {
        this->dt = this->calculateDeltaTime();
        
        this->updateMousePosistion();

        this->pollevent();

        this->updateCollisions();

        for (auto& physball : this->physBalls)
        {
            updatePhysPos(physball, dt);
            updateAcceleration(physball, gravity);
            updateApplyConstriants(physball);
        }

        this->deltaclock.restart();
    }
    //Updates Position
    void updatePhysPos(PhysicsBall &physball, float delta)
    {
        physball.updatePhysBall(delta);
    }
    //Updates acceleration.
    void updateAcceleration(PhysicsBall &physball, sf::Vector2f acc)
    {
        physball.accelerate(acc);
    }
    //Screen collision checks
    void updateApplyConstriants(PhysicsBall &physball)
    {
        const float friction = 0.75f;
        if (physball.getPosition().x + physball.getRadius() > videoMode.width)
        {
            physball.setPosition(
                sf::Vector2f((videoMode.width-physball.getRadius()), physball.getPosition().y + (physball.getVelocity().y * friction))
            );
        }
        if (physball.getPosition().y + physball.getRadius() > videoMode.height)
        {
            physball.setPosition(
                sf::Vector2f(physball.getPosition().x + (physball.getVelocity().x * friction), (videoMode.height - physball.getRadius()))
            );
        }
        if (physball.getPosition().x - physball.getRadius() < 0.f)
        {
            physball.setPosition(
                sf::Vector2f(physball.getRadius(), physball.getPosition().y + (physball.getVelocity().y * friction))
            );
        }
        if (physball.getPosition().y - physball.getRadius() < 0.f)
        {
            physball.setPosition(
                sf::Vector2f(physball.getPosition().x + (physball.getVelocity().x * friction), physball.getRadius())
            );
        }
    }

    void updateCollisions()
    {
        
        // Runs through all objects and pushes them apart if they're to close together
        for (int i = 0; i < physBalls.size(); i++)
        {
            PhysicsBall& physball_1 = physBalls[i];
            for (int k{ i + 1 }; k < physBalls.size(); k++)
            {
                PhysicsBall& physball_2 = physBalls[k];
                
                sf::Vector2f vel = physball_1.getPosition() - physball_2.getPosition();
                const float dist2 = (vel.x * vel.x) + (vel.y * vel.y);
                const float mindist = physball_1.getRadius() + physball_2.getRadius();

                if (dist2 < mindist * mindist)
                {
                    const float dist = sqrt(dist2);
                    const sf::Vector2f n = vel / dist;
                    const float delta = 0.5f * (dist - mindist);

                    physball_1.setPosition(physball_1.getPosition() - n * (delta * 0.5f));
                    physball_2.setPosition(physball_2.getPosition() + n * (delta * 0.5f));
                }
            }
        }
    }

    //Render Functions

    //Main render function
    void render()
    {
        this->window->clear();
        
        for (auto physball : this->physBalls)
        {
            physball.render(*this->window);
        }

        this->window->display();
    }
    
    //
    //Gets whether or not the window is open
    const bool running()
    {
        return this->window->isOpen();
    }


    //Constructors and deconstructors 

    Simulation()
    {
        initVariables();
        initWindow();
    }
    
    ~Simulation()
    {
        delete this->window;
    }

};

任何帮助将不胜感激!

我试图在 SFML 中创建一个 Verlet 集成引擎,但每当它们碰撞时,球体就会消失。

SFML Verlet 集成

评论

0赞 Lutz Lehmann 11/10/2023
您的碰撞代码如何保持冲量和能量?位置校正破坏了 Verlet 方法的这种基本形式中使用的位置序列假设。如果您使用一步法而不是多步法,会更容易。
0赞 Dreadward07 11/10/2023
我是 Verlet Integration 的新手,您能给我举一个单步配方的例子吗?
0赞 Lutz Lehmann 11/10/2023
这主要是速度 Verlet 方法。状态是位置和速度,而不是一对位置。
0赞 Dreadward07 11/11/2023
那么我该如何保持冲动呢?我试过加回速度,但没有用。另外,我应该注意,我错误地标记了“vel”变量,因为它实际上表示用于分离它们的碰撞轴。

答:

0赞 Dreadward07 11/11/2023 #1

在我的 getPosition() 函数期间,我实际上获取的是 SFML 对象位置,而不是该帧的更新位置。在我改变它之后,物理学工作得完美无缺。

评论

0赞 Community 11/16/2023
正如目前所写的那样,你的答案尚不清楚。请编辑以添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以在帮助中心找到有关如何写出好答案的更多信息。