美文网首页
python编程:从入门到实践#02 项目

python编程:从入门到实践#02 项目

作者: Liuxu01 | 来源:发表于2019-04-02 23:02 被阅读0次

项目1 外星人入侵

第12章 武装飞船

Pygame,是一组功能强大而有趣的模块,可用于管理图形、动画乃至声音,让你能够更轻松地开发复杂的游戏。
创建游戏是趣学语言的理想方式。

12.1 规划项目

开发大型项目时,做好规划后再动手编写项目很重要。规划可确保你不偏离轨道,从而提高项目成功的可能性。
编写有关游戏《外星人入侵》的描述:


image.png

12.2 安装Pygame

Pygame项目托管在代码分享网站Bitbucket中。
对应于Python3.7版本的pygame下载地址:
https://pypi.org/project/Pygame/#files

image.png
(牢骚:真心不易啊,折腾这么久。卸载了以前安装的python,重新下载Anaconda镜像,再设置pip,再下载pygame)
image.png

12.3 开始游戏项目

12.3.1 创建Pygame窗口以及相应用户输入

# -*- coding:gbk -*-
import sys

import pygame

def run_game():
    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    screen = pygame.display.set_mode((1200,800))
    pygame.display.set_caption("Alien Invasion")

    # 开始游戏的主循环
    while True:
    
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
        # 让最近绘制的屏幕可见
        pygame.display.flip()
    
run_game()

pygame.init()初始化背景设置,让pygame能够正确地工作。
事件是用户玩游戏时执行的操作,如按键或移动鼠标。为让程序响应事件,编写一个事件循环,以侦听事件,并根据发生的事件执行响应的方法。
最后run_game()初始化游戏并开始主循环。

12.3.2 设置背景色

pygame默认创建一个黑色屏幕。

# -*- coding:gbk -*-
import sys

import pygame

def run_game():
    # 初始化游戏并创建一个窗口
    pygame.init()
    screen = pygame.display.set_mode((1200,800))
    pygame.display.set_caption("Alien Invasion2")

    # 设置背景色
    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()

在pygame中,颜色是以RGB值指定的。(红色、绿色、蓝色值)
,每个值的可能取值范围都为0~255.颜色值(255,0,0)表示红色,(0,255,0)表示绿色,(0,0,255)表示蓝色。

12.3.3 创建设置类

# -*- coding:gbk -*-
class Settings():
    """存储《外星人入侵》的所有设置的类"""

    def __init__(self):
        """初始化游戏的设置"""
        # 屏幕设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230,230,230)

创建Settings实例并使用它来访问设置。

# -*- coding:gbk -*-
import sys
import pygame
from settings import Settings

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 Invasion3")

    # 开始游戏的主循环
    while True:
        # 监视键盘和鼠标事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
    
        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
    
        # 让最近绘制的屏幕可见
        pygame.display.flip()
    
run_game()

12.4 添加飞船图像

加载图像,再使用pygame方法blit()绘制。

  • 为游戏选择素材时,务必要注意许可。
  • 在游戏中几乎可以使用任何类型的图像文件,但使用位图(.bmp)文件最为简单,因为pygame默认加载位图。虽然可配置pygame以使用其他文件类型,但有些文件类型要求你在计算机上安装相应的图像库。
  • 选择图像时,要特别注意其背景色。请尽可能选择背景透明的图像,这样可使用图像编辑器将其背景设置为任何颜色。
    - END - 2019/4/2 22:52

12.4.1 创建Ship类

# -*- coding:gbk -*-
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.screen.blit(self.image,self.rect)

为加载图像,我们调用pygame.image.load(),这个函数返回一个表示飞船的surface,我们将这个surface存储到self.image中。
用get_rect()获取相应surface的属性rect。处理rect对象时,可使用矩形四角和中心的x/y坐标,通过设置这些值来指定矩形的位置。设置相应rect对象的属性:center、centerx、centery、top、bottom、left、right等。
注意:在pygame中,原点(0,0)位于屏幕左上角,向右下方移动时,坐标值将增大。在1200X800的屏幕上,原点位于左上角,而右下角的坐标为(1200,800)。

12.4.2 在屏幕上绘制飞船

# -*- coding:gbk -*-
import sys
import pygame
from settings import Settings
from ship import Ship

