Complete Python Pygame 'Plane War' Game with Music, Explosions, Scoring, and Controls
This article presents a complete Python pygame implementation of a classic 'Plane War' game, detailing background music integration, enemy destruction effects, scoring system, and player controls, and provides the full source code spanning over three hundred lines for readers to study and run.
The article introduces a full-featured "Plane War" game built with Python's pygame library, covering the addition of background music, enemy hit animations, explosion effects, scoring, and comprehensive player controls. It explains the programming approach, including class creation and function calls, and then provides the complete source code for readers to explore and run.
<span><span>import</span> pygame <span># 导入动态模块(.dll .pyd .so) 不需要在包名后边跟模块名</span></span></code><code><span><span>from</span> pygame.locals <span>import</span> *</span></code><code><span><span>import</span> time</span></code><code><span><span>import</span> random</span></code><code><span><span>import</span> sys</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span># 定义常量(定义后,不再改值)</span></span></code><code><span>WINDOW_HEIGHT = <span>768</span></span></code><code><span>WINDOW_WIDTH = <span>512</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span>enemy_list = []</span></code><code><span>score = <span>0</span></span></code><code><span>is_restart = <span>False</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>class</span> <span>Map</span>:</span></span></code><code><span><span><span>def</span> <span>__init__</span><span>(self, img_path, window)</span>:</span></span></code><code><span>self.x = <span>0</span></span></code><code><span>self.bg_img1 = pygame.image.load(img_path)</span></code><code><span>self.bg_img2 = pygame.image.load(img_path)</span></code><code><span>self.bg1_y = - WINDOW_HEIGHT</span></code><code><span>self.bg2_y = <span>0</span></span></code><code><span>self.window = window</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>move</span><span>(self)</span>:</span></span></code><code><span><span># 当地图1的 y轴移动到0,则重置</span></span></code><code><span><span>if</span> self.bg1_y >= <span>0</span>:</span></code><code><span>self.bg1_y = - WINDOW_HEIGHT</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span># 当地图2的 y轴移动到 窗口底部,则重置</span></span></code><code><span><span>if</span> self.bg2_y >= WINDOW_HEIGHT:</span></code><code><span>self.bg2_y = <span>0</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span># 每次循环都移动1个像素</span></span></code><code><span>self.bg1_y += <span>3</span></span></code><code><span>self.bg2_y += <span>3</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>display</span><span>(self)</span>:</span></span></code><code><span><span>"""贴图"""</span></span></code><code><span>self.window.blit(self.bg_img1, (self.x, self.bg1_y))</span></code><code><span>self.window.blit(self.bg_img2, (self.x, self.bg2_y))</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>class</span> <span>HeroBullet</span>:</span></span></code><code><span><span>"""英雄子弹类"""</span></span></code><code><span><span><span>def</span> <span>__init__</span><span>(self, img_path, x, y, window)</span>:</span></span></code><code><span>self.img = pygame.image.load(img_path)</span></code><code><span>self.x = x</span></code><code><span>self.y = y</span></code><code><span>self.window = window</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>display</span><span>(self)</span>:</span></span></code><code><span>self.window.blit(self.img, (self.x, self.y))</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>move</span><span>(self)</span>:</span></span></code><code><span><span>"""子弹向上飞行距离"""</span></span></code><code><span>self.y -= <span>6</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>is_hit_enemy</span><span>(self, enemy)</span>:</span></span></code><code><span><span>if</span> pygame.Rect.colliderect(</span></code><code><span>pygame.Rect(self.x, self.y, <span>20</span>, <span>31</span>),</span></code><code><span>pygame.Rect(enemy.x, enemy.y, <span>100</span>, <span>68</span>)</span></code><code><span>): <span># 判断是否交叉</span></span></code><code><span><span>return</span> <span>True</span></span></code><code><span><span>else</span>:</span></code><code><span><span>return</span> <span>False</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>class</span> <span>EnemyPlane</span>:</span></span></code><code><span><span>"""敌人飞机类"""</span></span></code><code><span><span><span>def</span> <span>__init__</span><span>(self, img_path, x, y, window)</span>:</span></span></code><code><span>self.img = pygame.image.load(img_path) <span># 图片对象</span></span></code><code><span>self.x = x <span># 飞机坐标</span></span></code><code><span>self.y = y</span></code><code><span>self.window = window <span># 飞机所在的窗口</span></span></code><code><span>self.is_hited = <span>False</span></span></code><code><span>self.anim_index = <span>0</span></span></code><code><span>self.hit_sound = pygame.mixer.Sound(<span>"E:/飞机大战/baozha.ogg"</span>)</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>move</span><span>(self)</span>:</span></span></code><code><span>self.y += <span>10</span></span></code><code><span><span># 到达窗口下边界,回到顶部</span></span></code><code><span><span>if</span> self.y >= WINDOW_HEIGHT:</span></code><code><span>self.x = random.randint(<span>0</span>, random.randint(<span>0</span>, WINDOW_WIDTH - <span>100</span>))</span></code><code><span>self.y = <span>0</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>plane_down_anim</span><span>(self)</span>:</span></span></code><code><span><span>"""敌机被击中动画"""</span></span></code><code><span><span>if</span> self.anim_index >= <span>21</span>: <span># 动画执行完</span></span></code><code><span>self.anim_index = <span>0</span></span></code><code><span>self.img = pygame.image.load(</span></code><code><span><span>"E:/飞机大战/img-plane_%d.png"</span> % random.randint(<span>1</span>, <span>7</span>))</span></code><code><span>self.x = random.randint(<span>0</span>, WINDOW_WIDTH - <span>100</span>)</span></code><code><span>self.y = <span>0</span></span></code><code><span>self.is_hited = <span>False</span></span></code><code><span><span>return</span></span></code><code><span><span>elif</span> self.anim_index == <span>0</span>:</span></code><code><span>self.hit_sound.play()</span></code><code><span>self.img = pygame.image.load(</span></code><code><span><span>"E:/飞机大战/bomb-%d.png"</span> % (self.anim_index // <span>3</span> + <span>1</span>))</span></code><code><span>self.anim_index += <span>1</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>display</span><span>(self)</span>:</span></span></code><code><span><span>"""贴图"""</span></span></code><code><span><span>if</span> self.is_hited:</span></code><code><span>self.plane_down_anim()</span></code><span><br/></span></code><code><span>self.window.blit(self.img, (self.x, self.y))</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>class</span> <span>HeroPlane</span>:</span></span></code><code><span><span><span>def</span> <span>__init__</span><span>(self, img_path, x, y, window)</span>:</span></span></code><code><span>self.img = pygame.image.load(img_path) <span># 图片对象</span></span></code><code><span>self.x = x <span># 飞机坐标</span></span></code><code><span>self.y = y</span></code><code><span>self.window = window <span># 飞机所在的窗口</span></span></code><code><span>self.bullets = [] <span># 记录该飞机发出的所有子弹</span></span></code><code><span>self.is_hited = <span>False</span></span></code><code><span>self.is_anim_down = <span>False</span></span></code><code><span>self.anim_index = <span>0</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>is_hit_enemy</span><span>(self, enemy)</span>:</span></span></code><code><span><span>if</span> pygame.Rect.colliderect(</span></code><code><span>pygame.Rect(self.x, self.y, <span>120</span>, <span>78</span>),</span></code><code><span>pygame.Rect(enemy.x, enemy.y, <span>100</span>, <span>68</span>)</span></code><code><span>): <span># 判断是否交叉</span></span></code><code><span><span>return</span> <span>True</span></span></code><code><span><span>else</span>:</span></code><code><span><span>return</span> <span>False</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>plane_down_anim</span><span>(self)</span>:</span></span></code><code><span><span>"""敌机被击中动画"""</span></span></code><code><span><span>if</span> self.anim_index >= <span>21</span>: <span># 动画执行完</span></span></code><code><span>self.is_hited = <span>False</span></span></code><code><span>self.is_anim_down = <span>True</span></span></code><code><span><span>return</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span>self.img = pygame.image.load(</span></code><code><span><span>"E:/飞机大战/bomb-%d.png"</span> % (self.anim_index // <span>3</span> + <span>1</span>))</span></code><code><span>self.anim_index += <span>1</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>display</span><span>(self)</span>:</span></span></code><code><span><span>"""贴图"""</span></span></code><code><span><span>for</span> enemy <span>in</span> enemy_list:</span></code><code><span><span>if</span> self.is_hit_enemy(enemy):</span></code><code><span>enemy.is_hited = <span>True</span></span></code><code><span>self.is_hited = <span>True</span></span></code><code><span>self.plane_down_anim()</span></code><span><br/></span></code><code><span>self.window.blit(self.img, (self.x, self.y))</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>display_bullets</span><span>(self)</span>:</span></span></code><code><span><span># 贴子弹图</span></span></code><code><span>deleted_bullets = []</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>for</span> bullet <span>in</span> self.bullets:</span></code><code><span><span># 判断 子弹是否超出 上边界</span></span></code><code><span><span>if</span> bullet.y >= <span>-31</span>: <span># 没有出边界</span></span></code><code><span>bullet.display()</span></code><code><span>bullet.move()</span></code><code><span><span>else</span>: <span># 飞出边界</span></span></code><code><span>deleted_bullets.append(bullet)</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>for</span> enemy <span>in</span> enemy_list:</span></code><code><span><span>if</span> bullet.is_hit_enemy(enemy): <span># 判断是否击中敌机</span></span></code><code><span>enemy.is_hited = <span>True</span></span></code><code><span>deleted_bullets.append(bullet)</span></code><code><span><span>global</span> score</span></code><code><span>score += <span>10</span></span></code><code><span><span>break</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>for</span> out_window_bullet <span>in</span> deleted_bullets:</span></code><code><span>self.bullets.remove(out_window_bullet)</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>move_left</span><span>(self)</span>:</span></span></code><code><span><span>"""往左飞"""</span></span></code><code><span><span>if</span> self.x >= <span>0</span> <span>and</span> <span>not</span> self.is_hited:</span></code><code><span>self.x -= <span>10</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>move_right</span><span>(self)</span>:</span></span></code><code><span><span>"""往右飞"""</span></span></code><code><span><span>if</span> self.x <= WINDOW_WIDTH - <span>120</span> <span>and</span> <span>not</span> self.is_hited:</span></code><code><span>self.x += <span>10</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>move_up</span><span>(self)</span>:</span></span></code><code><span><span>"""往上飞"""</span></span></code><code><span><span>if</span> self.y >= <span>0</span> <span>and</span> <span>not</span> self.is_hited:</span></code><code><span>self.y -= <span>5</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>move_down</span><span>(self)</span>:</span></span></code><code><span><span>"""往下飞"""</span></span></code><code><span><span>if</span> self.y <= WINDOW_HEIGHT - <span>78</span> <span>and</span> <span>not</span> self.is_hited:</span></code><code><span>self.y += <span>5</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span><span>def</span> <span>fire</span><span>(self)</span>:</span></span></code><code><span><span>"""发射子弹"""</span></span></code><code><span><span># 创建子弹对象 子弹x = 飞机x + 飞机宽度的一半 - 子弹宽度的一半</span></span></code><code><span>bullet = HeroBullet(<span>"E:/飞机大战/bullet_17.png"</span>, self.x +</span></code><code><span><span>60</span> - <span>10</span>, self.y - <span>31</span>, self.window)</span></code><code><span><span># 显示子弹(贴子弹图)</span></span></code><code><span>bullet.display()</span></code><code><span>self.bullets.append(bullet) <span># 为了避免子弹对象被释放(只有局部变量引用对象,方法一执行完就会释放)</span></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>class</span> <span>Game</span>:</span></span></code><code><span><span><span>def</span> <span>__init__</span><span>(self)</span>:</span></span></code><code><span>pygame.init()</span></code><code><span><span># 设置标题</span></span></code><code><span>pygame.display.set_caption(<span>"飞机大战 v1.0"</span>)</span></code><code><span><span># 设置图标</span></span></code><code><span>game_ico = pygame.image.load(<span>"E:/飞机大战/app.ico"</span>)</span></code><code><span>pygame.display.set_icon(game_ico)</span></code><code><span>pygame.mixer.music.load(<span>"E:/飞机大战/bg2.ogg"</span>)</span></code><code><span><span># 游戏结束的音效(超级玛丽)</span></span></code><code><span>self.gameover_sound = pygame.mixer.Sound(<span>"E:/飞机大战/gameover.wav"</span>)</span></code><code><span><span># 循环播放背景音乐</span></span></code><code><span>pygame.mixer.music.play(<span>-1</span>)</span></code><code><span><span># 创建窗口 set_mode((窗口尺寸))</span></span></code><code><span>self.window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))</span></code><code><span><span># 创建地图对象</span></span></code><code><span>self.game_map = Map(<span>"E:/飞机大战/img_bg_level_%d.jpg"</span> %</span></code><code><span>random.randint(<span>1</span>, <span>5</span>), self.window)</span></code><code><span><span># 创建对象</span></span></code><code><span>self.hero_plane = HeroPlane(<span>"E:/飞机大战/hero2.png"</span>, <span>240</span>, <span>500</span>, self.window)</span></code><code><span>enemy_plane1 = EnemyPlane(<span>"E:/飞机大战/img-plane_%d.png"</span> % random.randint(</span></code><code><span><span>1</span>, <span>7</span>), random.randint(<span>0</span>, WINDOW_WIDTH - <span>100</span>), <span>0</span>, self.window)</span></code><code><span>enemy_plane2 = EnemyPlane(<span>"E:/飞机大战/img-plane_%d.png"</span> % random.randint(<span>1</span>, <span>7</span>), random.randint(<span>0</span>, WINDOW_WIDTH - <span>100</span>), random.randint(<span>-150</span>, <span>-68</span>),</span></code><code><span>self.window)</span></code><code><span>enemy_plane3 = EnemyPlane(<span>"E:/飞机大战/img-plane_%d.png"</span> % random.randint(<span>1</span>, <span>7</span>), random.randint(<span>0</span>, WINDOW_WIDTH - <span>100</span>), random.randint(<span>-300</span>, <span>-140</span>),</span></code><code><span>self.window)</span></code><code><span>enemy_list.append(enemy_plane1)</span></code><code><span>enemy_list.append(enemy_plane2)</span></code><code><span>enemy_list.append(enemy_plane3)</span></code><code><span>self.enemy_list = enemy_list</span></code><code><span><span># 创建文字对象</span></span></code><code><span>self.score_font = pygame.font.Font(<span>"E:/飞机大战/SIMHEI.TTF"</span>, <span>40</span>)</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>draw_text</span><span>(self, content, size, x, y)</span>:</span></span></code><code><span>font_obj = pygame.font.Font(<span>"E:/飞机大战/SIMHEI.TTF"</span>, size)</span></code><code><span>text = font_obj.render(content, <span>1</span>, (<span>255</span>, <span>255</span>, <span>255</span>))</span></code><code><span>self.window.blit(text, (x, y))</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>wait_game_input</span><span>(self)</span>:</span></span></code><code><span>while <span>True</span>:</span></code><code><span>for event in pygame.event.get():</span></code><code><span>if event.type == QUIT:</span></code><code><span>sys.exit()</span></code><code><span>pygame.quit()</span></code><code><span>elif event.type == KEYDOWN:</span></code><code><span>if event.key == K_ESCAPE:</span></code><code><span>sys.exit()</span></code><code><span>pygame.quit()</span></code><code><span>elif event.key == K_RETURN:</span></code><code><span>global is_restart, score</span></code><code><span>is_restart = <span>True</span></span></code><code><span>score = <span>0</span></span></code><code><span>return</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>game_start</span><span>(self)</span>:</span></span></code><code><span># 贴背景图片</span></code><code><span>self.game_map.display()</span></code><code><span>self.draw_text(<span>"飞机大战"</span>, <span>40</span>, WINDOW_WIDTH / <span>2</span> - <span>100</span>, WINDOW_HEIGHT / <span>3</span>)</span></code><code><span>self.draw_text(<span>"按Enter开始游戏, Esc退出游戏."</span>, <span>28</span>, WINDOW_WIDTH /<span>3</span> - <span>140</span>, WINDOW_HEIGHT /<span>2</span>)</span></code><code><span>pygame.display.update()</span></code><code><span>self.wait_game_input()</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>game_over</span><span>(self)</span>:</span></span></code><code><span># 先停止背景音乐</span></code><code><span>pygame.mixer.music.stop()</span></code><code><span># 再播放音效</span></code><code><span>self.gameover_sound.play()</span></code><code><span># 贴背景图片</span></code><code><span>self.game_map.display()</span></code><code><span>self.draw_text(<span>"战机被击落,得分为 %d"</span> % score, <span>28</span>, WINDOW_WIDTH / <span>3</span> - <span>100</span>, WINDOW_HEIGHT / <span>3</span>)</span></code><code><span>self.draw_text(<span>"按Enter重新开始, Esc退出游戏."</span>, <span>28</span>, WINDOW_WIDTH / <span>3</span> - <span>140</span>, WINDOW_HEIGHT / <span>2</span>)</span></code><code><span>pygame.display.update()</span></code><code><span>self.wait_game_input()</span></code><code><span>self.gameover_sound.stop()</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>key_control</span><span>(self)</span>:</span></span></code><code><span># 获取事件,比如按键等 先显示界面,再根据获取的事件,修改界面效果</span></code><code><span>for event in pygame.event.get():</span></code><code><span># 判断是否是点击了退出按钮</span></code><code><span>if event.type == QUIT:</span></code><code><span>sys.exit() <span># 让程序终止</span></span></code><code><span>pygame.quit()</span></code><code><span># 判断是否是按下了键</span></code><code><span>elif event.type == KEYDOWN:</span></code><code><span># 检测按键是否是空格键</span></code><code><span>if event.key == K_SPACE:</span></code><code><span>self.hero_plane.fire()</span></code><code><span># 获取连续按下的情况</span></code><code><span>pressed_keys = pygame.key.get_pressed()</span></code><code><span>if pressed_keys[pygame.K_LEFT]:</span></code><code><span>self.hero_plane.move_left()</span></code><code><span>if pressed_keys[pygame.K_RIGHT]:</span></code><code><span>self.hero_plane.move_right()</span></code><code><span>if pressed_keys[pygame.K_UP]:</span></code><code><span>self.hero_plane.move_up()</span></code><code><span>if pressed_keys[pygame.K_DOWN]:</span></code><code><span>self.hero_plane.move_down()</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>display</span><span>(self)</span>:</span></span></code><code><span># 贴背景图</span></code><code><span>self.game_map.display()</span></code><code><span>self.game_map.move()</span></code><code><span># 贴飞机图</span></code><code><span>self.hero_plane.display()</span></code><code><span>self.hero_plane.display_bullets()</span></code><code><span># 贴敌机图</span></code><code><span>for enemy in enemy_list:</span></code><code><span>enemy.display()</span></code><code><span># 让敌机移动</span></code><code><span>if not enemy.is_hited:</span></code><code><span>enemy.move()</span></code><code><span># 贴得分文字</span></code><code><span>score_text = self.score_font.render(<span>"得分:%d"</span> % score, <span>1</span>, (<span>255</span>, <span>255</span>, <span>255</span>))</span></code><code><span>self.window.blit(score_text, (<span>10</span>, <span>10</span>))</span></code><code><span># 刷新界面 不刷新不会更新显示的内容</span></code><code><span>pygame.display.update()</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>run</span><span>(self)</span>:</span></span></code><code><span>if is_restart == <span>False</span>:</span></code><code><span>self.game_start()</span></code><code><span>while <span>True</span>:</span></code><code><span># 显示界面</span></code><code><span>self.display()</span></code><code><span>if self.hero_plane.is_anim_down:</span></code><code><span>self.hero_plane.is_anim_down = <span>False</span></span></code><code><span>global enemy_list</span></code><code><span>enemy_list = []</span></code><code><span>break</span></code><code><span># 键盘控制</span></code><code><span>self.key_control()</span></code><code><span># 每次循环,让程序休眠一会儿</span></code><code><span>time.sleep(<span>0.01</span>)</span></code><code><span>self.game_over()</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span><span>def</span> <span>main</span><span>()</span>:</span></span></code><code><span>"""主函数 一般将程序的入口"""</span></code><code><span># 运行游戏</span></code><code><span>while <span>True</span>:</span></code><code><span># 创建游戏对象</span></code><code><span>game = Game()</span></code><code><span>game.run()</span></code><code><span><br/></span></code><code><span><br/></span></code><code><span>if __name__ == <span>'__main__'</span>:</span></code><code><span>main()</span>Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
