Пример #1
0
class Car(pygame.sprite.Sprite):
    def __init__(self,
                 track,
                 color=None,
                 pos=None,
                 angle=None,
                 enable_ai=True):
        super().__init__()
        cars = {0: "white", 1: "yellow", 2: "red", 3: "green", 4: "blue"}
        if type(color) == int and color in cars.keys():
            color = cars[color]
        elif color is None or color not in cars.values():
            color = cars[random.randint(0, 4)]
        if pos is None:
            pos = track.starting_pos
        if angle is None:
            angle = track.starting_angle
        self.__track = track
        self.__base_image = pygame.image.load(
            os.path.join("pictures", "car_" + color + ".png")).convert_alpha()
        self.__image = self.__base_image.copy()
        rect = self.__image.get_rect(center=pos)
        self.__body = pymunk.Body(
            1, pymunk.moment_for_box(1, (rect.w - 14, rect.h - 16)))
        self.__body.position = pos
        self.__body.angle = math.radians(angle)
        self.__shape = pymunk.Poly.create_box(self.__body,
                                              (rect.w - 14, rect.h - 16), 5)
        self.__shape.friction = 1
        self.__shape.sensor = True
        self.__shape.filter = pymunk.ShapeFilter()
        track.space.add(self.__body, self.__shape)
        self.__filter = pymunk.ShapeFilter(mask=pymunk.ShapeFilter.ALL_MASKS)
        self.__scan_density = 120
        self.__scan_range = math.sqrt(self.__track.screen.get_width()**2 +
                                      self.__track.screen.get_height()**2)
        self.__scan_angles = [
            round(x / self.__scan_density * 360)
            for x in range(self.__scan_density)
        ]
        self.__scan_area = [(math.cos(2 * math.pi / self.__scan_density * x) *
                             self.__scan_range,
                             math.sin(2 * math.pi / self.__scan_density * x) *
                             self.__scan_range)
                            for x in range(0, self.__scan_density + 1)][:-1]
        self.__scan_area.reverse()
        self.__scan_intersections = deque()
        self.__sensor_data = {}
        self._update_sensors()
        self.__ai = Ai(math.degrees(self.angle), self.velocity, enable_ai)

    def __del__(self):
        self.__track.space.remove(self.__body, self.__shape)

    def _update_sensors(self):
        self.__sensor_data = {}
        self.__scan_intersections.clear()
        for scan_point in self.__scan_area:
            segment_info = self.__track.space.segment_query_first(
                self.pos, tuple(map(sum, zip(self.pos, scan_point))), 1,
                self.__filter)
            if segment_info:
                self.__scan_intersections.append(segment_info.point)
            else:
                self.reset()
                return
        self.__scan_intersections.rotate(1 + round(
            min(self.__scan_angles,
                key=lambda x: abs(x - (math.degrees(self.angle) % 360))) /
            (360 / self.__scan_density)))
        for angle, intersection in zip(self.__scan_angles,
                                       self.__scan_intersections):
            self.__sensor_data[angle] = intersection.get_distance(self.pos)

    def update(self):
        self.enable_collision(self.__track.collision)
        self.__image = pygame.transform.rotate(self.__base_image,
                                               -math.degrees(self.angle))
        self._update_sensors()
        if pygame.key.get_pressed()[K_d]:
            if self.__sensor_data:
                self.__track.window.message(
                    "{:.0f}".format(self.__sensor_data[0]), (1000, 200), "red")
                self.__track.window.message(
                    "{:.0f}".format(self.__sensor_data[48]), (1100, 200),
                    "green")
                self.__track.window.message(
                    "{:.0f}".format(self.__sensor_data[312]), (1200, 200),
                    "orange")
                for i, intersection in enumerate(self.__scan_intersections):
                    if i == 0:
                        pygame.draw.line(self.__track.screen, Color("red"),
                                         self.pos, intersection, 5)
                        continue
                    if i == 48 / (360 / self.__scan_density):
                        pygame.draw.line(self.__track.screen, Color("green"),
                                         self.pos, intersection, 5)
                        continue
                    if i == 312 / (360 / self.__scan_density):
                        pygame.draw.line(self.__track.screen, Color("orange"),
                                         self.pos, intersection, 5)
                        continue
                    pygame.draw.line(self.__track.screen, Color("black"),
                                     self.pos, intersection, 1)
        self.__track.screen.blit(self.__image,
                                 self.__image.get_rect(center=self.pos))

    def drive(self):
        if self.__ai.enabled and self.__sensor_data:
            self.__ai.input(self.__sensor_data)
            self.__ai.process()
            steer, accelerate = self.__ai.output()
            self.__body.apply_force_at_local_point((0, steer), (0, 0))
            self.__body.angular_velocity = steer / 250
            self.__body.apply_force_at_local_point((accelerate, 0), (0, 0))

    def turn_left(self):
        self.__body.apply_force_at_local_point((0, -500), (0, 0))
        self.__body.angular_velocity = -2

    def turn_right(self):
        self.__body.apply_force_at_local_point((0, 500), (0, 0))
        self.__body.angular_velocity = 2

    def accelerate(self):
        self.__body.apply_force_at_local_point((2000, 0), (0, 0))

    def reverse(self):
        self.__body.apply_force_at_local_point((-2000, 0), (0, 0))

    def reset(self):
        self.__body.position = self.__track.starting_pos
        self.__body.angle = math.radians(self.__track.starting_angle)
        self.__body.velocity = (0.0, 0.0)
        self.__ai.reset()

    def enable_collision(self, enable):
        if enable == self.__shape.sensor:
            if enable:
                self.__shape.sensor = False
                self.__filter = pymunk.ShapeFilter(
                    mask=pymunk.ShapeFilter.ALL_MASKS ^ 0x1)
                self.__shape.filter = pymunk.ShapeFilter(categories=0x1)
            else:
                self.__shape.sensor = True
                self.__filter = pymunk.ShapeFilter(
                    mask=pymunk.ShapeFilter.ALL_MASKS)
                self.__shape.filter = pymunk.ShapeFilter()

    def toggle_ai(self):
        self.__ai.enabled = not self.__ai.enabled

    @property
    def ai_enabled(self):
        return self.__ai.enabled

    @property
    def pos(self):
        return self.__body.position

    @property
    def angle(self):
        return self.__body.angle

    @property
    def velocity(self):
        return self.__body.velocity