Mercurial > skaapsteker
annotate skaapsteker/physics.py @ 103:aaef228b6358
Run kitsune, run! And by the running of the kitsune gravity is restored to normal.
author | Simon Cross <hodgestar@gmail.com> |
---|---|
date | Mon, 04 Apr 2011 20:46:44 +0200 |
parents | 449a9579ec7a |
children | 12ce1d131a72 |
rev | line source |
---|---|
22
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
1 """Model of gravity, acceleration, velocities and collisions. |
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
2 |
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
3 Works very closely with sprites/base.py. |
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
4 """ |
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
5 |
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
6 import pygame.sprite |
53 | 7 import pygame.draw |
39 | 8 import pygame |
9 import time | |
66 | 10 from constants import DEBUG, EPSILON |
22
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
11 |
51
8efb35009ba0
Switch to dirty sprites and layered dirty rendering groups in the hopes that it will provide access to speed-ups later on.
Simon Cross <hodgestar@gmail.com>
parents:
50
diff
changeset
|
12 class Sprite(pygame.sprite.DirtySprite): |
22
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
13 |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
14 # physics attributes |
22
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
15 mobile = True # whether the velocity may be non-zero |
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
16 gravitates = True # whether gravity applies to the sprite |
83
07a2ae603bdd
Correct physics comments - units are now pixel based, not metres
Neil Muller <drnlmuller@gmail.com>
parents:
80
diff
changeset
|
17 terminal_velocity = (300.0, 300.0) # maximum horizontal and vertial speeds (pixels / s) |
78
f29b7ada68c1
Fix bouncing and tweak universal constants to show it off.
Simon Cross <hodgestar@gmail.com>
parents:
68
diff
changeset
|
18 bounce_factor = (0.98, 1.05) # bounce factor |
39 | 19 |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
20 # collision attributes |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
21 # Sprite X collides with Y iff (X.collision_layer in Y.collides_with) and X.check_collides(Y) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
22 # Collisions result in the colliding movement being partially backed out, a call to X.bounce(frac) and a call to X.collided(Y) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
23 # X.bounce(frac) is only called for the first (as determined by backing out distance) collision in a multi-collision event |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
24 collision_layer = None # never collides with anything |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
25 collides_with = set() # nothing collides with this |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
26 |
39 | 27 def __init__(self, *args, **kwargs): |
28 super(Sprite, self).__init__(*args, **kwargs) | |
29 self.velocity = (0.0, 0.0) | |
30 self.rect = pygame.Rect(0, 0, 10, 10) # sub-classes should override | |
43 | 31 self.image = pygame.Surface((10, 10)) |
32 self.image.fill((0, 0, 200)) | |
51
8efb35009ba0
Switch to dirty sprites and layered dirty rendering groups in the hopes that it will provide access to speed-ups later on.
Simon Cross <hodgestar@gmail.com>
parents:
50
diff
changeset
|
33 self.visible = 1 |
8efb35009ba0
Switch to dirty sprites and layered dirty rendering groups in the hopes that it will provide access to speed-ups later on.
Simon Cross <hodgestar@gmail.com>
parents:
50
diff
changeset
|
34 self.dirty = 1 |
8efb35009ba0
Switch to dirty sprites and layered dirty rendering groups in the hopes that it will provide access to speed-ups later on.
Simon Cross <hodgestar@gmail.com>
parents:
50
diff
changeset
|
35 self.blendmode = 0 |
66 | 36 |
37 def init_pos(self): | |
38 self._float_pos = self.rect.topleft | |
39 | 39 |
53 | 40 def draw_debug(self, surface): |
41 pygame.draw.rect(surface, (240, 0, 0), self.rect, 1) | |
42 | |
39 | 43 def deltav(self, dv): |
44 v_x, v_y = self.velocity | |
45 v_x, v_y = v_x + dv[0], v_y + dv[1] | |
46 | |
47 t_v = self.terminal_velocity | |
48 v_x = max(min(v_x, t_v[0]), -t_v[0]) | |
49 v_y = max(min(v_y, t_v[1]), -t_v[1]) | |
50 | |
51 self.velocity = (v_x, v_y) | |
52 | |
53 def deltap(self, dt): | |
54 v_x, v_y = self.velocity | |
66 | 55 f_x, f_y = self._float_pos |
39 | 56 d_x, d_y = v_x * dt, v_y * dt |
66 | 57 f_x, f_y = f_x + d_x, f_y + d_y |
58 self._float_pos = f_x, f_y | |
59 self.rect.topleft = int(f_x), int(f_y) | |
60 | |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
61 def check_collides(self, other): |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
62 return True # default to relying purefly on collision_layer and collides_with |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
63 |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
64 def collided(self, other): |
98 | 65 pass |
66 | 66 |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
67 def bounce(self, other, normal): |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
68 """Alter velocity after a collision. |
78
f29b7ada68c1
Fix bouncing and tweak universal constants to show it off.
Simon Cross <hodgestar@gmail.com>
parents:
68
diff
changeset
|
69 |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
70 other: sprite collided with |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
71 normal: unit vector (tuple) normal to the collision |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
72 surface. |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
73 """ |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
74 v_x, v_y = self.velocity |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
75 b_x = 1.0 + self.bounce_factor[0] * other.bounce_factor[0] |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
76 b_y = 1.0 + self.bounce_factor[1] * other.bounce_factor[1] |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
77 v_x = (1.0 - normal[0] * b_x) * v_x |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
78 v_y = (1.0 - normal[1] * b_y) * v_y |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
79 self.velocity = v_x, v_y |
39 | 80 |
22
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
81 |
b815471d4b95
Move sprite base class into physics since they're tightly coupled.
Simon Cross <hodgestar@gmail.com>
parents:
18
diff
changeset
|
82 class World(object): |
23
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
83 |
103
aaef228b6358
Run kitsune, run! And by the running of the kitsune gravity is restored to normal.
Simon Cross <hodgestar@gmail.com>
parents:
98
diff
changeset
|
84 GRAVITY = 0.0, 9.8 * 20.0 # pixels / s^2 |
39 | 85 |
23
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
86 def __init__(self): |
57
e545236dd8f4
Change back to plain layered updater since everything is dirty all the time. Remove collision print since collisions appear to work.
Simon Cross <hodgestar@gmail.com>
parents:
53
diff
changeset
|
87 self._all = pygame.sprite.LayeredUpdates() |
23
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
88 self._mobiles = pygame.sprite.Group() |
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
89 self._gravitators = pygame.sprite.Group() |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
90 self._collision_groups = { None: pygame.sprite.Group() } |
39 | 91 self._last_time = None |
23
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
92 |
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
93 def add(self, sprite): |
66 | 94 sprite.init_pos() |
23
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
95 self._all.add(sprite) |
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
96 if sprite.mobile: |
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
97 self._mobiles.add(sprite) |
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
98 if sprite.gravitates: |
5c9f2eeeb629
Very start of world / physics engine.
Simon Cross <hodgestar@gmail.com>
parents:
22
diff
changeset
|
99 self._gravitators.add(sprite) |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
100 self._add_collision_group(sprite.collision_layer) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
101 for layer in sprite.collides_with: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
102 self._add_collision_group(layer) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
103 self._collision_groups[layer].add(sprite) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
104 |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
105 def _add_collision_group(self, layer): |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
106 if layer in self._collision_groups: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
107 return |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
108 self._collision_groups[layer] = pygame.sprite.Group() |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
109 |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
110 def _backout_collisions(self, sprite, others, dt): |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
111 frac, normal, idx = 0.0, None, None |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
112 v_x, v_y = sprite.velocity |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
113 abs_v_x, abs_v_y = abs(v_x), abs(v_y) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
114 |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
115 for i, other in enumerate(others): |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
116 clip = sprite.rect.clip(other.rect) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
117 # TODO: avoid continual "if abs_v_? > EPSILON" |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
118 frac_x = clip.width / abs_v_x if abs_v_x > EPSILON else dt |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
119 frac_y = clip.height / abs_v_y if abs_v_y > EPSILON else dt |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
120 if frac_x > frac_y: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
121 if frac_y > frac: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
122 frac, normal, idx = frac_y, (0, 1), i |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
123 else: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
124 if frac_x > frac: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
125 frac, normal, idx = frac_x, (1, 0), i |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
126 |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
127 sprite.deltap(max(-1.1 * frac, -dt)) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
128 sprite.bounce(others[idx], normal) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
129 for other in others: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
130 sprite.collided(other) |
39 | 131 |
132 def update(self): | |
133 if self._last_time is None: | |
134 self._last_time = time.time() | |
135 return | |
136 | |
137 # find dt | |
138 now = time.time() | |
139 self._last_time, dt = now, now - self._last_time | |
140 | |
141 # gravity | |
103
aaef228b6358
Run kitsune, run! And by the running of the kitsune gravity is restored to normal.
Simon Cross <hodgestar@gmail.com>
parents:
98
diff
changeset
|
142 dv = self.GRAVITY[0] * dt, self.GRAVITY[1] * dt |
39 | 143 for sprite in self._gravitators: |
144 sprite.deltav(dv) | |
145 | |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
146 # position update and collision check (do last) |
39 | 147 for sprite in self._mobiles: |
148 sprite.deltap(dt) | |
97
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
149 sprite_collides = sprite.rect.colliderect |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
150 collisions = [] |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
151 for other in self._collision_groups[sprite.collision_layer]: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
152 if sprite_collides(other) and sprite.check_collides(other): |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
153 collisions.append(other) |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
154 if collisions: |
a1d95c6152a0
Shiny new collision detection. Read code for usage information.
Simon Cross <hodgestar@gmail.com>
parents:
85
diff
changeset
|
155 self._backout_collisions(sprite, collisions, dt) |
50
7d411ed02eac
Initial stab at detecting collisions.
Simon Cross <hodgestar@gmail.com>
parents:
43
diff
changeset
|
156 |
39 | 157 def draw(self, surface): |
158 self._all.draw(surface) | |
53 | 159 if DEBUG: |
160 for sprite in self._all: | |
161 sprite.draw_debug(surface) |