Python游戏系列之四_发射子弹

  Python游戏系列之四_发射子弹


  在上一讲中,我们已经做到了飞机的自由移动,但还有一点小问题,就是飞机可能会移到屏幕外。

  我们来加上边界检测的代码,使程序更严谨:

    x = hero_pos[0] + offset[pygame.K_RIGHT] - offset[pygame.K_LEFT]
    y = hero_pos[1] + offset[pygame.K_DOWN] - offset[pygame.K_UP]

    # 判断x坐标是否左边或右边越界
    if x < 0:
        x = 0
    elif x > SCREEN_WIDTH - hero_rect1.width:
        x = SCREEN_WIDTH - hero_rect1.width
    else:
        x = x

    # 判断y坐标是否上边或下边越界
    if y < 0:
        y = 0
    elif y > SCREEN_HEIGHT - hero_rect1.height:
        y = SCREEN_HEIGHT - hero_rect1.height
    else:
        y = y

    hero_pos[0] = x
    hero_pos[1] = y

  在本节中,我们将让飞机发射子弹。

  主要规则如下:

  1. 我方飞机会自动发射子弹,发射频率是固定的。

  2. 子弹方向自下向上,移动速度固定。

  3. 子弹超出屏幕显示范围,则消失。

  4. 本节暂不考虑子弹击中敌机的问题。


  一、技术铺垫

  本节中会用到Python面向对象编程的知识,所以先介绍一下Python中的“类”。

  学过Java的同学,应该会比较容易接受Python中“类”的概念。

  先来看一段代码:

#!/usr/bin/python
# -*- coding: UTF-8 -*-


# 定义一个学生类
class Student:

    # 构造方法1
    def __init__(self):
        self.name = 'Unknown'
        self.age = 0

    # 构造方法2
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 普通方法
    def detail(self):
        print 'Name: ', self.name
        print 'Age: ', self.age

# 创建Student类的对象,并调用构造方法2
s1 = Student('Lily', 16)
# 调用普通方法
s1.detail()

  与Java语言差别较大的,就是在定义每个方法时,都有一个名为“self”的参数。

  实际上,在调用方法时,会把当前对象传递给形参”self“。

  例如:s1.detail(),这时会把s1对象传递给”self“形参。

  “self”的用法,相当于Java语言中的”this“。

  __init__()方法是构造方法,可以重载。


  二、精灵类

  ”精灵(Sprite)“,是游戏编程的术语,用于表示在背景上可以自由移动的一个图形块。

  在我们的游戏中,有我方飞机,有敌方飞机,有无数子弹,如果什么都要我们处理,那就太麻烦了。

  在pygame.sprite模块中,精灵类(Sprite)提供了很多常用功能,可以让我们自己的类(如飞机、子弹)继承它。

  精灵组类(Group)可以将多个精灵构成一组,适用于子弹的管理。


  精灵组的使用示例:

# 创建精灵组
group = pygame.sprite.Group()
# 向精灵组中添加精灵
group.add(精灵)
# 更新精灵组,此时会调用每个精灵的update()方法
group.update()
# 将精灵组绘制在surface上
group.draw(screen)

  三、源代码

  下面是重构后的代码,由于引入了类,所以代码有较大的变动:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import pygame
from sys import exit


# 我方飞机类,继承自Sprite类
class Hero(pygame.sprite.Sprite):
    # 构造方法,参数分别是我方飞机图片和起始坐标
    def __init__(self, hero_surface, hero_init_pos):
        # 调用父类的构造方法
        pygame.sprite.Sprite.__init__(self)
        # 设置属性
        self.image = hero_surface  # image属性:我方飞机图片
        self.rect = self.image.get_rect()  # rect属性:矩形
        self.rect.topleft = hero_init_pos  # 矩形左上角坐标
        self.speed = 6  # speed属性:我方飞机移动速度,比上例调快了一倍
        self.bullets = pygame.sprite.Group()  # bullets属性:子弹组,使用精灵组

    # 移动方法,参数是offset列表
    def move(self, offset):
        # 计算新的x、y坐标
        x = self.rect.left + offset[pygame.K_RIGHT] - offset[pygame.K_LEFT]
        y = self.rect.top + offset[pygame.K_DOWN] - offset[pygame.K_UP]
        # 对rect属性赋值,调整我方飞机位置
        # 同时防止我方飞机越出边界
        if x < 0:
            self.rect.left = 0
        elif x > SCREEN_WIDTH - self.rect.width:
            self.rect.left = SCREEN_WIDTH - self.rect.width
        else:
            self.rect.left = x
        if y < 0:
            self.rect.top = 0
        elif y > SCREEN_HEIGHT - self.rect.height:
            self.rect.top = SCREEN_HEIGHT - self.rect.height
        else:
            self.rect.top = y

    # 发射子弹方法,参数为子弹图片
    def shoot(self, bullet_surface):
        # 子弹初始位置在我方飞机的上方居中位置
        bullet = Bullet(bullet_surface, self.rect.midtop)
        # 将子弹添加到子弹组中
        self.bullets.add(bullet)