def run_game():
    # 初始化pygame、设置和屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
        (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion4")

    # 创建一艘飞船
    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()
不知道为什么飞船是在左下角,而不是中间?.png

问题已解决,原因在于ship设置中,self.rect.centerx误写成了self.rect_centerx


image.png

在创建屏幕后创建一个名为ship的Ship实例,必须在主while循环前面创建该实例,以免每次循环时都创建一艘飞船。填充背景后,调用ship.blitme()将飞船绘制到屏幕上,确保它出现在背景前面。

12.5 重构:模块game_functions

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

12.5.1 函数check_events()

game_functions.py

# -*- coding:gbk -*-
import sys
import pygame

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

导入事件检查循环要使用的sys和pygame。

# -*- coding:gbk -*-

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    # 初始化pygame、设置和屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
    (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion4_1")

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

    # 开始游戏主循环
    while True:
        gf.check_events()
    
        # 每次循环时都重绘屏幕
        screen.fill(ai_settings.bg_color)
        ship.blitme()
    
        # 让最近绘制的屏幕可见
        pygame.display.flip()
    
run_game()

在主程序文件中,不再需要直接导入sys,因为当前只在模块game_functions中使用了它。

12.5.2 函数update_screen()

# -*- coding:gbk -*-
import sys
import pygame

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

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

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

# -*- coding:gbk -*-

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    # 初始化pygame、设置和屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
    (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion4_1")

    # 创建一艘飞船
    ship = Ship(screen)
    # 开始游戏主循环
    while True:
        gf.check_events()
        gf.update_screen(ai_settings,screen,ship)
    
run_game()

12-1 蓝色天空:创建一个背景为蓝色的pygmae窗口

# -*- coding:gbk -*-
import sys
import pygame

def run_game():
    """初始化游戏,并创建一个屏幕"""
    pygame.init()
    screen = pygame.display.set_mode((800,600))
    pygame.display.set_caption("Blue Screen")

    # 设置背景色
    bg_color = (0,0,255)

    # 开始游戏主循环
    while True:
        """检测键盘和鼠标事件"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
    
        # 每次循环时都重绘屏幕
        screen.fill(bg_color)
    
        # 让最近绘制的屏幕可见
        pygame.display.flip()

run_game()
image.png

12.6 驾驶飞船

控制屏幕图像的移动呢

12.6.1 响应按键

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

import sys
import pygame

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

alien_invasion.py

    # 开始游戏主循环
    while True:
        gf.check_events(ship)
        gf.update_screen(ai_settings,screen,ship)

12.6.2 允许不断移动

ship.py

# -*- coding:gbk -*-
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
    
        # 移动标志
        self.moving_right = False
    
    def update(self):
        """根据移动标志调整飞船的位置"""
        if self.moving_right:
            self.rect.centerx += 1
    
    def blitme(self):
        """在指定位置绘制飞船"""
        self.screen.blit(self.image,self.rect)

game_functions.py

# -*- coding:gbk -*-
import sys
import pygame

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.moving_right = True
    
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_RIGHT:
                ship.moving_right = False

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

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

alien_invasion.py

# -*- coding:gbk -*-

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    # 初始化pygame、设置和屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode(
    (ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion4_1")

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

    # 开始游戏主循环
    while True:
        gf.check_events(ship)
        ship.update()
        gf.update_screen(ai_settings,screen,ship)
            
run_game()

12.6.3 左右移动

image.png
image.png

12.6.4 调整飞船的速度

image.png
image.png
image.png

12.6.5 限制飞船的活动范围

 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

12.6.6 重构check_events()

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)

12.7 简单回顾

12.7.1 alien_invasion.py

12.7.2 settings.py

12.7.3 game_functions.py

12.7.4 ship.py

12.8 射击

编写玩家按空格键时发射子弹(小矩形)的代码。子弹将在屏幕中向上穿行,抵达屏幕上边缘后消失。

12.8.1 添加子弹设置

def __init__(self):
    --snip--
    # 子弹设置
    self.bullet_speed_factor = 1
    self.bullet_width = 3
    self.bullet_height = 15
    self.bullet_color = (60,60,60)

12.8.2 创建Bullet类

image.png

Bullet类继承了我们从模块pygame.sprite中导入的Sprite类。


image.png

12.8.3 将子弹存储到编组中

创建一个编组(group)

image.png

12.8.4 开火

image.png image.png

12.8.5 删除已消失的子弹

当子弹抵达屏幕顶端后消失,这仅仅是因为pygame无法在屏幕外面绘制他们。这些子弹实际上依然存在,它们的y坐标为负数,且越来越小。这是个问题,因为他们将继续消耗内存和处理能力。
需要将已消失的子弹删除,否则游戏所做的无谓工作将越来越多,进而变得越来越慢。

image.png

在for循环中,不应从列表或编组中删除条目,因此必须遍历编组的副本。

12.8.6 限制子弹数量

image.png
image.png

12.8.7 创建函数update_bullets()

image.png
image.png

12.8.8 创建函数fire_bullet()

image.png
- END - 2019/4/6 15:17

相关文章

网友评论

      本文标题:python编程:从入门到实践#02 项目

      本文链接:https://www.haomeiwen.com/subject/wjobbqtx.html