Source code for examples.mousefollower.mousefollower2

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Example usage of the symplehfsm statemachine for a game entity using pygame.
"""

__version__ = "0.0.1.dev"
__author__ = "dr0iddr0id {at} gmail [dot] com (C) 2012"

import math

import pygame

from operator import methodcaller

import symplehfsm
from symplehfsm import Structure
from symplehfsm import SympleDictHFSM
from symplehfsm import BaseHFSMTests

# ------------------------------------------------------------------------------


# ------------------------------------------------------------------------------

### 1. define the events the statemachin understands
###    here we have the range sensor output and the update defined as events

[docs]class Events(object): in_range = 1 out_of_range = 2 update = 3 # ------------------------------------------------------------------------------ ### 2. define the actions (the action methods like entry, exit, guard and action methods of the ### states and transitions) the statemachine can call ### here only in the following state has something to be done
[docs]class Actions(object):
[docs] def following_update(self): pass # ------------------------------------------------------------------------------ ### 3. define the structure of the statemachine, e.g. what states it has and what transitions between those states ### also define the entry, exit, guard and action method to call (only if needed)
structure = Structure() # state, parent, initial, entry, exit structure.add_state("parent", None, False) # all containing state structure.add_state( "idle", "parent", True) structure.add_state( "following", "parent", False) # handling state, event, next state, action, (guard,) name structure.add_trans( "following", Events.update, "following", methodcaller("following_update"), name="following->following") # self transition structure.add_trans( "following", Events.out_of_range, "idle", name="following->idle") structure.add_trans( "idle", Events.in_range, "following", name="idle->following") # for greater execution speed, apply when statemachine is stable and works as expected # structure.do_optimize() # ------------------------------------------------------------------------------ ### 4. implement the real actions used, e.g. the entity behavior in each state
[docs]class EntityActions(object): def __init__(self, entity): self.entity = entity
[docs] def following_update(self): mx, my = pygame.mouse.get_pos() dx = mx - self.entity.x dy = my - self.entity.y dist = math.hypot(dx, dy) if dist > 0: dirx = dx / dist diry = dy / dist self.entity.x += dirx * self.entity.speed self.entity.y += diry * self.entity.speed # ------------------------------------------------------------------------------ ### 5. the entity that will use the state machine. It has also the proximity sensor code ### to generate the in_range/out_of_range events
[docs]class Entity(Actions): def __init__(self): self.x = 300 self.y = 300 self.radius = 100 self.speed = 1 self.sm = SympleDictHFSM(structure, EntityActions(self)) self.sm.init() # self.mouse_in_range = False
[docs] def update(self, dt): # # little caching, only send an event to statemachine if something actually changed # in_range = self.proximity_sensor() # if in_range != self.mouse_in_range: # self.mouse_in_range = in_range # if in_range: # self.sm.handle_event(Events.in_range) # else: # self.sm.handle_event(Events.out_of_range) # this code is without caching, just throwing an event at the state that gets ignored if self.proximity_sensor(): self.sm.handle_event(Events.in_range) else: self.sm.handle_event(Events.out_of_range) # update self.sm.handle_event(Events.update)
[docs] def proximity_sensor(self): mx, my = pygame.mouse.get_pos() dx = self.x - mx dy = self.y - my dist = math.hypot(dx, dy) if dist < self.radius: return True return False
[docs] def draw(self, surf): pygame.draw.circle(surf, (255, 255, 0), (self.x, self.y), 5, 0) pygame.draw.circle(surf, (255, 0, 0), (self.x, self.y), self.radius, 1) # ------------------------------------------------------------------------------ ### little main loop
[docs]def main(): entity = Entity() screen = pygame.display.set_mode((800, 600)) clock = pygame.time.Clock() running = True while running: dt = clock.tick(30) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: running = False entity.update(dt) screen.fill((0, 0, 0)) entity.draw(screen) pygame.display.flip() # ------------------------------------------------------------------------------
if __name__ == '__main__': main()