游戏 | 飞机大战

0. 运行结果

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

1. 目标

Python面向对象;Pygame模块;游戏开发;飞机大战

2. 知识准备

2.1 创建图形窗口

(1)游戏的初始化和退出

pygame.init()  # 导入并初始化所有pygame模块
pygame.quit()  # 卸载所有pygame模块

(2)游戏坐标系

原点(0,0)在左上角

x轴水平向右,逐渐增加。

y轴水平向下,逐渐增加。

所有可见的元素都是以矩形区域描述的。(x,y)(width,height)

# pygame提供的Rect类用于描述矩形区域。
Rect(x,y,width,height)

例子:
hero_rect = pygame.Rect(100,500,120,125)
print("英雄的原点:%d %d " % (hero_rect.x,hero_rect.y))
print("英雄的尺寸:%d %d" % hero_rect.size)

(3)创建游戏主窗口

# pygame提供的display模块用于创建、管理游戏窗口
pygame.display.set_mode()   # 初始化游戏显示窗口
pygame.display.updata()     # 刷新屏幕内容显示,稍后使用

例子:
# 创建主窗口
screen = pygame.display.set_mode((512, 768))

# 1.创建游戏窗
self.screen = pygame.display.set_mode((SCREEN_RECT.width+200, SCREEN_RECT.height))   
pygame.display.set_caption('飞机大战')   # 窗口标题
self.screen.fill(pygame.Color("lightskyblue"))   # 背景填充颜色

2.2 图像绘制

在屏幕看到一个图像的内容:

① 加载:使用 pygame.image.load() 加载图像的数据

② 绘制:使用游戏屏幕对象screen,调用 blit() 方法,将图像绘制在指定位置。

③ 显示:调用 pygame.diaplay.update() 方法更新屏幕显示

## 例子1 绘制背景图和英雄飞机
import pygame
pygame.init()

screen = pygame.display.set_mode((512, 768))        # 创建主窗口

bg = pygame.image.load("./images/bg/bg2.jpg")        # 加载
screen.blit(bg, (0, 0))                              # 绘制 
### 注:blit()方法的第二个元素可以传入一个元组,也可以是一个矩形对象hero_Rect

hero = pygame.image.load("./images/hero/hero_b_04.png")
screen.blit(hero, (200, 600))
pygame.display.update()                              # 最后统一更新显示

while True:
    pass
pygame.quit()

附加1:
绘制文字:

myfont = pygame.font.Font(None, 48, bold=False, italic=False)   # 字体(字体、大小、粗体、斜体)
txt = myfont.render("pygame", True, (255, 0, 0))  # 内容(文字、取消锯齿、颜色)
screen.blit(txt, (10, 100))    # 绘制(txt、位置)

注意:
print(pygame.font.get_fonts())     # 查找当前电脑的字体列表

附加2:
pygame的颜色定义(R,G,B)

(255, 255, 255)  # 白色
(0, 0, 0)        # 黑色
(255, 0, 0)      # 红色
(0, 255, 0)      # 绿色
(0, 0, 255)      # 蓝色
(255, 128, 0)    # 橘色
(128, 128, 128)  # 灰色

2.3 游戏循环和游戏时钟

(1)动画原理

多张静止的照片连续、快速的播放,产生连贯的视觉效果。

每次绘制的结果称为帧,也就是每次执行pygame.diaplay.update()的结果。

一般在电脑每秒绘制60次,就能够达到高品质的动画效果。

(2)游戏循环 ----> 游戏的开始

游戏的两个组成部分:

游戏初始化游戏循环
设置游戏窗口设置刷新帧率
绘制图像初始位置检测用户交互
设置游戏时钟更新所有图像位置
更新屏幕显示

(3)游戏时钟 —> 控制游戏循环的速度

pygame.time.Clock可以设置刷新帧率。要使用时钟对象需要两步:

在游戏初始化设置一个时钟对象;游戏循环中让时钟对象调用tick(帧率)方法。
(tick方法可以根据上次被调用的时间,自动设置游戏循环中的延时)

