python学习(十二) 武装飞机游戏(下)

驾驶飞船

  • 学会如何控制屏幕图像移动

响应按键

  • 通过方法pygame.event.get()来获取事件,将每次事件都为注册为KEYDOWN事件。

  • game_functions

import sys

import pygame

def check_event(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

def update_screen(ai_settings,screen,ship):
    """更新屏幕上的图像,并切换新屏幕"""
    #每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()

    #让最近绘制的屏幕可见
    pygame.display.flip()
  • alien_invasion.py
import sys

import pygame

from settings import Settings

from ship import Ship

import game_functions as gf

def  run_game():
    #初始化游戏并创建一个屏幕对象
    ai_settings = Settings()
    pygame.init()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_heigth))
    pygame.display.set_caption("ALien Invasion")

    ship = Ship(screen)


    #开始游戏的主循环
    while  True:
        #监视键盘和鼠标事件
        # for event in pygame.event.get():
        #   if event.type == pygame.QUIT:
        #       sys.exit()
        gf.check_event(ship)
        gf.update_screen(ai_settings,screen,ship)



run_game()

允许不断移动

  • 结合使用KEYDOWN和KRYUP事件,以及一个moving_right的标识来实现持续运动。

  • 飞船不动是,标识moving_right将为False。玩家按下右箭头键时,将moving_right标识设置为True.而玩家松开时。标识重新设置为False.

  • 飞船的属性是由Ship类控制的,将这个类添加一个名为moving_right的属性和一个update()的方法。

  • ship.py

import pygame

class Ship():

    def __init__(self,screen):
        """初始化飞船并设置初始位置"""

        self.screen = screen

        # 加载飞船图像并获取外接矩形
        self.image = pygame.image.load(r'D:\study_note\python_study\alien_invasion\images\ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        self.moving_right = False
        self.moving_left = False

        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

    def update(self):
        """根据移动标识来调整飞船的位置"""
        if self.moving_right:
            self.rect.centerx += 1

        elif self.moving_left:
            self.rect.centerx -= 1

    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image,self.rect)
  • game_functions.py
import sys

import pygame

def check_event(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.moving_right = True

            elif event.key == pygame.K_LEFT:
                ship.moving_left = True

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False

            elif event.key == pygame.K_LEFT:
                ship.moving_left = False

def update_screen(ai_settings,screen,ship):
    """更新屏幕上的图像,并切换新屏幕"""
    #每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    ship.blitme()

    #让最近绘制的屏幕可见
    pygame.display.flip()

  • alien_invasion.py

    import sys
    
    import pygame
    
    from settings import Settings
    
    from ship import Ship
    
    import game_functions as gf
    
    def  run_game():
      #初始化游戏并创建一个屏幕对象
      ai_settings = Settings()
      pygame.init()
      screen = pygame.display.set_mode(
          (ai_settings.screen_width,ai_settings.screen_heigth))
      pygame.display.set_caption("ALien Invasion")
    
      #设置背景色
    # bg_cokor = ai_settings.bg_color()
    
      #创建一艘飞船
      ship = Ship(screen)
    
    
      #开始游戏的主循环
      while  True:
    
          gf.check_event(ship)
          ship.update()
          gf.update_screen(ai_settings,screen,ship)
    
    
    run_game()
    

调整飞船的速度

  • 在Settings类中添加属性ship_speed_factor,用于控制飞船的速度。

  • Settings.py

class Settings():
    """存储《外星人入侵》游戏的所有设置类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200
        self.screen_heigth = 800
        self.bg_color = (230,230,230)
        self.ship_speed_factor = 1.5
  • 将ship_speed_factor设置为1.5,但是rect的centerx等属性只能存储正数。

  • 对ship.py修改

import pygame

class Ship():

    def __init__(self,ai_settings,screen):
        """初始化飞船并设置初始位置"""

        self.screen = screen

        # 加载飞船图像并获取外接矩形
        self.image = pygame.image.load(r'D:\study_note\python_study\alien_invasion\images\ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        self.moving_right = False
        self.moving_left = False
        self.ai_settings = ai_settings

        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

        #将飞船的属性center中存储小数值
        self.center = float(self.rect.centerx)

    def update(self):
        """根据移动标识来调整飞船的位置"""
        if self.moving_right:
            self.center += self.ai_settings.ship_speed_factor

        elif self.moving_left:
            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)
  • 最后修改alien_invasion.py
import sys

import pygame

from settings import Settings

from ship import Ship

import game_functions as gf

def  run_game():
    #初始化游戏并创建一个屏幕对象
    ai_settings = Settings()
    pygame.init()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_heigth))
    pygame.display.set_caption("ALien Invasion")

    #设置背景色
#   bg_cokor = ai_settings.bg_color()

    #创建一艘飞船
    ship = Ship(ai_settings,screen)


    #开始游戏的主循环
    while  True:
        #监视键盘和鼠标事件
        # for event in pygame.event.get():
        #   if event.type == pygame.QUIT:
        #       sys.exit()
        gf.check_event(ship)
        ship.update()
        gf.update_screen(ai_settings,screen,ship)

        # #每次循环时都重绘屏幕
        # screen.fill(ai_settings.bg_color)
        # ship.blitme()

        # #让最近绘制的屏幕可见
        # pygame.display.flip()

run_game()

限制飞船的活动范围

  • 修改ship类的方法update()

  • ship.py

import pygame

class Ship():

    def __init__(self,ai_settings,screen):
        """初始化飞船并设置初始位置"""

        self.screen = screen

        # 加载飞船图像并获取外接矩形
        self.image = pygame.image.load(r'D:\study_note\python_study\alien_invasion\images\ship.bmp')
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()
        self.moving_right = False
        self.moving_left = False
        self.ai_settings = ai_settings

        # 将每艘新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom

        #将飞船的属性center中存储小数值
        self.center = float(self.rect.centerx)

    def update(self):
        """根据移动标识来调整飞船的位置"""
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor

        elif 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)

  • self.rect.right返回飞船外接矩形的右边缘的x坐标,如果这个值小于self.screen_rect.right的值,说明飞船未碰及屏幕右边缘

重构check_events()

  • 将其部分代码放在两个函数中:一个处理KEYDOWN事件,一个处理KEYUP 事件。
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_event(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()

射击

  • 添加射击功能

添加子弹设置

  • 更新settings,在init()末尾存储所需的值
class Settings():
    """存储《外星人入侵》游戏的所有设置类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200
        self.screen_heigth = 800
        self.bg_color = (230,230,230)
        self.ship_speed_factor = 1.5
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60.60.60

创建bullet类

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """对飞船发射的子弹进行管理的类"""
    
    def __init__(self, ai_settings,screen,ship):
        super(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
        #表示子弹的位置
        self.rect.y = self.y

    def draw_bullet(self):
        """在屏幕上绘制子弹"""
        pygame.draw.rect(self.screen,self.color,self.rect)
        
  • Bullet类继承了我们模块pygame.sprite中导入的sprite类,可以将游戏中相关的元素编组,然后可以操作组中的所有元素。
  • 函数draw.rect()用于存储self.color中的颜色填充表示子弹的rect占据屏幕的位置。

将子弹存储在编组中。

  • 让玩家每次按空格的时候发射子弹
import sys

import pygame

from settings import Settings

from ship import Ship

import game_functions as gf

from pygame.sprite import Group

def  run_game():
    #初始化游戏并创建一个屏幕对象
    ai_settings = Settings()
    pygame.init()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_heigth))
    pygame.display.set_caption("ALien Invasion")

    #设置背景色
#   bg_cokor = ai_settings.bg_color()

    #创建一艘飞船
    ship = Ship(ai_settings,screen)

    #创建一个用于存储子弹的编组
    bullets = Group()


    #开始游戏的主循环
    while  True:
        #监视键盘和鼠标事件
        # for event in pygame.event.get():
        #   if event.type == pygame.QUIT:
        #       sys.exit()
        gf.check_event(ai_settings,screen,ship,bullets)
        ship.update()
        bullets.update()
        gf.update_screen(ai_settings,screen,ship,bullets)

        # #每次循环时都重绘屏幕
        # screen.fill(ai_settings.bg_color)
        # ship.blitme()

        # #让最近绘制的屏幕可见
        # pygame.display.flip()

