前言
在之前我们已经创建好了目录,并且编写好了游戏入口的模块。今天的内容主要是讲讲需求的分析以及项目各模块的代码初步编写。
在正式编写代码前,碎碎念几句。在正式编写一个项目代码之前,实际是有很多工作要做的。就项目而言,简单的定项,需求对齐,项目架构设计,实际的代码编写,测试,上线等过程都是有的。
我们这个属于学习人家的项目,所有我们直接从代码编写开始,也可以从架构设计开始。直接从代码开始,就是直接按着书上的步骤来,直接写代码就好了,这个是最简单,有手就行,对于新手来说这样也做也不是大问题,但是如果是想从事编程工作或者想深入的学习下去,个人还是建议从架构设计开始,自己分析,自己设计。哪怕这个项目很简单,或者自己分析设计的有问题,也没关系,失败更能让人印象深刻。
之所以在这个时候讲这些,因为项目目录和main
模块,是相对固定的内容,哪怕换一个项目,先把这两部分搞完也没关系,在实际业务代码的编写中查缺补漏也没问题。但是你不先根据需求设计项目的架构,写一点是一点,后面改起来麻烦就大了。
外星人入侵是个小项目,改改无所谓,而且这个项目网上一搜代码都能找到,但是实际遇到的项目,没有代码给你参考,项目代码量可能几十万行,写到后面发现设计得到不好,那怎么办?
这是设计思维的问题,哪怕我们前期是个螺丝钉,这种活不需要我们操心,写代码就行,但是人总是要成长的不是吗?目光要看向以后。
分析
先看看项目需求。
我们需要开发一个外星人入侵的小游戏,实际上就是简易版的雷霆战机,并且我们一期开发只需要实现基本的游戏功能就行,我们先大概整理一下:
main
模块。main
模块是游戏入口,没入口怎么玩!- 系统设置模块。主要用于设置游戏的一些信息,窗口大小,飞船移动速度,外星人移动速度等等。
- 飞船模块。没有飞船怎么打外星人!
- 子弹模块。没有子弹飞船只能和外星人一换一了!
- 外星人模块。游戏就叫外星人入侵,你和我说没有外星人?
main模块
main
模块已经写好了,主要是项目启动模块,并且在该模块中,实现游戏窗口相关的功能代码。
import sys
import pygame
def run_game():
"""启动游戏"""
# 初始化pygame
pygame.init()
# 新建窗口
screen = pygame.display.set_mode((1200, 800))
# 窗口命名
pygame.display.set_caption("alien_invasion")
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# 刷新屏幕
pygame.display.flip()
if __name__ == '__main__':
run_game()
注意的是游戏窗口渲染的时候是在循环中监听事件,从而做出对应的响应。作为main
模块,我们应该保持main
模块代码的简洁性以及可读性,因此我们需要将窗口渲染的一些业务实现代码封装到一个单独的模块中,在main
模块进行调用即可。
我们再单独建立一个game_function.py
模块,后面都简称gf
模块
注意这个时候我们这个模块啥也没有。看看main
模块,窗口创建和命名就两行,够简洁了。
那么我们的目光看向事件监听。嗯~,竟然有四行,后面可能还会更多的,毕竟后面飞船移动和射击还要监听按键的按下和松开事件,封装,必须封装!
封装后的gf
模块
import sys
import pygame
def check_event():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
封装好以后,再改造一下main
模块,主要是导入gf
模块和调用check_event
函数
import pygame
import alien_invasion.game_functions as gf
def run_game():
"""启动游戏"""
# 初始化pygame
pygame.init()
# 新建窗口
screen = pygame.display.set_mode((1200, 800))
# 窗口命名
pygame.display.set_caption("alien_invasion")
while True:
# 处理监听事件
gf.check_event()
# 刷新屏幕
pygame.display.flip()
if __name__ == '__main__':
run_game()
目前为止,main
模块的基础代码编写就改造好了,后续随着其他功能的实现,继续在gf
模块编写函数,再由main
模块调用即可。
展示一下当前的目录结构:
系统设置模块
这个模块简单点就是存储一些系统设置信息,比如游戏窗口的宽高,背景颜色,飞船和外星人的移动速度等等。
如果是想要复杂点的功能,我们可以另外编写函数修改这些信息即可。
那么我们创建setting
模块,并且简单的设置几个值。
class Setting:
"""系统设置类"""
def __init__(self):
# 窗口宽
self.screen_width = 1200
# 窗口高
self.screen_height = 800
# 背景颜色
self.bg_color = (230, 230, 230)
# 窗口名
self.caption = "Alien Invasion"
既然已经使用了系统设置模块并定义了窗口的一些属性,那么我们再对main
模块改造一下,在创建窗口的时候使用设置类的值,而不是直接在代码中写死。
import pygame
import alien_invasion.game_functions as gf
from alien_invasion.setting import Setting
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)
while True:
# 处理监听事件
gf.check_event()
# 刷新屏幕
pygame.display.flip()
if __name__ == '__main__':
run_game()
在main
模块中,我们已经使用系统设置类中的值替换了原本写死的值,相比以前,可读性和可维护性是不是大大提高了。
当前的目录结构如下:
飞船模块
飞船模块其实就是对飞船的抽象与封装。因此我们需要根据飞船的特性和行为抽象出飞船的属性并封装飞船的一些函数。
飞船可以抽象出哪些属性?
- 飞船是有图片渲染的,那么飞船需要外接一个矩形,并且具备长宽等属性。
- 飞船是可以移动的,简单点就是左右移动,复杂点就是上下左右移动。
- 飞船是在游戏窗口中渲染的,那么我们需要在飞船中注入一个窗口对象。
- 飞船的属性比如图片,大小应该是可以修改的。我们通过系统设置模块进行设置,那么我们需要在飞船中注入一个设置模块。
飞船可以封装哪些行为?
- 移动。
ps:可能有人问飞船不是可以发射子弹吗?项目编写了子弹模块,子弹有自己的生成子弹函数,但是子弹的初始位置是根据飞船的位置定义的。
那么飞船模块简单的编写如下:
import pygame
class Ship:
def __init__(self, setting, screen):
"""初始化飞船"""
self.screen = screen
self.setting = setting
# 加载图片并外接矩形
self.image = pygame.transform.scale(
pygame.image.load('E:/python_project/alien_invasion/assets/images/ship.bmp'), (50, 50))
# 飞船外接的矩形
self.rect = self.image.get_rect()
# 窗口矩形
self.screen_rect = screen.get_rect()
def move(self):
"""移动飞船"""
pass
因为这里还是编写基础代码,所以移动函数的功能先不写,用pass
占位。
当前的目录结构如下:
需要注意的是这里我上传了两张图片,是飞船和外星人的图片,我直接放上来,大家自己保存一下。
格式可能有点问题,我直接在pdf截图保存的,大家可以自行寻找资源替代,不嫌麻烦和慢的话私信我找我要也行,就是我不会实时看私信。。。
外星人模块
外星人模块其实和飞船模块差不多,起码基础代码时一致的,这里不过多描述,大家自己思考一下。
外星人基础模块代码如下:
import pygame
class Alien:
"""外星人类"""
def __init__(self, setting, screen):
"""初始化外星人并设置其初始位置"""
super(Alien, self).__init__()
self.screen = screen
self.setting = setting
# 加载图片并外接矩形
self.image = pygame.transform.scale(
pygame.image.load('F:/python_project/AlienTrespass/src/main/xq/images/alien.bmp'), (50, 50))
self.rect = self.image.get_rect()
self.screen_rect = screen.get_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
def move(self):
"""向上移动子弹"""
pass
这里稍微解释一下为什么需要注入一个飞船对象。因为我们子弹的生成这个功能的实现不是飞船的行为,而是新建一个子弹对象,但是这个子弹对象初始位置在飞船外接矩形的上方。
当前的项目目录结构如下:
至此,我们通过对项目的分析,已经将实现基础功能所需的模块一一分析完毕,并编写出了基础的代码,后面就是一步步根据游戏业务进行业务代码编写填充了。
结尾
今天内容有点多,但是重要,需要认真理解!
基本上整个项目基础已经打好,接下来的工作就是考验逻辑能力和代码编写了。
我的记录教程和书籍的教程不太一样。我是先总再分,书籍的内容是直接一个个模块功能开始的,两种方式没有谁好谁坏,我都是学人家的按书中来的,后面我再进行总结记录,这是属于我自己的学习方式而已。
大家喜欢哪种各自学习即可。