您的位置:首页 > 编程语言 > Python开发

python基础教程 29章 DIY街机游戏

2017-06-26 23:40 274 查看
#!/usr/bin/python

# *-*coding:utf-8 *-*

"""

ubuntu 16.10 python3

sudo pip install pygame

安装 pygame-1.9.3

文档
http://www.pygame.org/docs/
方法索引

pygame v1.9.2 documentation 
https://www.pygame.org/docs/genindex.html#G
---------------------------------------------------

初次实现

简单的“天上掉秤砣”动画(weights.py)

"""

import sys, pygame

from pygame import *

from random import randrange

class Weight(pygame.sprite.Sprite):

def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = Weight_image
#get_rect()返回一个Rect实例,包括渲染文本的大小和偏移量
self.rect = self.image.get_rect()
self.reset()

def reset(self):
"""
将秤砣移动到屏幕顶端的随机位置. http://www.pygame.org/docs/ref/rect.html Pygame使用Rect对象来存储和操作矩形区域。
Rect对象具有几个虚拟属性,可用于移动和对齐Rect:top laft centerx...
"""
self.rect.top = -self.rect.height
self.rect.centerx = randrange(screen_size[0])

def update(self):
"""
更新秤砣,显示下一帧
self.rect.top增加到大于600时更新图像,
设置的显示尺寸高为600,也就是秤砣下降到显示屏幕底部更新
"""
self.rect.top += 1
if self.rect.top > screen_size[1]:
self.reset()

#初始化

#pygame.init()初始化所有导入的pygame模块,display.set_mode()初始化用于显示的窗口或屏幕

#set_mode(resolution=(0,0), flags=0, depth=0)初始化用于显示的窗口或屏幕

#resolution参数是表示宽度和高度的一对数字。flags 参数是附加选项的集合。深度参数表示用于颜色的位数。

#pygame.mouse.set_visible()隐藏或显示鼠标光标

pygame.init()

screen_size = 800,600

pygame.display.set_mode(screen_size, RESIZABLE)#FULLSCREEN

pygame.mouse.set_visible(0)

#载入秤砣的图像pygame.image.load()从文件源加载图像

#convert()创建更改像素格式的Surface的新副本

#没有参数调用convert()新的surface将具有与display surface相同的像素格式,并以尽可能快的速度显示新surface

Weight_image = pygame.image.load('/home/asu/Download/pic.jpg')

Weight_image = Weight_image.convert() #...to match the display 匹配显示

#创建一个子图形组(sprite group),增加Weight

#pygame.sprite.RenderUpdates()此类派生自 pygame.sprite.Group().它具有一个扩展的draw()方法来跟踪屏幕的变化区域。

#RenderUpdates按照添加顺序绘制Sprites的子类。add()将sprites对象添加到此组

sprites = pygame.sprite.RenderUpdates()

sprites.add(Weight())

#读取屏幕表面 #pygame.display.get_surface()返回一个可用于画图的surface对象

#色彩通过RGB三原色表示,(红绿蓝,每个值的范围为0-255)

#pygame.Surface.fill()用纯色填充表面如果没有rect参数,整个Surface将被填充.rect参数将限制填充到特定区域 #pygame.display.flip()将全部显示更新到屏幕

screen = pygame.display.get_surface()

bg = (255,255,255)#white

screen.fill(bg)

pygame.display.flip()

#用于清除子图形

def clear_callback(surf, rect):
surf.fill(bg, rect)

#

while True:
#检查退出事件
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()
#清除前面的位置
#擦除最后一个Group.draw()调用中使用的Sprite。
#但是, 它也可以是采用两个参数的回调函数;目标曲面和要清除的区域。后台回调函数会被多次调用。
sprites.clear(screen, clear_callback)
#更新所有子图形。当调用Group对象的update方法时,它就会自动调用所有Srite对象的update方法
sprites.update()

