使用pyglet切换场景
有没有人能推荐一下在 pyglet 中如何切换场景?比如:
- 从菜单切换到游戏
- 从游戏切换到菜单
- 从菜单切换到帮助
- 等等
我想到的唯一方法就是使用不同的窗口,但我很确定这完全不是正确的做法。或者是重载所有窗口的事件函数。
抱歉如果我没有表达清楚,但任何帮助都会很感激。
4 个回答
cocos2d.org这个框架是建立在pyglet基础上的,并且它包含了场景管理的功能。
这里有一个大致的类结构图,可能对你有帮助:
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()
你有一个游戏类(Game),它负责管理窗口,并决定给用户显示哪个界面。在这里,我用“界面”(Screen)来指代用户正在互动的部分,比如主菜单(MainMenu)或者关卡播放(LevelPlayer)。关键在于界面里的 clear() 方法,你需要实现这个方法来清除所有正在显示的精灵、媒体、组和批处理。你还需要在清除时移除窗口的处理程序,并在开始时重新设置它们。
你可以在这里看到这个解决方案的实际应用: https://github.com/superjoe30/lemming/tree/master/lemming
我不是很有经验,但我用的方法是这样的。它并不明确区分“状态”或“场景”,而是依赖于将不同的物品添加(或移除)到我的游戏世界中。每个物品可能都有自己的按键处理方法,并且知道如何以及何时创建其他类似的物品。
举个具体的例子:
GameItem是所有可以放入世界中的物品的一个子类。它只是一个简单的属性集合,没有任何行为。游戏世界中的物品,比如Bush(灌木)、Player(玩家)等,都是从这个类派生出来的,还有一些HUD(用户界面)物品,比如ScoreDisplay(得分显示)和MainMenu(主菜单)。
World就是一个GameItems的集合。
我的“更新”函数会遍历世界中的所有物品,如果它们有更新方法,就调用它。
我的“绘制”函数也会遍历世界中的所有物品,绘制每一个物品。(很多物品可以通过调用像pyglet的Batch.draw这样的方式一次性绘制出来)
在应用程序启动时,我首先添加到世界中的物品是一个MainMenu对象。它有一个on_key_down方法。
World.add会查看正在添加的物品。如果这个物品有on_key_down方法,就会把这个处理方法添加到pyglet的按键处理器堆栈中。(类似地,在World.remove时会撤销这个操作)(实际上,仔细想想,我觉得world.add在添加物品时会触发一个事件。如果应用程序的键盘处理模块感兴趣,那么在收到这些事件时,它会添加物品的按键处理器,但这和问题关系不大)
MainMenu.on_key_handler方法会查看按下的键,如果是开始游戏的键,就会进行一系列的调用:
# 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)
这只是一个简单的例子,但希望你能明白,主菜单可以通过将其他菜单添加到世界中,而不是直接开始游戏,来显示其他菜单。
一旦进入游戏,你可以通过简单地调用'world.remove'来移除所有不想显示的物品,然后调用'world.add'来添加新场景中的所有物品,从而改变“场景”。
使用这种技术的游戏例子是之前的pyweek参赛作品SinisterDucks: http://code.google.com/p/brokenspell/ (不过,公平地说,它在游戏过程中并没有明显的“场景”。它只是用这种技术来管理菜单、游戏结束画面等)