Pygame级别/菜单状态

用我的代码下面是什么将是最简单和最简单的方法来实现游戏状态来控制水平? 如果我想从一个标题屏幕开始,然后加载一个级别,并完成下一个级别? 如果有人能解释最简单的方法来处理这将是伟大的!

import pygame from pygame import * WIN_WIDTH = 1120 - 320 WIN_HEIGHT = 960 - 320 HALF_WIDTH = int(WIN_WIDTH / 2) HALF_HEIGHT = int(WIN_HEIGHT / 2) DISPLAY = (WIN_WIDTH, WIN_HEIGHT) DEPTH = 0 FLAGS = 0 CAMERA_SLACK = 30 def main(): global level pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ") timer = pygame.time.Clock() level = 0 bg = Surface((32,32)) bg.convert() bg.fill(Color("#0094FF")) up = left = right = False entities = pygame.sprite.Group() player = Player(32, 32) enemy = Enemy(32,32) platforms = [] x = 0 y = 0 if level == 0: level = [ " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " E ", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPP PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPP P", " PPPP P", " PPPP PPPPPPP", " PPPPPPPPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", "PPPPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPPP PPPP PPPPPPP", "PPP PPPP", "PPP PPPP", "PPP PPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",] #background = pygame.image.load("Untitled.png") total_level_width = len(level[0]) * 32 total_level_height = len(level) * 32 # build the level for row in level: for col in row: if col == "P": p = Platform(x, y) platforms.append(p) entities.add(p) if col == "E": e = ExitBlock(x, y) platforms.append(e) entities.add(e) x += 32 y += 32 x = 0 camera = Camera(complex_camera, total_level_width, total_level_height) entities.add(player) entities.add(enemy) while 1: timer.tick(60) for e in pygame.event.get(): if e.type == QUIT: raise SystemExit, "QUIT" if e.type == KEYDOWN and e.key == K_ESCAPE: raise SystemExit, "ESCAPE" if e.type == KEYDOWN and e.key == K_UP: up = True if e.type == KEYDOWN and e.key == K_LEFT: left = True if e.type == KEYDOWN and e.key == K_RIGHT: right = True if e.type == KEYUP and e.key == K_UP: up = False if e.type == KEYUP and e.key == K_LEFT: left = False if e.type == KEYUP and e.key == K_RIGHT: right = False # draw background for y in range(20): for x in range(25): screen.blit(bg, (x * 32, y * 32)) # draw background #screen.blit(background, camera.apply((0,0))) #draw entities for e in entities: screen.blit(e.image, camera.apply(e)) # update player, update camera, and refresh player.update(up, left, right, platforms) enemy.update(platforms) camera.update(player) pygame.display.flip() class Camera(object): def __init__(self, camera_func, width, height): self.camera_func = camera_func self.state = Rect(0, 0, width, height) def apply(self, target): try: return target.rect.move(self.state.topleft) except AttributeError: return map(sum, zip(target, self.state.topleft)) def update(self, target): self.state = self.camera_func(self.state, target.rect) def complex_camera(camera, target_rect): l, t, _, _ = target_rect _, _, w, h = camera l, t, _, _ = -l + HALF_WIDTH, -t +HALF_HEIGHT, w, h l = min(0, l) # stop scrolling left l = max(-(camera.width - WIN_WIDTH), l) # stop scrolling right t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling bottom return Rect(l, t, w, h) class Entity(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) class Player(Entity): def __init__(self, x, y): Entity.__init__(self) self.xvel = 0 self.yvel = 0 self.onGround = False self.image = Surface((32,32)) self.image.fill(Color("#0000FF")) self.image.convert() self.rect = Rect(200, 1200, 32, 32) def update(self, up, left, right, platforms): if self.rect.top > 1440 or self.rect.top < 0: main() if self.rect.left > 1408 or self.rect.right < 0: main() if up: if self.onGround: self.yvel = 0 self.yvel -= 10 # only jump if on the ground if left: self.xvel = -10 if right: self.xvel = 10 if not self.onGround: self.yvel += 0.3 # only accelerate with gravity if in the air if self.yvel > 80: self.yvel = 80 # max falling speed if not(left or right): self.xvel = 0 self.rect.left += self.xvel # increment in x direction self.collide(self.xvel, 0, platforms) # do x-axis collisions self.rect.top += self.yvel # increment in y direction self.onGround = False; # assuming we're in the air self.collide(0, self.yvel, platforms) # do y-axis collisions def collide(self, xvel, yvel, platforms): for p in platforms: if pygame.sprite.collide_rect(self, p): if isinstance(p, ExitBlock): pygame.event.post(pygame.event.Event(QUIT)) if xvel > 0: self.rect.right = p.rect.left if xvel < 0: self.rect.left = p.rect.right if yvel > 0: self.rect.bottom = p.rect.top self.onGround = True if yvel < 0: self.rect.top = p.rect.bottom class Enemy(Entity): def __init__(self, x, y): Entity.__init__(self) self.yVel = 0 self.xVel = 0 self.image = Surface((32,32)) self.image.fill(Color("#00FF00")) self.image.convert() self.rect = Rect(300, 1200, 32, 32) self.onGround = False self.right_dis = False def update(self, platforms): if not self.onGround: self.yVel += 0.3 if self.rect.left == 96: self.right_dis = False if self.rect.right == 480: self.right_dis = True if not self.right_dis: self.xVel = 2 if self.right_dis: self.xVel = -2 self.rect.left += self.xVel # increment in x direction self.collide(self.xVel, 0, platforms) # do x-axis collisions self.rect.top += self.yVel # increment in y direction self.onGround = False; # assuming we're in the air self.collide(0, self.yVel, platforms) # do y-axis collisions def collide(self, xVel, yVel, platforms): for p in platforms: if pygame.sprite.collide_rect(self, p): if xVel > 0: self.rect.right = p.rect.left if xVel < 0: self.rect.left = p.rect.right if yVel > 0: self.rect.bottom = p.rect.top self.onGround = True if yVel < 0: self.rect.top = p.rect.bottom class Platform(Entity): def __init__(self, x, y): Entity.__init__(self) #self.image = Surface([32, 32], pygame.SRCALPHA, 32) #makes blocks invisible for much better artwork self.image = Surface((32,32)) #makes blocks visible for building levels self.image.convert() self.rect = Rect(x, y, 32, 32) def update(self): pass class ExitBlock(Platform): def __init__(self, x, y): Platform.__init__(self, x, y) self.image = pygame.image.load("end.png") if __name__ == "__main__": main() 

