.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/plot_angry_shapes.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_plot_angry_shapes.py: AngryShapes =========================== This example shows how to create games inspired by AngryBirds. .. GENERATED FROM PYTHON SOURCE LINES 7-361 .. container:: sphx-glr-animation .. raw:: html
.. rst-class:: sphx-glr-script-out .. code-block:: none /home/runner/.local/lib/python3.10/site-packages/b2d/testbed/backend/matplotlib_gif_gui/matplotlib_gif_gui.py:52: UserWarning: You passed in an explicit save_count=240 which is being ignored in favor of frames=240. self.ani = animation.FuncAnimation( | .. code-block:: default from b2d.testbed import TestbedBase import math import numpy import b2d class AngryShapes(TestbedBase): name = "AngryShapes" class Settings(TestbedBase.Settings): substeps: int = 2 def draw_segment(self, p1, p2, color, line_width=1): screen_p1 = self._point(self.world_to_screen(p1)) screen_p2 = self._point(self.world_to_screen(p2)) screen_color = self._uint8_color(color) screen_line_width = self._line_width(line_width) cv.line(self._image, screen_p1, screen_p2, screen_color, screen_line_width) def draw_polygon(self, vertices, color, line_width=1): # todo add C++ function for this screen_vertices = numpy.array( [self._point(self.world_to_screen(v)) for v in vertices], dtype="int32" ) screen_color = self._uint8_color(color) screen_line_width = self._line_width(line_width) cv.polylines( self._image, [screen_vertices], True, screen_color, screen_line_width, 8 ) def draw_solid_polygon(self, vertices, color): # todo add C++ function for this screen_vertices = numpy.array( [self._point(self.world_to_screen(v)) for v in vertices], dtype="int32" ) screen_color = self._uint8_color(color) cv.fillPoly(self._image, [screen_vertices], screen_color, 8) def __init__(self, settings=None): super(AngryShapes, self).__init__(settings=settings) self.targets = [] self.projectiles = [] self.marked_for_destruction = [] self.emitter = None # particle system pdef = b2d.particle_system_def( viscous_strength=0.9, spring_strength=0.0, damping_strength=100.5, pressure_strength=1.0, color_mixing_strength=0.05, density=0.1, ) self.psystem = self.world.create_particle_system(pdef) self.psystem.radius = 1 self.psystem.damping = 0.5 self.build_outer_box() self.build_castle() self.build_launcher() self.arm_launcher() self.build_explosives() def build_outer_box(self): # the outer box shape = b2d.edge_shape([(100, 0), (600, 0)]) box = self.world.create_static_body( position=(0, 0), fixtures=b2d.fixture_def(shape=shape, friction=1) ) def build_target(self, pos): t = self.world.create_dynamic_body( position=pos, fixtures=[ b2d.fixture_def(shape=b2d.circle_shape(radius=4), density=1.0), b2d.fixture_def( shape=b2d.circle_shape(radius=2, pos=(3, 3)), density=1.0 ), b2d.fixture_def( shape=b2d.circle_shape(radius=2, pos=(-3, 3)), density=1.0 ), ], linear_damping=0, angular_damping=0, user_data="target", ) self.targets.append(t) def build_castle(self): def build_pyramid(offset, bar_shape, n): def build_brick(pos, size): hsize = [s / 2 for s in size] self.world.create_dynamic_body( position=( pos[0] + hsize[0] + offset[0], pos[1] + hsize[1] + offset[1], ), fixtures=b2d.fixture_def( shape=b2d.polygon_shape(box=hsize), density=8 ), user_data="brick", ) bar_length = bar_shape[0] bar_width = bar_shape[1] nxm = n for y in range(nxm): py = y * (bar_length + bar_width) nx = nxm - y for x in range(nx): px = x * bar_length + y * (bar_length) / 2.0 if y + 1 < nxm - 1: if x == 0: px += bar_width / 2 if x + 1 == nx: px -= bar_width / 2 build_brick((px, py), (bar_width, bar_length)) if x < nx - 1: self.build_target( pos=( px + offset[0] + bar_length / 2, py + offset[1] + bar_width, ) ) build_brick( (px + bar_width / 2, py + bar_length), (bar_length, bar_width), ) build_pyramid(offset=(100, 0), bar_shape=[40, 4], n=4) build_pyramid(offset=(400, 0), bar_shape=[30, 3], n=4) def build_launcher(self): self.launcher_anchor_pos = (30, 0) self.launcher_anchor = self.world.create_static_body( position=self.launcher_anchor_pos ) def arm_launcher(self): self.reload_time = None self.is_armed = True self.projectile_radius = 3 projectile_pos = (self.launcher_anchor_pos[0], self.launcher_anchor_pos[1] / 2) self.projectile = self.world.create_dynamic_body( position=projectile_pos, fixtures=b2d.fixture_def( shape=b2d.circle_shape(radius=self.projectile_radius), density=100.0 ), linear_damping=0, angular_damping=0, user_data="projectile", ) self.projectiles.append(self.projectile) self.projectile_joint = self.world.create_distance_joint( self.launcher_anchor, self.projectile, length=1, stiffness=10000 ) self.mouse_joint = None def build_explosives(self): self.explosives = [] def on_mouse_down(self, p): if self.is_armed: body = self.world.find_body(pos=p) if body is not None and body.user_data is not None: print("got body") if body.user_data == "projectile": print("got projectile") kwargs = dict( body_a=self.groundbody, body_b=body, target=p, max_force=50000.0 * body.mass, stiffness=10000.0, ) self.mouse_joint = self.world.create_mouse_joint(**kwargs) body.awake = True return True return False def on_mouse_move(self, p): if self.is_armed: if self.mouse_joint is not None: self.mouse_joint.target = p return True return False def on_mouse_up(self, p): if self.is_armed: if self.mouse_joint is not None: self.world.destroy_joint(self.mouse_joint) if self.projectile_joint is not None: self.world.destroy_joint(self.projectile_joint) self.projectile_joint = None self.mouse_joint = None delta = self.launcher_anchor.position - b2d.vec2(p) scaled_delta = delta * 50000.0 print(scaled_delta) self.projectile.apply_linear_impulse_to_center(scaled_delta, True) self.reload_time = self.elapsed_time + 1.0 self.is_armed = False return False def begin_contact(self, contact): body_a = contact.body_a body_b = contact.body_b ud_a = body_a.user_data ud_b = body_b.user_data if ud_b == "projectile": body_a, body_b = body_b, body_a ud_a, ud_b = ud_b, ud_a if ud_a == "projectile": if ud_b == "target" or ud_b == "brick": self.marked_for_destruction.append(body_a) emitter_def = b2d.RandomizedRadialEmitterDef() emitter_def.emite_rate = 20000 emitter_def.lifetime = 0.7 emitter_def.enabled = True emitter_def.inner_radius = 0.0 emitter_def.outer_radius = 1.0 emitter_def.velocity_magnitude = 1000.0 emitter_def.start_angle = 0 emitter_def.stop_angle = math.pi emitter_def.transform = b2d.Transform(body_a.position, b2d.Rot(0)) self.emitter = b2d.RandomizedRadialEmitter(self.psystem, emitter_def) self.emitter_die_time = self.elapsed_time + 0.02 def pre_step(self, dt): if self.reload_time is not None: if self.elapsed_time >= self.reload_time: self.arm_launcher() # delete contact bodies for body in self.marked_for_destruction: if body in self.projectiles: self.projectiles.remove(body) self.world.destroy_body(body) if body == self.projectile: self.reload_time = self.elapsed_time + 1.0 self.marked_for_destruction = [] # delete bodies which have fallen down for body in self.world.bodies: if body.position.y < -100: if body.user_data == "projectile": self.projectiles.remove(body) if body.user_data == "target": self.targets.remove(body) self.world.destroy_body(body) # emmiter if self.emitter is not None: self.emitter.step(dt) if self.elapsed_time >= self.emitter_die_time: self.emitter = None def draw_target(self, target): center = target.position center_l = target.get_world_point((-3, 3)) center_r = target.get_world_point((3, 3)) eye_left = target.get_world_point((-1, 1)) eye_right = target.get_world_point((1, 1)) pink = [c / 255 for c in (248, 24, 148)] self.debug_draw.draw_solid_circle( center=center, radius=4, axis=None, color=pink ) self.debug_draw.draw_solid_circle( center=center_l, radius=2, axis=None, color=pink ) self.debug_draw.draw_solid_circle( center=center_r, radius=2, axis=None, color=pink ) # schnautze nose_center = target.get_world_point((0, -1)) nose_center_l = target.get_world_point((-0.3, -1)) nose_center_r = target.get_world_point((0.3, -1)) self.debug_draw.draw_circle( center=nose_center, radius=2, # axis=None, color=(1, 1, 1), line_width=0.2, ) # eyes for nose_center in [nose_center_l, nose_center_r]: self.debug_draw.draw_solid_circle( center=nose_center, radius=0.6, axis=None, color=(1, 1, 1) ) # eyes for eye_center in [eye_left, eye_right]: self.debug_draw.draw_solid_circle( center=eye_center, radius=1, axis=None, color=(1, 1, 1) ) self.debug_draw.draw_solid_circle( center=eye_center, radius=0.7, axis=None, color=(0, 0, 0) ) def draw_projectile(self, projectile): center = projectile.position # center_l = target.get_world_point((-3,3)) # center_r = target.get_world_point(( 3,3)) eye_left = projectile.get_world_point((-1, 1)) eye_right = projectile.get_world_point((1, 1)) self.debug_draw.draw_solid_circle( center=center, radius=self.projectile_radius * 1.1, axis=None, color=(1, 0, 0), ) # eyes for eye_center in [eye_left, eye_right]: self.debug_draw.draw_solid_circle( center=eye_center, radius=1, axis=None, color=(1, 1, 1) ) self.debug_draw.draw_solid_circle( center=eye_center, radius=0.7, axis=None, color=(0, 0, 0) ) def post_debug_draw(self): for target in self.targets: self.draw_target(target) for projectile in self.projectiles: self.draw_projectile(projectile) if __name__ == "__main__": ani = b2d.testbed.run(AngryShapes) ani .. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 38.008 seconds) .. _sphx_glr_download_auto_examples_plot_angry_shapes.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_angry_shapes.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_angry_shapes.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_