#绘制所有子图形 draw刷新Sprite图像并跟踪更改的区域
#将所有Sprite绘制到表面,与Group.draw()相同。此方法还返回屏幕上已更改的矩形区域列表。
#返回的Rect列表应该传递给pygame.display.update().这将有助于在软件驱动的显示模式下的性能.
#这种类型的更新通常仅对具有非动画背景的目的地有帮助。
updates = sprites.draw(screen)
#print(updates)[<rect(320, 598, 176, 2)>] #[<rect(320, 600, 0, 0)>, <rect(320, 599, 176, 1)>]

#更新所需的显示部分. 更新给定参数矩形列表部分
#它只允许更新屏幕的一部分,而不是整个区域.如果没有参数传递,则会更新整个Surface区域,如pygame.display.flip
pygame.display.update(updates)
#pygame.display.flip()不在乎性能使用filp()更新整个显示区域

----------------------------------------------------------------------再次实现-------------------------------------------------------------------------------------------------------------------------------

#!/usr/bin/python

# *-*coding:utf-8 *-*

"""

29-2 Squish配置文件

如果游戏太快或者太慢,请修改速度变量

"""

#改变这些设置,以便在游戏中使用其他图像

Banana_image = 'Banana.png'

Weight_image = 'weight.png'

Splash_image = 'weight.png'

#改变这些设置以影响一般的外观

Screen_size = 800, 600

Background_color = 255,255,255

#Background_color = 0,0,0

margin = 30

full_screen = 1

font_size = 48

#这些设置会影响游戏的表现行为

Drop_speed = 1

Banana_speed = 10

Speed_increase = 1

Weights_per_level = 2

Banana_pad_top = 40

Banana_pad_side = 20
-------------------------------------------------------------------------------------------

#!/usr/bin/python

# *-*coding:utf-8 *-*

"""

29-3 Squish的Game对象(object.py)

这个模块包括Squish的游戏对象

"""

import pygame, config, os

from random import randrange

class SquishSprite(pygame.sprite.Sprite):
"""
Squish中所有子图形的泛型超类,构造函数负责载入图像,设置子图形的rect和area属性
并且允许它在指定区域内进行移动,area由屏幕的大小和留白决定
"""
#inflate()增大或者缩小矩形,返回一个新矩形。大小由给定的偏移量决定,赋值将缩小矩形
def __init__(self, image):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image).convert()
self.rect = self.image.get_rect()
screen = pygame.display.get_surface()
shrink = -config.margin * 2
self.area = screen.get_rect().inflate(shrink, shrink)

class Weight(SquishSprite):
"""
落下的秤砣。使用SquishSprite构造函数设置它的秤砣图像,并且会以给定的速度
作为构造函数的参数来设置下落的速度
"""
def __init__(self, speed):
SquishSprite.__init__(self, config.Weight_image)
self.speed = speed
self.reset()

def reset(self):
"""
将秤砣移动到屏幕顶端(视线外),放置到任意水平位置上
"""
x = randrange(self.area.left, self.area.right)
self.rect.midbottom = x,0

def update(self):
"""
根据它的速度将秤砣垂直移动(下落)一段距离,并且根据它是否触及屏幕底端来设置landed属性
"""
self.rect.top += self.speed
self.landed = self.rect.top >= self.area.bottom

class Banana(SquishSprite):
"""
绝望的香蕉。它使用SquishSprite构造函数设置香蕉的图像,并且会停留在屏幕底端
它的水平位置由当前的鼠标位置(有一定限制)决定"""
def __init__(self):
SquishSprite.__init__(self, config.Banana_image)
self.rect.bottom = self.area.bottom
#在没有香蕉的部分进行填充。如果秤砣移动到了这些区域,它不会被判定为碰撞
self.pad_top = config.Banana_pad_top
self.pad_side = config.Banana_pad_side