首先,让我们摆脱这些丑陋的if-blocks:

 for e in pygame.event.get(): if e.type == QUIT: raise SystemExit, "QUIT" if e.type == KEYDOWN and e.key == K_ESCAPE: raise SystemExit, "ESCAPE" if e.type == KEYDOWN and e.key == K_UP: up = True if e.type == KEYDOWN and e.key == K_LEFT: left = True if e.type == KEYDOWN and e.key == K_RIGHT: right = True if e.type == KEYUP and e.key == K_UP: up = False if e.type == KEYUP and e.key == K_LEFT: left = False if e.type == KEYUP and e.key == K_RIGHT: right = False 

我们可以将它们改写为:

 for e in pygame.event.get(): if e.type == QUIT: raise SystemExit, "QUIT" if e.type == KEYDOWN and e.key == K_ESCAPE: raise SystemExit, "ESCAPE" pressed = pygame.key.get_pressed() up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)] 

这将在稍后派上用场。


返回主题:我们想要的是一堆不同的场景 。 每个场景都必须负责自己的屏幕渲染和事件处理。

让我们尝试将现有代码提取到游戏场景中 ,以便稍后添加其他场景。 我们首先创build一个空的Scene类,它将成为我们场景的基类:

 class Scene(object): def __init__(self): pass def render(self, screen): raise NotImplementedError def update(self): raise NotImplementedError def handle_events(self, events): raise NotImplementedError 

我们的计划是覆盖每个子类中的每个方法,所以我们在基类中引发NotImplementedError ,所以我们很容易发现,如果我们忘了这么做(我们也可以使用ABC,但让我们保持简单)。

现在让我们把与运行游戏状态有关的所有东西(基本上是所有东西)都放到一个新的GameScene类中。

 class GameScene(Scene): def __init__(self): super(GameScene, self).__init__() level = 0 self.bg = Surface((32,32)) self.bg.convert() self.bg.fill(Color("#0094FF")) up = left = right = False self.entities = pygame.sprite.Group() self.player = Player(32, 32) self.enemy = Enemy(32,32) self.platforms = [] x = 0 y = 0 if level == 0: level = [ " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " E ", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPP PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPP P", " PPPP P", " PPPP PPPPPPP", " PPPPPPPPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", "PPPPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPPP PPPP PPPPPPP", "PPP PPPP", "PPP PPPP", "PPP PPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",] #background = pygame.image.load("Untitled.png") total_level_width = len(level[0]) * 32 total_level_height = len(level) * 32 # build the level for row in level: for col in row: if col == "P": p = Platform(x, y) self.platforms.append(p) self.entities.add(p) if col == "E": e = ExitBlock(x, y) self.platforms.append(e) self.entities.add(e) x += 32 y += 32 x = 0 self.camera = Camera(complex_camera, total_level_width, total_level_height) self.entities.add(self.player) self.entities.add(self.enemy) def render(self, screen): for y in range(20): for x in range(25): screen.blit(self.bg, (x * 32, y * 32)) for e in self.entities: screen.blit(e.image, self.camera.apply(e)) def update(self): pressed = pygame.key.get_pressed() up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)] self.player.update(up, left, right, self.platforms) self.enemy.update(self.platforms) self.camera.update(self.player) def handle_events(self, events): for e in events: if e.type == KEYDOWN and e.key == K_ESCAPE: pass #somehow go back to menu 