# 子弹类,继承自Sprite类
class Bullet(pygame.sprite.Sprite):
    # 构造方法,参数分别是子弹图片和起始位置
    def __init__(self, bullet_surface, bullet_init_pos):
        # 调用父类的构造方法
        pygame.sprite.Sprite.__init__(self)
        # 设置属性
        self.image = bullet_surface  # image属性:子弹图片
        self.rect = self.image.get_rect()  # rect属性:矩形
        self.rect.topleft = bullet_init_pos  # 矩形左上角坐标
        self.speed = 8  # speed属性:子弹移动速度

    # 移动方法
    def update(self):
        # 修改子弹坐标
        self.rect.top -= self.speed
        # 如果子弹移出屏幕上方,则销毁子弹对象
        if self.rect.top < -self.rect.height:
            self.kill()

# 屏幕宽、高
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640

# 游戏帧率
FRAME_RATE = 60

# 动画周期
ANIMATE_CYCLE = 30

# 创建游戏窗口
pygame.init()
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
pygame.display.set_caption('飞机游戏')

# 加载图片资源
bg = pygame.image.load('bg1.jpg')  # 背景图片
shoot_img = pygame.image.load('shoot.png')  # 游戏资源图片
hero_surface = list()
hero_surface.append(shoot_img.subsurface(pygame.Rect(0, 99, 102, 126)))  # 我方飞机图片1
hero_surface.append(shoot_img.subsurface(pygame.Rect(165, 360, 102, 126)))  # 我方飞机图片2
bullet_surface = shoot_img.subsurface(pygame.Rect(1004, 987, 9, 21))  # 子弹图片

# 创建我方飞机对象
hero_pos = [200, 500]
hero = Hero(hero_surface[0], hero_pos)

# 其它变量
ticks = 0  # 计数器
offset = {pygame.K_LEFT: 0, pygame.K_RIGHT: 0, pygame.K_UP: 0, pygame.K_DOWN: 0}  # 我方飞机移动值
clock = pygame.time.Clock()  # 时钟

while True:
    # 控制游戏帧率
    clock.tick(FRAME_RATE)

    # 改变我方飞机图片,以产生动画效果
    ticks += 1
    if ticks >= ANIMATE_CYCLE:
        ticks = 0
    hero.image = hero_surface[ticks // (ANIMATE_CYCLE // 2)]

    # 我方飞机发射子弹
    # 每10帧发射1次子弹
    if ticks % 10 == 0:
        hero.shoot(bullet_surface)
    # 子弹移动
    hero.bullets.update()  # 精灵组update时,会调用所有精灵的update方法

    # 绘制背景
    screen.blit(bg, [0, 0])
    # 绘制我方飞机
    screen.blit(hero.image, hero.rect)
    # 绘制子弹
    hero.bullets.draw(screen)  # 精灵组draw时,会将所有精灵绘制在surface上
    # 更新屏幕
    pygame.display.update()

    # pygame.event.get()方法可以从“事件队列”中,得到所有的事件
    for event in pygame.event.get():
        # 退出事件
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()
        # 键盘按下事件
        if event.type == pygame.KEYDOWN:
            if event.key in offset:
                offset[event.key] = hero.speed  # 我方飞机的移动值为speed属性值
        # 键盘松开事件
        if event.type == pygame.KEYUP:
            if event.key in offset:
                offset[event.key] = 0
    # 根据我方飞机的移动值,移动飞机
    hero.move(offset)

  在程序的开始部分,定义了一个“我方飞机”类和“子弹”类,其中“我方飞机”类中定义了一个名为bullets的属性,它是一个精灵组,存储了所有子弹。


  在游戏循环中主要做了这三件事情:

  1. 更新游戏数据(例如定时产生子弹、子弹定时移动、我方飞机的图片切换以产生动画效果等)

  2. 绘制游戏界面(第1步的更新游戏数据只是修改变量的值,这一步是根据变量的值绘制界面)

  3. 用户事件处理


  具体代码请根据注释自行研读。


  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值