首页 飞机大战
文章
取消

飞机大战

一、设计选题

《飞机大战》

二、课题简介

飞机大战是由腾讯公司的微信团队开发的一款微信小游戏,这款游戏以太空主题的画面为游戏背景,由玩家控制飞机,发射炮弹击毁敌方飞机。画面简洁有趣,规则简单易懂,操作便捷易上手。这款游戏不同于腾讯开发的以盈利为目标的其他游戏,抛去了现在游戏的花里胡哨,回归了游戏最简单的目的,仅仅是为了好玩,没有门槛,没有引导用户充钱。让我们们都能够找到童年玩掌机类游戏的乐趣。不过可惜《飞机大战》已经被淘汰,所以在这重现飞机大战,表达对现在国内缺乏真正让人快乐的游戏的惋惜和对这款经典游戏的致敬。并且希望国内的游戏市场能够变得更好,也能让更多的人可以改变对游戏的偏见,让大家能够真正感受到这第九大艺术的魅力。

三、运行环境

Windows系统,PyCharm

Pygame,random,os


四、具体实现

1. 架构设计

系统架构如图1所示。

Snipaste_2022-04-15_11-11-40

下面分别介绍各个部分的作用及功能:

game.py,游戏主模块,封装Game类并负责启动游戏。

game_hud.py,游戏面板模块,封装指示器面板类。

game_items.py,游戏元素模块,封装英雄飞机,子弹,敌机,道具等游戏元素类,并定义全局变量。

game_music.py,游戏音乐模块,封装音乐播放器类。

font,放置游戏中使用的字体素材。

images,放置了游戏中使用的图片素材。

sound,放置了游戏中使用的声音素材。