还不完美,但一个好的开始。 与实际游戏相关的一切都被提取到自己的课堂上。 一些variables必须是实例variables,所以他们必须通过self访问。

现在我们需要修改main函数来实际使用这个类:

 def main(): pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ") timer = pygame.time.Clock() running = True scene = GameScene() while running: timer.tick(60) if pygame.event.get(QUIT): running = False return scene.handle_events(pygame.event.get()) scene.update() scene.render(screen) pygame.display.flip() 

请注意,我改变了两件小事:我使用pygame.event.get(QUIT)来首先获得一个可能的QUIT -event,因为这是我们在主循环中interesset的唯一事件。 所有其他事件直接传递到当前场景: scene.handle_events(pygame.event.get())

在这一点上,我们可以考虑提取一些类到他们自己的文件,但是让我们继续。

我们来创build一个标题菜单:

 class TitleScene(object): def __init__(self): super(TitleScene, self).__init__() self.font = pygame.font.SysFont('Arial', 56) self.sfont = pygame.font.SysFont('Arial', 32) def render(self, screen): # beware: ugly! screen.fill((0, 200, 0)) text1 = self.font.render('Crazy Game', True, (255, 255, 255)) text2 = self.sfont.render('> press space to start <', True, (255, 255, 255)) screen.blit(text1, (200, 50)) screen.blit(text2, (200, 350)) def update(self): pass def handle_events(self, events): for e in events: if e.type == KEYDOWN and e.key == K_SPACE: self.manager.go_to(GameScene(0)) 

这只是显示一个绿色的背景和一些文字。 如果玩家按下SPACE ,我们要开始第一级。 注意这一行:

 self.manager.go_to(GameScene(0)) 

在这里我将参数0传递给GameScene类,所以让我们改变它,让它接受这个参数:

 class GameScene(Scene): def __init__(self, level): ... 

level = 0可以删除,你已经猜到了。

那么什么是self.manager ? 这只是一个帮助我们的场景。

 class SceneMananger(object): def __init__(self): self.go_to(TitleScene()) def go_to(self, scene): self.scene = scene self.scene.manager = self 

它从标题场景开始,并将每个场景manager字段设置为允许改变当前场景。 如何实现这样的场景pipe理器有很多的可能性,这是最简单的方法。 一个缺点就是每个场景都要知道后面是哪个场景,但是现在不应该打扰我们。

让我们使用我们的新SceneMananger

 def main(): pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ") timer = pygame.time.Clock() running = True manager = SceneMananger() while running: timer.tick(60) if pygame.event.get(QUIT): running = False return manager.scene.handle_events(pygame.event.get()) manager.scene.update() manager.scene.render(screen) pygame.display.flip() 

直截了当。 让我们快速添加一个二级和一个输/赢的屏幕,我们完成了。

 class CustomScene(object): def __init__(self, text): self.text = text super(CustomScene, self).__init__() self.font = pygame.font.SysFont('Arial', 56) def render(self, screen): # ugly! screen.fill((0, 200, 0)) text1 = self.font.render(self.text, True, (255, 255, 255)) screen.blit(text1, (200, 50)) def update(self): pass def handle_events(self, events): for e in events: if e.type == KEYDOWN: self.manager.go_to(TitleScene()) 

以下是完整的代码。 注意对Player类的更改:不是再次调用main函数,而是调用场景中的方法来指示玩家到达出口或死亡。

另外,我改变了玩家和敌人的位置。 现在,您可以指定实体在关卡中出现的位置。 例如, Player(5, 40) 5,40 Player(5, 40)将在等级的第5列,第40行创build玩家。 作为奖励,敌人进行适当的碰撞检测。

