def detect_all_collisions(player): # 1. Fetch all possiple collisions all_collisions = { 'FLOOR': [], 'CEILING': [], 'LEFT_WALL': [], 'RIGHT_WALL': [], } for barrier in Barrier.instances: all_collisions = merge_into_list_dict( all_collisions, get_collision(barrier=barrier, moving_object=player)) # 2. Reduce all collisions to the relevant ones, example: # all_collisions['FLOOR'] = [3.0, 4.2, 2.2, 4.0] # -> relevant_collisions['FLOOR'] = 4.2 return reduce_to_relevant_collisions(all_collisions)
def detect_all_collisions(moving_player): # 1. Fetch all possiple collisions all_collisions = { 'FLOOR': [], 'OBJECTS_ON_TOP': [], 'OBJECTS_BELOW': [], 'LEFT_WALL': [], 'RIGHT_WALL': [], } for player in Player.instances: if player != moving_player: collision = get_collision(barrier=player, moving_object=moving_player, stacked_collision=True) all_collisions = merge_into_list_dict(all_collisions, collision) # 2. Reduce all collisions to the relevant ones, example: # all_collisions['FLOOR'] = [3.0, 4.2, 2.2, 4.0] # -> relevant_collisions['FLOOR'] = 4.2 return reduce_to_relevant_collisions(all_collisions)
def update_for_collisions(self, new_velocity, new_position, timedelta): # 1. Detect combat collisions with enemies. "MOVING_OBJECT" # refers to the player. "BARRIER" refers to the enemy. combat_collisions = Enemy.detect_all_collisions(self) for enemy in combat_collisions['BARRIER_KILLED']: enemy.kill() self.enemies_killed += 1 if len(combat_collisions['MOVING_OBJECT_KILLED']) > 0: self.kill() return # 2. Detecting movement collisions with Barriers and other # players. Collisions should refer to the new velocity # and new position that is why we preliminarily update # the players state (self.) self.velocity, self.position = new_velocity, new_position movement_collisions = reduce_to_relevant_collisions( merge_into_list_dict(Barrier.detect_all_collisions(self), Player.detect_all_collisions(self))) # The strategy now is to go through all the collisions that # were detected and successively adjust new_velocity and # new_position if needed. In the end the players state (self. # will be updated with the adjusted position/velocity once # again new_collisions = { 'CEILING': None, 'FLOOR': None, 'LEFT_WALL': None, 'RIGHT_WALL': None, 'OBJECTS_ON_TOP': movement_collisions['OBJECTS_ON_TOP'], } # "Snap" to Wall/Floor/Ceiling when hitting one. Example # Moving left & new_x = -0.04 # Wall detected at x=0 # Snaps to x=0 and velocity *= -1 (turns around) def snap_to_barrier(side): dimension = 1 if (side in ('FLOOR', 'CEILING')) else 0 coordinate_direction = +1 if (side in ('FLOOR', 'LEFT_WALL')) else -1 new_velocity[dimension] = 0 limit_center_offset = ((self.size[dimension] / 2) - ERROR_MARGIN) * coordinate_direction new_position[ dimension] = movement_collisions[side] + limit_center_offset new_collisions[side] = movement_collisions[side] # 3. React to vertical collisions if movement_collisions['CEILING'] is not None: if new_velocity[1] > -ERROR_MARGIN: snap_to_barrier('CEILING') # Edge case for two players standing on top of each other with # the bottom player jumping up and only the top player hitting # a ceiling. This snippet is needed so that the upper player is # not being "forced into the wall" and the bottom player detects # the ceiling indirectly for _object in movement_collisions['OBJECTS_BELOW']: _object.velocity[1] = 0 limit_center_offset = (_object.size[1]) + ( self.size[1] / 2) + 2 * ERROR_MARGIN _object.position[ 1] = movement_collisions['CEILING'] - limit_center_offset elif movement_collisions[ 'FLOOR'] is not None and new_velocity[1] < +ERROR_MARGIN: snap_to_barrier('FLOOR') # 4. React to horizontal collisions. Left and Right wall block # is only applied if the player is moving towards that barrier if movement_collisions[ 'LEFT_WALL'] is not None and new_velocity[0] < ERROR_MARGIN: snap_to_barrier('LEFT_WALL') elif movement_collisions[ 'RIGHT_WALL'] is not None and new_velocity[0] > -ERROR_MARGIN: snap_to_barrier('RIGHT_WALL') # 5. When standing on top of another player and that player moves # then the player on top should also be moved by that players # movement = the player on top will be carried around (as long # as he doe not hit a wall if len(movement_collisions['OBJECTS_BELOW']) > 0: _object = movement_collisions['OBJECTS_BELOW'][0] if (_object.velocity[0] < 0 and new_collisions['LEFT_WALL'] is None or _object.velocity[0] > 0 and new_collisions['RIGHT_WALL'] is None): new_position[0] += _object.velocity[0] * timedelta # 6. Now new_velocity and new_position has been adjusted to conform # with collisions -> player state will be updated with the adjusted # values self.collisions = new_collisions self.velocity = [round(v, COORDINATE_PRECISION) for v in new_velocity] self.position = [round(p, COORDINATE_PRECISION) for p in new_position]
def update_for_collisions(self, new_velocity, new_position, timedelta): # 1. Detecting movement collisions with Barriers and other # players. Collisions should refer to the new velocity # and new position that is why we preliminarily update # the players state (self.) self.velocity, self.position = new_velocity, new_position movement_collisions = reduce_to_relevant_collisions( Barrier.detect_all_collisions(self)) # The strategy now is to go through all the collisions that # were detected and successively adjust new_velocity and # new_position if needed. In the end the players state (self. # will be updated with the adjusted position/velocity once # again new_collisions = { 'CEILING': None, 'FLOOR': None, 'LEFT_WALL': None, 'RIGHT_WALL': None } # "Snap" to Wall/Floor/Ceiling when hitting one. Example # Moving left & new_x = -0.04 # Wall detected at x=0 # Snaps to x=0 and velocity *= -1 (turns around) def snap_to_barrier(side): dimension = 1 if (side in ('FLOOR', 'CEILING')) else 0 coordinate_direction = +1 if (side in ('FLOOR', 'LEFT_WALL')) else -1 new_velocity[dimension] = 0 limit_center_offset = ((self.size[dimension] / 2) - ERROR_MARGIN) * coordinate_direction new_position[ dimension] = movement_collisions[side] + limit_center_offset new_collisions[side] = movement_collisions[side] # 2. React to vertical collisions if movement_collisions['CEILING'] is not None: if new_velocity[1] > -ERROR_MARGIN: snap_to_barrier('CEILING') elif movement_collisions[ 'FLOOR'] is not None and new_velocity[1] < +ERROR_MARGIN: snap_to_barrier('FLOOR') # 3. React to horizontal collisions. Left and Right wall block # is only applied if the player is moving towards that barrier if movement_collisions[ 'LEFT_WALL'] is not None and new_velocity[0] < ERROR_MARGIN: snap_to_barrier('LEFT_WALL') elif movement_collisions[ 'RIGHT_WALL'] is not None and new_velocity[0] > -ERROR_MARGIN: snap_to_barrier('RIGHT_WALL') # 4. Now new_velocity and new_position has been adjusted to conform # with collisions -> player state will be updated with the adjusted # values self.collisions = new_collisions self.velocity = [round(v, COORDINATE_PRECISION) for v in new_velocity] self.position = [round(p, COORDINATE_PRECISION) for p in new_position]