Chapter12 武装飞船

本文介绍了如何使用Python的Pygame库开发一款名为"外星人入侵"的游戏。首先规划了游戏项目,包括玩家驾驶飞船移动和射击外星人的基本操作。接着详细讲解了如何创建Pygame窗口、响应用户输入、设置背景色、创建Ship类以及管理游戏事件和屏幕更新。此外,还涵盖了飞船的移动和射击功能的实现,包括子弹的创建、存储和删除。整个过程通过模块化编程进行了重构,使得代码更易于管理和扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.规划项目

在游戏<<外星人入侵>>中,玩家控制着一艘最初出现在屏幕底部中央的飞船.玩家可以使用箭头键左右移动飞船,还可以使用空格键进行射击.游戏开始时,一群外星人出现在天空中,它们在屏幕中向下移动.玩家的任务是射杀这些外星人.玩家将所有外星人都消灭干净后,将出现以群新的外星人,他们移动的速度更快.只要有歪心何忍撞到了玩家的飞船,或者到达了屏幕底部,玩家就损失一艘飞船.玩家损失三艘飞船后,游戏结束.

2.开始游戏项目

2.1 创建Pygame窗口以及响应用户输入

游戏的基本结构:

import sys

import pygame

def run_game():
    """初始化游戏并创建一个屏幕对象"""
    pygame.init()
    screen = pygame.display.set_mode((1200,800))
    pygame.display.set_caption("Alien Invation")
    
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
                
        # 让最近回值的屏幕可见
        pygame.display.flip()
        
run_game()

首先,我们导入了模块sys和pygame.模块pygame包含开发游戏所需的功能.玩家退出时,我们将使用模块sys来退出游戏
游戏<<外星人入侵>>开头是run_game()函数.
(1)pygame.init()初始化背景设置,让Pygame能正确工作

(2)pygame.display.set_mode((1200,800))来创建一个名为screen的显示窗口,这个游戏的所有图形元素都将在其中绘制.实参(1200,800)是个元组,指定了游戏窗口的尺寸.通过将这些尺寸值传递给pygame.display.set_mode()创建一个一个宽1200像素,高800像素的游戏窗口.

(3)对象screen是一个surface.在Pygame中,surface是屏幕的一部分,用于显示游戏元素.在这个游戏中,每个元素(如外星人或飞船)都是一个surface

(4)这个游戏是由一个while循环控制,其中包含一个事件循环以及管理屏幕更新的代码.事件是由用户玩游戏时执行的操作,如按键或者移动鼠标.为了让程序响应事件,我们编写一个事件循环,以侦听事件,并根据发生的事件执行响应任务.

(5)为了访问Pygame检测到的事件,我们使用方法 pygame.event.get().所有键盘和鼠标都将促进这个事件.

(6)调用pygame.display.flip(),命令Pygame让最近绘制的屏幕可见.在这里,每执行一次while循环时都绘制一个空屏幕,并擦去旧屏幕,以显示元素的新位置,并在原来的位置隐藏元素,从而营造平滑移动的效果

2.2 设置背景色

import sys

import pygame

def run_game():
    """初始化游戏并创建一个屏幕对象"""
    pygame.init()
    screen = pygame.display.set_mode((1200,800))
    pygame.display.set_caption("Alien Invation")
    
    # 设置背景色
    bg_color = (230,230,230)
    
    # 开始游戏的主循环
    while True:
    
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
                         
        # 每次循环都重绘屏幕
        screen.fill(bg_color)
        
        # 让最近绘制的屏幕可见
        pygame.display.flip()
run_game()

首先,我么创建了一种背景色,并将其存储在bg_color中.该颜色只需指定一次,因此我们在进入主循环前指定它
我们调用方法screen.fill(),用背景色填充屏幕;这个方法只接受一个实参:一种颜色

2.3 创建设置类

每次给游戏添加新功能时,通常也将引入一些新设置.下面编写一个名为settings的模块,其中包含一个名为settings的类,用于将所有设置存储在一个地方,以免再代码中到处添加设置.这让函数调用更加简单,且在项目增大时修改游戏外观更容易,要修改欧系,只需改setting.py当中的一些值,无需寻找散步再文件中的不同设置

class Settings():
    """存储 外星人入侵 的所有设置的类"""
    def __init__(self):
        """ 初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230,230,230)
ai_settings = Settings()
screen = pygame.display.set_mode((self.screen_width,self.screen_height))
---
	screen.fill(ai_settings.bg_color)

3.添加飞船图像

3.1 创建Ship类

import pygame
class Ship():
    def __init__(self,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        #加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images\ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        #将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom     
    def blitme(self):
        """在指定位置绘制飞船"""
        self.rect.blit(self.image,self.rect)

首先我们导入了模块pygame,Ship的方法__init__()接受两个参数:引用self和screen,然后后者指定了要将飞船绘制到什么地方.

为加载图像,我们调用了pygame.image.load(),这个函数返回一个表示飞船的surface,而我们将这个飞船存储到了self.image中

加载图像后,我们使用self.image.get_rect获取相应的属性rect(矩形).处理rect对象时,可使用据行的四角和中兴的x和y的坐标.要将游戏元素居中,可设置响应rect对象的属性center,centerx,centery…

我们将把飞船放在屏幕底部中央.首先将表示屏幕的矩形存储到self.screen_rect中,然后再将其放置底部中央

我们定义了方法blitme(),它根据self.rect指定位置将图像绘制在屏幕上.

3.2 在屏幕上绘制飞船

import sys
from settings import Settings
from ship import Ship
import pygame

def run_game():
    """初始化游戏并创建一个屏幕对象"""
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invation")
    
    # 创建一艘飞船
    ship = Ship(screen)
    
    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()        
                
        # 每次循环都重绘屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()
        
        # 让最近绘制的屏幕可见
        pygame.display.flip()
        
run_game()

4.重构:模块game_functions

在大型项目中,经常需要添加新代码前重构既有代码.重构旨在简化既有代码的结构,使其更容易拓展.在此,我们创建一个名为game_functions的新模块,它将存储大量让游戏运行的函数.

4.1 函数check_events()

我们首先把管理事件的代码移动到一个名为check_events()的函数中,以简化run_game()并隔离事件管理循环.通过隔离事件循环,可将事件管理与游戏的其他方面(如屏幕更新)分离

import sys
import pygame
def check_events():
    """响应按键和鼠标事件"""
    for eventt in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()  

这个模块导入了事件检查循环要用的sys和pygame.当前check_events()不需要任何形参,其复制了alien_events()的调用

4.2 函数update_screen()

def update_screen(ai_settings,screen,ship):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()

5. 驾驶飞船

下面让玩家能够左右移动飞船,在用户按住左或右箭头时作出响应.

5.1 按键响应

每当用户按键时,都将在Pygame中注册一个新事件.事件是通过方法pygame.event.get()获取的,因此在函数check_events()中我们需要指定检查哪些类型的事件.每次按键都被注册为一个KEYDOWN事件.
检查KEYDOWN事件时,我们需要检查按下的是否时特定的键.更新check_events()函数

def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                #向右移动飞船
                ship.rect.centerx += 1

5.2 不断移动

玩家按住右箭头不放时,我们希望飞船不断地向右移动,直到玩家松开为止.

我们将让游戏检测pygame.KEYUP事件,以便玩家松开右箭头时我们能够知道这一点;然后我们结合使用KEYDOWN和KEYUP事件,以及一个moving_right的标志来实现持续移动

飞船不动时,标志moving_right为False,玩家按下右箭头时,标志为True,玩家松开时,标志为False.

飞船的属性都是由ship类控制,因此我们将给这个类添加一个名为moving_right的属性和一个名为update()的方法,update用于检验标志moving_right的状态

5.3 左右移动、调整飞船速度、限制飞船活动范围、重构check_events()

—ship.py

import pygame
class Ship():
    def __init__(self,ai_settings,screen):
        """初始化飞船并设置其初始位置"""
        self.screen = screen
        self.ai_settings = ai_settings
        #加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images\ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        #将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom
        #在飞船的属性center中存储小数值
        self.center = float(self.rect.centerx)
        #移动标志
        self.moving_right = False
        self.moving_left = False
    def update(self):
        """根据移动标志调整飞船的位置"""
        #更新center值,而不是rect
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor
        # 根据self.center更新rect对象
        self.rect.centerx = self.center
    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image,self.rect)

—game_function.py

import sys
import pygame
def check_keydown_events(event,ship):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
def check_keyup_events(event,ship):
    """响应松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False   
def check_events(ship):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event,ship)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event,ship)              
def update_screen(ai_settings,screen,ship):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()

—alien_invasion.py

import sys
from settings import Settings
from ship import Ship
import game_functions as gf
import pygame
def run_game():
    """初始化游戏并创建一个屏幕对象"""
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invation")
    # 创建一艘飞船
    ship = Ship(ai_settings,screen)
    # 开始游戏的主循环
    while True:
        gf.check_events(ship)
        ship.update()
        gf.update_screen(ai_settings,screen,ship)
run_game()

6 射击

下面添加射击功能,我们将编写玩家按空格键时发射子弹的代码.子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失

6.1 添加子弹设置

—settings.py

	#子弹设置
        ## 这些设置创建宽3像素、高15像素的深灰色子弹,子弹的速度比飞船稍低
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60,60,60

6.2 创建Bullet类

—bullet.py

import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
    """一个对飞船发射子弹进行管理的类"""
    def __init__(self,ai_settings,screen,ship):
        """在飞船所处位置创建一个子弹对象"""
        supper(Bullet,self).__init__()
        self.screen = screen
        #在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
        self.rect = Pygame.Rect(0,0,ai_settings.bullet_width,
                                ai_settings.bullet_height)
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top
        #存储用小数表示子弹位置
        self.y = float(self.rect.y)
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor
    def update(self):
        """向上移动子弹"""
        #更新表示子弹位数的小数值
        self.y -= self.speed_factor
        #更新表示子弹的rect的位置
        self.rect.y = self.y
    def draw_bullet(self):
        """在屏幕上绘制子弹"""
        pygame.draw.rect(self.screen,self.color,self.rect)

6.3 将子弹存储到编组中

在玩家每次按空格键时都射发出一颗子弹.

import sys
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
import pygame
def run_game():
    """初始化游戏并创建一个屏幕对象"""
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invation")
    # 创建一艘飞船
    ship = Ship(ai_settings,screen)
    #创建一个用于存储子弹的编组
    bullets = Group()
    # 开始游戏的主循环
    while True:
        gf.check_events(ai_settings,screen,ship,bullets)
        ship.update()
        bullets.update()
        gf.update_screen(ai_settings,screen,ship,bullets)
run_game()

6.4 开火

import sys
import pygame
from bullet import Bullet
def check_keydown_events(event,ai_settings,screen,ship,bullets):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:
        #创建一颗子弹,并将其加入到编组bullets中
        new_bullet = Bullet(ai_settings,screen,ship)
        bullet.add(new_bullet)
def check_keyup_events(event,ship):
    """响应松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False   
def check_events(ai_settings,screen,ship,bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event,ai_settings,screen,ship,bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event,ship)              
def update_screen(ai_settings,screen,ship,bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    # 在飞船和外星人后面重新绘制所有子弹
    for bullet in bullets.sprites():
        bullte.draw_bullet()
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()
    

6.5 删除已经消失的子弹

#删除已经消失的子弹
        for bullet in bullets.copy():
            if bullet.rect.bottom <= 0:
                bullets.remove(bullet)
        print(len(bullets))

6.6 子弹设置

—game_function.py

import sys
import pygame
from bullet import Bullet
def check_keydown_events(event,ai_settings,screen,ship,bullets):
    """响应按键"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:
        fire_bullet(ai_settings,screen,ship,bullets)
def fire_bullet(ai_settings,screen,ship,bullets):
    """如果没有达到限制,就发射一颗子弹"""
    #创建一颗子弹,并将其加入到编组bullets中
    if len(bullets) < ai_settings.bullet_allowed:
        new_bullet = Bullet(ai_settings,screen,ship)
        bullet.add(new_bullet)
def check_keyup_events(event,ship):
    """响应松开"""
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False   
def check_events(ai_settings,screen,ship,bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event,ai_settings,screen,ship,bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event,ship)              
def update_screen(ai_settings,screen,ship,bullets):
    """更新屏幕上的图像,并切换到新屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(ai_settings.bg_color)
    # 在飞船和外星人后面重新绘制所有子弹
    for bullet in bullets.sprites():
        bullte.draw_bullet()
    ship.blitme()
    # 让最近绘制的屏幕可见
    pygame.display.flip()
def update_bullets(bullets):
    """更新子弹位置,并删除已经消失的子弹"""
    # 更新子弹的位置
    bullets.update()
    #删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)

— alien_invasion.py

import sys
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
import pygame
def run_game():
    """初始化游戏并创建一个屏幕对象"""
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invation")
    # 创建一艘飞船
    ship = Ship(ai_settings,screen)
    #创建一个用于存储子弹的编组
    bullets = Group()
    # 开始游戏的主循环
    while True:
        gf.check_events(ai_settings,screen,ship,bullets)
        ship.update()
        gf.update_bullets(bullets)
        gf.update_screen(ai_settings,screen,ship,bullets)
run_game()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值