Quellcode für miniworlds_physics.physics_world

import pymunk as pymunk_engine
from miniworlds import Line, World
from miniworlds.tools import actor_inspection
import miniworlds_physics.physics_world_event_manager as event_manager
import miniworlds_physics.physics_world_connector as world_connector


[Doku] class PhysicsWorld(World): """ A PhysicsWorld is a playing field on which objects follow physical laws. The PhysicsWorld itself defines some values with which the physics engine can be influenced, e.g. the gravity in the world. All actors on a PhysicsWorld have an attribute ``actor.physics``, with which you can change the physical properties of the object. """ def __init__( self, columns: int = 40, rows: int = 40, ): self.gravity_x: float = 0 self.gravity_y: float = 900 self.debug: bool = False self._accuracy = 1 self.space = pymunk_engine.Space() self.space.gravity = self.gravity_x, self.gravity_y self.space.iterations = 35 self.space.damping = 0.9 self.space.collision_persistence = 10 self._damping = 0.9 self.physics_actors = list() self.touching_methods = set() # filled in actor_manager self.separate_methods = set() # filled in actor_manager self._registered_physics_handlers = set() self._physics_collision_types = {} self._next_physics_collision_type = 1 super().__init__(columns, rows) def _create_event_manager(self): return event_manager.PhysicsWorldEventManager(self) @property def accuracy(self): """Sets number of physics-steps performed in each frame. Default: 1 """ return self._accuracy @accuracy.setter def accuracy(self, value: int): if value < 1: raise ValueError("PhysicsWorld.accuracy must be at least 1") self._accuracy = value @staticmethod def _get_world_connector_class(): """needed by get_world_connector in parent class""" return world_connector.PhysicsWorldConnector def get_physics_collision_methods_for_actor(self, actor): """Gets all collision methods for actor :meta private: """ # gets all method names methods = [ method_name for method_name in dir(actor) if method_name.startswith("on_touching_") or method_name.startswith("on_separation_from_") ] # get methods from method names return [ getattr(actor, method_name) for method_name in methods if hasattr(actor, method_name) and callable(getattr(actor, method_name)) ]
[Doku] def get_physics_collision_type(self, actor_class): """Return a stable pymunk collision type for an actor class.""" if actor_class not in self._physics_collision_types: self._physics_collision_types[actor_class] = self._next_physics_collision_type self._next_physics_collision_type += 1 return self._physics_collision_types[actor_class]
[Doku] def on_new_actor(self, actor): """Synchronizes newly added actors with the physics simulation. Actors that have physics disabled are removed from pymunk so they stay as non-simulated world objects. Args: actor: The newly added actor. """ if not actor.physics.simulation: connector = world_connector.PhysicsWorldConnector(self, actor) connector.remove_actor_from_physics()
def simulate_all_physics_actors(self): """Iterates over all actors and process physics-simulation Processes phyisics-simulation in three steps * Convert miniworlds-position/direction to pymunk position/direction * Simulate a step in physics-engine * Convert pymunk position/direction to miniworlds position/direction :meta private: """ if len(self.physics_actors) > 0: # pre-process [ actor.physics._simulation_preprocess_actor() for actor in self.physics_actors ] # simulate steps = self.accuracy for _ in range(steps): # if self.physics.space is not None: - can be removed self.space.step(1 / (60 * steps)) # post-process [ actor.physics._simulation_postprocess_actor() for actor in self.physics_actors ] @property def gravity(self) -> tuple: """Defines gravity in physics world. Gravity is a 2-tuple with gravity in x-direction and y-direction. Default gravity: x=0, y=500 Examples: Get all actors at mouse position: .. code-block:: python world = PhysicsWorld(400,400) world.gravity = (0, 0) """ return self.gravity_x, self.gravity_y @gravity.setter def gravity(self, value: tuple): self.gravity_x = value[0] self.gravity_y = value[1] self.space.gravity = self.gravity_x, self.gravity_y @property def damping(self): """Amount of simple damping to apply to the space. A value of 0.9 means that each body will lose 10% of its velocity per second. Defaults to 1. """ return self._damping @damping.setter def damping(self, value: float): self._damping = value self.space.damping = self._damping def pymunk_touching_collision_listener(self, arbiter, space, data): """Handles collisions - Handled by pymunk engine :meta private: """ # Translate pymunk variables to miniworlds variables. # Arbiter contains the two colliding actors. t1 = arbiter.shapes[0].actor t2 = arbiter.shapes[1].actor collision = dict() # get touching methods, e.g. `on_touching_circle` for method in self.touching_methods: # _cls_search_string = method.__name__[len("on_touching_"):].lower() # Get class by method name # filter_class = actor_class_inspection.ActorClassInspection(self).find_actor_class_by_classname( # _cls_search_string # ) # sets parameter for method if method.__self__ == t1: other = t2 else: other = t1 # call instance method with correct parameters # if isinstance(other, filter_class): actor_inspection.ActorInspection(method.__self__).get_and_call_method( method.__name__, [other, collision] ) return True def pymunk_separation_collision_listener(self, arbiter, space, data): """Handles collisions - Handled by pymunk engine :meta private: """ # Translate pymunk variables to miniworlds variables. # Arbiter contains the two colliding actors. t1 = arbiter.shapes[0].actor t2 = arbiter.shapes[1].actor collision = dict() # get touching methods, e.g. `on_touching_circle` for method in self.separate_methods: # _cls_search_string = method.__name__[len("on_separation_from_"):].lower() # Get class by method name # filter_class = actor_class_inspection.ActorClassInspection(self).find_actor_class_by_classname( # _cls_search_string # ) # sets parameter for method if method.__self__ == t1: other = t2 else: other = t1 # call instance method with correct parameters # if isinstance(other, filter_class): actor_inspection.ActorInspection(method.__self__).get_and_call_method( method.__name__, [other, collision] ) return True
[Doku] def connect(self, actor1, actor2) -> "Line": line = Line(actor1.center, actor2.center) line.physics.simulation = None line.border = 1 line.fill = True line.color = (255, 0, 0, 100) @line.register def act(self): self.start_position = actor1.center self.end_position = actor2.center return line