简体   繁体   English

你会如何处理python游戏中的插值?

[英]How would you handle interpolation in a python game?

I'm currently writing a very simple game using python and pygame. 我目前正在使用python和pygame编写一个非常简单的游戏。 It has stuff that moves. 它有移动的东西。 And to make this stuff move smoothly I arranged the main game loop as said in Fix Your Timestep , with interpolation. 为了让这些东西顺利移动,我安排了主要的游戏循环,如Fix Your Timestep中所述,使用插值。

Here is how I handle interpolation now. 这是我现在处理插值的方法。

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]

The lerp_xy() function and data types 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')

The data types are of course temporary, but I still expect they will have the x and y attributes in the future. 数据类型当然是暂时的,但我仍然期望它们将来会有x和y属性。 update: lerper.alpha is updated each frame. 更新:每帧更新 lerper.alpha。

Ship is added as a single object - it's the player ship. Ship被添加为单个对象 - 它是玩家船。 Stars are added as a list. 星星被添加为列表。 Bullets are added as a dict {id, Bullet}, since bullets are added and removed all the time, I have to track which bullet is which, interpolate if both are present and do something if it just has been added or deleted. 子弹被添加为dict {id,Bullet},因为子弹一直被添加和删除,我必须跟踪哪个子弹,如果两者都存在则进行插值,如果刚添加或删除则执行某些操作。

Anyway this code right here is crap. 无论如何,这里的代码就是垃圾。 It grew as I added features, and now I want to rewrite it to be more generic, so it can continue growing and not become an unpythonic stinky pile of poo. 它随着我添加的功能而增长,现在我想将它重写为更通用的,所以它可以继续增长而不会成为一个单声道臭臭的poo。

Now I'm still pretty new to Python, though I feel pretty comfortable with list comprehensions, generators and coroutines already. 现在我还是Python的新手,虽然我对列表推导,生成器和协同程序已经感觉很舒服了。

What I have the least experience with is the OO side of Python, and designing an architecture of something bigger than a 10-line hacky disposable script. 我最不体验的是Python的OO方面,并设计了比10行hacky一次性脚本更大的架构。

The question is not a question as in something I don't know and can't do anything about it. 这个问题不是我不知道的事情,也不能做任何事情。 I'm sure I'm capable of rewriting this pretty simple code that will work in some way close to what I want. 我确信我能够重写这个非常简单的代码,它可以在某种程度上接近我想要的。

What I do want to know, is the way experienced Python programmers will solve this simple problem in a pythonic way, so I (and of course others) could learn what is considered an elegant way of handling such a case among Python developers. 我真正想知道的是,经验丰富的Python程序员将以pythonic的方式解决这个简单的问题,所以我(当然还有其他人)可以学习在Python开发人员中处理这种情况的优雅方式。

So, what I approximately want to achieve, in pseudocode: 那么,我想以伪代码实现的目标:

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

However don't let that pseudocode stop you if you have a better solution in mind =) 但是,如果您有更好的解决方案,请不要让伪代码阻止您=)

So. 所以。 Make all game objects as separate/inherited class? 将所有游戏对象作为单独/继承的类? Force them all to have id? 强迫他们都有身份证? Add them all as list/dict lerper.add([ship]) ? 将它们全部添加为list / dict lerper.add([ship]) Make a special container class inheriting from dict/whatever? 创建一个继承自dict / whatever的特殊容器类? What do you consider an elegant, pythonic way of solving this? 您认为解决这个问题的优雅,pythonic方式是什么? How would you do it? 你会怎么做?

This may not be what you are looking for, but it will hopefully nudge you in a helpful direction in trying to write a game. 这可能不是你想要的,但它有望在尝试编写游戏时向你推动一个有用的方向。 The following recipes written for Python 3.x provide an example of writing working modules. 为Python 3.x编写的以下配方提供了编写工作模块的示例。

  • vector (provides a class for working with 2D coordinates and does more than complex numbers) vector (提供一个用于处理2D坐标的类,并且不仅仅是complex
  • processing (provides an extensible framework for creating an animation or making a simple game) 处理 (提供可扩展的框架,用于创建动画或制作简单的游戏)
  • boids (demonstrates how to create a structured animation that runs independently from frame rate) boids (演示如何创建独立于帧速率运行的结构化动画)

You might look at the code provided above and use it as an inspiration for writing your framework or structuring your code so it becomes reusable. 您可以查看上面提供的代码,并将其用作编写框架或构建代码的灵感,以便它可以重用。 The referenced project was inspired by Processing.org . 引用的项目受Processing.org的启发。

I wrote a half-finished Breakout clone that has similar "objects moving around" code as yours, so I'll share how I did it. 我写了一个半完成的Breakout克隆,它有类似的“物体移动”代码,所以我将分享我是如何做到的。

Anything with a position and velocity is instantiated from the Projectile class. 任何具有位置和速度的东西都会从Projec类中实例化。 Non-moving objects can be projectiles too. 非移动物体也可以是抛射物。 A projectile is responsible for updating its own position when somebody calls tick on it. 抛射负责更新自己的位置,当有人呼吁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

Later, you may want to replace x and y with something like a axis-aligned bounding box , so you can do collision detection between projectiles. 之后,您可能希望将xy替换为轴对齐的边界框 ,这样您就可以在射弹之间进行碰撞检测。

All projectiles that interact with one another live in a Layer, which is responsible for tick ing each projectile. 所有彼此相互作用的射弹都存在于一个层中,该层负责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

Set up is just a matter of creating game objects with the properties you want, and adding them to a layer. 设置只需使用您想要的属性创建游戏对象,并将它们添加到图层。

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

Drawing is where your program and mine diverge - everything in breakout is a rectangle, so every game object can be drawn in the same way. 绘图是您的程序和我的分歧 - 突破中的所有内容都是一个矩形,因此每个游戏对象都可以以相同的方式绘制。 But a space invader doesn't look like a star, and a bullet doesn't look like a space ship, so they would all need unique code. 但是太空入侵者看起来不像星星,子弹看起来不像太空船,所以他们都需要独特的代码。 At this point, you should consider making Ship, Star, Bullet, Enemy, etc, which are subclasses of Projectile. 此时,您应该考虑制造Ship,Star,Bullet,Enemy等,它们是Projectile的子类。 Then each one can specify their own appearance. 然后每个人都可以指定自己的外观。 They could also have individual behaviors beyond moving in straight lines - ex. 除了直线行动之外,他们还可以有个人行为 - 例如 Ships respond to keypresses, Enemies accelerate towards ships, etc. 船舶响应按键,敌人加速驶向船只等。

Here is how I ended up handling the interpolation: 以下是我最终处理插值的方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM