2012-08-01 55 views
5

我目前正在使用python和pygame編寫一個非常簡單的遊戲。它有東西移動。爲了讓這個東西順利移動,我按照Fix Your Timestep的說法安排了主要遊戲循環,並進行了插值。你將如何處理python遊戲中的插值?

這是我現在如何處理插值。

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的OO方面,並且設計了比10行hacky一次性腳本更大的東西。

這個問題不是一個問題,因爲我不知道什麼,也不能做任何事情。我敢肯定,我有能力重寫這個非常簡單的代碼,它可以在某種程度上接近我想要的。我想知道的是,有經驗的Python程序員會以pythonic的方式解決這個簡單的問題,所以我(當然還有其他人)可以學習在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?將它們全部添加爲列表/字典lerper.add([ship])?做一個特殊的容器類繼承字典/什麼?你認爲什麼是解決這個問題的優雅,pythonic方式?你會怎麼做?

回答

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

我寫了一個半完成的突破克隆,其中包含與您的代碼類似的「對象移動」代碼,所以我會分享我是如何做到的。

任何具有位置和速度的東西都是從Projectile類實例化的。非移動物體也可以是射彈。當有人呼叫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 

以後,你可能想要的東西,如一個axis-aligned bounding box更換xy,所以你可以做的拋射之間的碰撞檢測。

所有與彼此相互作用的投射物都存在於一個圖層中,這個圖層負責每個投射物。

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) 

繪畫是你的程序和我岔開 - 一切都在突圍是一個矩形,所以每場比賽對象可以以同樣的方式繪製。但是太空侵略者看起來不像星星,子彈看起來不像太空船,所以他們都需要獨特的代碼。在這一點上,你應該考慮製作「子彈」的子類Ship,Star,Bullet,Enemy等等。然後每個人都可以指定他們自己的外表。他們也可能擁有超越直線移動的個人行爲 - 例如。船隻對按鍵響應,敵人加速朝向船隻等。

+1

你看了整個問題?我沒有看到任何與插值有關的內容或我在答案中提出的問題。我在你的回答中看到的唯一「想法」是「讓每個遊戲對象自己處理插值」,但你甚至不提及它。O.o – Mikka 2012-08-01 19:11:42

+0

也許我誤解了你的問題。我認爲,「我如何修改我的遊戲,使其更加通用,並且可以輕鬆擴展?」。如果你的問題實際上是「如何修改我的插值算法使其更通用?」,那麼你已經猜到了我的觀點:讓每個遊戲對象處理自己的插值。 (最好只有一次,在射彈基類中) – Kevin 2012-08-01 19:19:48

2

這可能不是你正在尋找的東西,但它有望在試圖編寫遊戲時幫助你在一個有用的方向。爲Python 3.x編寫的以下配方提供了編寫工作模塊的示例。

  • vector(提供一類與2D座標的工作和不超過complex號)
  • processing(提供用於創建動畫或進行簡單的遊戲,可擴展的框架)
  • boids(演示瞭如何創建一個獨立於幀速率運行的結構化動畫)

您可以看看上面提供的代碼,並將其用作編寫框架或str的靈感指導你的代碼,使它變得可重用。引用的項目受到Processing.org的啓發。