Source code for rubato.structure.gameobject.physics.rigidbody

"""
The Rigidbody component describes how the physics engine handles a game object.
"""
from __future__ import annotations

from .. import Component
from .... import Vector, Time, Math


[docs]class RigidBody(Component): """ A RigidBody implementation with built in physics and collisions. Rigidbodies require hitboxes. Args: mass: The mass of the rigidbody. Defaults to -1. gravity: The gravity of the rigidbody. Defaults to (0, 0). friction: The friction of the rigidbody. Defaults to 0. static: Whether the rigidbody is static. Defaults to False. bounciness: The bounciness of the rigidbody. Defaults to 0. max_speed: The maximum speed of the rigidbody. Defaults to (INF, INF). velocity: The velocity of the rigidbody. Defaults to (0, 0). ang_vel: The angular velocity of the rigidbody. Defaults to 0. pos_correction: The positional correction of the rigidbody. Defaults to 0.25. offset: The offset of the rigidbody from the gameobject. Defaults to (0, 0). rot_offset: The offset of the rigidbody's rotation from the gameobject. Defaults to 0. z_index: The z-index of the rigidbody. Defaults to 0. """ def __init__( self, mass: float = 1, gravity: Vector | tuple[float, float] = (0, 0), friction: float = 0, static: bool = False, bounciness: float = 0, max_speed: Vector | tuple[float, float] = (Math.INF, Math.INF), velocity: Vector | tuple[float, float] = (0, 0), ang_vel: float = 0, pos_correction: float = 0.25, offset: Vector | tuple[float, float] = (0, 0), rot_offset: float = 0, z_index: int = 0 ): super().__init__(offset=offset, rot_offset=rot_offset, z_index=z_index) self.static: bool = static """Whether the rigidbody is static (as in, it does not move).""" self.gravity: Vector = Vector.create(gravity) """The acceleration of the gravity that should be applied.""" self.friction: float = friction """The friction coefficient of the Rigidbody (usually a value between 0 and 1).""" self.max_speed: Vector = Vector.create(max_speed) """The maximum speed of the Rigidbody.""" self.pos_correction: float = pos_correction """The positional correction of the rigidbody.""" self.velocity: Vector = Vector.create(velocity) """The current velocity of the Rigidbody.""" self.ang_vel: float = ang_vel """The current angular velocity of the Rigidbody.""" self.singular: bool = True if mass == 0 or self.static: self.inv_mass: float = 0 """The inverse mass of the Rigidbody.""" else: self.inv_mass: float = 1 / mass self.bounciness: float = bounciness """How bouncy the rigidbody is (usually a value between 0 and 1).""" @property def mass(self) -> float: """The mass of the Rigidbody.""" if self.inv_mass == 0: return 0 else: return 1 / self.inv_mass @mass.setter def mass(self, new: float): if new == 0: self.inv_mass = 0 else: self.inv_mass: float = 1 / new def _tick(self): """Applies general kinematic laws to the rigidbody.""" self.velocity += self.gravity * Time.fixed_delta self.velocity.clamp(-self.max_speed, self.max_speed) # pylint: disable=invalid-unary-operand-type self.gameobj.pos += self.velocity * Time.fixed_delta self.gameobj.rotation += self.ang_vel * Time.fixed_delta
[docs] def add_force(self, force: Vector | tuple[float, float]): """ Applies a force to the Rigidbody. Args: force: The force to add. """ self.velocity.x += force[0] * self.inv_mass * Time.fixed_delta self.velocity.y += force[1] * self.inv_mass * Time.fixed_delta
[docs] def add_impulse(self, impulse: Vector | tuple[float, float]): """ Applies an impulse to the rigidbody. Args: impulse: The impulse to add. """ self.velocity.x += impulse[0] * Time.fixed_delta self.velocity.y += impulse[1] * Time.fixed_delta
[docs] def add_cont_force(self, force: Vector | tuple[float, float], time: float): """ Add a continuous force to the Rigidbody. A continuous force is a force that is continuously applied over a time period. (the force is added every frame for a specific duration). Args: force: The force to add. time: The time in seconds that the force should be added. """ if time <= 0: return else: self.add_force(force) Time.next_frame(lambda: self.add_cont_force(force, time - Time.delta_time))
[docs] def add_cont_impulse(self, impulse: Vector | tuple[float, float], time: float): """ Add a continuous impulse to the Rigidbody. A continuous impulse is a impulse that is continuously applied over a time period. (the impulse is added every frame for a specific duration). Args: impulse: The impulse to add. time: The time in seconds that the impulse should be added. """ if time <= 0: return else: self.add_impulse(impulse) Time.next_frame(lambda: self.add_cont_impulse(impulse, time - Time.delta_time))
[docs] def fixed_update(self): """The physics loop for the rigidbody component.""" if not self.static: self._tick()
[docs] def stop(self): """Stops the rigidbody by setting velocity and ang_vel to 0.""" self.velocity.x = 0 self.velocity.y = 0 self.ang_vel = 0
[docs] def clone(self) -> RigidBody: return RigidBody( mass=self.mass, gravity=self.gravity.clone(), friction=self.friction, static=self.static, bounciness=self.bounciness, max_speed=self.max_speed.clone(), velocity=self.velocity.clone(), ang_vel=self.ang_vel, pos_correction=self.pos_correction, offset=self.offset.clone(), rot_offset=self.rot_offset, z_index=self.z_index )