clock = pygame.time.Clock()
clock.tick(60)           # 游戏循环内部的代码,每秒钟执行60次

(4)监听事件

事件:游戏启动后,用户对游戏所作的操作。点击鼠标,点击关闭按钮,按下按键。

监听:捕获用户的具体操作,做出针对性的反应。

## pygame中提供的 pygame.event.get() 可以捕获用户当前所有操作的事件列表

# 以下代码非常固定,适用于编写,退出所有pygame游戏。
while True:
    # 设置帧率
    clock.tick(60)
    # 监听事件
    for event in pygame.event.get():
        # 判断用户是否点击了关闭按钮
        if event.type == pygame.QUIT:
            print("游戏退出...")
            pygame.quit()                # 卸载pygame模块
            exit()                       # 终止当前程序,退出系统

(5)英雄的简单动画

实现英雄飞机从底部飞到顶部,再回到底部,再飞往顶部,如此循环。

提示:每一次调用update()方法之前,需要把需要的游戏图像都重新绘制一遍。而且要最先绘制背景图像,消除残影。

import pygame

###################################### 1.游戏初始化 ###############################

pygame.init()                                 # 初始化pygame模块
screen = pygame.display.set_mode((512, 768))  # 创建主窗口

# 图片的加载与绘制
bg = pygame.image.load("./images/bg/bg2.jpg")
screen.blit(bg, (0, 0))
hero = pygame.image.load("./images/hero/hero_b_04.png")
screen.blit(hero, (200, 600))
pygame.display.update()

# 设置游戏时钟
clock = pygame.time.Clock()

# 设置飞机初始位置
hero_rect = pygame.Rect(200, 600, 122, 105)

######################################## 2.游戏循环 ##################################

while True:
    # 1. 设置帧率
    clock.tick(60)
    # 2. 监听事件
    for event in pygame.event.get():
        # 判断用户是否点击了关闭按钮
        if event.type == pygame.QUIT:
            print("游戏退出...")
            pygame.quit()                # 卸载pygame模块
            exit()                       # 退出系统
    # 3. 修改飞机位置
    hero_rect.y -= 5
    if hero_rect.y <= 0-hero_rect.height:
        hero_rect.y = 768
    # 4. 调用 blit方法绘制
    screen.blit(bg, (0, 0))        # 为了消除残影,应在绘制新飞机之前,重新绘制背景图
    screen.blit(hero, hero_rect)
    # 5. update更新显示
    pygame.display.update()

pygame.quit()         # 卸载pygame模块

2.4 精灵和精灵组(两个高级类)

(1)概念

上述案例,图像加载、绘制和位置变化,需要程序员编写大量代码。为了简化开发程序,pygame提供了两个类:

① 精灵:pygame.sprite.Sprite ---- 存储图像数据、位置的对象
② 精灵组:pygame.sprite.Group ----包含 多个精灵的对象
在这里插入图片描述

精灵(需要派生子类)
image记录图像数据
rect记录图像位置
update()更新精灵位置
kill()从所有组中删除
精灵组
__init __(self,*精灵):
add(*sprites)向组中增加精灵
sprites()返回所有精灵列表
update()让组中所有的精灵调用各自的update方法
draw(screen)将组中所有精灵的图像,各自绘制到屏幕的rect位置上

所以,游戏可以分为当前的两部分:

游戏初始化游戏循环
创建精灵精灵组.update()
创建精灵组精灵组.draw(screen)
pygame.display.update()

(2)派生精灵子类

GameSprite 继承自 pygame.sprite.Sprite

GameSprite
属性image、rect、speed
方法__init __(self,image_name,speed=1) 、update(self)

注意:

在重写__init __方法的时候,应该super()一下继承父类的__init __方法。

image 的 get_rect() 方法,可以返回 pygame.Rect(0,0,图像宽,图像高)

(3)创建敌机

## 1. 初始化中:

# 创建敌机的精灵
enemy = GameSprite("./images/enemy/enemy.png")
enemy1 = GameSprite("./images/enemy/enemy11.png", speed=2)
# 创建敌机的精灵组
enemy_group = pygame.sprite.Group(enemy,enemy1)

# 2. 游戏循环中:

# 让敌机精灵组调用两个方法
    enemy_group.update()          # 让组中的所有精灵更新位置
    enemy_group.draw(screen)      # 绘制精灵组中的所有的精灵

附加:在另一个文件新建精灵类,在主文件中导入,创建精灵。

from sprites import Player
square = Player()
group = pygame.sprite.Group(square)

3. 编写游戏代码

3.1 搭建游戏框架

设计 PlaneGame 类如下:

游戏初始化游戏循环
设置游戏窗口设置刷新帧率
创建游戏时钟事件监听
创建精灵、精灵组碰撞检测
更新绘制精灵组
更新屏幕显示
属性方法
screen__init __(self):完成所有的游戏初始化动作
clock__create__sprites(self)
精灵组或精灵__start_game(self):开启整个游戏循环
__check_collide(self) :碰撞检测
__event_handler(self):事件监听
__update_sprites(self) :更新/绘制图像
__game_over:游戏结束

① 由初始化方法完成游戏的初始化动作:使用__init __方法完成游戏初始化的全部内容,调用其中的私有方法__create_sprites()创建精灵或精灵组。

3.2 实现飞机大战主游戏类

(1)文件

plan_main:

  1. 封装主游戏类
  2. 创建游戏对象
  3. 启动游戏
import pygame
from plane_sprites import *

# 封装主游戏类
class PlaneGame(object):
    """飞机大战主游戏"""
    def __init__(self):
        print("游戏初始化")

    def start_game(self):
        print("游戏开始...")

if __name__ == '__main__':

    # 创建游戏对象
    game = PlaneGame()
    # 启动游戏
    game.start_game()

plane_sprites:

  1. 封装游戏中所有需要使用的精灵子类
  2. 提供游戏的相关工具

注意:
① 一般设置超参数(每个字母都大写),在提供工具的palne_sprites文件中。

3.3 游戏背景

原理:两张相同的背景图像的交替向下滚动

(1)设计背景类:

# 由GameSprite派生出背景类
class BackGround(GameSprite):
    """游戏背景精灵"""
    def __init__(self, is_alt=False):     # is_alt判断是否为替换背景图像

        # 1.调用父类方法实现背景精灵的创建(加载图像、位置、速度)
        super().__init__("./images/bg/bg2.jpg")

        # 2.判断是否为交替图像,设置初始位置
        if is_alt:
            self.rect.y = -self.rect.height

    def update(self):                             # 重写update方法
        # 1.调用父类方法,实现向下移动
        super().update()
        # 2.判断是否移出屏幕,将图像设置到屏幕上方
        if self.rect.y >= SCREEN_RECT.height:
            self.rect.y = -self.rect.height

(2)设置对象

    def __create_sprites(self):
        # 1.创建背景精灵和精灵组
        bg1 = BackGround()
        bg2 = BackGround(is_alt=True)
        self.back_group = pygame.sprite.Group(bg1, bg2)

3.4 敌机

(1)原理:使用定时器添加敌机。

敌机规律:

  1. 每隔1秒出现1架飞机
  2. 飞机向下移动,速度不相同
  3. 飞机出现的水平位置也不相同
  4. 从屏幕底部飞出后,不再返回,销毁。

(2)定时器(每隔一段时间,去执行一些动作)

## 在pygame中使用 pygame.time.set_timer()添加定时器
set_timer(eventid, milliseconds)  # 事件代号,间隔时间(毫秒)

步骤:

① 定义定时器常量 – eventid

② 初始化中,调用set_timer方法设置定时器事件

③ 游戏循环中,监听游戏事件

说明:

① 使用定时器可以间隔一定的时间创建事件(代号),然后我们在事件监听中捕获该事件,执行相应操作。

