使用下面的代码,什么是实现游戏状态以控制关卡的最简单、最容易的方法?如果我想从标题屏幕开始,然后加载关卡,并在完成后进入下一关?如果有人能解释处理这个问题的最简单方法那就太好了!
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 块:
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)]
这以后会很有用。
回到主题:我们想要的是一堆不同的场景。每个场景必须负责自己的屏幕渲染和事件处理。
让我们尝试将现有代码提取到游戏场景中,以便以后可以添加其他场景。我们首先创建一个空Scene类,它将作为我们场景的基类:
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,但让我们保持简单)。
NotImplementedError
现在让我们把与游戏运行状态相关的所有内容(基本上是所有内容)放入一个新GameScene类中。
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
虽然还不完美,但这是一个好的开始。与实际游戏玩法相关的所有内容都被提取到其自己的类中。一些变量必须是实例变量,因此必须通过 访问它们self。
self
现在我们需要改变main函数来实际使用这个类:
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事件,因为这是我们在主循环中唯一感兴趣的事件。所有其他事件都直接传递到当前场景:scene.handle_events(pygame.event.get())。
pygame.event.get(QUIT)
QUIT
scene.handle_events(pygame.event.get())
此时,我们可以考虑将一些类提取到它们自己的文件中,但我们继续进行下去。
让我们创建一个标题菜单:
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类,因此让我们改变它以便它接受这个参数:
0
class GameScene(Scene): def __init__(self, level): ...
正如您猜测的那样,该线level = 0可以被删除。
level = 0
那么它是什么self.manager?它只是一个帮我们管理场景的小辅助类。
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字段设置为自身,以允许更改当前场景。实现这样的场景管理器有很多种可能,这是最简单的方法。缺点是每个场景都必须知道哪个场景在它之后,但这现在不应该困扰我们。
manager
让我们使用新的SceneMananger:
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
另外,我改变了玩家和敌人的位置。现在,您可以指定实体在关卡中出现的位置。例如,Player(5, 40)将在关卡的第 5 列、第 40 行创建玩家。作为奖励,敌人可以进行正确的碰撞检测。
Player(5, 40)
我将关卡描述提取到名为的字典中levels,这样就可以根据需要轻松更改和添加关卡(稍后,您可能希望每个关卡都有一个文件,因此这是一个好的开始)。它可以扩展以保存玩家的起始位置,但您也可以创建一个特殊的图块,例如关卡描述中的*起始位置和敌人。E
levels
*
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()