Skip to content

Commit 02560ad

Browse files
committed
Add comments and docstrings.
1 parent 1e1bd57 commit 02560ad

File tree

6 files changed

+153
-86
lines changed

6 files changed

+153
-86
lines changed

config.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
1-
SCREEN_WIDTH = 600
2-
SCREEN_HEIGHT = 600
1+
SCREEN_WIDTH = 600 # The width of the formation designer and simulation
2+
SCREEN_HEIGHT = 600 # The height of the formation designer and simulation
33

4-
DEBUG = False
5-
6-
MAX_SPEED = 2
7-
VISION_RANGE = 150
8-
COLLISION_RANGE = 20
4+
MAX_SPEED = 2 # Max speed of a unit
5+
VISION_RANGE = 150 # The vision range of a unit
6+
COLLISION_RANGE = 20 # The collision range of a unit
97

8+
# Colour definitions for easy referencing
109
WHITE = (255, 255, 255)
1110
RED = (255, 0, 0)
1211
CYAN = (0, 100, 200)
1312
GREEN = (0, 100, 0)
1413

14+
# The priority and order the forces are applied in.
1515
FORCE_PRIORITY_LIST = (
1616
'avoidance', 'formation',
1717
)
1818

19-
AV_WEIGHT = 0.5
20-
F_WEIGHT = 0.6
21-
22-
23-
def debug_print(*args):
24-
if DEBUG:
25-
string = ''
26-
for item in args:
27-
string += ' ' + str(item)
28-
print string
19+
AV_WEIGHT = 0.5 # The weighting of avoidance behaviours
20+
F_WEIGHT = 0.6 # The weighting of the formation behaviour

enemy.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
from boid import Boid
1+
from unit import Unit
22
from vec2d import Vec2d
33

44

5-
class Enemy(object, Boid):
5+
class Enemy(object, Unit):
6+
"""Enemy class. Based on the Unit class."""
67
def __init__(self, start, end):
7-
Boid.__init__(self, start, None, None)
8+
"""Create an Enemy with the path from start to end."""
9+
Unit.__init__(self, start, None, None)
810
self._start = Vec2d(start.x, start.y)
911
self._end = end
1012
self._target = end
1113

1214
def apply_velocity(self, **kwargs):
15+
"""Calculate the required velocity to head towards the current target.
16+
Apply the velocity and regenerate the vertices.
17+
"""
1318
if self.position.get_distance(self._target) < 30:
1419
if self._target == self._start:
1520
self._target = self._end
@@ -22,4 +27,5 @@ def apply_velocity(self, **kwargs):
2227
self.generate_vertices()
2328

2429
def update(self, **kwargs):
30+
"""Overridden update method. Just applies velocity."""
2531
self.apply_velocity()

formation.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
from vec2d import Vec2d
2-
from boid import Boid
2+
from unit import Unit
33
from waypoint import Waypoint
44

55

66
class Formation(object):
7+
"""Formation that handles all units it manages. Used to handle unit
8+
placement and waypoint updating.
9+
"""
710
def __init__(self, filename=None):
11+
"""Create a formation. If a filename is specified, the formation
12+
is loaded from there, otherwise, the file has to be specified later.
13+
"""
814
self.waypoint = None
915
self._center = Vec2d(0, 0)
1016
self._direction = Vec2d(0, 0)
@@ -14,6 +20,9 @@ def __init__(self, filename=None):
1420
self.parse_formation_file(filename)
1521

1622
def parse_formation_file(self, open_file):
23+
"""Open and parse the given formation file. Set up all the required
24+
variables for movement and units and their waypoints.
25+
"""
1726
if open_file:
1827
self._center = Vec2d(eval(open_file.readline()))
1928
self._direction = Vec2d(eval(open_file.readline()))
@@ -22,34 +31,54 @@ def parse_formation_file(self, open_file):
2231
self.waypoints.append(Waypoint(Vec2d(eval(line)), self.center))
2332

