gale.ai.formation module

This file contains support for coordinated group movement: a FormationPattern gives each slot in a formation an offset relative to an anchor point (LineFormation, WedgeFormation, CircleFormation are fixed patterns; ScalableFormationPattern adapts one to however many members currently occupy it), SlotAssignment assigns characters to slots minimizing total movement, and FormationManager ties it all together as “two-level” steering: the anchor moves on its own (driven by whatever steering behavior you give it, e.g. Arrive at a destination), and each member is steered towards anchor.position + pattern.slot_offset(slot, member_count) – so members follow the anchor’s motion for free, without each one needing to know where the group as a whole is going.

Note that “emergent” formations (a flock/crowd that stays together without any explicit pattern) don’t need any of this: they fall out of combining Separation, a cohesion Seek towards the group’s centroid, and VelocityMatch (alignment) with BlendedSteering, all of which already exist in gale.ai.steering.

Author: Alejandro Mujica (aledrums@gmail.com)

class gale.ai.formation.FormationPattern[source]

Bases: object

Base class for a formation pattern: gives the offset, relative to the formation’s anchor, that a given slot should occupy.

slot_offset(slot, member_count)[source]
Parameters:
  • slot (int) – Index of the slot to get the offset of.

  • member_count (int) – How many members currently occupy the formation.

Returns:

The offset, relative to the anchor’s position, slot should be placed at.

Return type:

Vector2

class gale.ai.formation.LineFormation(spacing=40)[source]

Bases: FormationPattern

Slots placed in a straight line, spaced spacing apart, centered on the anchor.

Parameters:

spacing (float)

slot_offset(slot, member_count)[source]
Parameters:
  • slot (int) – Index of the slot to get the offset of.

  • member_count (int) – How many members currently occupy the formation.

Returns:

The offset, relative to the anchor’s position, slot should be placed at.

Return type:

Vector2

class gale.ai.formation.WedgeFormation(spacing=40, depth=40)[source]

Bases: FormationPattern

Slots placed in a V shape opening behind the anchor (slot 0), two per following row, spreading out and back.

Parameters:
  • spacing (float)

  • depth (float)

slot_offset(slot, member_count)[source]
Parameters:
  • slot (int) – Index of the slot to get the offset of.

  • member_count (int) – How many members currently occupy the formation.

Returns:

The offset, relative to the anchor’s position, slot should be placed at.

Return type:

Vector2

class gale.ai.formation.CircleFormation(radius=60)[source]

Bases: FormationPattern

Slots placed evenly around a circle centered on the anchor.

Parameters:

radius (float)

slot_offset(slot, member_count)[source]
Parameters:
  • slot (int) – Index of the slot to get the offset of.

  • member_count (int) – How many members currently occupy the formation.

Returns:

The offset, relative to the anchor’s position, slot should be placed at.

Return type:

Vector2

class gale.ai.formation.ScalableFormationPattern(base_pattern, reference_count=4)[source]

Bases: FormationPattern

Wraps another FormationPattern and scales its spacing so the whole formation’s footprint stays close to a target size regardless of how many members currently occupy it, instead of growing without bound as members are added.

Parameters:
slot_offset(slot, member_count)[source]
Parameters:
  • slot (int) – Index of the slot to get the offset of.

  • member_count (int) – How many members currently occupy the formation.

Returns:

The offset, relative to the anchor’s position, slot should be placed at.

Return type:

Vector2

class gale.ai.formation.SlotAssignment[source]

Bases: object

Assigns characters to formation slots minimizing total movement, by greedily giving each character the closest still-available slot – not globally optimal, but cheap and good enough to avoid members crossing paths on every reassignment, and stable enough that a member usually keeps its slot across calls.

static assign(characters, pattern, anchor)[source]
Parameters:
  • characters (Sequence[Kinematic]) – The characters to assign to slots.

  • pattern (FormationPattern) – The formation pattern to assign slots from.

  • anchor (Vector2) – The formation’s current anchor position.

Returns:

A mapping from slot index to the character assigned to it, for slots 0..len(characters) - 1.

Return type:

Dict[int, Kinematic]

class gale.ai.formation.FormationManager(anchor, pattern)[source]

Bases: object

Drives a group of characters as a formation: an anchor Kinematic represents the group’s overall position/orientation (steer it yourself with any SteeringBehavior, e.g. Arrive at a destination), and each member gets a per-slot target Kinematic positioned at anchor.position + pattern.slot_offset(slot, member_count) for you to steer the member towards (e.g. with Arrive). Call update() once a frame after moving the anchor, and add_member/remove_member as the group’s membership changes – both trigger a fresh SlotAssignment so slots stay filled without gaps.

Usage example:

anchor = Kinematic(x=0, y=0) manager = FormationManager(anchor, WedgeFormation()) manager.add_member(soldier_1) manager.add_member(soldier_2)

# In the game loop, after steering the anchor with your own # behavior (e.g. anchor_arrive.get_steering() then anchor.update(…)): manager.update() for member in manager.members:

slot_target = manager.slot_kinematic(member) Arrive(member, slot_target).get_steering(dt)

Parameters:
set_pattern(pattern)[source]

Switch to a different formation pattern (e.g. a different tactical “play”), reassigning slots on the next update().

Parameters:

pattern (FormationPattern) – The new pattern to arrange members in.

Return type:

None

add_member(character, role=None)[source]
Parameters:
  • character (Kinematic) – The character to add to the formation.

  • role (str | None) – An optional label for whichever slot this character ends up assigned to (e.g. “leader”, “flank”). The default value is None.

Return type:

None

remove_member(character)[source]
Parameters:

character (Kinematic) – The character to remove from the formation.

Return type:

None

update()[source]

Reassign slots (via SlotAssignment) and refresh each member’s slot-target Kinematic position/orientation. Call this once a frame, after moving self.anchor.

Return type:

None

slot_kinematic(character)[source]
Parameters:

character (Kinematic) – A member of this formation.

Returns:

The Kinematic representing character’s current slot target, to be steered towards (e.g. with Arrive). Only valid after update() has been called at least once since character was added.

Raises:

KeyError – If character has never been assigned a slot yet.

Return type:

Kinematic

role_of(character)[source]
Parameters:

character (Kinematic) – A member of this formation.

Returns:

The role character was added with, or None if it wasn’t given one.

Return type:

str | None