在pygame游戏退出时保存对象列表 [英] Save a list of objects on exit of pygame game

查看:84
本文介绍了在pygame游戏退出时保存对象列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一个已定义的类的列表,这些类在程序的出口处被导出..看起来像这样:

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.在此代码中,它之所以有效,是因为PlayerBlockSurfaces是类变量,而不是实例变量,因此不会保存到磁盘.如果要使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