我正在为视频游戏编程课程的决赛编写代码,我想知道如何为我的游戏创建保存文件,以便用户可以玩,然后稍后再回来。任何想法如何做到的,我之前做过的每件事都是单运行程序。
我正在为视频游戏编程课程的决赛编写代码,我想知道如何为我的游戏创建保存文件,以便用户可以玩,然后稍后再回来。任何想法如何做到的,我之前做过的每件事都是单运行程序。
Answers:
您需要使用序列化将变量存储在硬盘中。序列化有很多类型,.NET XML是一种通用格式,尽管有二进制和JSON序列化器可用。我不是C ++程序员,但是快速搜索找到了一个有关C ++序列化的示例:
有些库提供序列化功能。其他答案中提到了一些。
您将感兴趣的变量可能与游戏状态有关。例如,您可能想知道此类信息
您不会真正在乎使用什么纹理(除非您的播放器可以更改其外观,这是一种特殊情况),因为它们通常是相同的。您需要集中精力保存重要的游戏状态数据。
当您开始游戏时,您可以正常开始“新”游戏(这会加载纹理,模型等),但是在适当的时候,您将保存文件中的值重新加载到游戏状态对象中,从而替换了“默认”新游戏。游戏状态。然后,您允许播放器继续播放。
我在这里已经对其进行了极大的简化,但是您应该了解一般的想法。如果您有更具体的问题,请在此处提出一个新问题,我们可以尝试为您提供帮助。
通常,这是特定于您的游戏的。我敢肯定,到目前为止,您已经了解了如何在类中读写文件。基本思想是:
您写的内容取决于您,取决于您的游戏。一种写方法是将所需的变量按特定顺序写为字节流。然后在加载时,以相同的顺序将它们读入程序。
例如(使用快速伪代码):
SaveGame(FileInput file) {
file.writeInt(playerLevel);
file.writeInt(playerHealth);
file.writeInt(gameProgress);
}
LoadGame(FileInput file) {
if(file.exists()) {
playerLevel= file.readInt();
playerHealth = file.readInt();
gameProgress = file.readInt();
} else {
playerLevel = 1;
playerHealth = 100;
gameProgress = 0;
}
}
可能有很多方法可以做到这一点,但是我一直发现并且个人和专业使用的最简单方法是创建一个包含所有要保存的值的结构。
struct SaveGameData
{
int characterLevel; // Any straight up values from the player
int inventoryCount; // Number of items the player has on them or stored or what not
int[STAT_COUNT] statistics; // This is usually a constant size (I am tracking X number of stats)
// etc
}
struct Item
{
int itemTypeId;
int Durability; // also used as a 'uses' count for potions and the like
int strength; // damage of a weapon, protection of armor, effectiveness of a potion
// etc
}
然后,我仅使用基本的文件IO值在文件中读写数据。ventoryCount是文件中主要SaveGameData结构之后保存的Item结构的数量,因此我知道在获取该数据后要读取多少个Item结构。这里的关键是,当我想保存一些新内容时,除非它包含项目列表或类似内容,否则我要做的就是在某个位置为结构添加一个值。如果它是一个项目列表,那么我将不得不添加一个读通道,就像我已经暗示了Item对象一样,在主标头中添加一个计数器,然后在条目中添加。
这样做的缺点是,在没有特殊处理的情况下,使不同版本的保存文件彼此不兼容(即使它只是主结构中每个条目的默认值)。但是总的来说,这仅需添加新的数据值并在需要时将值放入其中即可轻松扩展系统。
再次,有很多方法可以做到这一点,这可能比C ++引向C的更多机会,但是它已经完成了工作!
首先,您需要确定需要保存哪些数据。例如,这可以是角色的位置,他的得分和硬币数量。当然,您的游戏可能会更加复杂,因此您需要保存其他数据,例如关卡号和敌人列表。
接下来,编写代码以将其保存到文件中(使用ofstream)。可以使用的相对简单的格式如下:
x y score coins
因此文件看起来像:
14 96 4200 100
这意味着他以4200分和100枚硬币处于第(14,96)位。
您还需要编写代码以加载该文件(使用ifstream)。
可以通过在文件中包含敌人的位置来保存敌人。我们可以使用以下格式:
number_of_enemies x1 y1 x2 y2 ...
首先number_of_enemies
读取,然后通过一个简单的循环读取每个位置。
我认为最好的选择是boost :: serialization,因为它很容易在层次结构中向下传播。您只需要调用顶部对象的保存/加载函数,即可轻松创建所有类。
参见:http : //www.boost.org/doc/libs/1_49_0/libs/serialization/doc/index.html
您的游戏将危及您需要转换为字节(序列化)以便存储它们的数据结构(希望吗?)。将来,您可以将这些字节装回,然后将其转换回原始结构(反序列化)。在C ++中,它并不是那么棘手,因为反射非常有限。但是,有些图书馆仍然可以为您提供帮助。我写了一篇关于它的文章:https : //rubentorresbonet.wordpress.com/2014/08/25/an-overview-of-data-serialization-techniques-in-c/ 基本上,我建议您看看谷物库(如果您可能针对C ++ 11编译器)。无需像protobuf一样创建中间文件,因此,如果需要快速的结果,则可以在其中节省一些时间。您也可以在二进制和JSON之间进行选择,因此在这里调试很有帮助。
最重要的是,如果出于安全考虑,您可能希望对存储的数据进行加密/解密,尤其是在使用诸如JSON之类的人类可读格式时。AES之类的算法在这里很有帮助。
您需要fstream
用于输入/输出文件。语法很简单,例如EX:
#include <fstream>
// ...
std::ofstream flux ; // to open a file in ouput mode
flux.open("myfile.whatever") ;
要么
#include <fstream>
// ...
std::ifstream flux ; // open a file in input mode
flux.open("myfile.whatever") ;
您的文件上还可以执行其他操作:append,binary,trunc等。您将使用与上面相同的语法,例如,我们将std :: ios ::((flags)放到了:
ios::out
用于输出操作ios::in
用于输入操作ios::binary
用于二进制(原始字节)IO操作,而不是基于字符的ios::app
在文件末尾开始写入ios::trunc
如果文件已经存在,请删除旧内容并替换为新内容ios::ate
-将文件指针定位在“末尾”以进行输入/输出例如:
#include <fstream>
// ...
std::ifstream flux ;
flux.open("myfile.whatever" , ios::binary) ;
这是一个更完整但简单的示例。
#include <iostream>
#include <fstream>
using namespace std ;
int input ;
int New_Apple ;
int Apple_Instock ;
int Eat_Apple ;
int Apple ;
int main()
{
bool shouldQuit = false;
New_Apple = 0 ;
Apple_Instock = 0 ;
Eat_Apple = 0 ;
while( !shouldQuit )
{
cout << "------------------------------------- /n";
cout << "1) add some apple " << endl ;
cout << "2) check apple in stock " << endl ;
cout << "3) eat some apple " << endl ;
cout << "4) quit " << endl ;
cout << "------------------------------------- /n";
cin >> input ;
switch (input)
{
case 1 :
{
system("cls") ;
cout << "------------------------------------ /n";
cout << " how much apple do you want to add /n";
cout << "------------------------------------ /n";
cin >> New_Apple ;
ifstream apple_checker ;
apple_checker.open("apple.apl") ;
apple_checker >> Apple_Instock ;
apple_checker.close() ;
Apple = New_Apple + Apple_Instock ;
ofstream apple_adder ;
apple_adder.open("apple.apl") ;
apple_adder << Apple ;
apple_adder.close() ;
cout << "------------------------------------ /n";
cout << New_Apple << " Apple has been added ! /n";
cout << "------------------------------------ /n";
break;
}
case 2 :
{
system("cls") ;
ifstream apple_checker ;
apple_checker.open("apple.apl") ;
apple_checker >> Apple_Instock ;
apple_checker.close() ;
cout << "------------------------------------ /n";
cout << " there is " << Apple_Instock ;
cout << "apple in stock /n" ;
cout << "------------------------------------ /n";
break;
}
case 3 :
{
system("cls") ;
cout << "------------------------------------ /n";
cout << "How many apple do you want to eat /n" ;
cout << "------------------------------------ /n";
cin >> Eat_Apple ;
ifstream apple_checker ;
apple_checker.open("apple.apl") ;
apple_checker >> Apple_Instock ;
apple_checker.close() ;
Apple = Apple_Instock - Eat_Apple ;
ofstream apple_eater ;
apple_eater.open("apple.apl") ;
apple_eater << Apple ;
apple_eater.close() ;
cout << "----------------------------------- /n";
cout << Eat_Apple ;
cout << " Apple has been eated! /n";
cout << "----------------------------------- /n";
cout << Apple << " Apple left in stock /n";
cout << "----------------------------------- /n";
break;
}
case 4 :
{
shouldQuit = true;
break;
}
default :
{
system("cls") ;
cout << "------------------------------------ /n";
cout << " invalide choice ! /n";
cout << "------------------------------------ /n";
break;
}
}
}
return 0;
}
goto
s,您将在公共场所被私刑。不要使用goto
s。