前言
飞船模块的功能基本已经完成。今天继续完成子弹模块的功能。
子弹模块
子弹和飞船模块,在游戏逻辑中有一种生成与被生成的表面关系,因为子弹在游戏中是由飞船发射的。但是在我们实际抽象的过程中,飞船与子弹并不是is
的关系,甚至可以说不是has
的关系。因此我们需要将两个对象封装到不同模块来完成我们的功能。
考虑子弹是由飞船’'生成",我们在子弹对象生成渲染的时候,应该将飞船实时位置传递到子弹对象中。
此外,相比于飞船,子弹是需要一直生成的,因此屏幕可以存在多个子弹,我们需要一些容器对子弹进行管理。同时子弹的移动不像飞船到了边界就要停止,通常来说子弹触碰到窗口边界的时候子弹就应该消失,可以直接不管让子弹移动到窗口外不被加载,也可以让子弹在到达边界时自己销毁。
bullet
bullet.py
模块的基础信息我们在最开始就写好了,现在我们需要根据我们实际的游戏业务进行填充。
import pygame
class Bullet:
def __init__(self, setting, screen, ship):
super(Bullet, self).__init__()
self.screen = screen
# 创建子弹矩形
self.rect = pygame.Rect(0, 0, setting.bullet_width, setting.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
def move(self):
"""向上移动子弹"""
pass
首先是介绍子弹的属性以及值。
self.screen
游戏窗口信息,这个不多做介绍,毕竟子弹是在窗口上显示的。self.rect
子弹矩形,我们可以将子弹理解为一个实心矩形,并且通过系统设置文件配置子弹的长度和宽度。self.rect.centerx
这个相信大家已经很熟了,子弹矩形的中心x坐标是飞船中心x坐标。self.rect.top
这个应该也不陌生,子弹矩形的顶部,设置等于飞船的顶部。这样就会有一种子弹从飞船出来的感觉。大家也可以改造一下定义一个self.rect.bottom
的属性,至于值,大家自己发挥,很简单的我不写出来了。
填充业务代码
上色
我们需要个给子弹上一个颜色!
self.color = setting.bullet_color
移动
子弹是自行移动的,所以我们的移动函数并不需要按键控制,只要每次渲染的时候调用一下move
函数即可。
def move(self):
"""向上移动子弹"""
# 更新子弹位置
self.rect.y -= 1
生成子弹
我们需要一个新的函数,在调用这个函数的时候,飞船的上方出现一个子弹。也就是我们说的发射子弹的函数。
函数比较简单,就是在窗口中绘制一个矩形就行。
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen, self.color, self.rect)
至此,我们的子弹模块已经填充好了。
import pygame
class Bullet:
def __init__(self, setting, screen, ship):
super(Bullet, self).__init__()
self.screen = screen
# 创建子弹矩形
self.rect = pygame.Rect(0, 0, setting.bullet_width, setting.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# 子弹颜色
self.color = setting.bullet_color
def move(self):
"""向上移动子弹"""
# 更新子弹位置
self.rect.y -= 1
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen, self.color, self.rect)
发射子弹
在编写这部分代码以前,我们需要理解一下信息:
- 在游戏中,我们定义按下空格发射子弹,因此我们要对空格键按下的进行监听并处理。
- 游戏中并不是只存在一个子弹,我们需要一个能管理多个子弹的容器。
- 子弹会一直往上移动,但是当子弹穿过上边界时,我们需要移除该子弹。如果能容忍资源的浪费,也可以不管。
子弹组
在pygame中,我们要实现组的功能,需要继承Sprite。改造bullet模块代码如下:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, setting, screen, ship):
super(Bullet, self).__init__()
self.screen = screen
# 创建子弹矩形
self.rect = pygame.Rect(0, 0, setting.bullet_width, setting.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# 子弹颜色
self.color = setting.bullet_color
# 原来是move,继承后改成update
def update(self):
"""向上移动子弹"""
# 更新子弹位置
self.rect.y -= 1
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen, self.color, self.rect)
我们需要在main
模块中创建管理多个子弹的容器,而pygame
的中group
可以很好的完成这个任务。
# 创建子弹编组
bullets = Group()
同时我们处理监听事件时需要把新建的这个子弹组传入,因为我们每次按下空格,就需要将一枚子弹放入这个组中。
gf.check_events(ship, setting, screen, bullets)
最后我们需要更新子弹的位置,并在屏幕绘制中绘制子弹
# 更新子弹
gf.update_bullets(setting, screen, bullets)
# 更新屏幕
gf.update_screen(setting, screen, ship, bullets)
监听事件
每次按下空格键,我们就需要调用一个名叫fire
的函数,将子弹放入子弹组中。
def check_event(ship, setting, screen, bullets):
for event in pygame.event.get():
if event.type == pygame.QUIT:
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.key == pygame.K_UP:
ship.moving_top = True
elif event.key == pygame.K_DOWN:
ship.moving_bottom = True
elif event.key == pygame.K_q:
pygame.quit()
elif event.key == pygame.K_SPACE:
fire(bullets, screen, setting, ship)
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
elif event.key == pygame.K_UP:
ship.moving_top = False
elif event.key == pygame.K_DOWN:
ship.moving_bottom = False
def fire(bullets, screen, setting, ship):
# 创建一颗子弹,并将其加入到编组bullets中
new_bullet = Bullet(setting, screen, ship)
bullets.add(new_bullet)
可以看到移动飞船的代码占据很长篇幅,我们再次封装一下这些参数。
def check_event(ship, setting, screen, bullets):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship, setting, screen, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def fire(bullets, screen, setting, ship):
# 创建一颗子弹,并将其加入到编组bullets中
new_bullet = Bullet(setting, screen, ship)
bullets.add(new_bullet)
def check_keydown_events(event, ship, setting, screen, 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_UP:
ship.moving_top = True
elif event.key == pygame.K_DOWN:
ship.moving_bottom = True
elif event.key == pygame.K_SPACE:
fire(bullets, screen, setting, ship)
elif event.key == pygame.K_q:
sys.exit()
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
elif event.key == pygame.K_UP:
ship.moving_top = False
elif event.key == pygame.K_DOWN:
ship.moving_bottom = False
这样如果再有按键的监听时间,直接对应到check_keydown_events
和check_keyup_events
两个函数中操作即可。
更新子弹
在main
模块中,我们调用了gf
模块的更新子弹函数,这里我们需要填充这部分代码。
def update_bullets(bullets):
"""更新子弹"""
bullets.update()
# 删除已经消失的子弹
for bullet in bullets.copy():
# 当子弹矩形底部坐标小于0,说明子弹已经出了上边界
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
另外我们在更新屏幕函数中也要绘制子弹。
def update_screen(setting, screen, ship, bullets):
"""更新屏幕"""
# 填充背景色
screen.fill(setting.bg_color)
# 加载飞船
ship.blitme()
# 绘制子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
# 让最近绘制的屏幕可见
pygame.display.flip()
发射子弹
当上面代码编写完毕,运行main
模块,在屏幕上已经可以自由移动飞船并发射子弹了!
效果图:
但是你发现子弹移动速度有点快,怎么办呢?我们可以给子弹移动设置一个速度,每次移动以这个速度为单位。
那么我们需要改造bullet
模块,代码改造如下:
属性
# 子弹位置
self.y = float(self.rect.y)
# 子弹速度
self.speed = setting.bullet_speed
函数
def update(self):
"""向上移动子弹"""
# 更新子弹位置
self.y -= self.speed
self.rect.y = self.y
这个时候,可以看到我们子弹速度已经没有之前快了。
ps: 飞船也可以设置速度移动哦!
代码示例
贴一下本次修改后模块代码。
`main`模块:
import pygame
from pygame.sprite import Group
import alien_invasion.game_functions as gf
from alien_invasion.setting import Setting
from alien_invasion.ship import Ship
def run_game():
"""启动游戏"""
# 初始化pygame
pygame.init()
# 定义一个系统设置对象
setting = Setting()
# 新建窗口
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))
# 窗口命名
pygame.display.set_caption(setting.caption)
# 定义一个飞船对象
ship = Ship(setting, screen)
# 创建子弹编组
bullets = Group()
while True:
# 处理监听事件
gf.check_event(ship, setting, screen, bullets)
# 移动飞船
ship.move()
# 更新子弹位置
gf.update_bullets(bullets)
# 刷新屏幕
gf.update_screen(setting, screen, ship, bullets)
if __name__ == '__main__':
run_game()
bullet
模块:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, setting, screen, ship):
super(Bullet, self).__init__()
self.screen = screen
# 创建子弹矩形
self.rect = pygame.Rect(0, 0, setting.bullet_width, setting.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# 子弹颜色
self.color = setting.bullet_color
# 子弹位置
self.y = float(self.rect.y)
# 子弹速度
self.speed = setting.bullet_speed
def update(self):
"""向上移动子弹"""
# 更新子弹位置
self.y -= self.speed
self.rect.y = self.y
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen, self.color, self.rect)
setting
模块:
class Setting:
"""系统设置类"""
def __init__(self):
# 窗口宽
self.screen_width = 1200
# 窗口高
self.screen_height = 800
# 背景颜色
self.bg_color = (230, 230, 230)
# 窗口名
self.caption = "Alien Invasion"
# 子弹宽
self.bullet_width = 3
# 子弹长
self.bullet_height = 15
# 子弹颜色
self.bullet_color = (60, 60, 60)
# 飞船移动速度
self.bullet_speed = 0.2
gf
模块:
import sys
import pygame
from alien_invasion.bullet import Bullet
def check_event(ship, setting, screen, bullets):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship, setting, screen, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def fire(bullets, screen, setting, ship):
# 创建一颗子弹,并将其加入到编组bullets中
new_bullet = Bullet(setting, screen, ship)
bullets.add(new_bullet)
def check_keydown_events(event, ship, setting, screen, 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_UP:
ship.moving_top = True
elif event.key == pygame.K_DOWN:
ship.moving_bottom = True
elif event.key == pygame.K_SPACE:
fire(bullets, screen, setting, ship)
elif event.key == pygame.K_q:
sys.exit()
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
elif event.key == pygame.K_UP:
ship.moving_top = False
elif event.key == pygame.K_DOWN:
ship.moving_bottom = False
def update_screen(setting, screen, ship, bullets):
"""更新屏幕"""
# 填充背景色
screen.fill(setting.bg_color)
# 加载飞船
ship.blitme()
# 绘制子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
# 让最近绘制的屏幕可见
pygame.display.flip()
def update_bullets(bullets):
"""更新子弹"""
bullets.update()
# 删除已经消失的子弹
for bullet in bullets.copy():
# 当子弹矩形底部坐标小于0,说明子弹已经出了上边界
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
如果运行有问题,可以对照代码参考或者私信我。
结尾
目前我们已经可以自由移动飞船并发射子弹了,后面我们就要生成外星人了。
加油!!!