run_game()

开火

  • 修改game_functions.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:
        #创建一个子弹,并将其加入编组bullets中
        new_bullet = Bullet(ai_settings,screen,ship)
        bullets.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_event(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():
        bullet.draw_bullet()

    ship.blitme()

    #让最近绘制的屏幕可见
    pygame.display.flip()

删除已经消失的子弹

  • 子弹到达屏幕顶端消失,仅仅是python在屏幕外无法绘制他们,这些子弹依然存在。他们会继续消耗内存和处理能力
import sys

import pygame

from settings import Settings

from ship import Ship

import game_functions as gf

from pygame.sprite import Group

def  run_game():
    #初始化游戏并创建一个屏幕对象
    ai_settings = Settings()
    pygame.init()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_heigth))
    pygame.display.set_caption("ALien Invasion")

    #设置背景色
#   bg_cokor = ai_settings.bg_color()
 
    #创建一艘飞船
    ship = Ship(ai_settings,screen)

    #创建一个用于存储子弹的编组
    bullets = Group()


    #开始游戏的主循环
    while  True:
        
        gf.check_event(ai_settings,screen,ship,bullets)
        ship.update()
        bullets.update()

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

        print(len(bullets))

        gf.update_screen(ai_settings,screen,ship,bullets)



run_game()

限制子弹的数量

  • 在setttings.py中存储所允许的最大的子弹数量
class Settings():
    """存储《外星人入侵》游戏的所有设置类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200
        self.screen_heigth = 800
        self.bg_color = (230,230,230)
        self.ship_speed_factor = 1.5
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60,60,60
        self.bullet_allowed = 3
  • 在game_functions,py的check_keydown_event(),在创建新子弹检查未消失的子弹的数量
class Settings():
    """存储《外星人入侵》游戏的所有设置类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200
        self.screen_heigth = 800
        self.bg_color = (230,230,230)
        self.ship_speed_factor = 1.5
        self.bullet_speed_factor = 1
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60,60,60
        self.bullets_allowed = 3

创建函数update_bullets()

  • 在game_gunctions.py添加
def update_bullets(bullets):
    #bullets.update()

    #删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
  • 主程序alien_invasion.py
import sys

import pygame

from settings import Settings

from ship import Ship

import game_functions as gf

from pygame.sprite import Group

def  run_game():
    #初始化游戏并创建一个屏幕对象
    ai_settings = Settings()
    pygame.init()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_heigth))
    pygame.display.set_caption("ALien Invasion")

    #设置背景色
#   bg_cokor = ai_settings.bg_color()
 
    #创建一艘飞船
    ship = Ship(ai_settings,screen)

    #创建一个用于存储子弹的编组
    bullets = Group()


    #开始游戏的主循环
    while  True:
        #监视键盘和鼠标事件
        # for event in pygame.event.get():
        #   if event.type == pygame.QUIT:
        #       sys.exit()
        gf.check_event(ai_settings,screen,ship,bullets)
        ship.update()
        bullets.update()

        #删除已经消失的子弹
        gf.update_bullets(bullets)

        print(len(bullets))

        gf.update_screen(ai_settings,screen,ship,bullets)

        # #每次循环时都重绘屏幕
        # screen.fill(ai_settings.bg_color)
        # ship.blitme()

        # #让最近绘制的屏幕可见
        # pygame.display.flip()

run_game()

创建函数fire_bullet()

  • 修改game_functions.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.bullets_allowed:
        new_bullet = Bullet(ai_settings,screen,ship)
        bullets.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_event(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():
        bullet.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)

总结

  • 使用pygame编写游戏的基本构建
  • 如何设置背景色,
  • 如何设置存储在可供游戏的各个部分访问的独立的类。
  • 如何在屏幕上绘制图形。
  • 如何让玩家控制游戏元素移动。
  • 如何创建自动移动的元素
  • 如何删除不需要的对象。
  • 如何定期重构代码。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容