Now that we’ve got the main game stage all set, we need to populate it with Actors. That is to say the things that move about the screen and interact with one another and with the players.
As previously mentioned, I’m going to approach this from the perspective of a new class of object so that we can easily add new elements without getting embroiled in spaghetti code. This will also mean that we can easily repurpose this framework for use in other game code at a later date.
So, let’s examine what we need for an actor. First and foremost, it’s going to need to know where it is, so we’ll want to have a pair of x and y coordinates. It should also know which direction it is moving in, and how rapidly, so we’ll want to have a speed, direction and for the hell of it an acceleration component too. While we’re at it, we may as well add a heading component to the position variables too, although we’re likely not to use those in this particular game.
That sorts us out for movement and position, but what else? Well, the object will need to know if it has to interact with anything else on screen, so we’ll add a flag to indicate whether the item is solid or not, and we’ll also add a visibility flag so that we can easily hide the element if we need to (this also allows us to add invisible barriers to the screen, which we’ll use to simulate the top and bottom boundaries of the playfield). We’ll also add a tag to each Actor so they can be individually referred to. In the main object list, this will amount to being a simple name which when we instantiate them, we can add a unique counter value to so that way we know which object we’re referring to.
Finally, we’ll add flags to indicate whether the object is active (ie; should be processing updates) and whether it is dying (ie: in the process of being cleaned up) … these are likely flags that we’ll not use here, but may prove useful in the general course of things.
That wraps up the object variables, but what about methods? We’ll obviously have the initialisation routine within the class which will set up default values for all the class variables, but we’ll probably want a Create event to set up specific values for this instance when it is first generated, we’ll also need to have instance specific update and draw methods so the item knows what to do and how to display itself. We’ll also want a routine to handle what happens when the object goes out of scope, so that means some sort of Termination subroutine and finally a method to handle collisions with other objects.
Individual actors are fine, but like any stage production, they need direction, so we want to also build in an ActorManager class. This will need to contain a list of all the current Actors and will have the responsibility for calling each of their update and draw methods at the correct time. It will also need to contain a master list of all possible actors that can be instantiated so we can spawn new actors in as needed.
For the purposes of our Pong game, I’m deliberately keeping all this stuff simple for now so I don’t have to implement more than is needed to get the game up and running – a concept known in Agile programming as Minimum Viable Product – we can later expand on these objects as and when we need to. Some pretty obvious omissions (to me at least) so far is the use of sprites to display colourful animated objects, the ability for objects to be drawn on multiple layers and control of the draw order of each actor.
But we have enough to be going on with for now.
I’m not going to go into depth in the actual code in this post, that’ll be reserved for the next one, so I’ll leave you with the skeleton object definitions that I’ve outlined above:
class Actor:
def __init__(self, manager, tag):
"""Performs any on-load initialisation of the Actor such as defining
sensible defaults for any instance level variables"""
self.x = self.y = 0
self.direction = self.speed = self.acceleration = 0
self.heading = 0
self.manager = manager
self.solid = self.visible = False
self.active = self.dying = False
self.tag = tag
self.id = "" # ID will be set when the object is instantiated
def create(self):
"""Code that executes when an instance of the object is created"""
pass
def update(self):
"""Code that executes once per cycle through the inner game loop"""
pass
def draw(self):
"""Code that executes once per frame to draw the actor"""
pass
def die(self):
"""Code that executes when the actor goes out of scope"""
pass
def on_collide(self, other):
"""Code that executes when the actor collides with another object"""
passclass ActorManager:
def __init__(self, game):
self.game = game # Reference to the main game object
self.objects = {} # Dictionary containing all the basic objects
self.instances = [] # List containing all the active actor instances
def load_objects(self, objects):
"""Initialisation function. Discards the current set of instantiable
objects and allows us to specify a new set"""
pass
def create_instance(self, tag):
"""Creates a new instance of an Actor"""
pass
def destroy_instance(self, instance):
"""Destroys an instance of an Actor"""
pass
def update(self):
"""Update all the active instances"""
def draw(self):
"""Draw all the active instances"""
pass
We know what we are, but know not what we may be.
William Shakespeare