我将关卡的描述提取到一个名为levels的字典中,所以根据需要很容易改变和添加关卡(之后,您可能需要每个关卡有一个文件,所以这是一个好的开始)。 它可以扩展到保持玩家的起始位置,但是你也可以创build一个特殊的瓦片,例如*在起始位置, E在敌人级别描述中。

 import pygame from pygame import * WIN_WIDTH = 1120 - 320 WIN_HEIGHT = 960 - 320 HALF_WIDTH = int(WIN_WIDTH / 2) HALF_HEIGHT = int(WIN_HEIGHT / 2) DISPLAY = (WIN_WIDTH, WIN_HEIGHT) DEPTH = 0 FLAGS = 0 CAMERA_SLACK = 30 levels = {0: {'level': [ " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " E ", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPP PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPP P", " PPPP P", " PPPP PPPPPPP", " PPPPPPPPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", "PPPPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPPP PPPP PPPPPPP", "PPP PPPP", "PPP PPPP", "PPP PPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",], 'enemies': [(9, 38)]}, 1: {'level': [ " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " E ", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPPP PPPPPPPPPPPPPPPP", " PPPPPPPPPPPPPPPP", " PPPP P", " PPPP P", " PPPP PPPPPPP", " PPPPPPPPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", " PPPP PPPPPPP", "PPPPP PPPP PPPPPPP", "PPP PPPPPPPPPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPP PPPPPPP", "PPP PPPPPPPP PPPP PPPPPPP", "PPP PPPP", "PPP PPPP", "PPP PPPPP PPPP", "PPP P PPPPPPPPPPPPPPPPPP", "PPP P PPPPPPPPPPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP", "PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",], 'enemies': [(9, 38), (18, 38), (15, 15)]}} class Scene(object): def __init__(self): pass def render(self, screen): raise NotImplementedError def update(self): raise NotImplementedError def handle_events(self, events): raise NotImplementedError class GameScene(Scene): def __init__(self, levelno): super(GameScene, self).__init__() self.bg = Surface((32,32)) self.bg.convert() self.bg.fill(Color("#0094FF")) up = left = right = False self.entities = pygame.sprite.Group() self.player = Player(5, 40) self.player.scene = self self.platforms = [] self.levelno = levelno levelinfo = levels[levelno] self.enemies = [Enemy(*pos) for pos in levelinfo['enemies']] level = levelinfo['level'] total_level_width = len(level[0]) * 32 total_level_height = len(level) * 32 # build the level x = 0 y = 0 for row in level: for col in row: if col == "P": p = Platform(x, y) self.platforms.append(p) self.entities.add(p) if col == "E": e = ExitBlock(x, y) self.platforms.append(e) self.entities.add(e) x += 32 y += 32 x = 0 self.camera = Camera(complex_camera, total_level_width, total_level_height) self.entities.add(self.player) for e in self.enemies: self.entities.add(e) def render(self, screen): for y in range(20): for x in range(25): screen.blit(self.bg, (x * 32, y * 32)) for e in self.entities: screen.blit(e.image, self.camera.apply(e)) def update(self): pressed = pygame.key.get_pressed() up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)] self.player.update(up, left, right, self.platforms) for e in self.enemies: e.update(self.platforms) self.camera.update(self.player) def exit(self): if self.levelno+1 in levels: self.manager.go_to(GameScene(self.levelno+1)) else: self.manager.go_to(CustomScene("You win!")) def die(self): self.manager.go_to(CustomScene("You lose!")) def handle_events(self, events): for e in events: if e.type == KEYDOWN and e.key == K_ESCAPE: self.manager.go_to(TitleScene()) class CustomScene(object): def __init__(self, text): self.text = text super(CustomScene, self).__init__() self.font = pygame.font.SysFont('Arial', 56) def render(self, screen): # ugly! screen.fill((0, 200, 0)) text1 = self.font.render(self.text, True, (255, 255, 255)) screen.blit(text1, (200, 50)) def update(self): pass def handle_events(self, events): for e in events: if e.type == KEYDOWN: self.manager.go_to(TitleScene()) class TitleScene(object): def __init__(self): super(TitleScene, self).__init__() self.font = pygame.font.SysFont('Arial', 56) self.sfont = pygame.font.SysFont('Arial', 32) def render(self, screen): # ugly! screen.fill((0, 200, 0)) text1 = self.font.render('Crazy Game', True, (255, 255, 255)) text2 = self.sfont.render('> press space to start <', True, (255, 255, 255)) screen.blit(text1, (200, 50)) screen.blit(text2, (200, 350)) def update(self): pass def handle_events(self, events): for e in events: if e.type == KEYDOWN and e.key == K_SPACE: self.manager.go_to(GameScene(0)) class SceneMananger(object): def __init__(self): self.go_to(TitleScene()) def go_to(self, scene): self.scene = scene self.scene.manager = self def main(): pygame.init() screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH) pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ") timer = pygame.time.Clock() running = True manager = SceneMananger() while running: timer.tick(60) if pygame.event.get(QUIT): running = False return manager.scene.handle_events(pygame.event.get()) manager.scene.update() manager.scene.render(screen) pygame.display.flip() class Camera(object): def __init__(self, camera_func, width, height): self.camera_func = camera_func self.state = Rect(0, 0, width, height) def apply(self, target): try: return target.rect.move(self.state.topleft) except AttributeError: return map(sum, zip(target, self.state.topleft)) def update(self, target): self.state = self.camera_func(self.state, target.rect) def complex_camera(camera, target_rect): l, t, _, _ = target_rect _, _, w, h = camera l, t, _, _ = -l + HALF_WIDTH, -t +HALF_HEIGHT, w, h l = min(0, l) # stop scrolling left l = max(-(camera.width - WIN_WIDTH), l) # stop scrolling right t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling bottom return Rect(l, t, w, h) class Entity(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) class Player(Entity): def __init__(self, x, y): Entity.__init__(self) self.xvel = 0 self.yvel = 0 self.onGround = False self.image = Surface((32,32)) self.image.fill(Color("#0000FF")) self.image.convert() self.rect = Rect(x*32, y*32, 32, 32) def update(self, up, left, right, platforms): if self.rect.top > 1440 or self.rect.top < 0: self.scene.die() if self.rect.left > 1408 or self.rect.right < 0: self.scene.die() if up: if self.onGround: self.yvel = 0 self.yvel -= 10 # only jump if on the ground if left: self.xvel = -10 if right: self.xvel = 10 if not self.onGround: self.yvel += 0.3 # only accelerate with gravity if in the air if self.yvel > 80: self.yvel = 80 # max falling speed if not(left or right): self.xvel = 0 self.rect.left += self.xvel # increment in x direction if self.collide(self.xvel, 0, platforms): # do x-axis collisions self.rect.top += self.yvel # increment in y direction self.onGround = False; # assuming we're in the air self.collide(0, self.yvel, platforms) # do y-axis collisions def collide(self, xvel, yvel, platforms): for p in platforms: if pygame.sprite.collide_rect(self, p): if isinstance(p, ExitBlock): self.scene.exit() return False if xvel > 0: self.rect.right = p.rect.left if xvel < 0: self.rect.left = p.rect.right if yvel > 0: self.rect.bottom = p.rect.top self.onGround = True if yvel < 0: self.rect.top = p.rect.bottom return True class Enemy(Entity): def __init__(self, x, y): Entity.__init__(self) self.yVel = 0 self.xVel = 2 # start moving immediately self.image = Surface((32,32)) self.image.fill(Color("#00FF00")) self.image.convert() self.rect = Rect(x*32, y*32, 32, 32) self.onGround = False def update(self, platforms): if not self.onGround: self.yVel += 0.3 # no need for right_dis to be a member of the class, # since we know we are moving right if self.xVel > 0 right_dis = self.xVel > 0 # create a point at our left (or right) feet # to check if we reached the end of the platform m = (1, 1) if right_dis else (-1, 1) p = self.rect.bottomright if right_dis else self.rect.bottomleft fp = map(sum, zip(m, p)) # if there's no platform in front of us, change the direction collide = any(p for p in platforms if p.rect.collidepoint(fp)) if not collide: self.xVel *= -1 self.rect.left += self.xVel # increment in x direction self.collide(self.xVel, 0, platforms) # do x-axis collisions self.rect.top += self.yVel # increment in y direction self.onGround = False; # assuming we're in the air self.collide(0, self.yVel, platforms) # do y-axis collisions def collide(self, xVel, yVel, platforms): for p in platforms: if pygame.sprite.collide_rect(self, p): if xVel > 0: self.rect.right = p.rect.left self.xVel *= -1 # hit wall, so change direction if xVel < 0: self.rect.left = p.rect.right self.xVel *= -1 # hit wall, so change direction if yVel > 0: self.rect.bottom = p.rect.top self.onGround = True if yVel < 0: self.rect.top = p.rect.bottom class Platform(Entity): def __init__(self, x, y): Entity.__init__(self) #self.image = Surface([32, 32], pygame.SRCALPHA, 32) #makes blocks invisible for much better artwork self.image = Surface((32,32)) #makes blocks visible for building levels self.image.convert() self.rect = Rect(x, y, 32, 32) def update(self): pass class ExitBlock(Platform): def __init__(self, x, y): Platform.__init__(self, x, y) self.image = Surface((32,32)) #makes blocks visible for building levels self.image.convert() self.rect = Rect(x, y, 32, 32) if __name__ == "__main__": main() 

