您的位置:首页 > 编程语言 > Python开发

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

2017-03-24 16:04 232 查看
  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. 用户事件处理

  具体代码请根据注释自行研读。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  pycharm python pygame