② eventid 基于常量 pygame.USEREVENT 来指定

③ 捕获:pygame.event.get()获取当前所有的事件列表,遍历列表,判断event.type == eventid 。

(3)设计敌机类

说明:

导入库的顺序:

import random     # 导入官方标准模块
import pygame     # 导入第三方模块
                  # 导入应用程序模块

(4)敌机销毁

精灵中的kill()方法,可以将精灵从所有精灵组中删除。

__del __内置方法会在对象被销毁前自动调用,可以用来判断敌机是否已经销毁。

3.5 英雄飞机

要点:

(1)捕获按键的两种方式:

① 事件监听

for event in pygame.event.get():       # 返回当前时刻所有的事件列表
	if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT
		print("向右移动...")

此方法的效果,是按下一次,捕获一次;连续一直按着按键,只能捕获一次。

② 使用键盘提供的方法,获取键盘按键

# 使用键盘提供的方法,获取键盘按键 - 返回按键元组
 keys_pressed = pygame.key.get_pressed()
 # 判断元组中对应的按键索引值
 if keys_pressed[pygame.K_RIGHT]:
 	print("向右移动...")

此方法的效果,是连续一直按着按键,也会连续捕获。

(2)控制英雄边界,不离开屏幕,需对x进行限制,不能在初始化中编写,要在update方法中。

(3)矩形区域 rect 的几个因素

pygame.rect
x(原点横坐标)y(原点纵坐标)
left(矩形左边界)right(矩形右边界)
centerx(矩形水平中心线)centery(矩形垂直中心线)
size(元组(width,height))width(矩形宽度)
height(矩形高度)center

(4)两个定时器常量

定义定时器的常量需要基于pygame.USEREVENT。若出现多个定时器时,常量的定义,可以依次+1。

# 创建敌机的定时器常量
ENEMY_EVENT = pygame.USEREVENT
# 英雄发射子弹定时器常量
HERO_FIRE = pygame.USEREVENT + 1

(5)同一页的类,也能够被用在其他类的方法中。

3.6 碰撞检测

pygame提供了两个非常方便的实现碰撞检测的方法:

① 两个精灵组中的精灵发生碰撞:

pygame.sprite.groupcollide(group1,group2,dokill1,dikill2,collied=None)

说明:

dokill1和dokill2都是布尔型变量。group1和group2发生碰撞,当dokill1为True时,group1被自动移除;同理,当dokill2为True时,group2被自动移除。

② 某个精灵和精灵组中的精灵发生碰撞:

pygame.sprite.spritecollide(sprite,group,dokill,collided=None) 返回一个与英雄碰撞的敌机列表

说明:

dokill为True时,碰撞时,group自动销毁;为False,碰撞时,group安然无恙。

③ 某个精灵和某个精灵发生碰撞:

pygame.sprite.collide_mask(sprite1,sprite2)   # 如果发生碰撞,则返回True

例子:
for flag_zombie in self.flag_zombie_group:
    if pygame.sprite.collide_mask(bullet, flag_zombie):
        self.peashooter_bullet.remove(bullet)
        flag_zombie.energy -= 1

④ 缩小矩形比例的检测

这个函数还有一个非常有用的变体:pygame.sprite.collide_rect_ratio()。这个函数需要一个额外的浮点类型的参数。这个参数用来指定检测矩形的百分比。

有的时候我们希望冲突检测更精准一些的话,就可以收缩检测的区域,让矩形更小一些,就是通过这个参数控制的。使用方法如下:

result = pygame.sprite.collide_rect_ratio( 0.5 )(sprite_1,sprite_2)

3.7 添加BGM

在主程序中加入:

import pygame							# 导入pygame资源包
file=r'E:\Python_Exercise\123.mp3'		# 音乐的路径
pygame.mixer.init()						# 初始化
pygame.mixer.music.load(file)	# 加载音乐文件
pygame.mixer.music.play()				# 开始播放音乐流
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值