//点的特性 const int DOT_WIDTH = 20; const int DOT_HEIGHT = 20; const int DOT_VEL = 200;
这里我们定义了与点相关的宽度、高度和速度常量。
既然我们在做与时间相关的移动,而不是与帧率相关,我们必须改变我们处理速度的方式。我们将基于每秒而不是基于每帧来计算速度。在原本的"运动"教程中,我们让点每帧移动10像素。由于程序运行在20帧每秒的帧率下,点也就是以200像素每秒的速度运动。
所以现在点的速度是200像素每秒。
既然我们在做与时间相关的移动,而不是与帧率相关,我们必须改变我们处理速度的方式。我们将基于每秒而不是基于每帧来计算速度。在原本的"运动"教程中,我们让点每帧移动10像素。由于程序运行在20帧每秒的帧率下,点也就是以200像素每秒的速度运动。
所以现在点的速度是200像素每秒。
//点 class Dot { private: //点的X、Y坐标 float x, y; //点的速度 float xVel, yVel; public: //初始化变量 Dot(); //处理按键并调整速度 void handle_input(); //移动点 void move( Uint32 deltaTicks ); //在屏幕上显示点 void show(); };
这里我们有我们的老朋友Dot类的另一个修订版本。
现在坐标和速度是浮点数。这是因为有可能点在一帧内移动小于1像素的距离。
假定这个程序在一台电脑上以300fps的帧率运行。为了让点以200pps的速度移动,点就必须以2/3像素每帧的速度移动。
当然我们还有视频和输入函数。
现在坐标和速度是浮点数。这是因为有可能点在一帧内移动小于1像素的距离。
假定这个程序在一台电脑上以300fps的帧率运行。为了让点以200pps的速度移动,点就必须以2/3像素每帧的速度移动。
译者注:fps = Frame Per Second,帧每秒; pps = Pixel Per Second,像素每秒。
我们还有一个传入时间差的move()
函数。对于那些没有学过物理的童鞋来说,注意一下这里的时间差就是时间的变化值。我们需要获得自上一帧以来经过的时间,从而决定点需要移动多远。
当然我们还有视频和输入函数。
void Dot::handle_input() { //如果一个按键被按下 if( event.type == SDL_KEYDOWN ) { //调整速度 switch( event.key.keysym.sym ) { case SDLK_UP: yVel -= DOT_VEL; break; case SDLK_DOWN: yVel += DOT_VEL; break; case SDLK_LEFT: xVel -= DOT_VEL; break; case SDLK_RIGHT: xVel += DOT_VEL; break; } } //如果一个按键被松开 else if( event.type == SDL_KEYUP ) { //调整速度 switch( event.key.keysym.sym ) { case SDLK_UP: yVel += DOT_VEL; break; case SDLK_DOWN: yVel -= DOT_VEL; break; case SDLK_LEFT: xVel += DOT_VEL; break; case SDLK_RIGHT: xVel -= DOT_VEL; break; } } }
就像你看到的那样,输入处理和以前的差不多。
void Dot::move( Uint32 deltaTicks ) { //想左或向右移动点 x += xVel * ( deltaTicks / 1000.f ); //如果点向左移动得太多 if( x < 0 ) { //移回去 x = 0; } //或者向右移动得太多 else if( x + DOT_WIDTH > SCREEN_WIDTH ) { //移回去 x = SCREEN_WIDTH - DOT_WIDTH; } //向上或向下移动点 y += yVel * ( deltaTicks / 1000.f ); //如果点向上移动得太多 if( y < 0 ) { //移回去 y = 0; } //如果点向下移动得太多 else if( y + DOT_HEIGHT > SCREEN_HEIGHT ) { //移回去 y = SCREEN_HEIGHT - DOT_HEIGHT; } }
这里是我们处理移动的地方。
我们接收时间差作为参数,它将告诉我们自上一次移动后经过的时间。如果程序是以100fps运行的,时间差就会是1/100秒。如果程序以200fps运行,时间差就会是1/200秒。如果程序以150fps运行,时间差就会是1/150秒……
计算移动距离的公式是这样:
使用基于时间移动能确保点始终以200pps的速度移动。
注意我们改变了我们的函数来确保点在边界内。不像以前那样使用撤销动作,这里我们在点抛出屏幕时把它拉回屏幕内。
我们接收时间差作为参数,它将告诉我们自上一次移动后经过的时间。如果程序是以100fps运行的,时间差就会是1/100秒。如果程序以200fps运行,时间差就会是1/200秒。如果程序以150fps运行,时间差就会是1/150秒……
计算移动距离的公式是这样:
速度(像素/秒) * 自上一帧经过的时间(秒)
所以如果程序以200fps运行:200 pps * 1/200 秒 = 1 像素
如果程序以100fps运行:200 pps * 1/100 秒 = 2 像素
……使用基于时间移动能确保点始终以200pps的速度移动。
注意我们改变了我们的函数来确保点在边界内。不像以前那样使用撤销动作,这里我们在点抛出屏幕时把它拉回屏幕内。
void Dot::show() { //显示点 apply_surface( (int)x, (int)y, dot, screen ); }
这里你可以看到
show()
函数基本没变,除了一点,我们必须将浮点数转换为整数来进行blit。
//退出标识 bool quit = false; //将要用到的点 Dot myDot; //跟踪自上次渲染所经过的时间 Timer delta; //初始化 if( init() == false ) { return 1; } //加载文件 if( load_files() == false ) { return 1; } //启动时间差计时器 delta.start();
这是
伴随着我们的点,我们还创建了一个计时器对象来测量帧之间的时间差。我们在进入主循环前启动了计时器。
main()
函数的顶部。
伴随着我们的点,我们还创建了一个计时器对象来测量帧之间的时间差。我们在进入主循环前启动了计时器。
//当用户还未退出 while( quit == false ) { //当有事件需要处理 while( SDL_PollEvent( &event ) ) { //为点处理事件 myDot.handle_input(); //如果用户叉掉了窗口 if( event.type == SDL_QUIT ) { //退出程序 quit = true; } } //移动点 myDot.move( delta.get_ticks() ); //重启时间差计时器 delta.start();
在我们主循环的顶部,我们处理了事件并移动了点。
点移动后,我们重启了时间差计时器,所以我们可以跟踪自上次移动后经过的时间。
点移动后,我们重启了时间差计时器,所以我们可以跟踪自上次移动后经过的时间。
//用白色填充屏幕 SDL_FillRect( screen, screen->clip_rect, SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) ); //在屏幕上显示点 myDot.show(); //更新屏幕 if( SDL_Flip( screen ) == -1 ) { return 1; } }
然后我们按正常的方式处理图像。
就像你看到的那样,我们没有捕获帧率,但由于我们的运动是基于时间的,所以多少帧率都无所谓。
就像你看到的那样,我们没有捕获帧率,但由于我们的运动是基于时间的,所以多少帧率都无所谓。