Python学习笔记45:游戏篇之外星人入侵(六)

45 篇文章 1 订阅
6 篇文章 0 订阅

前言

飞船模块的功能基本已经完成。今天继续完成子弹模块的功能。

子弹模块

子弹和飞船模块,在游戏逻辑中有一种生成与被生成的表面关系,因为子弹在游戏中是由飞船发射的。但是在我们实际抽象的过程中,飞船与子弹并不是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

首先是介绍子弹的属性以及值。

  1. self.screen
    游戏窗口信息,这个不多做介绍,毕竟子弹是在窗口上显示的。
  2. self.rect
    子弹矩形,我们可以将子弹理解为一个实心矩形,并且通过系统设置文件配置子弹的长度和宽度。
  3. self.rect.centerx
    这个相信大家已经很熟了,子弹矩形的中心x坐标是飞船中心x坐标。
  4. 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_eventscheck_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)

如果运行有问题,可以对照代码参考或者私信我。

结尾

目前我们已经可以自由移动飞船并发射子弹了,后面我们就要生成外星人了。

加油!!!

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值