5

私は現在、Pythonとpygameを使用して非常に単純なゲームを書いています。動くものがあります。そして、このようなものをスムーズに動かすために、 Fix Your Timestepで述べたように、補間を使用してメインゲームループを配置しました。

これが私が今補間を処理する方法です。

class Interpolator(object):
    """Handles interpolation"""
    def __init__(self):
        self.ship_prev = None
        self.ship = None

        self.stars_prev = []
        self.stars = []

        self.bullets_prev = {}
        self.bullets = {}

        self.alpha = 0.5

    def add_ship(self, ship):
        self.ship_prev = self.ship
        self.ship = ship

    def add_stars(self, stars):
        self.stars_prev = self.stars
        self.stars = stars[:]

    def add_bullets(self, bullets):
        self.bullets_prev = self.bullets
        self.bullets = bullets.copy()

    def add_enemies(self, enemies):
        self.enemies_prev = self.enemies
        self.enemies = enemies  # to be continued

    def lerp_ship(self):
        if self.ship_prev is None:
            return self.ship
        return lerp_xy(self.ship_prev, self.ship, self.alpha)

    def lerp_stars(self):
        if len(self.stars_prev) == 0:
            return self.stars
        return (lerp_xy(s1, s2, self.alpha) for s1, s2 in izip(self.stars_prev, self.stars))

    def lerp_bullets(self):
        keys = list(set(self.bullets_prev.keys() + self.bullets.keys()))
        for k in keys:
            # interpolate as usual
            if k in self.bullets_prev and k in self.bullets:
                yield lerp_xy(self.bullets_prev[k], self.bullets[k], self.alpha)
            # bullet is dead
            elif k in self.bullets_prev:
                pass
            # bullet just added
            elif k in self.bullets:
                yield self.bullets[k]

lerp_xy()関数とデータ型

def lerp_xy(o1, o2, alpha, threshold=100):
    """Expects namedtuples with x and y parameters."""
    if sqrt((o1.x - o2.x) ** 2 + (o1.y - o2.y) ** 2) > 100:
        return o2
    return o1._replace(x=lerp(o1.x, o2.x, alpha), y=lerp(o1.y, o2.y, alpha))

Ship = namedtuple('Ship', 'x, y')
Star = namedtuple('Star', 'x, y, r')
Bullet = namedtuple('Bullet', 'x, y')

データ型はもちろん一時的なものですが、将来的にはx属性とy属性を持つことを期待しています。更新: lerper.alphaはフレームごとに更新されます。

船は単一のオブジェクトとして追加されます-それはプレイヤーの船です。星がリストとして追加されます。弾丸はdict{id、Bullet}として追加されます。弾丸は常に追加および削除されるため、どちらの弾丸がどれであるかを追跡し、両方が存在するかどうかを補間し、追加または削除されたばかりの場合は何かを実行する必要があります。

とにかく、ここにあるこのコードはがらくたです。機能を追加するにつれて成長しましたが、今はもっと一般的なものに書き直して、成長を続け、うんちマークのような臭い山にならないようにしたいと思っています。

今でも私はPythonにかなり慣れていませんが、リスト内包表記、ジェネレーター、コルーチンについてはすでにかなり快適に感じています。

私が最も経験が少ないのは、Pythonのオブジェクト指向側であり、10行のハッキーな使い捨てスクリプトよりも大きなアーキテクチャを設計することです。

質問は私が知らないもののような質問ではなく、それについて何もできません。私は、私が望むものに何らかの形で近い形で機能するこの非常に単純なコードを書き直すことができると確信しています。

私が知りたいのは、経験豊富なPythonプログラマーがこの単純な問題をPythonの方法で解決する方法です。そのため、私(そしてもちろん他の人)は、Python開発者の間でそのようなケースを処理するエレガントな方法と見なされる方法を学ぶことができました。

だから、私がおおよそ達成したいことは、擬似コードで:

lerper = Interpolator()
# game loop
while(1):
    # physics
    # physics done
    lerper.add(ship)
    lerper.add(stars)
    lerper.add(bullets)
    lerper.add(enemies) # you got the idea

    # rendering
    draw_ship(lerper.lerp('Ship'))
    # or
    draw_ship(lerper.lerp_ship())

ただし、より良い解決策を考えている場合は、その擬似コードで停止させないでください=)

