簡體   English   中英

使用 SDL2 和 C++ 進行簡諧運動的球 animation

[英]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;
    }
}

我有一個簡諧運動 model,像這樣:

在此處輸入圖像描述

對應的代碼如下:

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 的最頂部減速,再次像藍色球 model。

對於 (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 = a cos(n t + m) + c 其中 x 是位移(米), a 是幅度,n 是周期,例如周期 (T) = 2PI/nt 是時間(秒),m 是相移,c 是中心。 這樣,當您需要 object 的速度時,您就有一個 function 遵循

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是垂直行程一半的垂直 position, a是行程的高度, t是時間, T是周期,即., 完成一個完整循環的時間,以返回相同的 state 或上 { position, 動量},以及時間偏移,即高度為零的時刻 cos。

因此,如果您希望球在t=0時在地板上,您希望 cos 處於最小值,即= -/2

您想在游戲時間t的 function 中管理 position ,因此您可以將計算時間(取決於您的計算能力)和游戲時間(您希望從一台機器到另一台機器保持不變)解耦。

因此你想要:

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

並且您將CorrectedScreenHeight = SCREEN_HEIGHT - DOT_HEIGHTTphi定義為系統的屬性。

在兩個連續的圖像之間,您增加t ,以獲得正確的體驗時間。 通常,您有 60 個圖像/秒(WPF、DirectX、web 等),因此連續圖像之間的周期為 1.0/60 秒,這在您的 function 中修改t 然后你的球的速度取決於T ,你可以獨立調整。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM