Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you handle interpolation in a python game?

Tags:

python

pygame

I'm currently writing a very simple game using python and 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.

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

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. update: lerper.alpha is updated each frame.

Ship is added as a single object - it's the player 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.

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.

Now I'm still pretty new to Python, though I feel pretty comfortable with list comprehensions, generators and coroutines already.

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.

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.

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])? Make a special container class inheriting from dict/whatever? What do you consider an elegant, pythonic way of solving this? How would you do it?

like image 854
Mikka Avatar asked Oct 07 '22 01:10

Mikka


1 Answers

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.

  • vector (provides a class for working with 2D coordinates and does more than complex numbers)
  • 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)

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.

like image 152
Noctis Skytower Avatar answered Oct 18 '22 07:10

Noctis Skytower