comparison skaapsteker/physics.py @ 627:35919d12b792

Path-based collision minimisation and axis-projection backout.
author Jeremy Thurgood <firxen@gmail.com>
date Sat, 07 May 2011 20:28:06 +0200
parents 1abb53ae1a6a
children 59556235dec7
comparison
equal deleted inserted replaced
626:1abb53ae1a6a 627:35919d12b792
10 import pygame.sprite 10 import pygame.sprite
11 from pygame.mask import from_surface 11 from pygame.mask import from_surface
12 12
13 from . import options 13 from . import options
14 from .constants import EPSILON 14 from .constants import EPSILON
15 from .utils import cadd, csub, cmul, cdiv, cclamp, cint, cneg, cabs 15 from .utils import (cadd, csub, cmul, cdiv, cclamp, cabsmax, cint, cneg, cabs,
16 rect_projection)
16 17
17 class Sprite(pygame.sprite.Sprite): 18 class Sprite(pygame.sprite.Sprite):
18 19
19 # physics attributes 20 # physics attributes
20 mobile = True # whether the velocity may be non-zero 21 mobile = True # whether the velocity may be non-zero
250 now = time.time() 251 now = time.time()
251 dt = now - self._last_time 252 dt = now - self._last_time
252 self._last_time = now 253 self._last_time = now
253 return dt 254 return dt
254 255
256
257 # def collide_sprite(self, dt, sprite):
258 # sprite.apply_velocity(dt)
259 # sprite_collides = sprite.collide_rect.colliderect
260 # collisions = []
261 # for other in self._collision_groups[sprite.collision_layer]:
262 # if sprite_collides(other.collide_rect) \
263 # and sprite.check_collides(other):
264 # collisions.append(other)
265 # if collisions:
266 # self._backout_collisions(sprite, collisions, dt)
267 # contact_rect = pygame.Rect(
268 # (sprite.collide_rect.left, sprite.collide_rect.bottom),
269 # (sprite.collide_rect.width, 1))
270 # return contact_rect.colliderect
271
272
273 def get_sprite_collisions(self, dt, sprite):
274 sprite.apply_velocity(dt)
275 sprite_collides = sprite.collide_rect.colliderect
276 collisions = []
277 for other in self._collision_groups[sprite.collision_layer]:
278 if (sprite_collides(other.collide_rect)
279 and sprite.check_collides(other)):
280 collisions.append(other)
281 return collisions
282
283
284 def path_collide(self, dt, sprite):
285 dts = [dt/10] * 9
286 dts.append(dt - sum(dts))
287 dtf_acc = 0
288 collisions = []
289 for dtf in dts:
290 dtf_acc += dtf
291 collisions = self.get_sprite_collisions(dtf, sprite)
292 for col in collisions:
293 if sprite.block and (col.floor or col.block):
294 return collisions, dtf_acc
295 return collisions, dt
296
297
298 def collide_sprite(self, dt, sprite):
299 initial_pos = sprite._float_pos
300 collisions = self.get_sprite_collisions(dt, sprite)
301 escape_vector = (0, 0)
302 if collisions:
303 # If we've collided, reset and try again with smaller time increments
304 sprite.update_position(initial_pos)
305 collisions, dtf = self.path_collide(dt, sprite)
306 for col in collisions:
307 if sprite.block and (col.floor or col.block):
308 escape_vector = cabsmax(escape_vector, rect_projection(sprite.collide_rect, col.collide_rect))
309 sprite.collided(col)
310 sprite.update_position(cadd(sprite._float_pos, escape_vector))
311 # if escape_vector[0] != 0:
312 # sprite.velocity = (0, sprite.velocity[1])
313 # if escape_vector[1] != 0:
314 # sprite.velocity = (sprite.velocity[0], 1)
315 # self._backout_collisions(sprite, collisions, dtf)
316 contact_rect = pygame.Rect(
317 cadd(sprite.collide_rect.bottomleft, (1, 0)),
318 (sprite.collide_rect.width, 1))
319 return contact_rect.colliderect
320
321
255 def update_sprite_positions(self, dt): 322 def update_sprite_positions(self, dt):
256 # position update and collision check (do last) 323 # position update and collision check (do last)
257 for sprite in self._mobiles: 324 for sprite in self._mobiles:
258 sprite.apply_velocity(dt) 325 collides = self.collide_sprite(dt, sprite)
259 sprite_collides = sprite.collide_rect.colliderect 326
260 collisions = [] 327 # Are we currently in contact with the ground?
261 for other in self._collision_groups[sprite.collision_layer]: 328 if not sprite.block:
262 if sprite_collides(other.collide_rect) \ 329 continue
263 and sprite.check_collides(other):
264 collisions.append(other)
265 if collisions:
266 self._backout_collisions(sprite, collisions, dt)
267 contact_rect = pygame.Rect(
268 (sprite.collide_rect.left, sprite.collide_rect.bottom),
269 (sprite.collide_rect.width, 1))
270 collides = contact_rect.colliderect
271 floors = [] 330 floors = []
272
273 # Are we currently in contact with the ground?
274 sprite.on_solid = False 331 sprite.on_solid = False
275 for other in self._collision_groups[sprite.collision_layer]: 332 for other in self._collision_groups[sprite.collision_layer]:
276 if (other.floor or other.block) and collides(other.floor_rect): 333 if (other.floor or other.block) and collides(other.floor_rect):
277 sprite.on_solid = True 334 sprite.on_solid = True
335 if sprite.velocity[1] > 0:
336 sprite.velocity = (sprite.velocity[0], 0)
278 floors.append(other) 337 floors.append(other)
279 sprite.check_floors(floors) 338 sprite.check_floors(floors)
280 339
281 def update(self): 340 def update(self):
282 if self._last_time is None: 341 if self._last_time is None: