今天我们来看看如何使用Pygame框架制作一个简单的跑酷游戏。这个游戏包含了基本的游戏元素,如玩家角色、障碍物、背景、音效等,可以作为入门Pygame游戏开发的一个不错的示例。
游戏概述
这是一个简单的横版跑酷游戏,玩家控制一个忍者角色,通过跳跃来躲避迎面而来的各种障碍物(汽车、飞机、外星人等)。游戏有三个状态:准备、游戏中和结束。
主要的游戏元素包括:
玩家角色(忍者)
障碍物(汽车、飞机等)
滚动背景
音效和背景音乐
计分系统
游戏UI(开始、结束界面等)
代码结构
游戏的主要代码结构如下:
class AudioManage: # 音频管理
class BaseSprite: # 基础精灵类
class EnemySprite: # 敌人精灵
class EnemyManage: # 敌人管理
class PlayerSprite: # 玩家精灵
class PlayerManage: # 玩家管理
class BGSprite: # 背景精灵
class BGManage: # 背景管理
class UISprite: # UI精灵
class UIManage: # UI管理
class GameManage: # 游戏主管理类
关键实现
1. 玩家控制
玩家通过上键控制忍者跳跃:
def is_jump(self): if self.jump_count > 0: AudioManage().set_sound_music(5) self.jump_count -= 1 self.velocity_y = -12 self.jump()
2. 障碍物生成
通过定时器事件定期生成新的障碍物:
pygame.time.set_timer(ENEMY_BORN, BORN_RATE) # 在事件循环中 if event.type == ENEMY_BORN: self.enemy.broth()
3. 碰撞检测
使用pygame的sprite collision检测玩家与障碍物的碰撞:
r = pygame.sprite.groupcollide(self.enemy.enemy_group, self.player.player_group, False, False) if r: # 处理碰撞后的游戏结束逻辑
4. 背景滚动
通过不断移动两个背景图片实现无缝滚动:
def update(self): self.rect.left -= self.speed if self.rect.left <= -WIDTH: self.rect.left = WIDTH
完整代码
这里我把我的代码放在这里,给大家做个参考,里面还有很多不完善的地方,图片素材什么的也需要大家自行寻找,我的素材大多数都是使用 " 非常糟糕的城市 " 这个游戏的图片素材。
import random import pygame WIDTH, HEIGHT = 500, 500 ENEMY_BORN = pygame.USEREVENT + 1 BORN_RATE = 3000 class AudioManage: @staticmethod def playing_bg_music(): pygame.mixer.music.load("./music/紧张.mp3") pygame.mixer.music.play(loops=True) pygame.mixer.music.set_volume(0.3) @staticmethod def ready_bg_music(): pygame.mixer.music.load("./music/split.mp3") pygame.mixer.music.play(loops=True) pygame.mixer.music.set_volume(0.3) @staticmethod def end_bg_music(): pygame.mixer.music.load("./music/沮丧.mp3") pygame.mixer.music.play(loops=True) pygame.mixer.music.set_volume(0.3) @staticmethod def set_sound_music(i): l0 = ["./music/汽车.mp3", "./music/直升机.mp3", "./music/飞机.mp3", "./music/降落伞.mp3", "./music/外星人.mp3", "./music/跳跃.mp3", "./music/失败.mp3"] pygame.mixer.Sound(l0[i]).play() class BaseSprite(pygame.sprite.Sprite): def __init__(self, image_name): super().__init__() self.image = pygame.image.load(image_name) self.rect = self.image.get_rect() class EnemySprite(BaseSprite): def __init__(self, image_name, em, speed=5): super().__init__(image_name) self.em = em self.image_name = image_name self.image = pygame.transform.flip(self.image, True, False) self.set_pos() self.speed = speed def set_pos(self): self.rect.left = WIDTH if self.image_name in ["./image/car1.png", "./image/car2.png", "./image/car3.png"]: AudioManage().set_sound_music(0) self.rect.bottom = 300 elif self.image_name in ["./image/plan.png"]: AudioManage().set_sound_music(2) self.rect.bottom = 225 elif self.image_name in ["./image/滑翔伞.png", "./image/直升机.png"]: if self.image_name == "./image/滑翔伞.png": AudioManage().set_sound_music(3) else: AudioManage().set_sound_music(1) self.rect.bottom = 150 elif self.image_name in ["./image/外星人1.png", "./image/外星人2.png"]: AudioManage().set_sound_music(4) self.rect.bottom = 75 def update(self): self.rect.left -= self.speed if self.rect.right < 0: self.kill() self.em.gm.ui.update_lab() class EnemyManage: def __init__(self, gm_): self.gm = gm_ self.init_enemy() def init_enemy(self): self.enemy_group = pygame.sprite.Group() self.enemy = EnemySprite("./image/car1.png", self) self.enemy.add(self.enemy_group) def broth(self): em_img_name = random.choice( ["./image/car1.png", "./image/car2.png", "./image/car3.png", "./image/外星人1.png", "./image/外星人2.png", "./image/滑翔伞.png", "./image/直升机.png", "./image/plan.png"]) self.enemy = EnemySprite(em_img_name, self) self.enemy.add(self.enemy_group) def clear(self): self.enemy_group.empty() def update(self): self.enemy_group.draw(self.gm.screen) self.enemy_group.update() class PlayerSprite(pygame.sprite.Sprite): def __init__(self, image_names): super().__init__() self.index = 0 self.images = [pygame.image.load(image) for image in image_names] self.image = self.images[self.index] self.rect = self.image.get_rect() self.rect.bottom = 300 self.rect.left = 100 self.velocity_y = 0 self.jump_count = 2 def jump(self): if self.rect.bottom >= 300: self.jump_count = 2 def is_jump(self): if self.jump_count > 0: AudioManage().set_sound_music(5) self.jump_count -= 1 self.velocity_y = -12 self.jump() def update(self): self.index += 1 if self.index >= len(self.images) * 10: self.index = 0 self.image = self.images[self.index // 10] self.velocity_y += 0.5 self.rect.y += self.velocity_y self.jump() if self.rect.bottom > 300: self.rect.bottom = 300 elif self.rect.top < 0: self.rect.top = 0 class PlayerManage: def __init__(self, gm_): self.gm = gm_ self.init_player() def init_player(self): self.player_group = pygame.sprite.Group() self.images = [f"./image/忍者{i}.png" for i in range(1, 3)] self.player = PlayerSprite(self.images) self.player.add(self.player_group) def broth(self): self.player = PlayerSprite([f"./image/忍者{i}.png" for i in range(1, 3)]) self.player.add(self.player_group) def clear(self): self.player_group.empty() def update(self): if not self.player_group: self.broth() self.player.update() self.player_group.draw(self.gm.screen) class BGSprite(BaseSprite): def __init__(self, image_name, start_x, speed=3): super().__init__(image_name) self.rect.left = start_x self.speed = speed def update(self): self.rect.left -= self.speed if self.rect.left <= -WIDTH: self.rect.left = WIDTH class BGManage: def __init__(self, gm_): self.gm = gm_ self.init_bg() def init_bg(self): self.bg_gaming_group = pygame.sprite.Group() self.bg1 = BGSprite("./image/load.png", 0) self.bg1.add(self.bg_gaming_group) self.bg2 = BGSprite("./image/load.png", WIDTH) self.bg2.add(self.bg_gaming_group) self.bg_ready_group = pygame.sprite.Group() self.bg3 = BGSprite("./image/background.png", 0) self.bg3.add(self.bg_ready_group) def update(self): if self.gm.game_state == "gaming": self.bg_gaming_group.draw(self.gm.screen) self.bg_gaming_group.update() else: self.bg_ready_group.draw(self.gm.screen) class UISprite(BaseSprite): def __init__(self, image_name, yes_no="yes", start_x=0, start_y=0): super().__init__(image_name) self.y_n = yes_no if self.y_n == "yes": self.rect.center = (WIDTH / 2, HEIGHT / 2) else: self.rect.left = start_x self.rect.top = start_y def is_collide(self): mouse_pos = pygame.mouse.get_pos() if self.rect.collidepoint(mouse_pos): return True class UIManage: def __init__(self, gm_ui): self.gm = gm_ui self.init_ready() self.init_end() self.font = pygame.font.Font("./font/幼圆.TTF", size=24) self.set_lab() def init_ready(self): self.ready_group = pygame.sprite.Group() self.ready_btn = UISprite("./image/ready.png") self.ready_btn.add(self.ready_group) def init_end(self): self.end_group = pygame.sprite.Group() self.end_btn = UISprite("./image/game_over.png") self.end_btn.add(self.end_group) self.con_btn = UISprite("./image/继续.png", "no", WIDTH - 180, HEIGHT - 88) self.con_btn.add(self.end_group) def set_lab(self): self.score_value = 0 self.labile = self.font.render(f"score:{self.score_value}", True, "red") def update_lab(self): self.score_value += 1 self.labile = self.font.render(f"score:{self.score_value}", True, "red") def check_collision(self): if self.gm.game_state == "ready": if self.ready_btn.is_collide(): self.gm.game_state = "gaming" self.gm.set_time() AudioManage().playing_bg_music() elif self.gm.game_state == "end": if self.con_btn.is_collide(): self.gm.game_state = "ready" AudioManage().ready_bg_music() def update(self): if self.gm.game_state == "ready": self.ready_group.draw(self.gm.screen) elif self.gm.game_state == "gaming": self.gm.screen.blit(self.labile, (200, 10)) elif self.gm.game_state == "end": self.gm.screen.blit(self.labile, (210, 200)) self.end_group.draw(self.gm.screen) class GameManage: def __init__(self): pygame.init() pygame.display.set_caption("小城跑酷") self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) self.game_state = "ready" self.ui = UIManage(self) self.bg = BGManage(self) self.player = PlayerManage(self) self.enemy = EnemyManage(self) self.clock = pygame.time.Clock() AudioManage().ready_bg_music() def set_time(self): pygame.time.set_timer(ENEMY_BORN, BORN_RATE) def check_event(self): for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() if event.type == pygame.KEYUP: if event.key == pygame.K_UP or event.key == pygame.K_w: if self.game_state == "gaming": self.player.player.is_jump() if event.type == ENEMY_BORN: self.enemy.broth() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: self.ui.check_collision() def update_draw(self): self.bg.update() if self.game_state == "ready": self.ui.update() self.reset_score() elif self.game_state == "gaming": self.ui.update() self.player.update() self.enemy.update() elif self.game_state == "end": self.ui.update() pygame.display.flip() def check_collider(self): # pass r = pygame.sprite.groupcollide(self.enemy.enemy_group, self.player.player_group, False, False) if r: for players in r.values(): for player in players: player.kill() self.game_state = "end" self.enemy.clear() pygame.time.set_timer(ENEMY_BORN, 0) AudioManage().set_sound_music(6) AudioManage().end_bg_music() def reset_score(self): self.ui.score_value = -1 self.ui.update_lab() def run(self): while True: self.clock.tick(60) self.check_event() self.update_draw() self.check_collider() gm = GameManage() gm.run()
部分游戏截图:
总结
这个简单的跑酷游戏涵盖了游戏开发的许多基本要素。通过这个项目,我们可以学习到:
- Pygame的基本使用
- 精灵(Sprite)的概念和应用
- 游戏循环的实现
- 用户输入处理
- 碰撞检测
- 音效和音乐的添加
- 简单的游戏UI实现
希望这个示例能帮助你更好地理解游戏开发的基本概念,并为你的Pygame学习之旅提供一个良好的起点。通过修改和扩展这个基础代码,你可以添加更多的游戏特性,如:
- 多种玩家角色
- 更复杂的障碍物模式
- 道具系统
- 关卡设计
- 更精美的图形和动画
- 存档和读档功能
- 多人游戏模式
记住,游戏开发是一个需要不断学习和实践的过程。从这个简单的项目开始,逐步提高你的技能,你将能够创造出更加复杂和有趣的游戏。
最后,建议你深入研究Pygame的官方文档,参与开源游戏项目,并在实践中不断总结经验。祝你在游戏开发的道路上取得成功!