使用 SDL2 和 C++ 的简谐运动球动画

A ball animation in simple harmonic motion using SDL2 and C++

我正在尝试模拟下面的球。注意球的简谐运动,球弹跳的最末端与中间的速度相比具有更小的速度:

我可以实现弹跳球,但它不是简谐运动:

对应代码如下:

Dot::Dot() {
//Initialize the offsets
mPosX = 300;
mPosY = 0;

//Initialize the velocity
mVelX = 0;
mVelY = 4;
}

void Dot::move() {

//Move the dot up or down
mPosY += mVelY;

//If the dot went too far up or down
if( ( mPosY < 0 ) || ( mPosY + DOT_HEIGHT > SCREEN_HEIGHT ) )
{
    //Move back
    mVelY = -mVelY;
    }
}

我有一个简谐运动模型,像这样:

对应代码如下:

Dot::Dot() {
//Initialize the offsets
mPosX = 300;
mPosY = 0;

//Initialize the velocity
mVelX = 0;
mVelY = 0;
}

void Dot::move() {
time_t current_time;
current_time = time(NULL);

mPosY = int(((460) - 10) * sin(2.4 * 2 * 3.141592 / 60 * current_time + (SCREEN_HEIGHT / 2) 
));

//const int SCREEN_HEIGHT = 480 
}

此实现的问题是:

(1)。球图像时不时出现,而不是像我一开始尝试模拟的蓝球模型那样连续出现

(2)。球远远超出 window 的顶部框架,而不是像蓝球模型一样在 window 的最顶部减速。

对于 (2),我知道我需要添加相移,即 A*sin(wt + x) 中的 x,但是更改此值并不能阻止球在window.

的顶部

关于如何解决这些问题有什么想法吗?

编辑:我能够通过对 mPosY 而不是 = 执行 += 来解决 (1),例如:

mPosY += int(4 * cos(2.4 * 2 * 3.141592 / 60 * current_time + (SCREEN_HEIGHT / 2) ));

但是,我仍然无法让球在我创建的window框架内上下弹跳。

我建议使用实际的简谐方程。 例如,如果您的显示尺寸为 (500, 500),则中心 Y 为 250。从那里说您的方程式的形式为 x = acos(nt + m) + c 其中 x 是位移(米),a 是振幅 n 是周期,例如周期 (T) = 2PI/n t 是时间(秒),m 是相移,c 是中心。这样当你需要物体的速度时,你有一个函数遵循

double Velocity(double time){
   double vel = derivative_of_displacement_equation(time);
   return vel;
}

所以在程序中,您调整方程以适应显示尺寸,然后将对象 X/Y 坐标设置为从位移方程返回的值(加上中心偏移,在本例中,如果中心位于屏幕中间,则将 Y 坐标设置为等式 PLUS 250)。请记住,坐标从 (0,0) 开始,因此您的位移方程(至少涉及比例因子的部分,在本例中为时间)改为负数。

我认为这是一些可以回答您问题的代码:

#include <SDL2/SDL.h>
#include <chrono>
#include <math.h>
#include <iostream>

const double PI = 3.14159265358979;

void draw_circle(SDL_Renderer *renderer, int x, int y, int radius, SDL_Color color)
{
    SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
    for (int w = 0; w < radius * 2; w++)
    {
        for (int h = 0; h < radius * 2; h++)
        {
            int dx = radius - w; // horizontal offset
            int dy = radius - h; // vertical offset
            if ((dx*dx + dy*dy) <= (radius * radius))
            {
                SDL_RenderDrawPoint(renderer, x + dx, y + dy);
            }
        }
    }
}

double Displacement(double time, double a, double n, double m, double c)
{
    double displacement = a*cos(n*time + m) + c;
    return displacement;
}

int main(int argc, char* argv[])
{
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window *window = SDL_CreateWindow("SHM", 0, 30, 500, 500, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_SHOWN);
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED );

    double timeDifference;
    std::chrono::steady_clock::time_point start, finish;
    start = std::chrono::steady_clock::now();
    finish = start;

    SDL_Event event;
    bool running = true;

    while (running){
        while (SDL_PollEvent(&event)){
            if (event.type == SDL_QUIT){
                running = false;
                break;
            }
        }

        SDL_SetRenderDrawColor(renderer, 255,255,255,255);
        SDL_RenderClear(renderer);

        finish = std::chrono::steady_clock::now();

        timeDifference = std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start).count();
        timeDifference = timeDifference / 1000000000;

        ///The "-(250-20) is the center y (250) minus the radius of the circle (20), and its - out the front as negative a due to coordinates
        double yPosition = round( Displacement(timeDifference, -(250-20), 2, 0, 250 )  );
        draw_circle(renderer, 250, yPosition, 20, {255,0,0});

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    SDL_Quit();

    return 0;
}

通常你有 a0 + a/2*cos (2**t/T + ) 其中 a0 是垂直行程的一半的垂直位置,a 是行程的高度,t 是时间,T 周期,即完成一个完整循环并返回到相同状态或 uple { position, momentum } 的时间,以及时间偏移,即高度为在 cos 的零处。

因此,如果您希望球在 t=0 处落在地板上,您希望 cos 最小,即 = -/2.

您想根据游戏时间 t 来管理您的位置,这样您就可以将计算时间(这取决于您的计算能力)和游戏时间(您希望从机到另一台)。

因此你想要:

auto VerticalPosition(double t)
-> double { return CorrectedScreenHeight/2*(1 + cos(2*PI*t/T + phi)); }

并且您在外部定义 CorrectedScreenHeight = SCREEN_HEIGHT - DOT_HEIGHTTphi 作为系统的属性。

在两个连续的图像之间,您递增t,以获得正确的体验时间。通常你有 60 images/s(WPF、DirectX、web 等),因此连续图像之间有 1.0/60s 的周期,这进入你修改 t 的函数。然后你的球的速度取决于 T,你可以独立调整。