def update(self):
"""
将Banana中心点的横坐标设定为当前鼠标指针的横坐标,并且使用rect的clamp方法确保
Banana停留在所允许的的范围内"""
#clamp()返回一个新矩形,它只能移动在参数矩形中。
self.rect.centerx = pygame.mouse.get_pos()[0]
self.rect = self.rect.clamp(self.area)

def touches(self, other):
"""
确定香蕉是否触碰到了另外的子图形(比如秤砣)。除了使用rect的colliderect方法外,首先
要计算一个不包括香蕉图像顶端和侧边的“空区域”的新矩形(使用rect的inflate方法对顶端和侧边进行填充
colliderect()方法检验两个矩形的是否重叠
"""
#使用适当的填充缩小边界.负数缩小香蕉图片的范围
bounds = self.rect.inflate(-self.pad_top, -self.pad_side)
#移动边界,将他们放置到Banana的底部
bounds.bottom = self.rect.bottom
#检查边界是否和其他对象的rect交叉
return bounds.colliderect(other.rect)

--------------------------------------------------------------------------------------------

#!/usr/bin/python

# *-*coding:utf-8 *-*

"""

29-4 主Game模块(Squish.py)

这个模块包括Squish游戏的主要游戏逻辑

"""

import os, sys, pygame

from pygame.locals import *

import objects, config

class State:
"""泛型游戏的超类,可以处理事件并且在给定的表面上显示自身"""

def handle(self, event):
"""只处理退出事件的默认事件处理"""
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN and event.key == K_ESCAPE:
sys.exit()

def firstDisplay(self, screen):
"""用于第一次显示状态,使用背景颜色填充边界"""
screen.fill(config.Background_color)
#调用flip,让更改可见
pygame.display.flip()

def display(self, screen):
"""
用于在已经显示过一次状态后再次显示。默认的行为是什么都不做
"""
pass

class Level(State):
"""
游戏等级。用于计算已经落下了多少秤砣,移动子图形以及其他和游戏逻辑相关的任务
"""
def __init__(self, number=1):
self.number = number
#本关还要落下多少秤砣?
self.remaining = config.Weights_per_level

speed = config.Drop_speed
#为每个大于1的等级都增加一个speed_increase
speed += (self.number-1) * config.Speed_increase
#创建秤砣和香蕉
self.weight = objects.Weight(speed)
self.banana = objects.Banana()
both = self.weight, self.banana #This could contain more sprites...
self.sprites = pygame.sprite.RenderUpdates(both)

def update(self, game):
"从前一帧更新游戏状态"
#更新所有子图形
self.sprites.update()
#如果香蕉碰到了秤砣,那么告诉游戏切换到GameOver状态
if self.banana.touches(self.weight):
game.nextState = GameOver()
#否则在秤砣落地使将其复位。如果本关内的所有秤砣都落下了,则让游戏切换到LevelCleared状态
elif self.weight.landed:
self.weight.reset()
self.remaining -= 1
if self.remaining == 0:
game.nextState = LevelCleared(self.number)

def display(self, screen):
"""
在第一次显示(只清空屏幕)后显示状态。与firshDisplay不同,这个方法使用
pygame.display.update对self.sprites.draw提供的、需要更新的矩形列表进行更新
"""
screen.fill(config.Background_color)
updates = self.sprites.draw(screen)
pygame.display.update(updates)

class Paused(State):
"""简单的暂停游戏状态。按下键盘上的任意键或点击鼠标都会结束这个状态"""
finished = 0 #用户结束暂停了吗
image = None #如果需要图片的话,将这个变量设定为文件名
text = ''
#将它设定为一些提示性文本

def handle(self, event):
"""
用State的handle的方法处理一般退出事件,以及对按键和鼠标点击作出反映来处理事件
如果键被按下或者鼠标被点击,将self.finished设定为真
"""

State.handle(self, event)
if event.type in [MOUSEBUTTONDOWN, KEYDOWN]:
self.finished = 1

