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()