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?
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.
complex
numbers)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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With