这是另一个运动教程的例子,能向你展示如何通过文件的输入或输出来加载或保存游戏数据。
#include "SDL/SDL.h" #include "SDL/SDL_image.h" #include <string> #include <fstream>
你需要包含fstream头文件来进行文件读写。它是一个标准库,并不是SDL的一部分。
//点 class Dot { private: //点的X/Y坐标 int x, y; //点的速度 int xVel, yVel; public: //初始化变量 Dot(); //处理按键并调整点的速度 void handle_input(); //移动点 void move(); //在屏幕上显示点 void show(); //设置点的x/y坐标 void set_x( int X ); void set_y( int Y ); //获得点的x/y坐标 int get_x(); int get_y(); };
这又是Dot类。这里仅有的一个真正的变化是我们增加了x/y坐标的get/set函数。
bool load_files( Dot &thisDot, Uint32 &bg ) { //加载点的图像 dot = load_image( "dot.png" ); //如果加载点时出现问题 if( dot == NULL ) { return false; } //打开一个文件以供读取 std::ifstream load( "game_save" );
在
ifstream(即 input file stream,输入文件流)让你能够从一个文件流中获得输入。当你将一个文件名传入构造函数时,它将打开那个文件以供读取。
load_files()
函数中,我们加载完图像后创建了一个ifstream对象。
ifstream(即 input file stream,输入文件流)让你能够从一个文件流中获得输入。当你将一个文件名传入构造函数时,它将打开那个文件以供读取。
//如果文件加载完成 if( load != NULL ) { //坐标 int offset; //关卡名 std::string level; //设置x坐标 load >> offset; thisDot.set_x( offset ); //设置y坐标 load >> offset; thisDot.set_y( offset );
如果加载文件时出现问题,ifstream对象将会为NULL。
这里我们检查文件是否加载成功。如果加载成功,我们声明"offset"来提取坐标,并声明"level"来决定如何设置背景。
然后,我们首先从文件获得第一个整数并设为点的x坐标。然后,我们获取第二个整数并将其设为y坐标。正如你看到的那样,我们我们采用与cin相同的方式从ifstream中获取整数。那是因为它们都是istream(即 input stream,输入流)。
文件"game_save"的内容将是类似于这样的内容:
0 0
White Level
我们可以看到,既然文件中的和我们在控制台应用中输入的都是字符,那么我们有理由将它们视为几乎一样。
这里我们检查文件是否加载成功。如果加载成功,我们声明"offset"来提取坐标,并声明"level"来决定如何设置背景。
然后,我们首先从文件获得第一个整数并设为点的x坐标。然后,我们获取第二个整数并将其设为y坐标。正如你看到的那样,我们我们采用与cin相同的方式从ifstream中获取整数。那是因为它们都是istream(即 input stream,输入流)。
文件"game_save"的内容将是类似于这样的内容:
0 0
White Level
我们可以看到,既然文件中的和我们在控制台应用中输入的都是字符,那么我们有理由将它们视为几乎一样。
//如果x坐标不合法 if( ( thisDot.get_x() < 0 ) || ( thisDot.get_x() > SCREEN_WIDTH - DOT_WIDTH ) ) { return false; } //如果y坐标不合法 if( ( thisDot.get_y() < 0 ) || ( thisDot.get_y() > SCREEN_HEIGHT - DOT_HEIGHT ) ) { return false; }
考虑到用户可以轻易地修改文件,我们也必须检查从文件中获得的坐标是否合法。
//跳过行末 load.ignore(); //获得下一行 getline( load, level ); //如果在尝试加载数据时发生了错误 if( load.fail() == true ) { return false; }
然后我们使用
对于使用Visual C++ 6.0的小伙伴们来说,你们必须使用
随后,我们检查从文件读取是否存在问题。如果存在问题,函数
ignore()
函数跳过了下一个字符('\n')。接着我们通过getline()
函数获得并存储下一行内容。getline()
函数与使用>>
不同,它会获取直到行末前的所有内容。
对于使用Visual C++ 6.0的小伙伴们来说,你们必须使用
std::getline()
。
随后,我们检查从文件读取是否存在问题。如果存在问题,函数
fail()
会返回true。
//如果关卡是白色的 if( level == "White Level" ) { //设置背景色 bg = SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ); } //如果关卡是红色的 else if( level == "Red Level" ) { //设置背景色 bg = SDL_MapRGB( screen->format, 0xFF, 0x00, 0x00 ); } //如果关卡是绿色的 else if( level == "Green Level" ) { //设置背景色 bg = SDL_MapRGB( screen->format, 0x00, 0xFF, 0x00 ); } //如果关卡是蓝色的 else if( level == "Blue Level" ) { //设置背景色 bg = SDL_MapRGB( screen->format, 0x00, 0x00, 0xFF ); }
既然我们有了关卡字符串,我们就依照这个设置背景色。
//关闭文件 load.close(); } //如果所有内容加载正常 return true; }
我们完成读取文件后,我们将文件关闭。
void clean_up( Dot &thisDot, Uint32 &bg ) { //释放表面 SDL_FreeSurface( dot ); //打开一个文件以供写入 std::ofstream save( "game_save" ); //将坐标写入文件 save << thisDot.get_x(); save << " "; save << thisDot.get_y(); save << "\n";
在
既然ifstream与cin类似,那么显然ofstream与cout类似。
In this piece of code we write the dot's offsets to a file. 在这部分代码中,我们将点的坐标写入了一个文件。
clean_up()
函数中,我们创建了一个ofstream来写入文件。ofstream(即 output file stream,输出文件流)让你能够输出到文件流中。
既然ifstream与cin类似,那么显然ofstream与cout类似。
In this piece of code we write the dot's offsets to a file. 在这部分代码中,我们将点的坐标写入了一个文件。
//背景的RGB值 Uint8 r, g, b; //从背景色中获取RGB值 SDL_GetRGB( bg, screen->format, &r, &g, &b );
然后我们用
SDL_GetRGB()
函数从背景中获取单独的R、G、B值。
//如果背景是白色 if( ( r == 0xFF ) && ( g == 0xFF ) && ( b == 0xFF ) ) { //将关卡类型写入文件 save << "White Level"; } //如果背景是红色 else if( r == 0xFF ) { //将关卡类型写入文件 save << "Red Level"; } //如果背景是绿色 else if( g == 0xFF ) { //将关卡类型写入文件 save << "Green Level"; } //如果背景是蓝色 else if( b == 0xFF ) { //将关卡类型写入文件 save << "Blue Level"; }
之后,我们写入关卡类型。
//关闭文件 save.close(); //退出SDL SDL_Quit(); }
最后,我们关闭文件。
//退出标志 bool quit = false; //初始化 if( init() == false ) { return 1; } //点 Dot myDot; //背景色 Uint32 background = SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ); //帧率校准器 Timer fps; //加载文件 if( load_files( myDot, background ) == false ) { return 1; }
这里你可以看到我们使用了
load_files()
函数。
//当用户还未推出 while( quit == false ) { //启动帧计数器 fps.start(); //当有事件要处理 while( SDL_PollEvent( &event ) ) { //为小点处理事件 myDot.handle_input(); //如果用户按下了按键 if( event.type == SDL_KEYDOWN ) { //根据按键更改背景 switch( event.key.keysym.sym ) { case SDLK_1: background = SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ); break; case SDLK_2: background = SDL_MapRGB( screen->format, 0xFF, 0x00, 0x00 ); break; case SDLK_3: background = SDL_MapRGB( screen->format, 0x00, 0xFF, 0x00 ); break; case SDLK_4: background = SDL_MapRGB( screen->format, 0x00, 0x00, 0xFF ); break; } } //如果用户叉掉了窗口 if( event.type == SDL_QUIT ) { //退出程序 quit = true; } } //移动点 myDot.move(); //填充背景 SDL_FillRect( screen, screen->clip_rect, background ); //在屏幕上显示点 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() ); } }
这里是主循环。要改变背景,你可以按下1,2,3或4。当你再次启动程序时,点和背景将会和你退出程序时的一样。
//清理并保存 clean_up( myDot, background ); }
最终,我们调用
clean_up()
函数来清理并保存数据。