当你有一个需要滚动的游戏时,(与没有滚动的游戏相比较)它们唯一的不同是带滚动的游戏必须要有一个“相机”。
因为你不能将整个关卡显示在窗口中,所以你必须选取你想看到的那一部分:
然后将它切割出来并把它显示在窗口中:
由于你必须移动相机来表明你想要做的事情,这会带来更多的工作。在这个程序里,相机会随着我们所控制的小黑点的运动而运动。
译者注:camera的翻译有很多种,有“相机”、“摄像头”、“摄像机”,我不太清楚用哪种最好,挑个最短的吧。
因为你不能将整个关卡显示在窗口中,所以你必须选取你想看到的那一部分:
然后将它切割出来并把它显示在窗口中:
由于你必须移动相机来表明你想要做的事情,这会带来更多的工作。在这个程序里,相机会随着我们所控制的小黑点的运动而运动。
//窗口属性
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;
//帧率
const int FRAMES_PER_SECOND = 20;
//小黑点的大小
const int DOT_WIDTH = 20;
const int DOT_HEIGHT = 20;
//关卡地图的大小
const int LEVEL_WIDTH = 1280;
const int LEVEL_HEIGHT = 960;
//表面
SDL_Surface *dot = NULL;
SDL_Surface *background = NULL;
SDL_Surface *screen = NULL;
//事件结构体
SDL_Event event;
//相机
SDL_Rect camera = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
这些是我们的全局变量。
我们有标准常量、表面、事件结构体以及其他的一些好东西,只是这次我们有两个新的常量,它们分别定义了关卡地图的宽和高。因为我们可以滚动,所以我们并不把所有东西都限制在窗口中,但是现在我们要将小黑点限制在关卡地图中。
就像我先前提到的,我们需要一个相机来定义我们要显示在窗口中的那一部分区域。由于窗口是一个矩形的区域,所以相机是个SDL_Rect类型的结构体。
这里我们将相机初始化,将其位置设为地图左上角,宽高设为窗口的宽高。
我们有标准常量、表面、事件结构体以及其他的一些好东西,只是这次我们有两个新的常量,它们分别定义了关卡地图的宽和高。因为我们可以滚动,所以我们并不把所有东西都限制在窗口中,但是现在我们要将小黑点限制在关卡地图中。
就像我先前提到的,我们需要一个相机来定义我们要显示在窗口中的那一部分区域。由于窗口是一个矩形的区域,所以相机是个SDL_Rect类型的结构体。
这里我们将相机初始化,将其位置设为地图左上角,宽高设为窗口的宽高。
//小黑点
class Dot
{
private:
//点的坐标
int x, y;
//点的速度
int xVel, yVel;
public:
//初始化变量
Dot();
//处理按键事件并调节点的速度
void handle_input();
//移动小黑点
void move();
//将点显示在窗口中
void show();
//根据点的位置设定相机
void set_camera();
};
这里是我们的dot类的定义。
这个和前面课程里的几乎一模一样,但这次我们有一个根据点的位置将相机居中的函数。
这个和前面课程里的几乎一模一样,但这次我们有一个根据点的位置将相机居中的函数。
void Dot::move()
{
//向左或向右移动小黑点
x += xVel;
//如果小黑点移动到左右边界外
if( ( x < 0 ) || ( x + DOT_WIDTH > LEVEL_WIDTH ) )
{
//撤销移动
x -= xVel;
}
//向上或向下移动小黑点
y += yVel;
//如果小黑点移动到上下边界外
if( ( y < 0 ) || ( y + DOT_HEIGHT > LEVEL_HEIGHT ) )
{
//撤销移动
y -= yVel;
}
}
这个函数与前面的很类似,但这里有一个关键的不同点:我们不再将点约束在窗口内了,现在我们是将点约束在整个关卡地图里。
void Dot::set_camera()
{
//根据点的位置将相机居中
camera.x = ( x + DOT_WIDTH / 2 ) - SCREEN_WIDTH / 2;
camera.y = ( y + DOT_HEIGHT / 2 ) - SCREEN_HEIGHT / 2;
//将相机约束在地图边界内
if( camera.x < 0 )
{
camera.x = 0;
}
if( camera.y < 0 )
{
camera.y = 0;
}
if( camera.x > LEVEL_WIDTH - camera.w )
{
camera.x = LEVEL_WIDTH - camera.w;
}
if( camera.y > LEVEL_HEIGHT - camera.h )
{
camera.y = LEVEL_HEIGHT - camera.h;
}
}
当我们设置相机时,我们先依据点的位置将相机居中。
因为我们不想让任何关卡外的东西显示在窗口中,所以我们还必须在相机居中后将它约束在关卡边界内。
于是我们要检测相机是否在地图边界内,如果发现它有一部分超出了边界,我们就将它移回边界内。
因为我们不想让任何关卡外的东西显示在窗口中,所以我们还必须在相机居中后将它约束在关卡边界内。
于是我们要检测相机是否在地图边界内,如果发现它有一部分超出了边界,我们就将它移回边界内。
void Dot::show()
{
//显示小黑点
apply_surface( x - camera.x, y - camera.y, dot, screen );
}
这个函数以相对于相机的坐标将小黑点blit到窗口中,这样可以保证它能与背景匹配。
//当用户还没有请求退出时
while( quit == false )
{
//启动帧计时器
fps.start();
//当有事件需要处理
while( SDL_PollEvent( &event ) )
{
//为小黑点处理事件
myDot.handle_input();
//如果用户单击了窗口右上角的关闭按钮
if( event.type == SDL_QUIT )
{
//退出程序
quit = true;
}
}
//移动小黑点
myDot.move();
//设置相机
myDot.set_camera();
//显示背景
apply_surface( 0, 0, background, screen, &camera );
//显示小黑点
myDot.show();
//更新屏幕
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
//捕获帧率
if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
{
SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - fps.get_ticks() );
}
}
现在这个是程序的主循环。
我们处理事件、移动小黑点、设置相机、将背景中位于相机内的区域应用到窗口中、显示小黑点、更新窗口,最后捕获帧率。
我们处理事件、移动小黑点、设置相机、将背景中位于相机内的区域应用到窗口中、显示小黑点、更新窗口,最后捕获帧率。