使用 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_HEIGHT
、T
和 phi
作为系统的属性。
在两个连续的图像之间,您递增t
,以获得正确的体验时间。通常你有 60 images/s(WPF、DirectX、web 等),因此连续图像之间有 1.0/60s 的周期,这进入你修改 t
的函数。然后你的球的速度取决于 T
,你可以独立调整。
我正在尝试模拟下面的球。注意球的简谐运动,球弹跳的最末端与中间的速度相比具有更小的速度:
我可以实现弹跳球,但它不是简谐运动:
对应代码如下:
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_HEIGHT
、T
和 phi
作为系统的属性。
在两个连续的图像之间,您递增t
,以获得正确的体验时间。通常你有 60 images/s(WPF、DirectX、web 等),因此连续图像之间有 1.0/60s 的周期,这进入你修改 t
的函数。然后你的球的速度取决于 T
,你可以独立调整。