简体   繁体   English

在Python中为基于文本的游戏制作保存文件

[英]Making a Savefile for a Text-Based Game in Python

tl;dr in bold below tl; dr以粗体显示在下面

I'm currently developing a text-based adventure game, and I've implemented a basic saving system. 我目前正在开发基于文本的冒险游戏,并且已经实现了基本的保存系统。

The process takes advantage of the 'pickle' module. 该过程利用了“棘手”模块。 It generates or appends to a file with a custom extension (when it is, in reality, a text file). 它生成或附加具有自定义扩展名的文件(实际上是文本文件)。 The engine pickles the player's location, their inventory, and, well, the last part is where it gets a little weird. 引擎会腌制玩家的位置,他们的存货,还有,最后一部分是让它有些怪异的地方。

The game loads dialog from a specially formatted script (Here I mean as in an actor's script). 游戏从特殊格式的脚本加载对话框(这里是指演员的脚本)。 Some dialog changes based on certain conditions (already spoken to them before, new event, etc.). 某些对话框会根据某些条件(已与他们交谈,发生新事件等)而发生更改。 So, for that third object the engine saves, it saves ALL dialog trees in their current positions. 因此,对于引擎保存的第三个对象,它会将所有对话框树保存在其当前位置。 As in, it saves the literal script in its current state. 与之类似,它将文字脚本保存为当前状态。

Here is the saving routine: 这是保存例程:

with open('save.devl','wb') as file:
    pickle.dump((current_pos,player_inv,dia_dict),file)
    for room in save_map:
        pickle.dump(room,file)
    file.close()

My problem is, this process makes a very ugly, very verbose, super large text file. 我的问题是,此过程生成了一个非常丑陋,非常冗长的超大文本文件。 Now I know that text files are basically the smallest files I can generate, but I was wondering if there was any way to compress or otherwise make more efficient the process of recording the state of everything in the game. 现在我知道文本文件基本上是我可以生成的最小文件,但是我想知道是否有任何方法可以压缩或以其他方式提高记录游戏中所有状态的过程。 Or, less preferably but better in the long run, just a smarter way to save the player's data. 或者,从长远来看,最好是一种更聪明的方法来保存播放器的数据,但从长远来看却不太理想,但更好。

The format of dialog was requested. 请求对话框的格式。 Here is a sample: 这是一个示例:

[Isaac]
a: Hello.|1. I'm Evan.|b|
b: Nice to meet you.|1. Where are you going?\2.Goodbye.|c,closer|
c: My cousin's wedding.|1. Interesting. Where are you from?\2. What do you know about the ship?\3. Goodbye.|e,closer|
closer: See you later.||break|
e: It's the WPT Magnus. Cruise-class zeppelin. Been in service for about three years, I believe.||c|
standing: Hello, again.|1. What do you know about the ship?\2.Goodbye.|e,closer|

The name in brackets is how the program identifies which tree to call. 方括号中的名称是程序识别要调用的树的方式。 Each letter is a separate branch in the tree. 每个字母是树中单独的分支。 The bars separate the branch into three parts: 1. What the character says 2. The responses you are allowed 3. Where each response goes, or if the player doesn't respond, where the player is directed afterwards. 条形图将分支分为三个部分:1.角色说什么2.允许您回答3.每个响应都去了哪里,或者如果玩家没有响应,则指向玩家之后的位置。

In this example, after the player has talked to Isaac, the 'a' branch is erased from the copy of the tree that the game stores in memory. 在此示例中,在玩家与Isaac交谈之后,从游戏存储在内存中的树的副本中删除了“ a”分支。 It then permanently uses the 'standing' branch. 然后,它将永久使用“常设”分支。

Pickle itself has other protocols that are all more compact than the default protocol (protocol 0) - which is the only one "text based" - the others are binary protocols. Pickle本身还有其他协议,它们都比默认协议(协议0)更紧凑-默认协议是唯一的一个“基于文本”的协议-其他协议都是二进制协议。

But them, you hardly would get more than 50% of the file size - to be able to enhance the answer, we need to know better what you are saving, and if there are smarter ways to save your data - for example, by avoiding repeating the same sub-data structure if it is present in several of your rooms. 但是,它们几乎不会超过文件大小的50%-为了增强答案,我们需要更好地了解要保存的内容,以及是否有更聪明的方法来保存数据(例如,避免如果您的多个房间中都存在相同的子数据结构,请重复此操作。 (Although if you are using object identity inside your game, Pickle should take care of that). (尽管如果您在游戏中使用对象标识,则Pickle应该注意这一点)。

That said, just change your pickle.dump calls to include the protocol parameter - the -1 value is equivalent to "HIGHEST_PROTOCOL", which is usually the most efficient: 也就是说,只需更改pickle.dump调用以包含协议参数-1值等效于“ HIGHEST_PROTOCOL”,通常这是最有效的:

pickle.dump(room,file, protocol=-1)

(loading the pickles do not require that the protocol is passed at all) (加载泡菜根本不需要通过协议)

Aditionally, you might want to use Python's zlib interface to compress pickle data. 另外,您可能想使用Python的zlib接口压缩泡菜数据。 That could give you another 20-30% file size reduction - you have to chain the calls to file.write, zlib.compress and pickle.dumps, so you will be easier with a little helper code - also you need to control file offsets, as zlib is not like pickle which advances the file pointer: 这可能会使您再减小20-30%的文件大小-您必须将调用链式链接到file.write,zlib.compress和pickle.dumps,因此使用一些辅助代码会更容易-还要控制文件的偏移量,因为zlib不像pickle那样使文件指针前进:

import pickle, zlib 

def store_obj(file_, obj):
    compressed = zlib.compress(pickle.dumps(obj, protocol=-1), level=9)
    file_.write(len(compressed).to_bytes(4, "little"))
    file_.write(compressed)

def get_obj(file_):

     obj_size = int.from_bytes(file_.read(4), "little")
     if obj_size == 0:
         return None
     data = zlib.decompress(self.file_.read(obj_size))
     return pickle.loads(data)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM