《外星人入侵》游戏要实现的是:玩家控制一艘出现在屏幕底部中央的飞船,可以使用箭头左右移动飞船,还可以使用空格来进行射击,游戏开始时候一群外星人出现在天空,他们在屏幕中向下移动,玩家的任务是射杀这些外星人,玩家将所有外星人都消灭干净后,会出现一群新的外星人,他们移动的速度会更快,只要外星人撞到了玩家的飞船或者是到达了屏幕底部,玩家就损失一艘飞船,损失三艘以后,游戏结束。
1. 安装Pygame
直接在pycharm里file-setting-添加(我的是无效的)
我的环境是anaconda+pycharm,安装流程如下
step1:下载pygame http://www.pygame.org/download.shtml
step2:将下载的whl文件剪贴到anaconda\Lib\site-packages目录下,Windows+R打开cmd依次进入文件所在目录,最后输入
pip install pygame-1.9.4-cp37-cp37m-win_amd64.whl
step3:此时应该已经安装完成,可以在cmd中输入 pip list 找一下其中有没有pygame。或者在输入import pygame
2. 开始游戏项目
2.1 创建Pygame窗口以及响应用户输入
#alien_invasion.py
import pygame
from settings import Settings
from ship import Ship
import sys
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:
#监视键盘和鼠标事件
for enevt in pygame.event.get():
if enevt.type == pygame.QUIT:
sys.exit()
screen.fill(ai_settings.bg_color)
ship.blitme()
pygame.display.flip()
run_game()
在此处代码的实参应为元组而不是int
所以应修改为:
2.2 设置背景颜色
在Pygame中颜色以RGB值来指定的即红绿蓝,每个值0~255。(255,0,0)表示红色,类推即可。
bg_color = (230,230,230)
2.3 创建设置类
把游戏添加的新功能引入一些新设置都存在一个Settings类里,便于添加和修改
#settings.py
class Settings():
def __init__(self):
self.screen_width = 1000
self.screen_height = 600
self.bg_color = (230,230,230)
2.4 添加飞船图像
在Pygame中使用.bmp位图最简单(因为是默认加载位图),加载图像以后用blit()绘制
2.5 创建飞船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()
self.screen_rect = screen.get_rect()
#将每艘新飞船放在屏幕底部中央
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
def blitme(self):
#在指定位置绘制飞船
self.screen.blit(self.image,self.rect)
2.6 在屏幕上绘制飞船
pygame.display.flip()
2.7重构:模块game_functions
重构旨在简化既有代码的结构,使其更容易扩展
#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改变为:
from settings import Settings
from ship import Ship
import game_functions as gf
from settings import Settings
import sys
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()
gf.update_screen(ai_settings,screen,ship)
run_game()
2.8 驾驶飞船
1)响应按键:每当用户按键时,都将在Pygame中注册一个事件,事件都是通过方法pygame.event.get()获取的,因此在函数check_events()中,我们需要指定要检查哪些类型的事件,每次按键都被注册为一个KEYDOWN事件,检测到KEYDOWN事件时我们需要检查按下的是否是特定的键。
#game_functions.py
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
此时check_events()包含形参ship且有检测KEYDOWN时间作出相应,判断是否右移再响应。
2)允许不断移动
#game_functions.py
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
3)左右移动
#game_functions.py
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.type == pygame.KEYUP: #松开按键即不再右移
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT: #松开按键即不再右移
ship.moving_left = False
4)调整飞船的速度
在这里的修改要注意的是rect的centerx等属性只能存储整数值,因此需要使用函数float()将self.rect.centerx的值转换为小数,将结果存到self.center中。在Ship中需要传入实参ai_settings.
5)限制飞船的活动范围
出现的错误:
错误的原因:
screen 是一个没有 rect 属性的pygame.Surface
代码的书写中有错误!!要细心呐!
6)重构check_events()
#game_functions.py
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
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
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()
7)简单回顾
alien_invasion.py: 创建一系列整个游戏都要用到的对象,存储在ai_settings中的设置、存储在screen中主显示surface以及一个飞船实例。包括游戏的主循环,调用check_events()、ship.update()以及update_screen()的while循环
settings.py: 初始化控制游戏外观和飞船速度的属性
game_functions.py: 在这个文件里包含一系列函数,游戏的大部分工作都是由它们完成的,函数check_events()检测相关事件,比如按键和松开,并使用付出函数来处理这些事件,目前这些函数管理飞船的移动。模块还包括update_screen(),用于每次执行主循环的时候都重回屏幕
ship.py: 管理飞船位置的方法以及在屏幕上绘制飞船的方法。
2.9 射击
1)添加子弹设置
#Settings.py
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60,60,60)
self.bullets_allowed = 3
2)创建Bullet类
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""一个对飞船发射的子弹进行管理的类"""
def __init__(self,ai_settings,screen,ship):
"""在飞船所处的位置创建一个子弹对象"""
super(Bullet,self).__init__()
self.screen = screen
#在(0.0)处创建一个表示子弹的巨型,再设置正确的位置
self.rect = pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_width)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
#存储用小数表示子弹的位置
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
"""向上移动子弹"""
#更新表示子弹位置的小数值
self.y -= self.speed_factor
#更新表示子弹的rect位置
self.rect.y = self.y
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen,self.color,self.rect)
3)将子弹存储到编组中
4)开火
5)删除已消失的子弹
6)限制子弹数量
7)创建函数update_bullets()
def update_bullets(bullets):
"""更新子弹的位置并删除已消失的子弹"""
#更新子弹的位置
bullets.update()
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
8)创建函数fire_bullet()
def fire_bullet(ai_settings,screen,ship,bullets):
if len(bullets)<ai_settings.bullets_allowed:
new_bullet =Bullet(ai_settings,screen,ship)
bullets.add(new_bullet)