Pyglet and ecs Part 2: Flying Around

So right now we can give entities images. However, they don’t do anything on screen other than stay very still in the location we put them. To change this we need to give the entities a Physics component this will allow for the entity to update its position.


#components.py

class Physics(Component):
    def __init__(self, vx=0.0, vy=0.0, thrust=100.0, rotate_speed=100.0):
        self.vx = vx #the velocity in the x axis
        self.vy = vy #the velocity in the y axis
        self.thrust = thrust #the amount of force we can apply
        self.rotate_speed = rotate_speed #how quickly we can rotate.

We also need to build the accompanying PhysicsSystem, which will require the use of Physics component and the Transform component.


#systems.py
class PhysicsSystem(System):
    def __init__(self):
        System.__init__(self)
        self.drag = 0.4

    def update(self, dt):
        for entity, phys_comp  in self.entity_manager.pairs_for_type(Physics):
            #we need our transform component to update the position of the entity
            pos_comp= self.entity_manager.component_for_entity(entity, Transform)

            #times it by the delta time to get frame rate independent movement.
            pos_comp.x += phys_comp.vx * dt
            pos_comp.y += phys_comp.vy * dt

            #apply drag to velocity.
            phys_comp.vx -= phys_comp.vx * self.drag *dt
            phys_comp.vy -= phys_comp.vy * self.drag *dt

To get our player to start moving we need to update the entity factory create_player() method and add the system into our SystemsManager. To test the movement system is working we can give our player an initial velocity, it’s a one line addition after the other components


#entity_factory.py

#.... rest of create_player()
world.em.add_component(e, Transform(100,100))
world.em.add_component(e, Physics(vx=50.0, thrust=400.0))

return e

#main.py

##add this directly -before- the other systems.
 world.sm.add_system(PhysicsSystem())

Running this code should see the player move to the right for a short while before stopping. (or as close as it can get to stopping – it wont ever reach zero currently!)

To control the ship the entity is going to need another component to signify it is being controlled by the player. This is our simplest component yet.

#components.py
class Player(Component):
    def __init__(self):
        pass

What this component is doing is flagging this entity as the player, so in our system we can ask for all entities with this component. The system will handle input from the player and update the physics component of the flagged entity.

#systems.py

class PlayerInputSystem(System):
    def __init__(self, window):
        System.__init__(self)
        self.key_handler = key.KeyStateHandler()

    def update(self, dt):
        pairs = self.entity_manager.pairs_for_type(Player)
        for entity, player_component in pairs:
            transform_component = self.entity_manager.component_for_entity(entity, Transform)
            physics_component = self.entity_manager.component_for_entity(entity, Physics)

            if self.key_handler[key.UP] or self.key_handler[key.W]:
                physics_component.vy += physics_component.thrust * dt

            if self.key_handler[key.DOWN] or self.key_handler[key.S]:
                physics_component.vy -= physics_component.thrust * dt

This simple implementation will cause your player ship to go up and down the screen – there are no bounds checks so it can leave the screen. The movement we want to have is rotation from the left and right keys and then the up and down keys regulating the thrust forward and back in the direction the ship is pointing. Using maths we can find out the force applied in the x and y coordinates from its angle.

#systems.py

class PlayerInputSystem(System):
    def __init__(self, window):
        System.__init__(self)
        self.key_handler = key.KeyStateHandler()

    def update(self, dt):
        pairs = self.entity_manager.pairs_for_type(Player)
        for entity, player_component in pairs:
            transform_component = self.entity_manager.component_for_entity(entity, Transform)
            physics_component = self.entity_manager.component_for_entity(entity, Physics)

            if self.key_handler[key.LEFT] or self.key_handler[key.A]:
                #turn the ship counter clockwise.
                transform_component.rotation -= physics_component.rotate_speed * dt

            if self.key_handler[key.RIGHT] or self.key_handler[key.D]:
                #turn the ship clockwise
                transform_component.rotation += physics_component.rotate_speed * dt

            if self.key_handler[key.UP] or self.key_handler[key.W]:
                angle_radians = -math.radians(transform_component.rotation)
                force_x = math.cos(angle_radians) * physics_component.thrust * dt
                force_y = math.sin(angle_radians) * physics_component.thrust * dt
                physics_component.vx += force_x
                physics_component.vy += force_y

            if self.key_handler[key.DOWN] or self.key_handler[key.S]:
                angle_radians = -math.radians(transform_component.rotation)
                force_x = math.cos(angle_radians) * physics_component.thrust * dt
                force_y = math.sin(angle_radians) * physics_component.thrust * dt
                physics_component.vx -= force_x
                physics_component.vy -= force_y

To get the player input to register properly we have to add this system slightly differently – we need to register the KeyStateHandler we made. So in our main file we need to add this system first in our list, above the PhysicsSystem and we also need to make sure to assign our player a Player component

#main.py

if __name__ == '__main__':
    input_sys = PlayerInputSystem(world.window)
    world.window.push_handlers(input_sys.key_handler)
    world.sm.add_system(input_sys)
#...

#entity_factory.py
#.... add to create player...
    world.em.add_component(e, Physics(vx=50.0, thrust=400.0))
    world.em.add_component(e, Player())

return e

The ship should now be controllable in an asteroid-type fashion! Play with things like the drag and thrust speeds to get a feel for how you want the movement to be.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s