2433
def set_waypoint(self, waypoint_pair_or_x, y=None):
34+
"""Set the formations waypoint. Either using a tuple, or by specifying
35+
the x and y coordinates as separate arguments.
36+
"""
2537
if y is None:
2638
self.waypoint = Vec2d(waypoint_pair_or_x)
2739
else:
2840
self.waypoint = Vec2d(waypoint_pair_or_x, y)
2941

3042
def set_center(self, center_pair_or_x, y=None):
43+
"""Set the center of the formation.Either using a tuple, or by
44+
specifying the x and y coordinates as separate arguments.
45+
"""
3146
if y is None:
3247
self.waypoint = center_pair_or_x
3348
else:
3449
self.waypoint = Vec2d(center_pair_or_x, y)
3550

3651
def gen_and_get_boids(self):
37-
return [Boid((position + self.center), waypoint, self)
52+
"""Generate all the units in the formation based on their relative
53+
positions and the formation center. Give them a waypoint managed by
54+
the formation. Return a list.
55+
"""
56+
return [Unit((position + self.center), waypoint, self)
3857
for position, waypoint in zip(self.positions, self.waypoints)]
3958

4059
@property
4160
def center(self):
61+
"""Return a Vec2D with the center for drawing purposes."""
4262
return Vec2d(int(self._center[0]), int(self._center[1]))
4363

4464
@property
4565
def direction(self):
66+
"""Get the direction. _direction is its location relative to the
67+
formation's center. Return Vec2D.
68+
"""
4669
return self._direction + self._center
4770

4871
@property
4972
def facing(self):
73+
"""Return the Vec2D vector describing the facing of the formation."""
5074
return self.direction - self._center
5175

5276
def update(self):
77+
"""Update the formation. If the center of the formation is too far
78+
from its waypoint, movement is started. The formation always moves
79+
toward direction, however, it rotates to face the waypoint. This allows
80+
for a more realistic looking movement/rotation.
81+
"""
5382
if self.waypoint and (self._center.get_distance(self.waypoint) > 10):
5483
acceleration = self.facing.normalized()
5584
to_waypoint = self.waypoint - self._center

formation_designer.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,35 @@
55
import math
66

77

8-
boundaries = True
9-
center = None
8+
center = None # The center of the formation
9+
# The vector direction - center is the direction the formation is facing
1010
direction = None
1111

1212

1313
def add_tuple(t1, t2):
14+
"""Add each element of tuple t1 and t2 and return a tuple."""
1415
return tuple((t1[i] + t2[i]) for i in range(len(t1)))
1516

1617

1718
def with_center(placed_units):
19+
"""Return the list of the non-relative version of the placed_units."""
1820
global center
1921
return [add_tuple(placed_unit, center) for placed_unit in placed_units]
2022

2123

2224
def distance_to(point1, point2):
25+
"""Calculate the distance between point1 and point2 and return a float."""
2326
return math.sqrt((point2[0] - point1[0])**2 + (point2[1] - point1[1])**2)
2427

2528

2629
def close_to(point, placed_units):
30+
"""Get all units close to point and return a list of these units."""
2731
return [unit for unit in placed_units
2832
if distance_to(point, unit) < config.COLLISION_RANGE + 10]
2933

3034

3135
def get_closest(point, close_units):
36+
"""Find the unit closest to point and return the closest tuple."""
3237
if close_units:
3338
closest = close_units[0]
3439
dist = distance_to(point, closest)
@@ -43,6 +48,7 @@ def get_closest(point, close_units):
4348

4449

4550
def get_closest_and_dist(point, close_units):
51+
"""Get the closest unit and the distance to it and return tuple."""
4652
closest = get_closest(point, close_units)
4753
if closest:
4854
return closest, distance_to(point, closest)
@@ -51,18 +57,17 @@ def get_closest_and_dist(point, close_units):
5157

5258

5359
def events(placed_units):
54-
global boundaries, center, direction
60+
"""Event section of the game loop. Handle user input. Return boolean."""
61+
global center, direction
5562

5663
for event in pygame.event.get():
57-
if event.type == pygame.QUIT:
64+
if event.type == pygame.QUIT: # Program window quit button press
5865
return False
59-
elif event.type == pygame.KEYDOWN:
66+
elif event.type == pygame.KEYDOWN: # Key pressed event
6067
if event.key == pygame.K_ESCAPE:
6168
return False
62-
elif event.key == pygame.K_SPACE:
63-
boundaries = not boundaries
6469
elif event.key == pygame.K_s and (pygame.key.get_mods() &
65-
pygame.KMOD_CTRL):
70+
pygame.KMOD_CTRL): # Save
6671
options = {
6772
'filetypes': [('Formations', '.fm')],
6873
'initialfile': 'formation1.fm',
@@ -75,7 +80,7 @@ def events(placed_units):
7580
save_file.write(str(placed_unit) + '\n')
7681
save_file.close()
7782
elif event.key == pygame.K_o and (pygame.key.get_mods() &
78-
pygame.KMOD_CTRL):
83+
pygame.KMOD_CTRL): # Open
7984
options = {
8085
'filetypes': [('Formations', '.fm')],
8186
'title': 'Open formation'}
@@ -87,12 +92,11 @@ def events(placed_units):
8792
print line
8893
placed_units.append(eval(line))
8994
open_file.close()
90-
elif event.type == pygame.MOUSEBUTTONDOWN:
95+
elif event.type == pygame.MOUSEBUTTONDOWN: # Mouse Event
9196
states = pygame.mouse.get_pressed()
92-
config.debug_print("STATES:", states)
9397
mouse_pos = pygame.mouse.get_pos()
9498
close_units = close_to(mouse_pos, with_center(placed_units))
95-
if states == (1, 0, 0): # Left click - Add
99+
if states == (1, 0, 0): # Left click - Add
96100
if not center:
97101
center = mouse_pos
98102
return True
@@ -103,7 +107,7 @@ def events(placed_units):
103107
if not close_units:
104108
placed_units.append((mouse_pos[0] - center[0],
105109
mouse_pos[1] - center[1]))
106-
elif states == (0, 0, 1): # Right click - Delete
110+
elif states == (0, 0, 1): # Right click - Delete
107111
if close_units:
108112
closest = get_closest(mouse_pos, close_units)
109113
print (center[0] - closest[0], center[1] - closest[1])
@@ -113,7 +117,8 @@ def events(placed_units):
113117

114118

115119
def render(screen, placed_units):
116-
global boundaries, center
120+
"""Render section of game loop. Handle unit drawing."""
121+
global center
117122
screen.fill((0, 0, 0))
118123
colour = config.WHITE
119124
closest_to_mouse, dist = get_closest_and_dist(pygame.mouse.get_pos(),
@@ -128,16 +133,18 @@ def render(screen, placed_units):
128133
for unit in with_center(placed_units):
129134
if unit == closest_to_mouse and dist <= config.COLLISION_RANGE + 10:
130135
colour = config.RED
131-
if boundaries:
132-
pygame.draw.circle(screen, colour, unit,
133-
config.COLLISION_RANGE + 10, 1)
136+
pygame.draw.circle(screen, colour, unit,
137+
config.COLLISION_RANGE + 10, 1)
134138
pygame.draw.circle(screen, colour, unit,
135139
2, 0)
136140
colour = config.WHITE
137141
pygame.display.flip()
138142

139143

140144
def main():
145+
"""Main game loop. Runs through every step of the game loop until the
146+
event section returns false.
147+
"""
141148
pygame.init()
142149
pygame.display.set_caption('Formation Designer')
143150
screen = pygame.display.set_mode(

main.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,76 +6,79 @@
66
import pygame
77
from vec2d import Vec2d
88

9-
boids = []
10-
enemies = []
11-
formation = None
12-
enemy_start = None
9+
units = [] # Units in the formation
10+
enemies = [] # Enemies added by the user
11+
formation = None # The formation being used
12+
enemy_start = None # The start point of the current enemy path
1313

1414

1515
def events():
16-
global boids, enemies, formation, enemy_start
16+
"""Event section of game loop. Handle user input. Return boolean."""
17+
global units, enemies, formation, enemy_start
1718
for event in pygame.event.get():
18-
if event.type == pygame.QUIT:
19+
if event.type == pygame.QUIT: # Program window quit button press
1920
return False
20-
elif event.type == pygame.KEYUP:
21+
elif event.type == pygame.KEYUP: # Key pressed event
2122
if event.key == pygame.K_r:
22-
boids = []
23+
units = []
2324
enemies = []
2425
formation = None
2526
enemy_start = None
2627
elif event.key == pygame.K_ESCAPE:
2728
return False
2829
elif event.key == pygame.K_o and (pygame.key.get_mods() &
29-
pygame.KMOD_CTRL):
30+
pygame.KMOD_CTRL): # Open file
3031
options = {
3132
'filetypes': [('Formations', '.fm')],
3233
'title': 'Open formation'}
3334
formation = Formation(tkFileDialog.askopenfile('r', **options))
34-
boids = formation.gen_and_get_boids()
35-
elif event.type == pygame.MOUSEBUTTONDOWN:
35+
units = formation.gen_and_get_boids()
36+
elif event.type == pygame.MOUSEBUTTONDOWN: # Mouse event
3637
states = pygame.mouse.get_pressed()
37-
config.debug_print("STATES:", states)
3838
mouse_pos = pygame.mouse.get_pos()
39-
if states == (1, 0, 0): # Left click
39+
if states == (1, 0, 0): # Left click - set enemy path
4040
if not enemy_start:
4141
enemy_start = mouse_pos
4242
else:
4343
enemies.append(Enemy(Vec2d(enemy_start), Vec2d(mouse_pos)))
4444
enemy_start = None
45-
elif states == (0, 0, 1): # Right click
45+
elif states == (0, 0, 1): # Right click - set waypoint
4646
if formation:
4747
formation.set_waypoint(mouse_pos)
4848
return True
4949

5050

51-
def loop(obstacles=None):
52-
global boids, formation
51+
def update(obstacles=None):
52+
"""Update the formation and units."""
53+
global units, formation
5354
if formation:
5455
formation.update()
55-
for boid in boids:
56+
for unit in units:
5657
if obstacles:
57-
boid.update(boids, obstacles)
58+
unit.update(units, obstacles)
5859
else:
59-
boid.update(boids)
60+
unit.update(units)
6061
for enemy in enemies:
6162
enemy.update()
6263

6364

6465
def render(screen):
65-
global boids, formation
66+
"""Render section of game loop. Handle drawing."""
67+
global units, formation
6668
screen.fill((0, 0, 0))
6769
if formation:
6870
pygame.draw.circle(screen, (0, 100, 200), formation.center, 5, 0)
69-
for boid in boids:
70-
pygame.draw.polygon(screen, boid.colour, boid.vertices)
71+
for unit in units:
72+
pygame.draw.polygon(screen, unit.colour, unit.vertices)
7173
for enemy in enemies:
7274
pygame.draw.polygon(screen, config.RED, enemy.vertices)
7375
pygame.display.flip()
7476

7577

7678
def main():
79+
"""Main game loop. Loop until events returns false."""
7780
pygame.init()
78-
pygame.display.set_caption("Boids - Prototype")
81+
pygame.display.set_caption("Reactive Formations")
7982
screen = pygame.display.set_mode(
8083
(config.SCREEN_WIDTH, config.SCREEN_HEIGHT))
8184
running = True
@@ -88,7 +91,7 @@ def main():
8891
clock.tick(60)
8992
pygame.time.wait(1)
9093
running = events()
91-
loop(enemies) if enemies else loop()
94+
update(enemies) if enemies else update()
9295
render(screen)
9396

9497
if __name__ == "__main__":

0 commit comments

Comments
 (0)