开发者

Switching scenes with pyglet

Can anybody recommend how to switch between scenes in pyglet. I.e.

  • menu > game
  • game > menu
  • menu > help
  • ect

The only way that i can think to do it off the top of my head is by using different windows, which i am quite sure would be the complete wrong way to do it. Or b开发者_如何转开发y overloading all of the window's event functions.

Sorry if i haven't made myself clear but any help would be appreciated


The cocos2d.org framework is built on pyglet and includes scene management.


Here is a rough schematic of a class structure that might work for you:

class Game(object):
    "This class contains the Scene which is the current scene the user is look ing at."
    def __init__(self):
        self.current_level = 0
        self.current_screen = MainMenu(self)

    def load(self):
        "Load progress from disk"
        pass

    def save(self):
        "Save progress to disk"
        pass

    def clearCurrentScreen(self):
        self.current_screen.clear()
        self.window.remove_handlers()

    def startCurrentScreen(self):
        self.window.set_handler("on_key_press", self.current_screen.on_key_press)
        # etc

        self.current_screen.start()

    def gotoNextLevel(self):
        "called from within LevelPlayer when the player beats the level"
        self.clearCurrentScreen()
        self.current_level += 1
        self.current_screen = LevelPlayer(self, game, self.current_level)
        self.startCurrentScreen()

    def startPlaying(self):
        "called by the main menu when the user selects an option"
        self.clearCurrentScreen()
        self.current_screen = LevelPlayer(self, game, self.current_level)
        self.startCurrentScreen()

    def execute(self):
        self.window = pyglet.window.Window()
        self.startCurrentScene()
        pyglet.app.run()


class Screen(object):
    def __init__(self):
        pass

    def start():
        pass

    def clear():
        "delete all graphical objects on screen, batches, groups, etc. Clear all state in pyglet."
        pass

    def on_key_press(self, key, etc):
        pass

    def on_draw(self):
        pass

    # etc

class LevelPlayer(Screen):
    "This class contains all your game logic. This is the class that enables the user to play through a level."
    def __init__(self, game, level_to_play):
        pass

    # be sure to implement methods from Screen

class MainMenu(Screen):
    "This class presents the title screen and options for new game or continue."
    def __init__(self, game):
        self.game = game

    def handleNewGame(self):
        self.game.startPlaying()

    def handleContinue(self):
        self.game.load()
        self.game.startPlaying()

    # be sure to implement methods from Screen


game = Game()
game.execute()

So you have a Game class who owns the window and that manages which Screen is displayed to the user. Here I use "Screen" to mean what the user is interacting with, for example a MainMenu, or a LevelPlayer. The key here is the clear() method of Screen, which you should implement to delete all the sprites, media, groups, and batches that you were displaying. You also have to remove the window handlers on clear and set them on start.

You can see this solution in action here: https://github.com/superjoe30/lemming/tree/master/lemming


I'm not very experienced, but for what it's worth, the method I use is as follows. It does not recognise explicit 'states' or 'scenes' as such, but rather it relies on the adding (and removal) of discrete items into my game world. Each such item may have its own key handlers, and knows how and when to create other such items.

A specific example:

GameItem is a subclass for all items that can be put into the world. It is a simple collection of attributes, with no behaviour. It is subclassed by items in the game world like Bush, Player, etc, and also by HUD items like ScoreDisplay and MainMenu.

World is just a collection of GameItems.

My 'update' function iterates through all items in the world, calling their update method if they have one.

My 'draw' function similarly iterates through all items in the world, drawing each of them. (many of them might be drawn en-masse by simply calling something like pyglet's Batch.draw)

Right at application start-up, one of the first items I add to the world is a MainMenu object. It has an on_key_down method.

World.add looks at the item being added. If an item has an on_key_down method, then it adds this handler to the pyglet key handlers stack. (Similarly, it undoes this in World.remove) (Actually, on reflection, I guess world.add raises an event when it adds an item. If the application's keyboard handler module is interested, then on recieving one of these events it then does the adding of the item's key hander, but this is kinda incidental to the question)

The MainMenu.on_key_handler method looks at the pressed key, and if it's the key to start the game, then it makes a series of calls:

# Do everything needed to start the game
# For dramatic pacing, any of these might be scheduled to be
# called in a second or so, using pyglet.clock.schedule_once
world.add(player)
camera.to_follow(player)
world.add(scoredisplay)

# Finally, remove the main menu from the world
# This will stop showing it on screen
# and it will remove its keyboard event handler
world.remove_item(self)

This is just a simple example, but hopefully you can see how, instead of starting the game, the mainmenu could instead display secondary menus by adding them to the world, or any such thing.

Once in the game, you can change 'scenes' by simply calling 'world.remove' on all items that you no longer want visible, and calling 'world.add' on all the items in the new scene.

An example of a game that uses this technique is previous pyweek entry SinisterDucks: http://code.google.com/p/brokenspell/ (although, to be fair, it does not feature distinct 'scenes' during gameplay. It merely usesthis technique to manage menus, game over screen, etc)


The solution i ended up using is by giving the main window a stack and having it pass all events out to items on the top of the stack.

i.e.

class GameWindow(pyglet.window.Window):
    def __init__(self,*args, **kwargs):
        pyglet.window.Window.__init__(self, *args, **kwargs)
        self.states = [PlayLevel(self)] ## Starting State

    def on_draw(self):
        if hasattr(self.states[-1],"on_draw"):
            self.states[-1].on_draw()

    def on_mouse_press(self,*args):
        if hasattr(self.states[-1],"on_mouse_press"):
            self.states[-1].on_mouse_press(*args)

ect. For all of the other events that i use

i am currently in the process of writing up some functions to go in GameWindow that will manage pushing and popping scenes from the stack

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