gale.ai.perception module
This file contains a perception/vision-cone system for stealth-style games: a guard’s cone of vision, split into a near zone (instant detection) and a far zone (slower, partial detection), together with a simple line-of-sight check against rectangular obstacles and a Perception class that turns sightings into an alert level posted on a Blackboard for a behavior tree to react to.
Author: Alejandro Mujica (aledrums@gmail.com)
- gale.ai.perception.has_line_of_sight(origin, target, obstacles=None)[source]
Check whether the straight segment from origin to target is not blocked by any of the given obstacles.
- Parameters:
origin (Vector2) – Point the sight line starts from.
target (Vector2) – Point the sight line is aimed at.
obstacles (Sequence[Rect] | None) – Axis-aligned rectangles that block vision. The default value is None, meaning nothing blocks vision.
- Returns:
Whether target is visible from origin, i.e. the segment does not cross any obstacle.
- Return type:
bool
- class gale.ai.perception.VisionCone(origin, range_near=100, range_far=300, half_angle=0.5235987755982988, orientation=0)[source]
Bases:
objectModels a guard’s field of view as a cone: an origin, a facing direction (orientation, in radians), and a half_angle spreading on both sides of that direction. Detection is split into two zones: range_near, within which a visible target is spotted right away, and range_far, up to which a visible target is only partially noticed (see awareness_gain), building up over time instead of being instant.
The cone’s pose can be given either as a plain (position, orientation) pair or as an object exposing .position/.orientation (such as a Kinematic), and it is re-read on every call so the cone always follows its owner as it moves and turns.
Usage example:
guard = Kinematic(x=100, y=100, orientation=0) cone = VisionCone(guard, range_near=80, range_far=250, half_angle=math.radians(30)) if cone.can_see_point(player.position, obstacles=walls):
…
- Parameters:
origin (Any | Vector2)
range_near (float)
range_far (float)
half_angle (float)
orientation (float)
- can_see_point(point, obstacles=None)[source]
- Parameters:
point (Vector2) – The point to test.
obstacles (Sequence[Rect] | None) – Axis-aligned rectangles that block vision. The default value is None, meaning nothing blocks vision.
- Returns:
Whether point lies within range_far, within half_angle of the facing direction, and (if obstacles are given) has a clear line of sight.
- Return type:
bool
- awareness_gain(point, dt, obstacles=None)[source]
Compute how much awareness should build up this tick for a target at point: full speed (dt per second, i.e. 1.0 * dt) within range_near, a fraction of that scaled by how far into the near..far band the target is when beyond range_near but still within range_far, and zero if point is not visible at all.
- Parameters:
point (Vector2) – Position of the potential target.
dt (float) – Time elapsed (in seconds) since the last update.
obstacles (Sequence[Rect] | None) – Axis-aligned rectangles that block vision. The default value is None, meaning nothing blocks vision.
- Returns:
The amount of awareness (in the same 0..1 scale used by Perception) to accumulate for this tick.
- Return type:
float
- class gale.ai.perception.AlertLevel(*values)[source]
Bases:
EnumHow aware a guard currently is of a target.
- UNAWARE = 'unaware'
- SUSPICIOUS = 'suspicious'
- ALERTED = 'alerted'
- class gale.ai.perception.Perception(vision_cones, blackboard=None, decay_rate=0.2, suspicious_threshold=0.3, alerted_threshold=1.0)[source]
Bases:
objectLayers an alert-level state machine on top of one or more VisionCones, turning raw sightings into a 0..1 “awareness” value for a single tracked target (a stealth guard tracking the player being the primary use case) and posting the resulting AlertLevel, together with the last known target position, onto a Blackboard so a behavior tree’s Condition/Action nodes can react to it without knowing anything about vision cones.
Blackboard keys written by update():
“alert_level”: the current AlertLevel.
“last_known_target_position”: the target’s position the last time it was seen (kept while SUSPICIOUS or ALERTED, untouched once the guard goes back to UNAWARE).
“awareness”: the raw 0..1 awareness float.
Usage example:
blackboard = Blackboard() cone = VisionCone(guard, range_near=80, range_far=250, half_angle=math.radians(30)) perception = Perception([cone], blackboard)
# In the game loop: perception.update(dt, player.position, obstacles=walls) if blackboard.get(“alert_level”) == AlertLevel.ALERTED:
…
- Parameters:
vision_cones (Sequence[VisionCone])
blackboard (Blackboard | None)
decay_rate (float)
suspicious_threshold (float)
alerted_threshold (float)
- vision_cones: Sequence[VisionCone]
- blackboard: Blackboard
- decay_rate: float
- suspicious_threshold: float
- alerted_threshold: float
- awareness: float
- update(dt, target_point, obstacles=None)[source]
Look for target_point through every vision cone, accumulate or decay awareness accordingly, and post the resulting state onto the blackboard.
- Parameters:
dt (float) – Time elapsed (in seconds) since the last update.
target_point (Vector2) – Current position of the tracked target.
obstacles (Sequence[Rect] | None) – Axis-aligned rectangles that block vision. The default value is None, meaning nothing blocks vision.
- Returns:
The alert level after this update.
- Return type: