def draw(self, camera: Camera) -> None:
        if camera.in_view(self.target.position):
            px, py = position = camera.project(self.target.position)
            dx, dy = dimension = camera.projection.scale(self.target.physics_data.size)
            camera.render_target.fill(C_SOMETHING, (px, py+dy, dx, abs(dy)))
            r = Rectangle(self.target.position, self.target.physics_data.size)
            r_min = camera.project(r.min()).to_int()
            r_max = camera.project(r.max()).to_int()
            draw.line(camera.render_target, *r_min, *r_max, C_D_GRAY)
            draw.circle(camera.render_target, *r_min, 2, C_BLUE)

            o_xy = Vector2(*v_div(v_add(r_min, r_max), 2)).to_int()
            l_xy = camera.projection.scale(self.target.shared_data.look_direction).to_int()
            visor_radius = 2
            visor_length = 2
            visor_color = C_GREEN
            visor = v_add(o_xy, v_mul(l_xy, visor_length))
            draw.line(camera.render_target, *o_xy, *visor, visor_color)
            draw.circle(camera.render_target, *visor, visor_radius, visor_color)

            cam_w, cam_h = camera.render_target.get_size()
            rows = range(0, cam_h, int(self.font.size + 5))
            text = (self.target.position,
                    self.target.state_machine.current_state.id.name)
            h_w = cam_w / 2
            for row, text in zip(rows, text):
                self.font.render_to(camera.render_target,
                                    (h_w, row),
                                    str(text),
                                    C_L_GRAY,
                                    C_BLACK)
    def update(self, g: Vector2 = Vector2(-1, -1)) -> None:
        self.target.contact_points.clear()
        bounding_box = Rectangle(self.target.position, self.target.size)

        self.velocity = Vector2(*v_add(self.velocity, v_mul(g, self.target.scene.delta_time)))
        h_trans, v_trans = translation = v_mul(self.velocity, self.target.scene.delta_time)
        h_rays, v_rays = bounding_box.directional_projection(v_norm(translation), v_len(translation))

        all_hits = static_ray_hits(self.target.scene.tile_grid, *h_rays)
        closest_hit = next(sorted(all_hits, key=lambda h: h.distance), default=None)
        h_absorb = bool(closest_hit)
        if h_absorb:
            # select closest hit distance and correct it to align with tile
            h_trans = closest_hit.ray.dir.x * closest_hit.distance + alignment_correction(closest_hit.point.x,
                                                                                          closest_hit.ray.dir.x)

        all_hits = static_ray_hits(self.target.scene.tile_grid, *v_rays)
        closest_hit = next(list(sorted(all_hits, key=lambda h: h.distance)), default=None)
        v_absorb = bool(closest_hit)
        if v_absorb:
            self.target.contact_points.extend((hit.ray.pos for hit in closest_hit))
            v_trans = closest_hit.ray.dir.y * closest_hit.distance + alignment_correction(closest_hit.point.y,
                                                                                          closest_hit.ray.dir.y)

        self.target.position = Point(*v_add(self.target.position, (h_trans, v_trans)))
        # todo observer for absorbing velocity
        h_vel, v_vel = self.velocity
        if h_absorb:
            h_vel = -h_vel
        if v_absorb:
            v_vel = -v_vel
        self.velocity = Vector2(h_vel, v_vel)
    def draw(self, camera: Camera) -> None:
        if camera.in_view(self.target.position):
            position = camera.project(self.target.position)
            dimension = camera.projection.scale(self.target.size)
            draw.rectangle(camera.render_target, (position, dimension), C_WHITE)
            if self.target.contact_points:
                p1, p2 = self.target.contact_points
                draw.line(camera.render_target, *camera.project(p1).to_int(), *camera.project(p2).to_int(), C_RED)

            r = Rectangle(self.target.position, self.target.size)
            draw.line(camera.render_target,
                      *camera.project(r.min()).to_int(),
                      *camera.project(r.max()).to_int(), C_RED)
            draw.circle(camera.render_target,
                        *camera.project(r.min()).to_int(),
                        3,
                        C_WHITE)
 def test_rect2circle(self):
     b = Rectangle(Point(0, 0), Vector2(2, 2))
     c1 = Circle(Point(1, 1), 1)
     self.assertTrue(b.intersect(c1))
     c2 = Circle(Point(1, 1), 5)
     self.assertTrue(b.intersect(c2))
     c3 = Circle(Point(0, 0), 1)
     self.assertTrue(b.intersect(c3))
     c4 = Circle(Point(-1, 1), 1)
     self.assertTrue(b.intersect(c4))
     c5 = Circle(Point(-1, 1), 0.99)
     self.assertFalse(b.intersect(c5))
     c6 = Circle(Point(3, 3), math.sqrt(2))
     self.assertTrue(b.intersect(c6))
     c7 = Circle(Point(3.01, 3.01), math.sqrt(2))
     self.assertFalse(b.intersect(c7))
 def test_rect2point(self):
     b = Rectangle(Point(0, 0), Vector2(2, 2))
     p1 = Point(1, 1)
     self.assertTrue(b.intersect(p1))
     p2 = Point(0, 0)
     self.assertTrue(b.intersect(p2))
     p3 = Point(2, 2)
     self.assertTrue(b.intersect(p3))
     p4 = Point(-.1, 1)
     self.assertFalse(b.intersect(p4))
     p5 = Point(3, 1)
     self.assertFalse(b.intersect(p5))
 def view_rectangle(self, view_offset: Vector2 = (0, 0)) -> Rectangle:
     return Rectangle(Point(*v_add(self.anchor, view_offset)), Vector2(*self.view_dimension()))
 def in_view(self, point: Point, view_offset: Vector2 = (0, 0)) -> bool:
     return Rectangle(Point(0, 0), Vector2(*self.dimension)).intersect(self.project(point, view_offset))
 def test_rect2rect(self):
     b = Rectangle(Point(0, 0), Vector2(2, 2))
     self.assertTrue(b.intersect(b))
     b1 = Rectangle(Point(1, 1), Vector2(2, 2))
     self.assertTrue(b.intersect(b1))
     b2 = Rectangle(Point(2, 2), Vector2(2, 2))
     self.assertTrue(b.intersect(b2))
     b3 = Rectangle(Point(2.1, 2.1), Vector2(2, 2))
     self.assertFalse(b.intersect(b3))
     b4 = Rectangle(Point(1, 1), Vector2(-2, -2))
     self.assertTrue(b.intersect(b4))
     b5 = Rectangle(Point(2, 2), Vector2(-2, -2))
     self.assertTrue(b.intersect(b5))
     b6 = Rectangle(Point(0, 0), Vector2(-2, -2))
     self.assertTrue(b.intersect(b6))
     b7 = Rectangle(Point(-.1, -.1), Vector2(-2, -2))
     self.assertFalse(b.intersect(b7))
 def test_rect2ray(self):
     b = Rectangle(Point(0, 0), Vector2(2, 2))
     r1 = create_ray(Point(-1, -1), Vector2(1, 1))
     self.assertTrue(b.intersect(r1))
     r2 = create_ray(Point(1, 1), Vector2(3, 3))
     self.assertTrue(b.intersect(r2))
     r3 = create_ray(Point(0, 0), Vector2(2, 2))
     self.assertTrue(b.intersect(r3))
     r4 = create_ray(Point(-1, -1), Vector2(3, 3))
     self.assertTrue(b.intersect(r4))
     r5 = create_ray(Point(-1, 0), Vector2(1, 0), 0.99)
     self.assertFalse(b.intersect(r5))
     r6 = create_ray(Point(3, 0), Vector2(-1, 0), 0.99)
     self.assertFalse(b.intersect(r6))
     r7 = create_ray(Point(-1, 1), Vector2(0, -1))
     self.assertFalse(b.intersect(r7))
     r8 = create_ray(Point(-1, 1), Vector2(1, -1))
     self.assertTrue(b.intersect(r8))
     r9 = create_ray(Point(-1, 1), Vector2(2, -1))
     self.assertTrue(b.intersect(r9))
Ejemplo n.º 10
0
    def update(self) -> None:
        g: Vector2 = Vector2(
            *v_mul((0, -1), self.target.physics_data.GRAVITY_MODIFIER))
        tile_grid = self.target.scene.tile_grid
        physics_data = self.target.physics_data
        delta_time = self.target.scene.delta_time
        bounding_box = Rectangle(self.target.position, physics_data.size)

        # calculate new velocity from acceleration
        physics_data.velocity = Vector2(
            *v_add(physics_data.velocity, v_mul(g, delta_time)))

        # calculate position change from velocity
        h_trans, v_trans = v_mul(physics_data.velocity, delta_time)

        # prepare static collision with bounding_box
        h_rays = bounding_box.directional_projection(h_trans, horizontal=True)
        # get all ray hits
        all_hits = (multi_ray_cast(tile_grid,
                                   blocked_tiles,
                                   *h_rays,
                                   only_hits=True))
        # try to select closest hit
        closest_hit = next(iter(sorted(all_hits, key=lambda h: h.distance)),
                           None)
        # if there is any hit
        h_absorb = bool(closest_hit)
        if h_absorb:
            # correct horizontal translation and align it with tile
            correction = alignment_correction(closest_hit.point.x,
                                              closest_hit.ray.dir.x)
            h_trans = closest_hit.ray.dir.x * closest_hit.distance + correction

        v_rays = tuple(
            bounding_box.directional_projection(v_trans, horizontal=False))
        all_hits = tuple(
            multi_ray_cast(tile_grid, blocked_tiles, *v_rays, only_hits=True))
        closest_hit = next(iter(sorted(all_hits, key=lambda h: h.distance)),
                           None)
        v_absorb = bool(closest_hit)
        if v_absorb:
            correction = alignment_correction(closest_hit.point.y,
                                              closest_hit.ray.dir.y)
            v_trans = closest_hit.ray.dir.y * closest_hit.distance + correction

        if v_trans < 0 and all_hits and (all_hits[0].ray != v_rays[0]
                                         or all_hits[-1].ray != v_rays[-1]):
            self.target.push_event(EventId.watch_out)

        # grid debugging
        # ----------------------------------------------------
        # from api.sax_engine.core import add_content_to_scene
        # from prefabs.debug import PointMarker
        # min_x, min_y = bounding_box.min().to_int()
        # max_x, max_y = bounding_box.max().to_int()
        # for x in range(min_x, max_x + 1):
        #     for y in range(min_y, max_y + 1):
        #         p = Point(*v_add((x, y), (0.5, 0.5)))
        #         add_content_to_scene(self.target.scene, PointMarker(p, (255, 0, 0), self.target.scene.delta_time))
        # ----------------------------------------------------

        # set new position
        remove_shape_from_grid(tile_grid, bounding_box, self.target)
        self.target.position = Point(
            *v_add(self.target.position, (h_trans, v_trans)))
        register_shape_on_grid(tile_grid, bounding_box, self.target)

        # adjust velocity based on collision data
        h_vel, v_vel = physics_data.velocity
        if h_absorb:
            self.target.push_event(EventId.touch_wall, orientation=h_absorb)
            h_vel = 0
        if v_absorb:
            if v_vel < 0:
                self.target.push_event(EventId.touch_floor)
            else:
                self.target.push_event(EventId.touch_ceiling)
            v_vel = 0
        physics_data.velocity = Vector2(h_vel, v_vel)