项目1:外星人入侵
本项目用到 pygame 模块,安装都很简单,有关该模块比较详细的介绍请打开链接:
https://blog.csdn.net/weixin_45728976/article/details/104347294
啥都不如敲代码来的实在,遇到不明白的知识点、过不去的坎再加以解决,慢慢就长知识了。
# -*- coding: utf-8 -*-
"""
文件名:alien_invasion.py
Created on Mon Mar 8 14:26:47 2021
@author: Administrator
"""
import sys
import pygame
def run_game():
# 初始化游戏并创建一个屏幕对象
# pygame.init() # 这行被注释掉好象也没啥影响
# 创建一600,400的屏幕,原文1200,800,在我的屏幕上过于大了点
screen = pygame.display.set_mode((600, 400))
pygame.display.set_caption("Alien Invasion") # 这是给窗口设置一个标题
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
# 原文没这行,因为点击窗口右上角的“X”无法关闭,加上后可以关闭了
pygame.quit()
sys.exit()
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
运行该代码会出现一个背景为黑色的窗口
学习该项目的目的是:掌握代码重构的方法。所以并不会深究pygame的各种功能及方法,因为,本人学习Python的目的是至力于股票方面的,而不是开发游戏,好象pygame对于股票方面也没太大用处。
接下来首次重构下代码
我对代码重构的理解是:在主程序足够大的时候,因为显得比较杂乱,就把其中的一部分或几部分具有一定功能的代码块提炼出来,重新定义为类或函数,单独放在一个或几个具有特定功能的程序文件中,也就是所谓的模块中,然后再回到主程序中调用该模块并做些相应的操作,这样做的目的主要是为了便于日后维护、添加功能、修改等等,更是为了主程序的更加简练,便于使其更容易扩展。
比如,针对该项目,目前有必要针对各种设置进行提炼,单独定义为一个或几个类或者函数模块,只所以要定义类,是因为类更有利于变量(类中称作为属性)在该类中通用,免得老是把参数设置来设置去,费话少说,自们还是来真格的:
项目设计到此代码如下:
# -*- coding: utf-8 -*-
"""
文件名:alien_invasion.py
Created on Tue Mar 9 11:28:49 2021
@author: Administrator
"""
import sys
import pygame
class Ship():
def __init__(self, screen):
""" 初始化飞船并设置其初始位置"""
self.screen = screen # 该参数指定了飞船绘制的位置
# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp') # 加载飞船位图
self.rect = self.image.get_rect() # 获取相应surface的属性rect
self.screen_rect = screen.get_rect() # 将表示屏幕的矩形存储在self.screen_rect中
# 将每般新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx # 将飞船的中心坐标与屏幕矩形的属性centerx对齐
self.rect.bottom = self.screen_rect.bottom # 将飞船的下边缘对齐屏幕的属性bottom
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
def run_game():
# 初始化游戏并创建一个屏幕对象
# pygame.init
"""
既然上面这行可有可无,依然还是将其注释掉吧!
等有用的时候再将其恢复
也许用到的模块在该用到的时候自己就初始化了呢
没必要的东西我不想让其再显示在程序文件中,显得多余且乱
"""
size = width, height = 600, 400
screen = pygame.display.set_mode(size)
# 设置背景色
bg_color = 255, 255, 255
# 实例化飞船
ship = Ship(screen)
pygame.display.set_caption("Alien Invasion")
# 开始游戏的主循环
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit() # 这行必加,不然点击窗口右上角的“X”不能关闭窗口
sys.exit()
# 每次循环时都重绘屏幕
screen.fill(bg_color)
# 绘制飞船
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
run_game()
项目到此,我们定义了一个Ship类,将飞船的位图添加到窗口中,运行该代码会打开一个窗口,在该窗口的底部中央位置出现一般飞船,接下来,我们对此代码重构:
首先将class Ship() 类提取出来,放在 ship.py程序文件中,并将有关设置也提取出来,放在 settings.py 中,当然这一切都是在Spyder 中进行的,它会自动将这些文件(模块)放在同目录(文件夹:alien_invasion 中)之下,就象如下:
飞船的相关程序文件:
ship.py
# -*- coding: utf-8 -*-
"""
文件名:ship.py
Created on Tue Mar 9 13:23:11 2021
@author: Administrator
"""
import pygame
class Ship():
def __init__(self, screen):
""" 初始化飞船并设置其初始位置"""
self.screen = screen # 该参数指定了飞船绘制的位置
# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp') # 加载飞船位图
self.rect = self.image.get_rect() # 获取相应surface的属性rect
self.screen_rect = screen.get_rect() # 将表示屏幕的矩形存储在self.screen_rect中
# 将每般新飞船放在屏幕俱底部中央
self.rect.centerx = self.screen_rect.centerx # 将飞船的中心坐标与屏幕矩形的属性centerx对齐
self.rect.bottom = self.screen_rect.bottom # 将飞船的下边缘对齐屏幕的属性bottom
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
有了这个文件,我们就可以在主程序文件中去掉相应的代码块,用该类的实例替换。相关的飞船图片如下,请将该图片下载之后存放在该目录下的文件夹 images中,如果还没有该文件夹那就自己建一个呗,并将该图片命名为:ship.bmp:
相关设置程序文件:
settings.py
# -*- coding: utf-8 -*-
"""
文件名:settings.py
Created on Tue Mar 9 11:28:03 2021
@author: Administrator
"""
class Settings():
"""存储《外星人入侵》所有设置的类"""
def __init__(self):
"""初始化游戏设置"""
# 屏幕设置
self.screen_width = 600
self.screen_height = 400
self.bg_color = 255, 255, 255
我们同样可以将主程序中的相关该设置的代码块去掉,用实例代替
接下来,我们将主程序中用到的一些函数也存放在游戏函数文件中,如下:
文件名: game_functions.py
# -*- coding: utf-8 -*-
"""
文件名:game_functions.py
Created on Tue Mar 9 15:28:44 2021
@author: Administrator
"""
import sys
import pygame
def check_events():
"""响应按键和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def update_screen(ai_settings, screen, ship):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
ship.blitme()
# 让最近绘制的屏幕可见
pygame.display.flip()
接下来,我们可以对主程序简化了:
# -*- coding: utf-8 -*-
"""
文件名:alien_invasion.py
Created on Tue Mar 9 11:28:49 2021
@author: Administrator
"""
# import sys # 这里用不到该模块了,将其注释掉
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
# 初始化游戏并创建一个屏幕对象
# pygame.init
"""
既然上面这行可有可无,依然还是将其注释掉吧!
等有用的时候再将其恢复
也许用到的模块在该用到的时候自己就初始化了呢
没必要的东西我不想让其再显示在程序文件中,显得多余且乱
"""
# 创建一个Settings实例
ai_settings = Settings()
# 调用类Settings()中的属性
size = ai_settings.screen_width, ai_settings.screen_height
bg_color = ai_settings.bg_color
screen = pygame.display.set_mode(size)
# 创建一个飞船实例,并传送实参screen
ship = Ship(screen)
pygame.display.set_caption("Alien Invasion")
# 开始游戏的主循环
while True:
# 调用game_function.py模块以监视键盘和鼠标事件
gf.check_events()
# 调用game_function.py中的update_screen()函数
gf.update_screen(ai_settings, screen, ship)
原程序先是定义一个run_game函数,最后一行又调用该函数运行主程序,这不是多此一举吗?既然程序是从开头到结尾、从上到下一路运行下来的,是必须一步步执行的,所以,我将定义该函数的过程去掉了,这样岂不是又升了两行代码?
程序到此大体框架已然构成,接下来就是让飞船动起来,并添加了飞船发射子弹的功能,真正进入游戏过程,各程序文件代码如下:
# -*- coding: utf-8 -*-
"""
文件名:alien_invasion.py
一个外星人入侵的游戏
Created on Tue Mar 9 11:28:49 2021
@author: Administrator
"""
# import sys # 这里用不到该模块了,将其注释掉
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions as gf
# 初始化游戏并创建一个屏幕对象
# pygame.init
"""
既然上面这行可有可无,依然还是将其注释掉吧!
等有用的时候再将其恢复
也许用到的模块在该用到的时候自己就初始化了呢
没必要的东西我不想让其再显示在程序文件中,显得多余且乱
"""
# 创建一个Settings实例
ai_settings = Settings()
# 调用类Settings()中的属性
size = ai_settings.size
bg_color = ai_settings.bg_color
screen = pygame.display.set_mode(size)
# 创建一个飞船实例,并传送实参screen
ship = Ship(ai_settings, screen)
# 创建一个用于存储子弹的编组
bullets = Group()
pygame.display.set_caption("Alien Invasion")
# 开始游戏的主循环
while True:
# 调用game_function.py模块以监视键盘和鼠标事件
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
# 调用game_function.py中的update_screen()函数
gf.update_screen(ai_settings, screen, ship, bullets)
# -*- coding: utf-8 -*-
"""
文件名:settings.py
一个游戏设置的类
Created on Tue Mar 9 11:28:03 2021
@author: Administrator
"""
class Settings():
"""存储《外星人入侵》所有设置的类"""
def __init__(self):
"""初始化游戏设置"""
# 屏幕设置
self.size = 600, 400
self.bg_color = 255, 255, 255
# 飞船设置
self.ship_speed_factor = 0.1
# 子弹设置
self.bullet_speed_factor = 2
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
# -*- coding: utf-8 -*-
"""
文件名:ship.py
设置飞船
Created on Tue Mar 9 13:23:11 2021
@author: Administrator
"""
import pygame
class Ship():
def __init__(self, ai_settings, screen):
""" 初始化飞船并设置其初始位置"""
self.screen = screen # 该参数指定了飞船绘制的位置
self.ai_settings = ai_settings
# 加载飞船图像并获取其外接矩形
self.image = pygame.image.load('images/ship.bmp') # 加载飞船位图
self.rect = self.image.get_rect() # 获取相应surface的属性rect
self.screen_rect = screen.get_rect() # 将表示屏幕的矩形存储在self.screen_rect中
# 将每般新飞船放在屏幕俱底部中央
self.rect.centerx = self.screen_rect.centerx # 将飞船的中心坐标与屏幕矩形的属性centerx对齐
self.rect.bottom = self.screen_rect.bottom # 将飞船的下边缘对齐屏幕的属性bottom
# 在飞船的属性center中存储小数值
self.center = float(self.rect.centerx)
# 移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船的位置"""
# 更新飞船的center值,而不是rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
# 根据self.center更新rect对象
self.rect.centerx = self.center
def