我也做了一个游戏,我有一个游戏菜单,一个关卡菜单,一个装载部分和一个游戏部分。
我这样做的方式是在主要的游戏循环中,
我经历了一堆elif语句,以确定游戏所处的“模式”,并执行相应的操作。
它似乎工作得很好,我build议你也一样。

我意识到我的代码真的很长,但是如果你去了#game loop (使用ctrl + ffind它),你可以看到elifs来确定模式。 希望这可以帮助。

 #basic stuff import pygame, sys, random pygame.init() window=pygame.display.set_mode((1500, 800), pygame.FULLSCREEN) winrect=window.get_rect() #colors GREEN=(19, 225, 30) BLUE=(41, 2, 245) YELLOW=(251, 240, 32) WHITE=(255, 255, 255) BLACK=(0, 0, 0) RED=(255, 0, 0) #text bigfont=pygame.font.SysFont('calibri', 75) font=pygame.font.SysFont('calibri', 40) texts={} so=bigfont.render('Ball Bounce', True, BLUE) rect=so.get_rect() rect.top=winrect.top+100 rect.centerx=winrect.centerx texts['title']=[so, rect] so=font.render('Start', True, BLUE) rect=so.get_rect() so1=pygame.Surface((400, 50)) so2=pygame.Surface((400, 50)) rect1=so1.get_rect() so1.fill(YELLOW) so2.fill(RED) pygame.draw.rect(so1, BLACK, rect1, 5) pygame.draw.rect(so2, BLACK, rect1, 5) rect.center=rect1.center so1.blit(so, rect) so2.blit(so, rect) rect1.centerx=winrect.centerx rect1.top=texts['title'][1].top+300 texts['start']=[so1, rect1, so2] so=bigfont.render('Levels', True, BLUE) rect=so.get_rect() rect.centerx=winrect.centerx rect.top=winrect.top+100 texts['levels']=[so, rect] #levels [locked, unlocked, completed/mouseover, rect, state(locked, unlocked, completed)] levels=[] lock=pygame.image.load('images/lock.png').convert() lock=pygame.transform.scale(lock, (100, 100)) lock.set_colorkey(lock.get_at((1, 1))) for i in range(1, 21): so=pygame.Surface((100, 100)) so.fill(YELLOW) rect=so.get_rect() pygame.draw.rect(so, BLACK, rect, 5) so1=pygame.Surface((100, 100)) so1.fill(RED) pygame.draw.rect(so1, BLACK, rect, 5) text=font.render(str(i), True, BLUE) textrect=text.get_rect() textrect.center=rect.center so.blit(text, textrect) so1.blit(text, textrect) locked=pygame.Surface((100, 100)) locked.blit(so, rect) locked.blit(lock, lock.get_rect()) if i<=5: rect.top=texts['levels'][1].bottom+25 elif i<=10: rect.top=levels[0][3].bottom+50 elif i<=15: rect.top=levels[7][3].bottom+50 else: rect.top=levels[12][3].bottom+50 if i==1 or i==6 or i==11 or i==16: rect.right=winrect.centerx-200 elif i==2 or i==7 or i==12 or i==17: rect.right=winrect.centerx-75 elif i==3 or i==8 or i==13 or i==18: rect.centerx=winrect.centerx elif i==4 or i==9 or i==14 or i==19: rect.left=winrect.centerx+75 else: rect.left=winrect.centerx+200 if i==1: levels.append([locked, so, so1, rect, 1]) else: levels.append([locked, so, so1, rect, 1]) #Wall class (0=horizontal, 1=vertical) class cwall(pygame.Rect): 'orientation (hor, vert), location, holesize, winrect' def __init__(self, orientation, location, holesize, winrect): self.orientation=orientation if orientation==0: self.height=5 self.width=winrect.width self.centery=location if orientation==1: self.width=5 self.height=winrect.height self.centerx=location self.holesize=holesize self.bbottomright=round(pygame.mouse.get_pos()[self.orientation]+self.holesize/2) self.ttopleft=round(pygame.mouse.get_pos()[self.orientation]-self.holesize/2) def update(self): self.bbottomright=round(pygame.mouse.get_pos()[self.orientation]+self.holesize/2) self.ttopleft=round(pygame.mouse.get_pos()[self.orientation]-self.holesize/2) if self.bbottomright<self.holesize: self.bbottomright=self.holesize if self.ttopleft>self.right-self.holesize and self.orientation==0: self.ttopleft=self.right-self.holesize if self.ttopleft>self.bottom-self.holesize and self.orientation==1: self.ttopleft=self.bottom-self.holesize #Ball Class class cball(pygame.Rect): 'diameter, speed, color, winrect' def __init__(self, diameter, speed, color, winrect): self.width=diameter self.height=diameter self.speed=speed self.color=color self.direction=random.randint(1, 4) self.center=(random.randint(round(diameter/2), round(winrect.right-diameter/2)), random.randint(round(diameter/2), round(winrect.bottom-diameter/2))) def update(self, winrect, walls): if self.direction/2==round(self.direction/2): self.right+=self.speed else: self.right-=self.speed if self.direction<=2: self.top+=self.speed else: self.top-=self.speed for wall in walls: if wall.collidepoint(self.center): if wall.orientation==0 and (self.centerx<wall.ttopleft or self.centerx>wall.bbottomright): if self.direction==1: self.direction=3 self.bottom=wall.top elif self.direction==2: self.direction=4 self.bottom=wall.top elif self.direction==3: self.direction=1 self.top=wall.bottom else: self.direction=2 self.top=wall.bottom elif wall.orientation==1 and (self.centery<wall.ttopleft or self.centery>wall.bbottomright): if self.direction==1: self.direction=2 self.left=wall.right elif self.direction==2: self.direction=1 self.right=wall.left elif self.direction==3: self.direction=4 self.left=wall.right else: self.direction=3 self.right=wall.left elif wall.orientation==0: if self.bottom>wall.top and self.centery<wall.top and (self.centerx<wall.ttopleft or self.centerx>wall.bbottomright): if self.direction==1: self.direction=3 self.bottom=wall.top elif self.direction==2: self.direction=4 self.bottom=wall.top elif self.top<wall.bottom and self.centery>wall.bottom and (self.centerx<wall.ttopleft or self.centerx>wall.bbottomright): if self.direction==3: self.direction=1 self.top=wall.bottom if self.direction==4: self.direction=2 self.top=wall.bottom else: if self.left<wall.right and self.centerx>wall.right and (self.centery<wall.ttopleft or self.centery>wall.bbottomright): if self.direction==1: self.direction=2 self.left=wall.right elif self.direction==3: self.direction=4 self.left=wall.right elif self.right>wall.left and self.centerx<wall.left and (self.centery<wall.ttopleft or self.centery>wall.bbottomright): if self.direction==2: self.direction=1 self.right=wall.left if self.direction==4: self.direction=3 self.right=wall.left if self.top<0: if self.direction==3: self.direction=1 self.top=0 elif self.direction==4: self.direction=2 self.topn=0 if self.bottom>winrect.bottom: if self.direction==1: self.direction=3 self.bottom=winrect.bottom elif self.direction==2: self.direction=4 self.bottom=winrect.bottom if self.left<0: if self.direction==1: self.direction=2 self.left=0 elif self.direction==3: self.direction=4 self.left=0 if self.right>winrect.right: if self.direction==2: self.direction=1 self.right=winrect.right if self.direction==4: self.direction=3 self.right=winrect.right for box in boxes: if box[0].collidepoint(self.center) and self.color==box[1]: return True return False #Game loop setup mode='title' #Game loop while True: if mode=='title': #event loop for event in pygame.event.get(): if event.type==pygame.QUIT: pygame.quit() sys.exit() if event.type==pygame.MOUSEBUTTONDOWN: if texts['start'][1].collidepoint(event.pos): mode='levels' if event.type==pygame.KEYDOWN: if event.key==pygame.K_ESCAPE: pygame.quit() sys.exit() #screen update window.fill(GREEN) mouse=pygame.mouse.get_pos() if texts['start'][1].collidepoint(mouse): window.blit(texts['start'][2], texts['start'][1]) else: window.blit(texts['start'][0], texts['start'][1]) window.blit(texts['title'][0], texts['title'][1]) pygame.display.update() elif mode=='levels': #event loop for event in pygame.event.get(): if event.type==pygame.QUIT: pygame.quit() sys.exit() if event.type==pygame.MOUSEBUTTONDOWN: for level in levels: if level[3].collidepoint(event.pos) and level[4]!=0: mode='loading' loadinglevel=levels.index(level)+1 if event.type==pygame.KEYDOWN: if event.key==pygame.K_ESCAPE: pygame.quit() sys.exit() #screen update window.fill(GREEN) for level in levels: if level[3].collidepoint(pygame.mouse.get_pos()) and level[4]==1: window.blit(level[2], level[3]) else: window.blit(level[level[4]], level[3]) window.blit(texts['levels'][0], texts['levels'][1]) pygame.display.update() elif mode=='loading': if loadinglevel==1: walls=[cwall(1, winrect.width/2, 100, winrect)] balls=[] for i in range(2): balls.append(cball(20, 3, GREEN, winrect)) for i in range(2): balls.append(cball(20, 3, YELLOW, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/2), winrect.height), GREEN), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), YELLOW)) elif loadinglevel==2: walls=[cwall(1, winrect.width/2, 100, winrect)] balls=[] for i in range(4): balls.append(cball(20, 3, GREEN, winrect)) for i in range(4): balls.append(cball(20, 3, YELLOW, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/2), winrect.height), GREEN), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), YELLOW)) elif loadinglevel==3: walls=[cwall(1, winrect.width/3, 100, winrect), cwall(1, 2*winrect.width/3, 100, winrect)] balls=[] for i in range(2): balls.append(cball(20, 3, GREEN, winrect)) for i in range(2): balls.append(cball(20, 3, YELLOW, winrect)) for i in range(2): balls.append(cball(20, 3, BLUE, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/3), winrect.height), GREEN), (pygame.Rect(round(winrect.width/3), 0, round(winrect.width/3), winrect.height), YELLOW), (pygame.Rect(round(2*winrect.width/3), 0, round(winrect.width/3), winrect.height), BLUE)) elif loadinglevel==4: walls=[cwall(1, winrect.width/3, 100, winrect), cwall(1, 2*winrect.width/3, 100, winrect)] balls=[] for i in range(4): balls.append(cball(20, 3, GREEN, winrect)) for i in range(4): balls.append(cball(20, 3, YELLOW, winrect)) for i in range(4): balls.append(cball(20, 3, BLUE, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/3), winrect.height), GREEN), (pygame.Rect(round(winrect.width/3), 0, round(winrect.width/3), winrect.height), YELLOW), (pygame.Rect(round(2*winrect.width/3), 0, round(winrect.width/3), winrect.height), BLUE)) elif loadinglevel==7: walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)] balls=[] for i in range(2): balls.append(cball(20, 3, GREEN, winrect)) for i in range(2): balls.append(cball(20, 3, YELLOW, winrect)) for i in range(2): balls.append(cball(20, 3, BLUE, winrect)) for i in range(2): balls.append(cball(20, 3, RED, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), GREEN), (pygame.Rect(0, round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), RED), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), round(winrect.height/2)), YELLOW), (pygame.Rect(round(winrect.width/2), round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), BLUE)) elif loadinglevel==8: walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)] balls=[] for i in range(4): balls.append(cball(20, 3, GREEN, winrect)) for i in range(4): balls.append(cball(20, 3, YELLOW, winrect)) for i in range(4): balls.append(cball(20, 3, BLUE, winrect)) for i in range(4): balls.append(cball(20, 3, RED, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), GREEN), (pygame.Rect(0, round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), RED), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), round(winrect.height/2)), YELLOW), (pygame.Rect(round(winrect.width/2), round(winrect.height/2), round(winrect.width/2), round(winrect.height/2)), BLUE)) elif loadinglevel==5: walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)] balls=[] for i in range(10): balls.append(cball(20, 3, RED, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), RED), (pygame.Rect(0, round(winrect.height/2), winrect.width, round(winrect.height/2)), WHITE), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), WHITE)) elif loadinglevel==6: walls=[cwall(1, winrect.width/2, 100, winrect), cwall(0, winrect.height/2, 100, winrect)] balls=[] for i in range(20): balls.append(cball(20, 3, RED, winrect)) boxes=((pygame.Rect(0, 0, round(winrect.width/2), round(winrect.height/2)), RED), (pygame.Rect(0, round(winrect.height/2), winrect.width, round(winrect.height/2)), WHITE), (pygame.Rect(round(winrect.width/2), 0, round(winrect.width/2), winrect.height), WHITE)) mode='playing' elif mode=='playing': while True: #event loop for event in pygame.event.get(): if event.type==pygame.QUIT: pygame.quit() sys.exit() if event.type==pygame.KEYDOWN: if event.key==pygame.K_ESCAPE: pygame.quit() sys.exit() #updates updates=[] for wall in walls: wall.update() for ball in balls: updates.append(ball.update(winrect, walls)) #Seeing if won won=True for update in updates: if not update: won=False break if won: if levels[loadinglevel][4]==0: levels[loadinglevel][4]=1 levels[loadinglevel-1][4]=2 mode='levels' break #blitting window.fill(WHITE) for box in boxes: pygame.draw.rect(window, box[1], box[0]) for wall in walls: if wall.orientation==0: pygame.draw.rect(window, BLACK, (wall.left, wall.top, wall.ttopleft, wall.height)) pygame.draw.rect(window, BLACK, (wall.bbottomright, wall.top, wall.right-wall.bbottomright, wall.height)) else: pygame.draw.rect(window, BLACK, (wall.left, wall.top, wall.width, wall.ttopleft)) pygame.draw.rect(window, BLACK, (wall.left, wall.bbottomright, wall.width, wall.bottom-wall.holesize)) for ball in balls: pygame.draw.circle(window, ball.color, ball.center, round(ball.width/2)) pygame.draw.circle(window, BLACK, ball.center, round(ball.width/2), 2) pygame.display.update() pygame.time.Clock().tick(100)