2. 核心功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
class Game(object):
    # 游戏类
    def __init__(self):
        # 游戏主窗口
        self.main_window = pygame.display.set_mode(SCREEN_RECT.size)
        pygame.display.set_caption("飞机大战")
        # 游戏状态
        self.is_game_over = False
        self.is_pause = False
        # 精灵组属性
        self.all_group = pygame.sprite.Group()      # 所有精灵组
        self.enemies_group = pygame.sprite.Group()  # 敌机精灵组
        self.supplies_group = pygame.sprite.Group()   # 道具精灵组
        # 创建精灵
        # 背景精灵,交替滚动
        self.all_group.add(Background(False), Background(True))
        # 指示器面板
        self.hud_panel = HudPanel(self.all_group)
        # 创建敌机
        self.create_enemies()
        # 英雄精灵
        self.hero = Hero(self.all_group)
        # 设置面板中炸弹数量
        self.hud_panel.show_bomb(self.hero.bomb_count)
        # 创建道具
        self.create_supplies()
        # 创建音乐播放器
        self.player = MusicPlayer("game_music.wav")
        self.player.play_music()
        
    def reset_game(self):
        # 重置游戏
        self.is_game_over = False
        self.is_pause = False
        self.hud_panel.reset_panel()
        # 设置英雄的初始位置
        self.hero.rect.midbottom = HERO_DEFAULT_MID_BOTTOM
        # 清空所有敌机
        for enemy in self.enemies_group:
            enemy.kill()
        # 清空残留子弹
        for bullet in self.hero.bullets_group:
            bullet.kill()
        # 重新创建敌机
        self.create_enemies()

    def event_handler(self):
        # 监听事件:return:如果监听到退出事件,返回True,否则给、返回False
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return True

            elif event.type == pygame.KEYDOWN and (event.key == pygame.K_ESCAPE or event.key == pygame.K_SPACE):
                if self.is_game_over:              # 游戏已经结束
                    self.reset_game()              # 重新开始游戏
                else:
                    self.is_pause = not self.is_pause   # 切换暂停状态
                    self.player.pause_music(self.is_pause)
            """以下为开发者按钮"""
            if event.type == pygame.KEYDOWN and event.key == pygame.K_l:
                self.hud_panel.lives_count += 1
                self.hud_panel.show_lives()
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_i:
                if self.hero.is_power==False:
                    self.hero.is_power = True
                else:
                    self.hero.is_power = False
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_a:
                self.hero.bomb_count += 1
                self.hud_panel.show_bomb(self.hero.bomb_count)
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_n:
                if self.hero.bullets_kind == 0:
                    self.hero.bullets_kind = 1
                else:
                    self.hero.bullets_kind = 0
            """结束"""
            # 判断是否正在游戏
            if not self.is_game_over and not self.is_pause:
                # 监听发射子弹事件
                if event.type ==HERO_FIRE_EVENT:
                    self.player.play_sound("bullet.wav")
                    self.hero.fire(self.all_group)
                # 监听关闭子弹增强事件
                if event.type == BULLET_ENHANCED_OFF_EVENT:
                    self.hero.bullets_kind = 0
                    pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 0)
                # 监听投放道具事件
                if event.type == THROW_SUPPLY_EVENT:
                    self.player.play_sound("supply.wav")
                    supply = random.choice(self.supplies_group.sprites())
                    supply.throw_supply()
                # 监听英雄牺牲事件
                if event.type == HERO_DEAD_EVENT:
                    print("英雄牺牲了...")

                    # 生命计数 -1
                    self.hud_panel.lives_count -= 1
                    # 更新生命计数显示
                    self.hud_panel.show_lives()

                    if self.hud_panel.lives_count > 0:
                        # 设置英雄回到的初始位置
                        self.hero.rect.midbottom = HERO_DEFAULT_MID_BOTTOM
                        # 开启无敌模式
                        self.hero.is_power = True

                    
                    # 更新炸弹显示
                    self.hud_panel.show_bomb(self.hero.bomb_count)

                    # 监听取消英雄无敌事件
                if event.type == HERO_POWER_OFF_EVENT:
                    print("取消无敌状态...")
                    # 设置英雄属性
                    self.hero.is_power = False
                    # 取消定时器
                    pygame.time.set_timer(HERO_POWER_OFF_EVENT, 0)
                # 监听玩家按下b,引爆一颗炸弹

                if event.type == pygame.KEYDOWN and event.key == pygame.K_b:
                    # 如果英雄没有牺牲同时有炸弹
                    if self.hero.hp > 0 and self.hero.bomb_count > 0:
                        self.player.play_sound("use_bomb.wav")
                    # 引爆炸弹
                    score = self.hero.blowup(self.enemies_group)
                    # 更新炸弹数量显示
                    self.hud_panel.show_bomb(self.hero.bomb_count)
                    # 更新游戏得分,如果游戏等级提升,创建新的敌机
                    if self.hud_panel.increase_score(score):
                        self.create_enemies()
        return False

    def start(self):
        # 开始游戏
        clock = pygame.time.Clock()             # 游戏时钟
        frame_counter = 0                       # 逐帧动画计数器
        while True:
            # 生命计数等于 0,表示游戏结束
            self.is_game_over = self.hud_panel.lives_count == 0
            if self.event_handler():            # 事件监听
                self.hud_panel.save_best_score()
                return
            # 判断游戏状态
            if self.is_game_over:
                self.hud_panel.panel_pause(True, self.all_group)
            elif self.is_pause:
                self.hud_panel.panel_pause(False, self.all_group)
            else:
                # 获得当前时刻的按键元组
                keys = pygame.key.get_pressed()
                # 水平移动基数
                move_hor = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
                # 垂直移动基数
                move_ver = keys[pygame.K_DOWN] - keys[pygame.K_UP]
                # 修改逐帧动画计数器
                frame_counter = (frame_counter + 1) % FRAME_INTERVAL
                # 更新 all_group 中所有精灵内容
                self.all_group.update(frame_counter == 0, move_hor, move_ver)
                self.hud_panel.panel_resume(self.all_group)
                # 碰撞检测
                self.check_collide()
            # 绘制 all_group 中的所有精灵
            self.all_group.draw(self.main_window)
            pygame.display.update()             # 更新显示
            clock.tick(60)                      # 设置刷新帧率
            
    def create_enemies(self):
        # 根据游戏级别创建不同数量的敌机
        # 敌机精灵组中的精灵数量
        count = len(self.enemies_group.sprites())
        # 要添加到的精灵组
        groups = (self.all_group, self.enemies_group)
        # 判断游戏级别及已有的敌机数量
        if self.hud_panel.level == 1 and count == 0:        # 关卡 1
            for i in range(16):
                Enemy(0, 3, *groups)
        elif self.hud_panel.level == 2 and count == 16:     # 关卡 2
            # 增加敌机的最大速度
            for enemy in self.enemies_group.sprites():
                enemy.max_speed = 5
            # 创建敌机
            for i in range(8):
                Enemy(0, 5, *groups)
            for i in range(2):
                Enemy(1, 1, *groups)
        elif self.hud_panel.level == 3 and count == 26:     # 关卡 3
            # 增加敌机的最大速度
            for enemy in self.enemies_group.sprites():
                enemy.max_speed = 7 if enemy.kind == 0 else 3
            # 创建敌机
            for i in range(8):
                Enemy(0, 7, *groups)
            for i in range(2):
                Enemy(1, 3, *groups)
            for i in range(2):
                Enemy(2, 1, *groups)

    def check_collide(self):
        """碰撞检测"""
        # 检测英雄飞机和敌机的碰撞
        collide_enemies = pygame.sprite.spritecollide(self.hero, self.enemies_group, False,
                                                      pygame.sprite.collide_mask)
        
        if not self.hero.is_power:
            enemies = pygame.sprite.spritecollide(self.hero, self.enemies_group, False,
                                                      pygame.sprite.collide_mask)
            enemies = list(filter(lambda x : x.hp > 0,enemies))
            if enemies:
                self.player.play_sound(self.hero.wav_name)
                self.hero.hp = 0
                
            for enemy in collide_enemies:
                enemy.hp = 0  # 摧毁发生碰撞的敌机
                # 检测敌机被子弹击中
        hit_enemies = pygame.sprite.groupcollide(self.enemies_group,
                                                     self.hero.bullets_group,
                                                     False,
                                                     False,
                                                     pygame.sprite.collide_mask)
        # 遍历字典
        for enemy in hit_enemies:
            # 已经被摧毁的敌机不需要浪费子弹
            if enemy.hp <= 0:
                continue
            # 遍历击中敌机的子弹列表
            for bullet in hit_enemies[enemy]:
                #  将子弹从所有精灵组中清除
                bullet.kill()
                # 修改敌机的生命值
                enemy.hp -= bullet.damage
                # 如果敌机没有被摧毁,继续下一颗子弹
                if enemy.hp > 0:
                    continue
                # 修改游戏得分并判断是否升级
                if self.hud_panel.increase_score(enemy.value):
                    # 播放升级音效
                    self.player.play_sound("upgrade.wav")
                    self.create_enemies()
                # 播放敌机炸弹音效
                self.player.play_sound(enemy.wav_name)
                # 退出遍历子弹列表循环
                break
        #  英雄拾取道具
        supplies = pygame.sprite.spritecollide(self.hero,
                                                self.supplies_group,
                                                False,
                                                pygame.sprite.collide_mask)
        if supplies:
            supply = supplies[0]
            # 播放使用道具音效
            self.player.play_sound(supply.wav_name)
            # 将道具设置到游戏窗口下方
            supply.rect.y = SCREEN_RECT.h
            # 判断道具类型
            if supply.kind == 0:  # 炸弹补给
                self.hero.bomb_count += 1
                self.hud_panel.show_bomb(self.hero.bomb_count)
            else:  # 设置子弹增强
                self.hero.bullets_kind = 1
                # 设置关闭子弹增强的定时器事件
                pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 8000)

    def create_supplies(self):
        """创建道具"""
        Supply(0, self.supplies_group, self.all_group)
        Supply(1, self.supplies_group, self.all_group)
        # 设置 30s 投放道具定时器事件
        pygame.time.set_timer(THROW_SUPPLY_EVENT, 10000)

if __name__ == '__main__':
    pygame.init()
    Game().start()
    pygame.quit()

五.项目演示

Snipaste_2022-04-15_11-14-42

Snipaste_2022-04-15_11-14-51

Snipaste_2022-04-15_11-15-00

Snipaste_2022-04-15_11-15-09

六、心得体会

Python的计算生态很强大,通过pygame可以很轻松的设计出一款2D游戏,通过此次课程设计,我初步理解了面向对象的思想,感受到了使用python语言进行游戏项目开发的乐趣。当游戏能够运行时,心中便会有一种成就感。希望以后可以灵活的掌握运用面向对象的编程技巧,并且能将其运用到python程序的实际开发中。

源码资源可在这找到:

https://github.com/Aye486/scaling-adventure.git

本文由作者按照 CC BY 4.0 进行授权

搜索与图论模板

数据结构模板