5

それで、私はかなり満足している煙用のパーティクル エンジンを作成しました。これは私のゲームにかなり適していると思います。
今、ゲームに実装する必要がありますが、少し問題があります。ゲームでパーティクル エンジンを使用する方法を誰かが説明できるかどうか疑問に思っていました。
両方のコード (煙とゲーム ファイル) を以下に追加しました。
パーティクルをゲーム ファイルとは別に保持したいが、ゲーム内で呼び出したい。
最終的には、エンジン内で呼び出し可能なパーティクル エフェクトをさらに作成したいと考えています。

誰か助けてくれませんか?動作させるには少し調整が必要かもしれません。

粒子コード:

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()
4

1 に答える 1

15

注意してください!ロングポスト!

粒子

まず、あなたの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関数を呼び出します。この関数は、 への入力リストに基づいて、0 ~ 3 個のパーティクルを返しますrandom.choice。これは、70% の確率で 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>]

ジェネレーターで next を呼び出すたびに、最大 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、そのパーティクルを 300pre_fill回 (またはそれ以上) 先に移動します。このように、いくつかの粒子はすでにそこにあります。

パーティクルを描画したい場合は、 の位置を使用してその位置を計算する必要がありEmitterます。また、ゲームのカメラを使用してこの位置を調整する必要があるため、位置を正しい最終位置に変換するパラメーターとして関数を受け入れるだけで、パーティクルがゲーム内で適切にスクロールします。

今すぐ使用するために、ゲーム クラスに大きな変更を加える必要はありませんEmitterGameScene呼び出された に新しいリストを作成し、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()

これで完了です。

アクション !

ここに画像の説明を入力

完全なリスト:

ゲーム.py

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()

粒子.py

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))

結論

パーティクルをゲームに統合することに成功しました。これは、さまざまな動作パターンを記述する自己完結型の小さな関数を記述し、ファクトリ関数を使用して構成することで実現しました。

そして、これを行っている間に、クロージャージェネレーターSRPファクトリー、および戦略パターンについて学びました。

色の変更などの新しい動作を簡単に追加したり、まったく異なる動作をする新しいパーティクル ファクトリを作成したりできますが、既存の関数を使用できます。

たとえば、パーティクルを成長させるこの関数を使用してみてください

def grow(amount):
    def _grow(particle):
        if random.randint(0,100) < particle.alive / 20:
            particle.size += amount
    return _grow

grow(0.5)の動作のリストに追加することによってsmoke_machine

ここに画像の説明を入力

小さな変化、印象的な効果。

なんていい日だ!

PS:このリポジトリで、より高速なバージョン (およびバグ修正) を見つけることができます。numpyitertoolspsycoおよびを使用し、大幅な速度向上のため pygame.surfarrayにモジュールを避けます。random

于 2013-02-15T10:23:22.963 に答える