def update(self, game):
"""
更新等级。如果按键被按下或者鼠标被点击(比如self.finished为真),那么告诉
游戏切换到下一个由self.nextState()表示的状态(应该由子类实现)
"""
if self.finished:
game.nextState = self.nextState()

def firstDisplay(self, screen):
"""暂停状态的第一次出现,绘制图像(如果有的话)并且生成文本 """
#首先,使用填充背景色的方式清空屏幕
screen.fill(config.Background_color)

#使用默认的外观和指定的大小创建Font对象
font = pygame.font.Font(None, config.font_size)

#获取self.text中的文本行,忽略开头和结尾的空行
lines = self.text.strip().splitlines()

#计算文本的高度(使用font.get_linesize())以获取每行文本的像素高度
height = len(lines) * font.get_linesize()
#计算文本的放置位置(屏幕中心).屏幕高度的一半减去文本高度的一半
center, top = screen.get_rect().center
top -= height // 2 #264
#如果有图片要显示
if self.image:
#载入图片
image = pygame.image.load(self.image).convert()
#获取它的rect
r = image.get_rect()
#将图片向下移动到其高度的一半距离. 
top += r.height // 2 
#将图片放置在文本上方20像素处.
r.midbottom = center, top-20
#将图片移动到屏幕上
screen.blit(image, r)

antialias = 1 #Smooth the text
black = 0,0,0 #Render it as balck

#生成所有行,从计算过的top开始,并且对于每一行向下移动font.get_linesize()像素
for line in lines:
text = font.render(line.strip(), antialias, black)
r = text.get_rect()
r.midtop = center, top
screen.blit(text, r)
top += font.get_linesize()

#显示所有更改
pygame.display.flip()

class Info(Paused):
"""简单的暂停状态,显示有关游戏的信息,在StartUp后显示"""
nextState = Level
text = '''
In this game you are a banana,
trying to survive a course in
self-defense against fruit. where the
particpents will "defend" whemselves
against you with a 16 ton weight
'''

class StartUp(Paused):
"""显示图片和欢迎信息的暂停状态,在Info状态前显示"""
nextState = Info
image = config.Splash_image
text = '''
Welcome to Squish,
the game of Fruit Self-Defense
'''

class LevelCleared(Paused):
"""提示用户过关的暂停状态。在next Level后显示"""
def __init__(self, number):
self.number = number
self.text = '''Level %i cleared
Click to start next level ''' % self.number

def nextState(self):
return Level(self.number+1)

class GameOver(Paused):
"""提示用户输掉游戏的状态"""
nextState = Level
text = '''
Game Over
Click to Restart, Esc to Quit'''

class Game:
"""负责主事件循环的游戏对象,任务包括在不同状态间切换"""
def __init__(self, *args):
#获取游戏和图像放置的目录
path = os.path.abspath(args[0])
dir = os.path.split(path)[0]
#移动哪个目录(这样图片文件可以在随后打开)
os.chdir(dir)
#无状态方式启动
self.state = None
#在第一个事件循环迭代中移动到StateUp
self.nextState = StartUp()

def run(self):
"""
这个方法动态设定变量,进行一些重要的初始化工作,并且进入主事件循环
"""
pygame.init()#初始化所有pygame模块
#决定以窗口模式还是全屏模式显示游戏
flag = 0
if config.full_screen:
#flag = FULLSCREEN
flag = RESIZABLE

screen_size = config.Screen_size
screen = pygame.display.set_mode(screen_size, flag)

pygame.display.set_caption('Fruit Self Defense')
pygame.mouse.set_visible(False)

#主循环
while True:
#1 如果nextState被修改了,那么移动到新状态,并且显示它(第一次)
if self.state != self.nextState:
self.state = self.nextState
self.state.firstDisplay(screen)
#2代理当前状态的事件处理
for event in pygame.event.get():
self.state.handle(event)
#3 更新当前状态
self.state.update(self)
#4显示当前状态
self.state.display(screen)

if __name__ == '__main__':
game = Game(*sys.argv)
game.run()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 游戏