new fire sim working fine
This commit is contained in:
@@ -31,6 +31,7 @@ class Config:
|
|||||||
# ===== Симулятор робота =====
|
# ===== Симулятор робота =====
|
||||||
MAP_WIDTH = 800
|
MAP_WIDTH = 800
|
||||||
MAP_HEIGHT = 600
|
MAP_HEIGHT = 600
|
||||||
|
BORDER_THICKNESS = 10
|
||||||
MAP_OBSTACLES = [
|
MAP_OBSTACLES = [
|
||||||
(200, 150, 100, 200),
|
(200, 150, 100, 200),
|
||||||
(500, 300, 150, 50),
|
(500, 300, 150, 50),
|
||||||
@@ -41,7 +42,7 @@ class Config:
|
|||||||
ROBOT_IMAGE_PATH = 'robot.png' # или None
|
ROBOT_IMAGE_PATH = 'robot.png' # или None
|
||||||
|
|
||||||
INITIAL_HP = 100
|
INITIAL_HP = 100
|
||||||
INITIAL_CHARGE = 100
|
INITIAL_FIRE_CHARGE = 4
|
||||||
COLLISION_DAMAGE = 10
|
COLLISION_DAMAGE = 10
|
||||||
|
|
||||||
# Конус перед роботом
|
# Конус перед роботом
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ def main():
|
|||||||
landmarks = result['landmarks']
|
landmarks = result['landmarks']
|
||||||
|
|
||||||
# Статический жест
|
# Статический жест
|
||||||
|
special = special_detector.predict(landmarks)
|
||||||
|
|
||||||
# Вкл/выкл управления жестами
|
# Вкл/выкл управления жестами
|
||||||
if special == 'cross' and cross_cooldown == 0:
|
if special == 'cross' and cross_cooldown == 0:
|
||||||
@@ -94,10 +95,12 @@ def main():
|
|||||||
last_dynamic = dynamic_gesture
|
last_dynamic = dynamic_gesture
|
||||||
dynamic_counter = 30
|
dynamic_counter = 30
|
||||||
|
|
||||||
|
'''
|
||||||
if dynamic_gesture == 'wave_left':
|
if dynamic_gesture == 'wave_left':
|
||||||
robot.reset_position()
|
robot.reset_position()
|
||||||
elif dynamic_gesture == 'wave_right':
|
elif dynamic_gesture == 'wave_right':
|
||||||
robot.restart_game()
|
robot.restart()
|
||||||
|
'''
|
||||||
|
|
||||||
# Отрисовка
|
# Отрисовка
|
||||||
vis = detector.draw_landmarks(frame, result['pose_landmarks'])
|
vis = detector.draw_landmarks(frame, result['pose_landmarks'])
|
||||||
|
|||||||
+147
-34
@@ -19,7 +19,18 @@ class DummySimRobot(RobotController):
|
|||||||
self.clock = pygame.time.Clock()
|
self.clock = pygame.time.Clock()
|
||||||
self.font = pygame.font.SysFont(None, 28)
|
self.font = pygame.font.SysFont(None, 28)
|
||||||
|
|
||||||
self.obstacles = config.MAP_OBSTACLES
|
border_thickness = config.BORDER_THICKNESS
|
||||||
|
if border_thickness > 0:
|
||||||
|
border_obstacles = [
|
||||||
|
(0, 0, self.width, border_thickness),
|
||||||
|
(0, self.height - border_thickness, self.width, border_thickness),
|
||||||
|
(0, 0, border_thickness, self.height),
|
||||||
|
(self.width - border_thickness, 0, border_thickness, self.height)
|
||||||
|
]
|
||||||
|
self.obstacles = list(config.MAP_OBSTACLES) + border_obstacles
|
||||||
|
else:
|
||||||
|
self.obstacles = list(config.MAP_OBSTACLES)
|
||||||
|
|
||||||
self.start = config.START_POS
|
self.start = config.START_POS
|
||||||
self.finish = config.FINISH_POS
|
self.finish = config.FINISH_POS
|
||||||
self.robot_radius = config.ROBOT_RADIUS
|
self.robot_radius = config.ROBOT_RADIUS
|
||||||
@@ -34,8 +45,8 @@ class DummySimRobot(RobotController):
|
|||||||
|
|
||||||
self.max_hp = config.INITIAL_HP
|
self.max_hp = config.INITIAL_HP
|
||||||
self.hp = self.max_hp
|
self.hp = self.max_hp
|
||||||
self.max_charge = config.INITIAL_CHARGE
|
self.max_fire_charge = config.INITIAL_FIRE_CHARGE
|
||||||
self.charge = self.max_charge
|
self.charge = self.max_fire_charge
|
||||||
self.collision_damage = config.COLLISION_DAMAGE
|
self.collision_damage = config.COLLISION_DAMAGE
|
||||||
self.extinguish_radius = config.EXTINGUISH_RADIUS
|
self.extinguish_radius = config.EXTINGUISH_RADIUS
|
||||||
self.extinguish_angle = math.radians(config.EXTINGUISH_ANGLE)
|
self.extinguish_angle = math.radians(config.EXTINGUISH_ANGLE)
|
||||||
@@ -43,10 +54,19 @@ class DummySimRobot(RobotController):
|
|||||||
|
|
||||||
self.fires = []
|
self.fires = []
|
||||||
self.num_fires = config.NUM_FIRES
|
self.num_fires = config.NUM_FIRES
|
||||||
|
self.fire_radius = config.FIRE_RADIUS
|
||||||
|
|
||||||
self.score = 0
|
self.score = 0
|
||||||
self.game_over = False
|
self.game_over = False
|
||||||
self.won = False
|
self.won = False
|
||||||
|
|
||||||
|
self.extinguish_active = False
|
||||||
|
self.extinguish_timer = 0
|
||||||
|
self.extinguish_cooldown = 0
|
||||||
|
self.extinguish_cone_length = 80
|
||||||
|
|
||||||
|
self.invincibility_timer = 0
|
||||||
|
|
||||||
self.x, self.y = self.start
|
self.x, self.y = self.start
|
||||||
self.angle = 0.0
|
self.angle = 0.0
|
||||||
self.linear_speed = 0.0
|
self.linear_speed = 0.0
|
||||||
@@ -57,18 +77,42 @@ class DummySimRobot(RobotController):
|
|||||||
def _generate_fires(self):
|
def _generate_fires(self):
|
||||||
self.fires.clear()
|
self.fires.clear()
|
||||||
attempts = 0
|
attempts = 0
|
||||||
while len(self.fires) < self.num_fires and attempts < 1000:
|
margin = 30 # минимальное расстояние между центрами костров
|
||||||
fx = random.randint(self.robot_radius, self.width - self.robot_radius)
|
|
||||||
fy = random.randint(self.robot_radius, self.height - self.robot_radius)
|
while len(self.fires) < self.num_fires and attempts < 2000:
|
||||||
|
fx = random.randint(self.robot_radius + 10, self.width - self.robot_radius - 10)
|
||||||
|
fy = random.randint(self.robot_radius + 10, self.height - self.robot_radius - 10)
|
||||||
|
|
||||||
|
# Проверка: не на препятствии
|
||||||
if self._point_collides_with_obstacle(fx, fy, self.robot_radius):
|
if self._point_collides_with_obstacle(fx, fy, self.robot_radius):
|
||||||
|
attempts += 1
|
||||||
continue
|
continue
|
||||||
if math.hypot(fx - self.start[0], fy - self.start[1]) < self.robot_radius * 2:
|
|
||||||
|
# Проверка: не слишком близко к старту
|
||||||
|
if math.hypot(fx - self.start[0], fy - self.start[1]) < self.robot_radius * 3:
|
||||||
|
attempts += 1
|
||||||
continue
|
continue
|
||||||
if math.hypot(fx - self.finish[0], fy - self.finish[1]) < self.robot_radius * 2:
|
|
||||||
|
# Проверка: не слишком близко к финишу
|
||||||
|
if math.hypot(fx - self.finish[0], fy - self.finish[1]) < self.robot_radius * 3:
|
||||||
|
attempts += 1
|
||||||
continue
|
continue
|
||||||
self.fires.append(Fire(fx, fy, 15))
|
|
||||||
|
# Проверка: не пересекается с уже существующими кострами
|
||||||
|
overlap = False
|
||||||
|
for fire in self.fires:
|
||||||
|
if math.hypot(fx - fire.x, fy - fire.y) < 2 * self.fire_radius + margin:
|
||||||
|
overlap = True
|
||||||
|
break
|
||||||
|
if overlap:
|
||||||
|
attempts += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.fires.append(Fire(fx, fy, self.fire_radius))
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
|
print(f"Сгенерировано {len(self.fires)} очагов возгорания")
|
||||||
|
|
||||||
def _point_collides_with_obstacle(self, x, y, radius):
|
def _point_collides_with_obstacle(self, x, y, radius):
|
||||||
for ox, oy, ow, oh in self.obstacles:
|
for ox, oy, ow, oh in self.obstacles:
|
||||||
closest_x = max(ox, min(x, ox + ow))
|
closest_x = max(ox, min(x, ox + ow))
|
||||||
@@ -81,6 +125,7 @@ class DummySimRobot(RobotController):
|
|||||||
self.linear_speed = max(-1.0, min(1.0, v))
|
self.linear_speed = max(-1.0, min(1.0, v))
|
||||||
self.angular_speed = max(-1.0, min(1.0, w))
|
self.angular_speed = max(-1.0, min(1.0, w))
|
||||||
|
|
||||||
|
'''
|
||||||
def send_fire_ext_burst_cmd(self):
|
def send_fire_ext_burst_cmd(self):
|
||||||
if self.charge <= 0:
|
if self.charge <= 0:
|
||||||
return False
|
return False
|
||||||
@@ -91,36 +136,72 @@ class DummySimRobot(RobotController):
|
|||||||
self.score += self.score_per_fire
|
self.score += self.score_per_fire
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
'''
|
||||||
|
|
||||||
def _is_fire_in_front(self, fire):
|
def send_fire_ext_burst_cmd(self):
|
||||||
dx = fire.x - self.x
|
if self.extinguish_cooldown > 0 or self.extinguish_active:
|
||||||
dy = fire.y - self.y
|
|
||||||
dist = math.hypot(dx, dy)
|
|
||||||
if dist > self.extinguish_radius:
|
|
||||||
return False
|
return False
|
||||||
angle_to_fire = math.atan2(dy, dx)
|
if self.charge <= 0:
|
||||||
angle_diff = abs(angle_to_fire - self.angle)
|
return False
|
||||||
angle_diff = min(angle_diff, 2*math.pi - angle_diff)
|
|
||||||
return angle_diff < self.extinguish_angle / 2
|
self.extinguish_active = True
|
||||||
|
self.extinguish_timer = 30
|
||||||
|
self.extinguish_cooldown = 10
|
||||||
|
|
||||||
|
self.charge -= 1
|
||||||
|
|
||||||
|
cone_len = self.extinguish_cone_length
|
||||||
|
cone_angle_half = self.extinguish_angle / 2
|
||||||
|
extinguished = False
|
||||||
|
|
||||||
|
for fire in self.fires[:]:
|
||||||
|
dx = fire.x - self.x
|
||||||
|
dy = fire.y - self.y
|
||||||
|
dist = math.hypot(dx, dy)
|
||||||
|
if dist > cone_len:
|
||||||
|
continue
|
||||||
|
|
||||||
|
angle_to_fire = math.atan2(dy, dx)
|
||||||
|
angle_diff = angle_to_fire - self.angle
|
||||||
|
angle_diff = (angle_diff + math.pi) % (2*math.pi) - math.pi #[-π, π]
|
||||||
|
|
||||||
|
if abs(angle_diff) <= cone_angle_half:
|
||||||
|
self.fires.remove(fire)
|
||||||
|
self.score += self.score_per_fire
|
||||||
|
extinguished = True
|
||||||
|
break
|
||||||
|
|
||||||
|
return extinguished
|
||||||
|
|
||||||
def update(self, dt=1/30.0):
|
def update(self, dt=1/30.0):
|
||||||
if self.game_over or self.won:
|
if self.game_over or self.won:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.invincibility_timer > 0:
|
||||||
|
self.invincibility_timer -= 1
|
||||||
|
if self.extinguish_timer > 0:
|
||||||
|
self.extinguish_timer -= 1
|
||||||
|
if self.extinguish_timer == 0:
|
||||||
|
self.extinguish_active = False
|
||||||
|
if self.extinguish_cooldown > 0:
|
||||||
|
self.extinguish_cooldown -= 1
|
||||||
|
|
||||||
|
# Движение
|
||||||
self.angle += self.angular_speed * dt * 2
|
self.angle += self.angular_speed * dt * 2
|
||||||
dx = self.linear_speed * math.cos(self.angle) * dt * 100
|
dx = self.linear_speed * math.cos(self.angle) * dt * 100
|
||||||
dy = self.linear_speed * math.sin(self.angle) * dt * 100
|
dy = self.linear_speed * math.sin(self.angle) * dt * 100
|
||||||
new_x = self.x + dx
|
new_x = self.x + dx
|
||||||
new_y = self.y + dy
|
new_y = self.y + dy
|
||||||
|
|
||||||
new_x = max(self.robot_radius, min(self.width - self.robot_radius, new_x))
|
# Проверка: столкновение
|
||||||
new_y = max(self.robot_radius, min(self.height - self.robot_radius, new_y))
|
|
||||||
|
|
||||||
if self._point_collides_with_obstacle(new_x, new_y, self.robot_radius):
|
if self._point_collides_with_obstacle(new_x, new_y, self.robot_radius):
|
||||||
self.hp -= self.collision_damage
|
if self.invincibility_timer == 0:
|
||||||
|
self.hp -= self.collision_damage
|
||||||
|
self.invincibility_timer = 30
|
||||||
else:
|
else:
|
||||||
self.x, self.y = new_x, new_y
|
self.x, self.y = new_x, new_y
|
||||||
|
|
||||||
|
# Конец игры?
|
||||||
if math.hypot(self.x - self.finish[0], self.y - self.finish[1]) < self.robot_radius:
|
if math.hypot(self.x - self.finish[0], self.y - self.finish[1]) < self.robot_radius:
|
||||||
self.won = True
|
self.won = True
|
||||||
if self.hp <= 0:
|
if self.hp <= 0:
|
||||||
@@ -128,26 +209,53 @@ class DummySimRobot(RobotController):
|
|||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
self.screen.fill((255,255,255))
|
self.screen.fill((255,255,255))
|
||||||
|
|
||||||
|
# Препятствия
|
||||||
for ox, oy, ow, oh in self.obstacles:
|
for ox, oy, ow, oh in self.obstacles:
|
||||||
pygame.draw.rect(self.screen, (100,100,100), (ox, oy, ow, oh))
|
pygame.draw.rect(self.screen, (100,100,100), (ox, oy, ow, oh))
|
||||||
|
|
||||||
|
# Финиш
|
||||||
pygame.draw.circle(self.screen, (0,200,0), (int(self.finish[0]), int(self.finish[1])), self.robot_radius, 3)
|
pygame.draw.circle(self.screen, (0,200,0), (int(self.finish[0]), int(self.finish[1])), self.robot_radius, 3)
|
||||||
|
|
||||||
|
# Костры
|
||||||
for fire in self.fires:
|
for fire in self.fires:
|
||||||
pygame.draw.circle(self.screen, (255,0,0), (int(fire.x), int(fire.y)), fire.radius)
|
pygame.draw.circle(self.screen, (255,0,0), (int(fire.x), int(fire.y)), fire.radius)
|
||||||
pygame.draw.circle(self.screen, (255,100,0), (int(fire.x), int(fire.y)), fire.radius-3)
|
pygame.draw.circle(self.screen, (255,100,0), (int(fire.x), int(fire.y)), fire.radius-3)
|
||||||
|
|
||||||
if self.robot_image:
|
# Конус тушения
|
||||||
rotated = pygame.transform.rotate(self.robot_image, -math.degrees(self.angle))
|
if self.extinguish_active:
|
||||||
rect = rotated.get_rect(center=(int(self.x), int(self.y)))
|
cone_surface = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
|
||||||
self.screen.blit(rotated, rect)
|
num_points = 20
|
||||||
else:
|
points = [(self.x, self.y)]
|
||||||
nose = (self.x + self.robot_radius * math.cos(self.angle),
|
half_angle = self.extinguish_angle / 2
|
||||||
self.y + self.robot_radius * math.sin(self.angle))
|
cone_len = self.extinguish_cone_length
|
||||||
left = (self.x + self.robot_radius * math.cos(self.angle + 2.5),
|
for i in range(num_points + 1):
|
||||||
self.y + self.robot_radius * math.sin(self.angle + 2.5))
|
angle = self.angle - half_angle + (self.extinguish_angle) * (i / num_points)
|
||||||
right = (self.x + self.robot_radius * math.cos(self.angle - 2.5),
|
x = self.x + cone_len * math.cos(angle)
|
||||||
self.y + self.robot_radius * math.sin(self.angle - 2.5))
|
y = self.y + cone_len * math.sin(angle)
|
||||||
pygame.draw.polygon(self.screen, (0,0,255), [nose, left, right])
|
points.append((x, y))
|
||||||
|
if len(points) > 2:
|
||||||
|
pygame.draw.polygon(cone_surface, (0, 200, 255, 80), points)
|
||||||
|
self.screen.blit(cone_surface, (0, 0))
|
||||||
|
|
||||||
|
# Робот
|
||||||
|
if self.invincibility_timer > 0 and (self.invincibility_timer % 6 < 3):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if self.robot_image:
|
||||||
|
rotated = pygame.transform.rotate(self.robot_image, -math.degrees(self.angle))
|
||||||
|
rect = rotated.get_rect(center=(int(self.x), int(self.y)))
|
||||||
|
self.screen.blit(rotated, rect)
|
||||||
|
else:
|
||||||
|
nose = (self.x + self.robot_radius * math.cos(self.angle),
|
||||||
|
self.y + self.robot_radius * math.sin(self.angle))
|
||||||
|
left = (self.x + self.robot_radius * math.cos(self.angle + 2.5),
|
||||||
|
self.y + self.robot_radius * math.sin(self.angle + 2.5))
|
||||||
|
right = (self.x + self.robot_radius * math.cos(self.angle - 2.5),
|
||||||
|
self.y + self.robot_radius * math.sin(self.angle - 2.5))
|
||||||
|
pygame.draw.polygon(self.screen, (0,0,255), [nose, left, right])
|
||||||
|
|
||||||
|
# Статистика
|
||||||
hp_text = self.font.render(f"HP: {self.hp}", True, (0,0,0))
|
hp_text = self.font.render(f"HP: {self.hp}", True, (0,0,0))
|
||||||
charge_text = self.font.render(f"Charge: {self.charge}", True, (0,0,0))
|
charge_text = self.font.render(f"Charge: {self.charge}", True, (0,0,0))
|
||||||
score_text = self.font.render(f"Score: {self.score}", True, (0,0,0))
|
score_text = self.font.render(f"Score: {self.score}", True, (0,0,0))
|
||||||
@@ -157,6 +265,7 @@ class DummySimRobot(RobotController):
|
|||||||
self.screen.blit(score_text, (10, 70))
|
self.screen.blit(score_text, (10, 70))
|
||||||
self.screen.blit(fires_text, (10, 100))
|
self.screen.blit(fires_text, (10, 100))
|
||||||
|
|
||||||
|
# Конец игры
|
||||||
if self.game_over:
|
if self.game_over:
|
||||||
msg = self.font.render("GAME OVER – Press R to restart", True, (255,0,0))
|
msg = self.font.render("GAME OVER – Press R to restart", True, (255,0,0))
|
||||||
self.screen.blit(msg, (self.width//2 - 150, self.height//2))
|
self.screen.blit(msg, (self.width//2 - 150, self.height//2))
|
||||||
@@ -195,6 +304,10 @@ class DummySimRobot(RobotController):
|
|||||||
self.score = 0
|
self.score = 0
|
||||||
self.game_over = False
|
self.game_over = False
|
||||||
self.won = False
|
self.won = False
|
||||||
|
self.invincibility_timer = 0
|
||||||
|
self.extinguish_active = False
|
||||||
|
self.extinguish_timer = 0
|
||||||
|
self.extinguish_cooldown = 0
|
||||||
self._generate_fires()
|
self._generate_fires()
|
||||||
|
|
||||||
def reset_position(self):
|
def reset_position(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user