多平台游戏通常如何存储保存数据?

我意识到这是一个广泛的问题,但我想知道在游戏存储保存数据方面是否存在“标准”(在不同的平台上是不同的 – Xbox / PS / PC / Mac /安卓/ iOS设备?)

例如“刺客信条”或“行尸走肉”这样的游戏:他们在多个平台上,通常必须存储关于玩家及其行为的足够信息。 他们是否使用像XML文件,数据库,或只是直接二进制转储? 从平台到平台有多less不同?

如果有游戏行业经验的人会回答这个问题,我将不胜感激。

我为迪斯尼有罪党写了保存游戏系统。 我可以给你一个关于它是如何完成的高层次的概述。 尽管它是作为Wii游戏发货的,但为了进行debugging,我们需要一个既能在Wii上也能在PC上运行的保存游戏系统。 免责声明:这是我写这样一个系统的唯一时间,绝不能声称它是编写保存游戏系统的标准方法(tm)。 这更像是个案研究。

从纯粹的devise角度来看,二进制文件总是很麻烦。 首先,它们非常难以debugging,因为您必须在hex编辑器中打开保存游戏文件。 其次,你不能下载在控制台上生成的保存游戏文件,并在PC上打开它,而没有确定你的字节顺序是否正确,这要求你A)有重复的序列化代码的大小端机或B)在swapBytes()调用中的序列化代码,根据您的机器的字节序来切换内容。 两者都笨重。

因此,我们正在寻找一个有能力的保存游戏系统

  • 能够以相同的格式从Wii和PC上读取和写入。
  • 很容易debugging我们的对象的序列化
  • 有一个版本控制系统,允许我们读取以前版本的游戏中保存的游戏文件,以在旧版本的testing过程中跟踪崩溃报告。
  • 忽略加载时间是因为从反序列化的保存文件加载游戏与从头开始加载新的级别的数量级相同。

考虑到这些想法,我们使用(大致)以下系统。 每个保存游戏文件保存为明文。 每个保存游戏文件的开头总是以如下的一行开始:

Version: 21 

然后,我们使用类似于以下内容的序列化和反序列化:

 const unsigned int kCurrentVersion = 24; void Serialize(GameState &state, const char *filename) { File saveFile = File::Open(filename); saveFile.Write("%d\n", kCurrentVersion); saveFile.Write("%f %f %f\n", state.playerPosition); saveFile.Write("%f %f %f\n", state.playerPantsColor); saveFile.Write("%d\n", state.kNumTacosIHadForLunch); } // Any save game file before this version is invalid... const unsigned int kMinimumSupportedVersion = 18; GameState Deserialize(const char *filename) { File saveFile = File::Open(filename); unsigned int version = 0; GameState savedState; if(saveFile.Read("%d\n", &version) != 1 || !version) { fprintf(stderr, "No version... no idea what's going on."); return kInvalidState; } if(version < kMinimumSupportedVersion || version > kCurrentVersion) { fprintf(stderr, "Unsupported version: %d\n", version); return kInvalidState; } float pos[3]; if(saveFile.Read("%f %f %f\n", &pos[0], &pos[0], &pos[0]) != 3) { fprintf(stderr, "Error reading position.\n"); return kInvalidState; } else { savedState.setPlayerPosition(pos); } if(version > 10) { float c[3]; if(saveFile.Read("%f %f %f\n", &c[0], &c[1], &c[2]) != 3) { fprintf(stderr, "Error reading pants color.\n"); return kInvalidState; } else { savedState.setPantsColor(c); } } if(version > 17 && version < 22) { int nsiwaat; if(saveFile.Read("%d\n", &nsiwaat) != 1) { fprintf(stderr, "Error reading the number of socks I wear at a time.\n"); return kInvalidState; } else { savedState.setNSIWAAT(nsiwaat); } } // ...etc return savedState; } 

从这里得到的关键是:你的序列化代码应该总是写出你的游戏状态的最新版本,不管它是什么。 但是,它也必须写出最新的版本,以便游戏的将来(和当前)版本能够知道如何读取文件。

反序列化代码使用这种技术有点麻烦。 阅读保存游戏文件需要非常防御性的方法。 有安全原因(即缓冲区溢出 ),为什么这是一个好主意。 这也降低了阅读损坏的游戏文件和加载不良数据和崩溃(或更糟,没有崩溃)的风险。 但是,为了debugging目的,您应该确保尽可能详细地说明为什么无法读取保存的游戏。 这将在未来的版本中为您节省很多麻烦,您认为事情应该是某种方式,但事实并非如此。 这个系统不仅非常有效,而且维护起来也非常方便。

我会尽可能使用SQL数据库的建议。 它们强大,众所周知,易于界面,可扩展和一切。 唯一的缺点可能是性能。 但是,因为你不问如何在数据库本身的每个更新性能上存储你的实时游戏对象,这很可能不成问题。

请务必了解如何序列化游戏状态的游戏相关部分。 把它们推入数据库应该相当容易。

您可以使用Google的协议缓冲区( https://developers.google.com/protocol-buffers )。