Source code for steering.static
# -*- coding: utf-8 -*-
""" Static movement
This module implements a series of classes and methods that emulate
the behavior of objects moving in a 2D space in a static way
(not involving acceleration)
Notes
-----
This might need a slightly better explaination
"""
import random
import pygame
from pygame_ai.utils import math_utils
[docs]class SteeringOutput(object):
""" Container for Steering data
This class is used as a container for the output of the
:py:class:`~StaticSteeringBehavior` algorithms.
Parameters
----------
velocity : :pgmath:`Vector2`, optional
Linear velocity, defaults to (0, 0)
rotation : int, optional
Angular velocity, defaults to 0
Attributes
----------
velocity : :pgmath:`Vector2`
Linear velocity
rotation : int
Angular velocity
"""
def __init__(self, velocity = None, rotation = None):
if velocity is None:
velocity = pygame.Vector2(0, 0)
self.velocity = velocity
if rotation is None:
rotation = 0
self.rotation = rotation
[docs] def update(self, gameobject, tick):
""" Update a :py:class:`~gameobject.GameObject`'s velocity and rotation
This method should be called once per loop, it updates the given
:py:class:`gameobject.GameObject`'s velocity and rotation based
on this :py:class:`~SteeringOutput`'s acceleration request
Parameters
----------
gameobject : :py:class:`~gameobject.GameObject`
The :py:class:`GameObject` that will be updated
tick : int
Time transcurred since last loop
"""
gameobject.velocity += self.linear * tick
gameobject.rotation += self.rotation * tick
if gameobject.velocity.length() > gameobject.max_speed:
gameobject.velocity.normalize_ip()
gameobject.velocity *= gameobject.max_speed
null_steering = SteeringOutput(velocity = pygame.Vector2(0, 0), rotation = 0)
""":py:class:`SteeringOutput` : Constant with 0 linear velocity and 0 angular velocity """
[docs]class StaticSteeringBehavior(object):
""" Template StaticSteeringBehavior class
This class is a template to supply base methods for StaticSteeringBehaviors.
This class is meant to be subclassed since the methods here are just placeholders
"""
def __repr__(self):
""" If not overriden, returns class name """
return type(self).__name__
[docs] def draw_indicators(self, screen, offset = (lambda pos: pos)):
""" Draws appropiate indicators for each :py:class:`~StaticSteeringBehavior`
Parameters
----------
screen: :pgsurf:`Surface`
Surface in which to draw indicators, normally this would be the screen Surface
offset: function, optional
Function that applies an offset to the object's position
This is meant to be used together with scrolling cameras,
leave empty if your game doesn't implement one,it defaults
to a linear function f(pos) -> pos
"""
pass
[docs] def get_steering(self):
""" Returns a steering request
Returns
-------
:py:class:`SteeringOutput`
Requested steering
"""
return null_steering
[docs]class Seek(StaticSteeringBehavior):
""" :py:class:`~StaticSteeringBehavior` that makes the character **Seek** a target
Parameters
----------
character: :py:class:`~gameobject.GameObject`
Character with this behavior
target: :py:class:`~gameobject.GameObject`
Target to **Seek**
"""
def __init__(self, character, target):
self.character = character
self.target = target
def get_steering(self):
# Create structure for output
steering = SteeringOutput()
# Get direction to the target
steering.velocity = self.target.position - self.character.position
# Velocity is along this direction at full speed
if(steering.velocity[0] != 0 or steering.velocity[1] != 0):
steering.velocity.normalize_ip()
steering.velocity *= self.character.max_speed
# Face in the direction of velocity
if(math_utils.is_not_null(steering.velocity)):
self.character.orientation = math_utils.get_angle_from_vector(steering.velocity)
# Return the steering
steering.rotation = 0
return steering
[docs]class Flee(StaticSteeringBehavior):
""" :py:class:`~StaticSteeringBehavior` that makes the character **Flee** from a target
Parameters
----------
character: :py:class:`~gameobject.GameObject`
Character with this behavior
target: :py:class:`~gameobject.GameObject`
Target to **Flee** from
"""
def __init__(self, character, target):
self.character = character
self.target = target
def get_steering(self):
# Create structure for output
steering = SteeringOutput()
# Get direction to the target
steering.velocity = self.character.position - self.target.position
# Velocity is along this direction at full speed
if(steering.velocity[0] != 0 or steering.velocity[1] != 0):
steering.velocity.normalize_ip()
steering.velocity *= self.character.max_speed
# Face in the direction of velocity
if(math_utils.is_not_null(steering.velocity)):
self.character.orientation = math_utils.get_angle_from_vector(steering.velocity)
# Return the steering
steering.rotation = 0
return steering
[docs]class Arrive(StaticSteeringBehavior):
""" :py:class:`~StaticSteeringBehavior` that makes the character **Arrive** at a target
Parameters
----------
character: :py:class:`~gameobject.GameObject`
Character with this behavior
target: :py:class:`~gameobject.GameObject`
Target to **Arrive** at
radius: int, optional
Distance from the center of the target at which the character will stop
time_to_arrive: float, optional
Estimated time, in seconds, to **Arrive** at the target
"""
def __init__(self, character, target, radius = None, time_to_arrive = 0.25):
# Complete unprovided values
if radius is None:
radius = int(math.sqrt((target.rect.height/2)**2 + (target.rect.width/2)**2)*1.5)
self.character = character
self.target = target
self.radius = radius
self.time_to_arrive = time_to_arrive
def get_steering(self):
# Create structure for output
steering = SteeringOutput()
# Get direction to the target
steering.velocity = self.target.position - self.character.position
# Check if we're within radius
if steering.velocity.length() < self.radius:
return null_steering
# Clip to get there in time_to_arrive
steering.velocity /= self.time_to_arrive
# If it is too fast, clip it to character's speed
if steering.velocity.length() > self.character.max_speed:
steering.velocity.normalize_ip()
steering.velocity *= self.character.max_speed
# Face in the direction of velocity
if(math_utils.is_not_null(steering.velocity)):
self.character.orientation = math_utils.get_angle_from_vector(steering.velocity)
# Return the steering
steering.rotation = 0
return steering
[docs]class Wander(StaticSteeringBehavior):
""" :py:class:`~StaticSteeringBehavior` that makes the character **Wander**
This behavior makes the character move with it's maximum speed in a
particular direction for a random period of time, after that the
character's orientation is changed randomly using the character's
:py:attr:`~gameobject.GameObject.max_rotation`.
Parameters
----------
character: :py:class:`~gameobject.GameObject`
Character with this behavior
"""
def __init__(self, character):
self.character = character
self.counter = 0
self.max_timer = random.randint(7, 13)
def get_steering(self):
# Output steering
steering = SteeringOutput()
steering.rotation = 0
# Get velocity from orientation
steering.velocity = math_utils.orientation_asvector(self.character.orientation)
steering.velocity *= self.character.max_speed
# Change orientation randomly after random amount of iterations
if(self.counter > self.max_timer):
steering.rotation = (random.random() - random.random())*self.character.max_rotation
self.counter = 0
self.max_timer = random.randint(7, 13)
self.counter += 1
return steering