0项目规划
0.1项目综述
在游戏《外星人入侵》中,玩家控制一艘出现在屏幕底部中央的飞船。玩家可以使用上下左右键来移动飞船,还可以使用空格键进行射击。游戏开始时,一支外星人军团出现在屏幕上方,他们左右移动的同时,不断向下挺进。游戏任务是射杀这些外星人,玩家将所有外星人击杀后,将出现一批移动速度更快的外星人。只有当飞船撞到外星人,或外星人到达屏幕底部时,玩家损失一艘飞船,当玩家损失三艘飞船后,游戏结束。
0.2工具准备
pygame
要用python写一个小游戏,需要借助pygame的帮助。
pygame的安装也非常的简单
pip install pygame
在cmd中,用以上语句安装即可。
为了在Pycharm中正常使用pygame
需要执行一下步骤:
打开Pycharm,File——Settings——Project:WorkSpace——Python Interpreter
点击右侧加号,在其中搜索Pygame,再点击Install即可(PS:这里可能需要点时间加载,耐心等待)
1开始游戏项目
1.1创建Pygame窗口以及响应用户输入
一切准备就绪,运行一个简单的代码,生成一个游戏窗口
#alien_invasion.py
import sys #退出游戏所需要的包
import pygame #开发游戏所需要的包
def run_game(): #定义一个函数run_game用来控制游戏启动
pygame.init() #初始化背景设置,让Pygame能够正确工作
screen = pygame.display.set_mode((1200,800))
#调用pygame.display.set_mode来创建一个名为screen的显示窗口
pygame.display.set_caption("Alien Invasion")
bg_color = (230,230,230) #定义bg_color为(230,230,230)
while True:
#通过while循环来控制游戏运行,在循环中包含一个事件循环以及管理屏幕更新的代码,整个游戏就是在不断的刷新中进行下去的。
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit() #当检测到pygame.QUIT事件后,调用sys.exit()来退出游戏
screen.fill(bg_color) #用screen.fill()方法,将前面定义的bg_color用来填充屏幕。
pygame.display.flip() #通过调用pygame.display.flip()来实现更新整个屏幕,擦除旧屏幕,保持显示出来的是最新屏幕。Pycharm解释器的解释为 Update the full display Surface to the screen
run_game()
运行结果:
好啦,一个游戏面板诞生了,之后在上面添加一些有趣的东西吧。
1.2创建设置类
为了能够在后期更方便的进行设置,同时使代码更简单,这里创建一个设置类
#settings.py
class Settings():
def __init__(self):
self.screen_width=1200
self.screen_height=800
self.bg_color=(230,230,230)
将设置参数放到设置类中以后,alien_invasion.py中的代码也要有一定的变化:
import sys
import pygame
def run_game():
pygame.init()
ai_settings = Settings() #另ai_settings为Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
#直接调用ai_settings中的宽与高
pygame.display.set_caption("Alien Invasion")
# bg_color = (230,230,230)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(ai_settings.bg_color)
#直接调用ai_settings中的背景色
pygame.display.flip()
run_game()
做到这里,我以为可以运行,但是我发现,运行报错
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "D:\Dev\Python\WorkSpace\Alienandspace\alien_invasion.py", line 17, in <module>
run_game()
File "D:\Dev\Python\WorkSpace\Alienandspace\alien_invasion.py", line 6, in run_game
ai_settings = Settings()
NameError: name 'Settings' is not defined
Process finished with exit code 1
原因是,我把alien_invasion.py与setting.py分为两个文件,但存放在同一个目录下,我认为这样就可以调用,但是并不行,开始尝试解决。
发现是没有将settings导入。
故将alien_invasion.py中加入语句
from settings import Settings
运行成功!
所以这里可以学到,同一目录下的不同文件需要导入才可以使用其文件中的函数
截至目前两个文件及代码分别是:
#settings.py
class Settings():
def __init__(self):
self.screen_width=1200
self.screen_height=800
self.bg_color=(230,230,230)
#alien_invasion.py
import sys
import pygame
from settings import Settings
def run_game():
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# bg_color = (230,230,230)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(ai_settings.bg_color)
pygame.display.flip()
run_game()
初步实现了窗口的绘制,大小背景色的调整。
2添加飞船图像
http://pixabay.com一个免费图库网站,速度慢
在项目的根目录下创建images文件夹,未来的各种图片都将存放在这里
2.1创建Ship类
#ship.py
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() #获取相应属性,将image视为一个矩形,之后的操作就可以仅针对这个矩形进行操作
self.screen_rect = screen.get_rect() #为了将飞船放在屏幕中央,所以也需要通过screen.get_rect()将整个屏幕视作一个矩形
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
#以上两行代码,表示将飞船中心的x坐标设置为屏幕矩形的属性centerx;将飞船下边缘的y坐标,设置为屏幕矩形的属性bottom
def blitme(self): #定义方法blitme()
self.screen.blit(self.image,self.rect)
#.blit()有两个参数,它可以根据self.rect设置的位置,将self.image绘制到屏幕上
至此一个Ship类就创建完成
要想让飞船出现在屏幕上,还需要在alien_invasion.py中做一些更改
#alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship #导入ship类
def run_game():
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
ship = Ship(screen) #创建一搜飞船,将创建飞船的操作放在while之外
# bg_color = (230,230,230)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(ai_settings.bg_color)
ship.blitme() #绘制飞船
pygame.display.flip()
run_game()
3重构
之前的代码结构较为繁琐,比如控制事件监听,屏幕更新的代码直接写在alien_invasion.py的while循环中,其实可以将其提取出来,单独写在一个文件中,需要使用时再进行调取。
因此建立一个新文件:game_functions.py
#game_functions.py
import sys
import pygame
def check_events(): #检查事件,以实现退出游戏
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def update_screen(ai_settings,screen,ship): #更新屏幕,包括绘制屏幕、背景色、飞船
screen.fill(ai_settings.bg_color)
ship.blitme()
pygame.display.flip()
在此基础上,就可以将alien_invasion.py的代码进行简化
#alien_invasion.py
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
pygame.init()
ai_settings = Settings()
screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
ship = Ship(screen)
# bg_color = (230,230,230)
while True: #可以明显看出,while循环中的语句大大减少
gf.check_events()
gf.update_screen(ai_settings,screen,ship)
run_game()
4驾驶飞船
4.1响应按键
为了使飞船移动,通常是由上下左右方向键来实现,因为事件都是通过方法pygame.event.get()来获取的,因此在函数check_event()中,可以加入控制飞船移动的语句
上代码#game_functions.py
#game_functions.py
import sys
import pygame
def check_events(ship):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
ship.rect.centerx+=1
def update_screen(ai_settings,screen,ship):
screen.fill(ai_settings.bg_color)
ship.blitme()
pygame.display.flip()
测试发现,这样只能不断地按方向键才能使飞船不断移动,按一下移动一下。无法按住方向键一直移动。
捋一下逻辑,之前实现飞船按一下键右移一下,是通过if语句,判断当监测到KEYDOWN和K_RIGHT时进行ship.rect.centerx+=1 通过这个方法,导致每监测到一次时,移动一下。
4.2连续移动
如果改变一个逻辑,当监测到KEYDOWN和K_RIGHT时使飞船进入一个转态(即:一直右移的状态)。
所以这里考虑用一个moving_right的标志来实现持续移动。
设,moving_right=True表示移动,moving_right=False表示不移动。
在初始,将飞船的默认moving_ringt设置为False,使飞船静止不动。
在监测到KEYDOWN和K_RIGHT时,使飞船获得moving_ring=True状态后,使飞船保持持续向右移动。
首先对ship.py进行修改
#ship.py
#ship.py
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()
self.screen_rect = screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.moving_right = False #设置默认为False
def update(self):
if self.moving_right: #当moving_right的值为真时,右移一位
self.rect.centerx += 1
def blitme(self): #定义方法blitme()
self.screen.blit(self.image,self.rect)
#.blit()有两个参数,它可以根据self.rect设置的位置,将self.image绘制到屏幕上
接下来对game_functions.py进行修改
#game_functions.py
import sys
import pygame
def check_events(ship):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.type == pygame.KEYUP:
if event.key ==pygame.K_RIGHT:
ship.moving_right = False
def update_screen(ai_settings,screen,ship):
screen.fill(ai_settings.bg_color)
ship.blitme()
pygame.display.flip()
最后在alien_invasion.py中进行调用
#alien_invasion.py
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
pygame.init()
ai_settings = Settings()
screen=pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
ship = Ship(screen)
while True:
gf.check_events() #一、监听,判断情况
ship.update() #二、根据情况更新状态
gf.update_screen(ai_settings,screen,ship)#三、根据更新的状态,刷新屏幕
run_game()
4.3上下左右移动
同理可的,对飞船实现上下左右移动,代码如下:
#ship.py
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()
self.screen_rect = screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down =False
def update(self):
if self.moving_right:
self.rect.centerx += 1
if self.moving_left:
self.rect.centerx -= 1
if self.moving_up:
self.rect.centery -= 1
if self.moving_down:
self.rect.centery += 1
def blitme(self):
self.screen.blit(self.image,self.rect)
#game_functions.py
import sys
import pygame
def check_events(ship):
for event in pygame.event.get():
if event.type == 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_up = True
elif event.key == pygame.K_DOWN:
ship.moving_down = True
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_up = False
elif event.key == pygame.K_DOWN:
ship.moving_down = False
def update_screen(ai_settings,screen,ship):
screen.fill(ai_settings.bg_color)
ship.blitme()
pygame.display.flip()
#alien_invasion.py
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
ship = Ship(screen)
while True:
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings,screen,ship)
run_game()
4.4调整飞船的速度&控制飞船行动范围&设置飞船大小&重构check_events()
这几件事一起做的,过程中没停下来,所以文档也就一起写吧,直接放代码
#ship.py
import pygame
class Ship():
def __init__(self,ai_settings,screen):
self.screen = screen
self.ai_settings = ai_settings
self.ship = pygame.image.load('images/ship.bmp')
image = self.ship #为了能单独设置飞船图片的大小,将上传的图片定义为ship,之后再定义为image,然后再用pygame.transform.scale(surface,(int,int))语句来自定义图片的大小。
#self.image = pygame.transform.scale(image,(200,110))为了代码的整洁,将这句代码改写如下
self.image = pygame.transform.scale(image,ai_settings.ship_size)
#这里的ship_size同样设置在settings.py中
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.center = float(self.rect.centerx)
self.middle = float(self.rect.centery)
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
def update(self):
if self.moving_right and self.rect.right < self.screen_rect.right:
#这里通过比较飞船image的rect的右侧与screen的rect的右侧来限制飞船不可超过屏幕右侧,下同
self.center += self.ai_settings.ship_speed_factor
#这里飞船的速度用self.ai_settings.ship_speed_factor来表示,这是在在settings中预设的一个值
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
if self.moving_up and self.rect.top > self.screen_rect.top:
self.middle -= self.ai_settings.ship_speed_factor
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.middle += self.ai_settings.ship_speed_factor
self.rect.centerx = self.center
self.rect.centery = self.middle
def blitme(self):
self.screen.blit(self.image,self.rect)
#game_functions.py
#这里对check_events进行重构,由于check_events要检查keydown与keyup两件事,而且每件事中所包含的内容也不少,所以这里考虑将其按keydown与keyup进行分离。将所有keydown状态下的操作写到check_keydown_events()中;将所有keyup状态下的操作写到check_keyup_events()中;最后在check_events()中再设置好这两种事件的相应条件即可
import sys
import pygame
def check_keydown_events(event,ship):
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_up = True
elif event.key == pygame.K_DOWN:
ship.moving_down = True
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_up = False
elif event.key == pygame.K_DOWN:
ship.moving_down = False
def check_events(ship):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event,ship)
elif event.type == pygame.KEYUP:
check_keyup_events(event,ship)
def update_screen(ai_settings,screen,ship):
screen.fill(ai_settings.bg_color)
ship.blitme()
pygame.display.flip()
#settings.py
class Settings():
def __init__(self):
self.screen_width=1200
self.screen_height=800
self.bg_color=(230,230,230)
self.ship_speed_factor = 1.5 #飞船移动速度
self.ship_size=(200,110) #飞船大小
#alien_invasion.py
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
def run_game():
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
ship = Ship(ai_settings,screen)
while True:
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings,screen,ship)
run_game()