因此,我为烟气制造了一个粒子引擎,对此我感到非常满意,我认为它非常适合我的游戏。
我现在需要在游戏中实现它,但遇到了一些麻烦。我想知道是否有人可以解释我将如何在游戏中使用粒子引擎。
我在下面添加了我的两个代码(用于烟雾和游戏文件)。
我想将粒子与游戏文件分开,但在游戏中调用它。
最终,我想在引擎中产生更多我也可以调用的粒子效果。
有人可以帮忙吗?它可能需要一些调整才能工作。
微粒代码:
import pygame,random
from pygame.locals import *
xmax = 1000 #width of window
ymax = 600 #height of window
class Smoke():
def __init__(self, startx, starty, col):
self.x = startx
self.y = random.randint(0, starty)
self.col = col
self.sx = startx
self.sy = starty
def move(self):
if self.y < 0:
self.x = self.sx
self.y = self.sy
else:
self.y -= 1
self.x += random.randint(-1, 2)
def main():
pygame.init()
screen = pygame.display.set_mode((xmax,ymax))
black = (0,0,0)
grey = (145,145,145)
light_grey = (192,192,192)
dark_grey = (183, 183, 183)
clock = pygame.time.Clock()
particles = []
for part in range(600):
if part % 2 > 0: col = grey
#elif part % 5 > 0: col = dark_grey
elif part % 3 > 0: col = dark_grey
else: col = light_grey
particles.append( Smoke(0, 500, col) )
exitflag = False
while not exitflag:
for event in pygame.event.get():
if event.type == QUIT:
exitflag = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
exitflag = True
screen.fill(black)
for p in particles:
p.move()
pygame.draw.circle(screen, p.col, (p.x, p.y), 15)
pygame.display.flip()
clock.tick(80)
pygame.quit()
if __name__ == "__main__":
main()
游戏代码
import pygame
from pygame import *
WIN_WIDTH = 1120 - 320
WIN_HEIGHT = 960 - 320
HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)
DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 0
FLAGS = 0
CAMERA_SLACK = 30
levels = {0: {'level': [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",],
'enemies': [(9, 38)]},
1: {'level': [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPPPPPPPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPP PPPP",
"PPP P PPPPPPPPPPPPPPPPPP",
"PPP P PPPPPPPPPPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",],
'enemies': [(9, 38), (18, 38), (15, 15)]}}
class Scene(object):
def __init__(self):
pass
def render(self, screen):
raise NotImplementedError
def update(self):
raise NotImplementedError
def handle_events(self, events):
raise NotImplementedError
class GameScene(Scene):
def __init__(self, levelno):
super(GameScene, self).__init__()
self.bg = Surface((32,32))
self.bg.convert()
self.bg.fill(Color("#0094FF"))
up = left = right = False
self.entities = pygame.sprite.Group()
self.player = Player(5, 40)
self.player.scene = self
self.platforms = []
self.levelno = levelno
levelinfo = levels[levelno]
self.enemies = [Enemy(*pos) for pos in levelinfo['enemies']]
level = levelinfo['level']
total_level_width = len(level[0]) * 32
total_level_height = len(level) * 32
# build the level
x = 0
y = 0
for row in level:
for col in row:
if col == "P":
p = Platform(x, y)
self.platforms.append(p)
self.entities.add(p)
if col == "E":
e = ExitBlock(x, y)
self.platforms.append(e)
self.entities.add(e)
x += 32
y += 32
x = 0
self.camera = Camera(complex_camera, total_level_width, total_level_height)
self.entities.add(self.player)
for e in self.enemies:
self.entities.add(e)
def render(self, screen):
for y in range(20):
for x in range(25):
screen.blit(self.bg, (x * 32, y * 32))
for e in self.entities:
screen.blit(e.image, self.camera.apply(e))
def update(self):
pressed = pygame.key.get_pressed()
up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)]
self.player.update(up, left, right, self.platforms)
for e in self.enemies:
e.update(self.platforms)
self.camera.update(self.player)
def exit(self):
if self.levelno+1 in levels:
self.manager.go_to(GameScene(self.levelno+1))
else:
self.manager.go_to(CustomScene("You win!"))
def die(self):
self.manager.go_to(CustomScene("You lose!"))
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN and e.key == K_ESCAPE:
self.manager.go_to(TitleScene())
class CustomScene(object):
def __init__(self, text):
self.text = text
super(CustomScene, self).__init__()
self.font = pygame.font.SysFont('Arial', 56)
def render(self, screen):
# ugly!
screen.fill((0, 200, 0))
text1 = self.font.render(self.text, True, (255, 255, 255))
screen.blit(text1, (200, 50))
def update(self):
pass
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN:
self.manager.go_to(TitleScene())
class TitleScene(object):
def __init__(self):
super(TitleScene, self).__init__()
self.font = pygame.font.SysFont('Arial', 56)
self.sfont = pygame.font.SysFont('Arial', 32)
def render(self, screen):
# ugly!
screen.fill((0, 200, 0))
text1 = self.font.render('Crazy Game', True, (255, 255, 255))
text2 = self.sfont.render('> press space to start <', True, (255, 255, 255))
screen.blit(text1, (200, 50))
screen.blit(text2, (200, 350))
def update(self):
pass
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN and e.key == K_SPACE:
self.manager.go_to(GameScene(0))
class SceneMananger(object):
def __init__(self):
self.go_to(TitleScene())
def go_to(self, scene):
self.scene = scene
self.scene.manager = self
def main():
pygame.init()
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
timer = pygame.time.Clock()
running = True
manager = SceneMananger()
while running:
timer.tick(60)
if pygame.event.get(QUIT):
running = False
return
manager.scene.handle_events(pygame.event.get())
manager.scene.update()
manager.scene.render(screen)
pygame.display.flip()
class Camera(object):
def __init__(self, camera_func, width, height):
self.camera_func = camera_func
self.state = Rect(0, 0, width, height)
def apply(self, target):
try:
return target.rect.move(self.state.topleft)
except AttributeError:
return map(sum, zip(target, self.state.topleft))
def update(self, target):
self.state = self.camera_func(self.state, target.rect)
def complex_camera(camera, target_rect):
l, t, _, _ = target_rect
_, _, w, h = camera
l, t, _, _ = -l + HALF_WIDTH, -t +HALF_HEIGHT, w, h
l = min(0, l) # stop scrolling left
l = max(-(camera.width - WIN_WIDTH), l) # stop scrolling right
t = max(-(camera.height-WIN_HEIGHT), t) # stop scrolling bottom
return Rect(l, t, w, h)
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class Player(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.xvel = 0
self.yvel = 0
self.onGround = False
self.image = Surface((32,32))
self.image.fill(Color("#0000FF"))
self.image.convert()
self.rect = Rect(x*32, y*32, 32, 32)
def update(self, up, left, right, platforms):
if self.rect.top > 1440 or self.rect.top < 0:
self.scene.die()
if self.rect.left > 1408 or self.rect.right < 0:
self.scene.die()
if up:
if self.onGround:
self.yvel = 0
self.yvel -= 10 # only jump if on the ground
if left:
self.xvel = -10
if right:
self.xvel = 10
if not self.onGround:
self.yvel += 0.3 # only accelerate with gravity if in the air
if self.yvel > 80: self.yvel = 80 # max falling speed
if not(left or right):
self.xvel = 0
self.rect.left += self.xvel # increment in x direction
if self.collide(self.xvel, 0, platforms): # do x-axis collisions
self.rect.top += self.yvel # increment in y direction
self.onGround = False; # assuming we're in the air
self.collide(0, self.yvel, platforms) # do y-axis collisions
def collide(self, xvel, yvel, platforms):
for p in platforms:
if pygame.sprite.collide_rect(self, p):
if isinstance(p, ExitBlock):
self.scene.exit()
return False
if xvel > 0: self.rect.right = p.rect.left
if xvel < 0: self.rect.left = p.rect.right
if yvel > 0:
self.rect.bottom = p.rect.top
self.onGround = True
if yvel < 0:
self.rect.top = p.rect.bottom
return True
class Enemy(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.yVel = 0
self.xVel = 2 # start moving immediately
self.image = Surface((32,32))
self.image.fill(Color("#00FF00"))
self.image.convert()
self.rect = Rect(x*32, y*32, 32, 32)
self.onGround = False
def update(self, platforms):
if not self.onGround:
self.yVel += 0.3
# no need for right_dis to be a member of the class,
# since we know we are moving right if self.xVel > 0
right_dis = self.xVel > 0
# create a point at our left (or right) feet
# to check if we reached the end of the platform
m = (1, 1) if right_dis else (-1, 1)
p = self.rect.bottomright if right_dis else self.rect.bottomleft
fp = map(sum, zip(m, p))
# if there's no platform in front of us, change the direction
collide = any(p for p in platforms if p.rect.collidepoint(fp))
if not collide:
self.xVel *= -1
self.rect.left += self.xVel # increment in x direction
self.collide(self.xVel, 0, platforms) # do x-axis collisions
self.rect.top += self.yVel # increment in y direction
self.onGround = False; # assuming we're in the air
self.collide(0, self.yVel, platforms) # do y-axis collisions
def collide(self, xVel, yVel, platforms):
for p in platforms:
if pygame.sprite.collide_rect(self, p):
if xVel > 0:
self.rect.right = p.rect.left
self.xVel *= -1 # hit wall, so change direction
if xVel < 0:
self.rect.left = p.rect.right
self.xVel *= -1 # hit wall, so change direction
if yVel > 0:
self.rect.bottom = p.rect.top
self.onGround = True
if yVel < 0:
self.rect.top = p.rect.bottom
class Platform(Entity):
def __init__(self, x, y):
Entity.__init__(self)
#self.image = Surface([32, 32], pygame.SRCALPHA, 32) #makes blocks invisible for much better artwork
self.image = Surface((32,32)) #makes blocks visible for building levels
self.image.convert()
self.rect = Rect(x, y, 32, 32)
def update(self):
pass
class ExitBlock(Platform):
def __init__(self, x, y):
Platform.__init__(self, x, y)
self.image = Surface((32,32)) #makes blocks visible for building levels
self.image.convert()
self.rect = Rect(x, y, 32, 32)
if __name__ == "__main__":
main()
最佳答案
谨防!长帖子!
粒子
首先,让我们看一下Smoke
类。它包含一些烟雾行为,但是您的主循环也包含。让我们通过创建一个通用Particle
类来解决该问题,该类什么都不做,只表示一个粒子:
class Particle():
def __init__(self, col, size, *strategies):
self.x, self.y = 0, 0
self.col = col
self.alive = 0
self.strategies = strategies
self.size = size
def kill(self):
self.alive = -1 # alive -1 means dead
def move(self):
for s in self.strategies:
s(self)
这个课没有多大作用。它是通用的,其所有行为(简单函数)都传递给它的
__init__
函数,并且粒子通过move
方法将这些函数应用于自身。粒子的行为
现在我们的粒子类非常灵活,让我们考虑一下粒子应如何表现,以便使粒子看起来像烟雾。
烟雾粒子应该上升,因此让我们创建一个向上移动粒子的函数:
def ascending(speed):
def _ascending(particle):
particle.y -= speed
return _ascending
烟雾粒子应该在某个时刻死亡,因此让我们编写一个在特定条件下会杀死的函数:
def kill_at(max_x, max_y):
def _kill_at(particle):
if particle.x < -max_x or particle.x > max_x or particle.y < -max_y or particle.y > max_y:
particle.kill()
return _kill_at
我们需要跟踪粒子的生存时间(稍后派上用场),因此我们需要一个让粒子老化的函数:
def age(amount):
def _age(particle):
particle.alive += amount
return _age
当烟雾上升时,它不应成一直线(多么无聊!),但应散开:
def fan_out(modifier):
def _fan_out(particle):
d = particle.alive / modifier
d += 1
particle.x += random.randint(-d, d)
return _fan_out
好的!现在我们的烟雾可以散开了,但是仍然有点无聊,所以让我们编写一个模拟一点风的函数:
def wind(direction, strength):
def _wind(particle):
if random.randint(0,100) < strength:
particle.x += direction
return _wind
现在,我们有一堆描述粒子行为的函数。它们都是小型且设备齐全的。您可以创建它们的无限数量,并根据需要将它们组合以创建不同的粒子。
烟
是时候创建一些粒子了:进入烟雾机!
def smoke_machine():
colors = {0: grey,
1: dark_grey,
2: light_grey}
def create():
for _ in xrange(random.choice([0,0,0,0,0,0,0,1,2,3])):
behaviour = age(1), ascending(1), fan_out(400), wind(1, 15), kill_at(1000, 1000)
p = Particle(colors[random.randint(0, 2)], random.randint(10, 15), *behaviour)
yield p
while True:
yield create()
那这到底是什么?简单的。它是不断发出新粒子的发生器。每当有人想检索下一个项目时,它都会调用其嵌套的
create
函数。该函数又根据random.choice
的输入列表返回0到3个粒子。这是一个很好的说法,它以0%的概率返回0个粒子,而每个以10%的概率返回1、2或3个粒子。在下一行中,我们定义粒子的行为。这只是功能的元组。注意每个函数调用如何返回其嵌套函数。这样就可以对这些功能进行参数化。
最后一步是为粒子随机分配颜色和大小。
您可以从命令行对其进行测试,以查看其工作方式:
>>> s=smoke_machine()
>>> list(next(s))
[]
>>> list(next(s))
[<particle.Particle instance at 0x02AD94B8>, <particle.Particle instance at 0x02
AD9030>]
>>> list(next(s))
[]
>>> list(next(s))
[]
>>> list(next(s))
[<particle.Particle instance at 0x02AD9030>]
>>> list(next(s))
[<particle.Particle instance at 0x02AD9418>, <particle.Particle instance at 0x02
AD93C8>]
>>> list(next(s))
[<particle.Particle instance at 0x02AD9030>]
看看我们每次在生成器上调用下一个如何返回多达3个粒子。
全部放在一起
我们如何将烟机与我们的游戏结合起来?让我们编写一个为我们处理它的类:
class Emitter(object):
def __init__(self, pos=(0, 0)):
self.particles = []
self.pos = pos
self.factories = []
def add_factory(self, factory, pre_fill=300):
self.factories.append(factory)
tmp = []
for _ in xrange(pre_fill):
n = next(factory)
tmp.extend(n)
for p in tmp:
p.move()
self.particles.extend(tmp)
def update(self):
for f in self.factories:
self.particles.extend(next(f))
for p in self.particles[:]:
p.move()
if p.alive == -1:
self.particles.remove(p)
def draw(self, screen, position_translater_func):
for p in self.particles:
target_pos = position_translater_func(map(sum, zip((p.x, p.y), self.pos)))
pygame.draw.circle(screen, p.col, target_pos, int(p.size))
发射器可以保存一堆工厂函数(例如我们的
smoke_machine
),可以在每次更新时将这些工厂的粒子添加到其self.particles
中,以便将它们绘制到屏幕上。让我们详细看一些功能:如果我们添加一个带有
add_factory
的新工厂,我们将其称为factory
并提前将其粒子移动300次(或任何pre_fill
)。这样,一些粒子已经在那里。如果要绘制粒子,则必须使用
Emitter
的位置来计算其位置。另外,我们需要使用游戏的摄影机来调整此位置,因此我们只接受一个函数作为参数,将我们的位置转换为正确的最终位置,以便我们的粒子在游戏中相应地滚动。现在,不需要对游戏类进行大的更改即可使用
Emitter
。我们只需要在GameScene
中创建一个名为self.emitter
的新列表和一个Emitter
,并将smoke_machine
工厂添加到其中。在
render
方法中,我们调用for e in self.emitter:
e.draw(screen, self.camera.apply)
并在
update
方法中调用for e in self.emitter:
e.update()
我们完成了!
行动 !
完整的 list :
game.py
省略了一些类(class),因为我达到了30000个字符限制:-)
import pygame
from pygame import *
from particle import Emitter, smoke_machine
WIN_WIDTH = 1120 - 320
WIN_HEIGHT = 960 - 320
HALF_WIDTH = int(WIN_WIDTH / 2)
HALF_HEIGHT = int(WIN_HEIGHT / 2)
DISPLAY = (WIN_WIDTH, WIN_HEIGHT)
DEPTH = 0
FLAGS = 0
CAMERA_SLACK = 30
levels = {0: {'level': [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP S PPPPPPPPPPPPPPPPPP",],
'enemies': [(9, 38)]},
1: {'level': [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" E ",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPPP PPPPPPPPPPPPPPPP",
" PPPPPPPPPPPPPPPP",
" PPPP P",
" PPPP P",
" PPPP PPPPPPP",
" PPPPPPPPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
" PPPP PPPPPPP",
"PPPPP PPPP PPPPPPP",
"PPP PPPPPPPPPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPP PPPPPPP",
"PPP PPPPPPPP PPPP PPPPPPP",
"PPP PPPP",
"PPP PPPP",
"PPP PPPPP PPPP",
"PPP P PPPPPPPPPPPPPPPPPP",
"PPP P PPPPPPPPPPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",
"PPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPP",],
'enemies': [(9, 38), (18, 38), (15, 15)]}}
...
class GameScene(Scene):
def __init__(self, levelno):
super(GameScene, self).__init__()
self.bg = Surface((32,32))
self.bg.convert()
self.bg.fill(Color("#0094FF"))
up = left = right = False
self.entities = pygame.sprite.Group()
self.player = Player(5, 40)
self.player.scene = self
self.platforms = []
self.levelno = levelno
levelinfo = levels[levelno]
self.enemies = [Enemy(*pos) for pos in levelinfo['enemies']]
level = levelinfo['level']
total_level_width = len(level[0]) * 32
total_level_height = len(level) * 32
self.emitter = []
# build the level
x = 0
y = 0
for row in level:
for col in row:
if col == "P":
p = Platform(x, y)
self.platforms.append(p)
self.entities.add(p)
if col == "E":
e = ExitBlock(x, y)
self.platforms.append(e)
self.entities.add(e)
if col == "S":
e = Emitter((x, total_level_height))
e.add_factory(smoke_machine())
self.emitter.append(e)
x += 32
y += 32
x = 0
self.camera = Camera(complex_camera, total_level_width, total_level_height)
self.entities.add(self.player)
for e in self.enemies:
self.entities.add(e)
def render(self, screen):
for y in range(20):
for x in range(25):
screen.blit(self.bg, (x * 32, y * 32))
for e in self.emitter:
e.draw(screen, self.camera.apply)
for e in self.entities:
screen.blit(e.image, self.camera.apply(e))
def update(self):
for e in self.emitter:
e.update()
pressed = pygame.key.get_pressed()
up, left, right = [pressed[key] for key in (K_UP, K_LEFT, K_RIGHT)]
self.player.update(up, left, right, self.platforms)
for e in self.enemies:
e.update(self.platforms)
self.camera.update(self.player)
def exit(self):
if self.levelno+1 in levels:
self.manager.go_to(GameScene(self.levelno+1))
else:
self.manager.go_to(CustomScene("You win!"))
def die(self):
self.manager.go_to(CustomScene("You lose!"))
def handle_events(self, events):
for e in events:
if e.type == KEYDOWN and e.key == K_ESCAPE:
self.manager.go_to(TitleScene())
...
if __name__ == "__main__":
main()
粒子
import pygame,random
def ascending(speed):
def _ascending(particle):
particle.y -= speed
return _ascending
def kill_at(max_x, max_y):
def _kill_at(particle):
if particle.x < -max_x or particle.x > max_x or particle.y < -max_y or particle.y > max_y:
particle.kill()
return _kill_at
def age(amount):
def _age(particle):
particle.alive += amount
return _age
def fan_out(modifier):
def _fan_out(particle):
d = particle.alive / modifier
d += 1
particle.x += random.randint(-d, d)
return _fan_out
def wind(direction, strength):
def _wind(particle):
if random.randint(0,100) < strength:
particle.x += direction
return _wind
class Particle():
def __init__(self, col, size, *strategies):
self.x, self.y = 0, 0
self.col = col
self.alive = 0
self.strategies = strategies
self.size = size
def kill(self):
self.alive = -1 # alive -1 means dead
def move(self):
for s in self.strategies:
s(self)
black = (0,0,0)
grey = (145,145,145)
light_grey = (192,192,192)
dark_grey = (183, 183, 183)
def smoke_machine():
colors = {0: grey,
1: dark_grey,
2: light_grey}
def create():
for _ in xrange(random.choice([0,0,0,0,0,0,0,1,2,3])):
behaviour = ascending(1), kill_at(1000, 1000), fan_out(400), wind(1, 15), age(1)
p = Particle(colors[random.randint(0, 2)], random.randint(10, 15), *behaviour)
yield p
while True:
yield create()
class Emitter(object):
def __init__(self, pos=(0, 0)):
self.particles = []
self.pos = pos
self.factories = []
def add_factory(self, factory, pre_fill=300):
self.factories.append(factory)
tmp = []
for _ in xrange(pre_fill):
n = next(factory)
tmp.extend(n)
for p in tmp:
p.move()
self.particles.extend(tmp)
def update(self):
for f in self.factories:
self.particles.extend(next(f))
for p in self.particles[:]:
p.move()
if p.alive == -1:
self.particles.remove(p)
def draw(self, screen, position_translater_func):
for p in self.particles:
target_pos = position_translater_func(map(sum, zip((p.x, p.y), self.pos)))
pygame.draw.circle(screen, p.col, target_pos, int(p.size))
结论
我们已经成功地将粒子整合到了游戏中。为此,我们编写了描述各种行为模式的小型独立功能,并使用工厂功能将其组成。
在此过程中,我们了解了closures,generators,SRP,factory-和strategy pattern。
现在,我们可以轻松添加新的行为,例如更改颜色,或创建行为完全不同但使用我们现有功能的新粒子工厂。
例如,尝试使用此函数,该函数将使粒子增长
def grow(amount):
def _grow(particle):
if random.randint(0,100) < particle.alive / 20:
particle.size += amount
return _grow
通过将
grow(0.5)
添加到smoke_machine
中的行为列表中零钱少,效果令人印象深刻。
多好的一天啊!
附注:您可以在this repository上找到更快的版本(和错误修正)。它使用
numpy
, itertools
, psyco
和 pygame.surfarray
并避开random
模块以提高速度。
关于python - 如何实现粒子引擎,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14885349/