それで。すべてのゲームオブジェクトを個別の/継承されたクラスとして作成しますか?それらすべてにIDを持たせる?それらすべてをlist/dictとして追加しますlerper.add([ship])か?dict / whateverから継承する特別なコンテナクラスを作成しますか?これを解決するためのエレガントでパイソン的な方法は何だと思いますか?どうしますか?

4

3 に答える 3

2

これはあなたが探しているものではないかもしれませんが、うまくいけば、ゲームを書こうとする際に役立つ方向にあなたを動かすでしょう。Python 3.x用に作成された次のレシピは、作業モジュールの作成例を示しています。

  • ベクトル(2D座標を操作するためのクラスを提供し、数値以上のことを行いcomplexます)
  • 処理(アニメーションを作成したり、簡単なゲームを作成したりするための拡張可能なフレームワークを提供します)
  • ボイド(フレームレートとは独立して実行される構造化されたアニメーションを作成する方法を示します)

上記のコードを見て、フレームワークを記述したり、コードを構造化して再利用できるようにするためのインスピレーションとして使用することができます。参照されたプロジェクトは、 Processing.orgに触発されました。

于 2012-08-01T19:13:28.897 に答える
1

私はあなたと同じような「オブジェクトが動き回る」コードを持つ半完成のブレイクアウトクローンを書いたので、私がそれをどのように行ったかを共有します。

位置と速度を持つものはすべて、発射物クラスからインスタンス化されます。動かないオブジェクトも発射物になる可能性があります。発射物は、誰かがそれを要求したときに自分の位置を更新する責任がtickあります。

class Projectile:
    def __init__(self):
        self.x = 0
        self.y = 0
        self.vel_x = 0
        self.vel_y = 0
    def tick(self, dt):
        self.x += dt * self.vel_x
        self.y += dt * self.vel_y

後で、発射体間の衝突検出を実行できるように、軸に沿ったバウンディングボックスのようなものxに置き換えたい場合があります。y

互いに相互作用するすべての発射体は、tick各発射体を処理する責任があるレイヤーに住んでいます。

class Layer:
    def __init__(self):
        self.projectiles = []
    def addProjectile(self, p):
        self.projectiles.add(p)
    def tick(self, dt):
        for p in self.projectiles:
            p.tick(dt)
        #possible future expansion: put collision detection here

セットアップは、必要なプロパティを使用してゲームオブジェクトを作成し、それらをレイヤーに追加するだけです。

#setup
l = Layer()

ship = Projectile()
#not shown: set initial position and velocity of ship
l.addProjectile(ship)

for i in range(numStars):
    star = Projectile()
    #not shown: set initial position and velocity of stars
    l.addProjectile(star)

#not shown: add bullets and enemies to l, the same way stars were

#game loop
while True:
    #get dt somehow
    dt = .42
    l.tick(dt)
    for projectile in l.projectiles:
        draw(l)

描画は、プログラムと鉱山が分岐する場所です。ブレイクアウトのすべてが長方形であるため、すべてのゲームオブジェクトを同じ方法で描画できます。しかし、宇宙侵略者は星のようには見えず、弾丸は宇宙船のようには見えないので、それらはすべて固有のコードを必要とします。この時点で、ProjectileのサブクラスであるShip、Star、Bullet、Enemyなどの作成を検討する必要があります。次に、それぞれが独自の外観を指定できます。彼らはまた、直線で動くことを超えて個々の行動をする可能性があります-例:船はキーを押すと反応し、敵は船に向かって加速します。

于 2012-08-01T19:04:05.583 に答える
1

これが私が補間を処理することになった方法です:

class Thing(object):
    """Generic game object with interpolation"""
    def __init__(self, x=0, y=0):
        self._x = self.x = x
        self._y = self.y = y

    def imprint(self):
        """call before changing x and y"""
        self._x = self.x
        self._y = self.y

    def __iter__(self):
        """handy to unpack like a tuple"""
        yield self.x
        yield self.y

Ship = Thing
Bullet = Thing


class Star(Thing):
    """docstring for Star"""
    def __init__(self, x, y, r):
        super(Star, self).__init__(x, y)
        self.r = r

    def __iter__(self):
        yield self.x
        yield self.y
        yield self.r


def lerp_things(things, alpha, threshold=100):
    """Expects iterables of Things"""
    for t in things:
        if sqrt((t._x - t.x) ** 2 + (t._y - t.y) ** 2) > threshold:
            yield (t.x, t.y)
        else:
            yield (lerp(t._x, t.x, alpha), lerp(t._y, t.y, alpha))
于 2012-08-06T20:08:51.633 に答える