在pygame游戏退出时保存对象列表 [英] Save a list of objects on exit of pygame game
问题描述
所以我有一个已定义的类的列表,这些类在程序的出口处被导出..看起来像这样:
So I have a list of defined classes that gets exported on the exit of the program.. and it looks like this:
<__main__.Block object at 0x02416B70>,
<__main__.Block object at 0x02416FF0>,
<__main__.Block object at 0x0241C070>,
<__main__.Block object at 0x0241C0D0>,
<__main__.Block object at 0x0241C130>,
<__main__.Block object at 0x0241C190>,
<__main__.Block object at 0x02416DF0>,
<__main__.Block object at 0x0241C250>,
<__main__.Block object at 0x0241C2B0>,
<__main__.Block object at 0x0241C310>,
<__main__.Block object at 0x0241C370>,
<__main__.Block object at 0x0241C3D0>,
<__main__.Block object at 0x0241C430>,
<__main__.Block object at 0x0241C490>,
<__main__.Block object at 0x0241C4F0>,
<__main__.Block object at 0x0241C550>,
<__main__.Block object at 0x0241C5B0>,
<__main__.Block object at 0x0241C610>
完美!正确的?现在,我应该可以轻松地将其转换为列表. 所以我用这个:
Perfect! Right? Now I should easily be able to convert that to a list.. So I use this:
x=x.split(",")
是的,它可以将其转换为列表,但是可以将类转换为字符串!使它们无法使用.
And it converts it to a list yes, but it turns the classes into strings! Making them un-usable.
基本上,我需要在文件关闭时将游戏状态暂停",然后在打开文件时重新加载它.
Basically what I need is to "suspend" the state of the game within a file when it is closed, and then reload it upon opening it.
那么如何在不将类名转换为字符串的情况下做到这一点呢?
So how can I do this without converting the class names to strings?
推荐答案
完美!对吧?
Perfect! Right?
不幸的是,没有.您在这里看到的(<__main__.Block object at 0x02416B70>
)是类实例的典型字符串表示形式.这只是一个简单的字符串,无法将该字符串转换回Block
的实例.
Sadly, no. What you see here (<__main__.Block object at 0x02416B70>
) is a typical string representation of a class instance. It's just a simple string, and there's no way to convert this string back to an instance of of Block
.
我认为您仍在通过那么您如何真正保持游戏状态呢?最简单的方法是使用标准的python模块 pickle
或 shelve
.
So how do you actually persist the state of the game? The easiest way is to use the standard python module pickle
or shelve
.
在下面的示例中,我将使用shelve
,因为您不会使用单个类来表示游戏状态:
In the following example, I'll use shelve
, because you don't use a single class to represent the game state:
架子"是一个持久的,类似于字典的对象. ...架子上的值...本质上可以是任意的Python对象...这包括大多数类实例,递归数据类型以及包含许多共享子对象的对象.键是普通的字符串.
A "shelf" is a persistent, dictionary-like object. ... the values ... in a shelf can be essentially arbitrary Python objects ... This includes most class instances, recursive data types, and objects containing lots of shared sub-objects. The keys are ordinary strings.
首先,当我们退出游戏时,我们想保存玩家和积木,所以让我们在游戏即将退出时调用一个新的save
函数:
First of all, when we exit the game, we want to save the player and the blocks, so let's call a new save
function when the game is about to exit:
while True:
...
for event in pygame.event.get():
if event.type == QUIT:
save(player, blocklist)
exit()
实施非常简单(为简便起见,没有错误处理):
The implementation is quite easy (no error handling for brevity):
def save(player, blocks):
f = shelve.open("save.bin")
f['player'] = player
f['blocks'] = blocks
f.close()
如您所见,使用shelve
与使用dict
一样容易.
As you see, using shelve
is as easy as using a dict
.
下一步是加载我们保存的数据.
Next step is loading our saved data.
player, blocklist = load() or (None, [])
我们调用一个新函数load
,该函数将返回已保存的播放器对象的元组和已保存的块对象的列表,或者返回None
.在None
的情况下,我们尚未创建播放器,并在空白处使用了空白列表.
We call a new function load
which will either return a tuple of the saved player object and a list of the saved block objects, or None
. In case of None
, we don't create a player yet and use an empty list for our blocks.
实现与save
函数一样简单:
def load():
try:
f = shelve.open("save.bin")
return f['player'], f['blocks']
except KeyError:
return None
finally:
f.close()
就是这样.
这是完整的代码:
import pygame,random
from pygame.locals import *
from collections import namedtuple
import shelve
pygame.init()
clock=pygame.time.Clock()
screen=pygame.display.set_mode((640,480))
max_gravity = 100
class Block(object):
sprite = pygame.image.load("dirt.png").convert_alpha()
def __init__(self, x, y):
self.rect = self.sprite.get_rect(centery=y, centerx=x)
class Player(object):
sprite = pygame.image.load("dirt.png").convert()
sprite.fill((0,255,0))
def __init__(self, x, y):
self.rect = self.sprite.get_rect(centery=y, centerx=x)
# indicates that we are standing on the ground
# and thus are "allowed" to jump
self.on_ground = True
self.xvel = 0
self.yvel = 0
self.jump_speed = 10
self.move_speed = 8
def update(self, move, blocks):
# check if we can jump
if move.up and self.on_ground:
self.yvel -= self.jump_speed
# simple left/right movement
if move.left: self.xvel = -self.move_speed
if move.right: self.xvel = self.move_speed
# if in the air, fall down
if not self.on_ground:
self.yvel += 0.3
# but not too fast
if self.yvel > max_gravity: self.yvel = max_gravity
# if no left/right movement, x speed is 0, of course
if not (move.left or move.right):
self.xvel = 0
# move horizontal, and check for horizontal collisions
self.rect.left += self.xvel
self.collide(self.xvel, 0, blocks)
# move vertically, and check for vertical collisions
self.rect.top += self.yvel
self.on_ground = False;
self.collide(0, self.yvel, blocks)
def collide(self, xvel, yvel, blocks):
# all blocks that we collide with
for block in [blocks[i] for i in self.rect.collidelistall(blocks)]:
# if xvel is > 0, we know our right side bumped
# into the left side of a block etc.
if xvel > 0: self.rect.right = block.rect.left
if xvel < 0: self.rect.left = block.rect.right
# if yvel > 0, we are falling, so if a collision happpens
# we know we hit the ground (remember, we seperated checking for
# horizontal and vertical collision, so if yvel != 0, xvel is 0)
if yvel > 0:
self.rect.bottom = block.rect.top
self.on_ground = True
self.yvel = 0
# if yvel < 0 and a collision occurs, we bumped our head
# on a block above us
if yvel < 0: self.rect.top = block.rect.bottom
colliding = False
Move = namedtuple('Move', ['up', 'left', 'right'])
def load():
try:
f = shelve.open("save.bin")
return f['player'], f['blocks']
except KeyError:
return None
finally:
f.close()
def save(player, blocks):
f = shelve.open("save.bin")
f['player'] = player
f['blocks'] = blocks
f.close()
player, blocklist = load() or (None, [])
while True:
screen.fill((25,30,90))
mse = pygame.mouse.get_pos()
key = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == QUIT:
save(player, blocklist)
exit()
if key[K_LSHIFT]:
if event.type==MOUSEMOTION:
if not any(block.rect.collidepoint(mse) for block in blocklist):
x=(int(mse[0]) / 32)*32
y=(int(mse[1]) / 32)*32
blocklist.append(Block(x+16,y+16))
else:
if event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
to_remove = [b for b in blocklist if b.rect.collidepoint(mse)]
for b in to_remove:
blocklist.remove(b)
if not to_remove:
x=(int(mse[0]) / 32)*32
y=(int(mse[1]) / 32)*32
blocklist.append(Block(x+16,y+16))
elif event.button == 3:
x=(int(mse[0]) / 32)*32
y=(int(mse[1]) / 32)*32
player=Player(x+16,y+16)
move = Move(key[K_UP], key[K_LEFT], key[K_RIGHT])
for b in blocklist:
screen.blit(b.sprite, b.rect)
if player:
player.update(move, blocklist)
screen.blit(player.sprite, player.rect)
clock.tick(60)
pygame.display.flip()
在这里您可以看到正在执行的加载和保存:
And here you can see loading and saving in action:
请注意,您不能通过这种方式保存(或刺戳")Surfaces
.在此代码中,它之所以有效,是因为Player
和Block
的Surfaces
是类变量,而不是实例变量,因此不会保存到磁盘.如果要使用Surface
实例变量刺穿"对象,则必须先删除Surface
(例如,将其设置为None
),然后再次加载(例如,在load
函数中).
Note that you can't save (or "pickle") Surfaces
this way. In this code, it works because the Surfaces
of Player
and Block
are class variables, not instance variables, and thus don't get saved to disk. If you want to "pickle" an object with a Surface
instance variable, you would have to remove the Surface
first (e.g. setting it to None
) and load it again (e.g. in the load
function).
这篇关于在pygame游戏退出时保存对象列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!