- BC20270371's blog
hhh
- 2025-6-5 19:22:54 @
def line_intersects_rect(self, x1, y1, x2, y2, rx1, ry1, rx2, ry2): # 检查线段是否与矩形相交 def ccw(A, B, C): return (C[1]-A[1]) * (B[0]-A[0]) > (B[1]-A[1]) * (C[0]-A[0])
def intersect(A, B, C, D):
return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)
# 矩形的四条边
edges = [
((rx1, ry1), (rx2, ry1)), # 上边
((rx2, ry1), (rx2, ry2)), # 右边
((rx2, ry2), (rx1, ry2)), # 下边
((rx1, ry2), (rx1, ry1)) # 左边
]
for edge in edges:
if intersect((x1, y1), (x2, y2), edge[0], edge[1]):
return True
# 检查线段是否完全在矩形内
if rx1 <= x1 <= rx2 and ry1 <= y1 <= ry2 and rx1 <= x2 <= rx2 and ry1 <= y2 <= ry2:
return True
return False
def line_rect_intersection(self, x1, y1, x2, y2, rx1, ry1, rx2, ry2):
# 计算线段与矩形的交点
def line_intersection(line1, line2):
xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])
def det(a, b):
return a[0] * b[1] - a[1] * b[0]
div = det(xdiff, ydiff)
if div == 0:
return None
d = (det(*line1), det(*line2))
x = det(d, xdiff) / div
y = det(d, ydiff) / div
return x, y
# 矩形的四条边
edges = [
((rx1, ry1), (rx2, ry1)), # 上边
((rx2, ry1), (rx2, ry2)), # 右边
((rx2, ry2), (rx1, ry2)), # 下边
((rx1, ry2), (rx1, ry1)) # 左边
]
intersections = []
line = ((x1, y1), (x2, y2))
for edge in edges:
intersection = line_intersection(line, edge)
if intersection:
# 检查交点是否在线段上
if min(x1, x2) <= intersection[0] <= max(x1, x2) and \
min(y1, y2) <= intersection[1] <= max(y1, y2):
intersections.append(intersection)
# 返回最近的交点
if intersections:
return min(intersections, key=lambda p: ((p[0] - x1)**2 + (p[1] - y1)**2))
return None
def find_nearest_enemy(self, unit, enemies):
# 查找最近的敌人
nearest = None
min_dist = float('inf')
for enemy in enemies:
if enemy["health"] > 0:
dist = self.distance(unit, enemy)
if dist < min_dist:
min_dist = dist
nearest = enemy
return nearest
def find_best_cover(self, unit, enemy):
# 寻找最佳掩体
best_cover = None
best_distance = float('inf')
# 计算敌人方向
angle = math.atan2(enemy["y"] - unit["y"], enemy["x"] - unit["x"])
# 寻找距离敌人最远的掩体
for cover in unit["cover_points"]:
# 计算掩体到敌人的距离
cover_to_enemy = math.sqrt((cover[0] - enemy["x"])**2 + (cover[1] - enemy["y"])**2)
# 计算掩体到单位的距离
cover_to_unit = math.sqrt((cover[0] - unit["x"])**2 + (cover[1] - unit["y"])**2)
# 检查掩体是否在敌人视线范围内
if cover_to_unit < 150 and cover_to_enemy > 100:
# 检查掩体是否提供遮挡
if not self.has_line_of_sight({"x": cover[0], "y": cover[1]}, enemy):
if cover_to_enemy > best_distance:
best_distance = cover_to_enemy
best_cover = cover
return best_cover
def astar(self, start, goal):
# A*寻路算法
def heuristic(a, b):
# 曼哈顿距离
return abs(a[0] - b[0]) + abs(a[1] - b[1])
# 网格大小
grid_size = 10
# 起点和终点的网格坐标
start_grid = (int(start[0] / grid_size), int(start[1] / grid_size))
goal_grid = (int(goal[0] / grid_size), int(goal[1] / grid_size))
# 定义网格边界
grid_width = 1024 // grid_size
grid_height = 668 // grid_size
# 初始化开放列表和关闭列表
open_set = {start_grid}
closed_set = set()
# 记录从起点到当前点的实际代价
g_score = {start_grid: 0}
# 记录从起点到终点的估计代价
f_score = {start_grid: heuristic(start_grid, goal_grid)}
# 记录路径
came_from = {}
# 八个方向的移动
directions = [(0, 1), (1, 0), (0, -1), (-1, 0), (1, 1), (-1, 1), (1, -1), (-1, -1)]
while open_set:
# 找到f值最小的节点
current = min(open_set, key=lambda node: f_score.get(node, float('inf')))
# 如果到达终点,构建路径
if current == goal_grid:
path = []
while current in came_from:
path.append((current[0] * grid_size, current[1] * grid_size))
current = came_from[current]
path.append(start)
path.reverse()
return path
# 将当前节点从开放列表移到关闭列表
open_set.remove(current)
closed_set.add(current)
# 检查所有相邻节点
for dx, dy in directions:
neighbor = (current[0] + dx, current[1] + dy)
# 检查边界
if not (0 <= neighbor[0] < grid_width and 0 <= neighbor[1] < grid_height):
continue
# 检查障碍物
real_pos = (neighbor[0] * grid_size, neighbor[1] * grid_size)
if self.is_in_obstacle(real_pos[0], real_pos[1]):
continue
# 如果在关闭列表中,跳过
if neighbor in closed_set:
continue
# 计算从起点到相邻节点的代价
tentative_g_score = g_score[current] + (14 if dx != 0 and dy != 0 else 10)
# 如果是新节点或找到了更好的路径
if neighbor not in open_set or tentative_g_score < g_score.get(neighbor, float('inf')):
# 记录路径
came_from[neighbor] = current
# 更新代价
g_score[neighbor] = tentative_g_score
f_score[neighbor] = tentative_g_score + heuristic(neighbor, goal_grid)
# 将相邻节点加入开放列表
open_set.add(neighbor)
# 如果没有找到路径,返回直接前往目标的路径
return [start, goal]
def follow_path(self, unit):
if unit["path"] and len(unit["path"]) > 1:
# 获取下一个路径点
next_point = unit["path"][1]
# 计算移动方向
dx = next_point[0] - unit["x"]
dy = next_point[1] - unit["y"]
distance = math.sqrt(dx*dx + dy*dy)
if distance > 5: # 如果距离大于5,移动
# 归一化方向向量
dx /= distance
dy /= distance
# 移动速度
speed = 3
# 移动单位
unit["x"] += dx * speed
unit["y"] += dy * speed
# 更新画布位置
self.canvas.coords(unit["canvas_obj"],
unit["x"] - 10, unit["y"] - 10,
unit["x"] + 10, unit["y"] + 10)
self.canvas.coords(unit["canvas_text"], unit["x"], unit["y"])
if not unit["is_player"]:
self.canvas.coords(unit["state_indicator"], unit["x"], unit["y"] - 20)
self.canvas.coords(unit["behavior_indicator"], unit["x"], unit["y"])
else:
# 更新玩家朝向指示器
self.canvas.coords(
unit["direction_line"],
unit["x"], unit["y"],
unit["x"] + math.cos(unit["direction"]) * 20,
unit["y"] + math.sin(unit["direction"]) * 20
)
else:
# 已到达下一个路径点,移除它
unit["path"].pop(1)
def update_player_position(self):
if not self.game_active:
return
# 获取玩家
player = self.defenders[0] if self.defender_turn else self.attackers[0]
# 计算移动方向
dx = 0
dy = 0
if self.key_pressed["w"]:
dy -= self.player_speed
if self.key_pressed["s"]:
dy += self.player_speed
if self.key_pressed["a"]:
dx -= self.player_speed
if self.key_pressed["d"]:
dx += self.player_speed
# 检查移动是否会碰到障碍物
new_x = player["x"] + dx
new_y = player["y"] + dy
if not self.is_in_obstacle(new_x, new_y):
# 更新玩家位置
player["x"] = new_x
player["y"] = new_y
# 更新画布位置
self.canvas.coords(player["canvas_obj"],
player["x"] - 15, player["y"] - 15,
player["x"] + 15, player["y"] + 15)
self.canvas.coords(player["canvas_text"], player["x"], player["y"])
# 更新玩家朝向指示器
self.canvas.coords(
player["direction_line"],
player["x"], player["y"],
player["x"] + math.cos(player["direction"]) * 20,
player["y"] + math.sin(player["direction"]) * 20
)
# 更新玩家记忆中的位置
if (player["x"], player["y"]) not in player["memory"]["visited_positions"]:
player["memory"]["visited_positions"].append((player["x"], player["y"]))
# 限制记忆大小
if len(player["memory"]["visited_positions"]) > 20:
player["memory"]["visited_positions"].pop(0)
# 检查是否触发陷阱
for trap in self.defense_objects[:]:
if trap["type"] == "trap" and not trap["triggered"]:
if self.distance(player, trap) < trap["trigger_radius"]:
self.trigger_trap(trap, player)
# 检查是否可以拆除拆弹器(防守方)
if self.defender_turn and self.bomb_planted and self.distance(player, self.defuser) < 30:
self.defend_defuser()
# 检查是否可以放置炸弹(进攻方)
if not self.defender_turn and not self.bomb_planted:
if self.distance(player, self.bomb_site) < self.bomb_site["radius"]:
# 显示放置炸弹提示
pass
# 每帧更新
self.root.after(16, self.update_player_position)
def update_ai_memory(self, unit):
# 更新AI记忆中的位置
if (unit["x"], unit["y"]) not in unit["memory"]["visited_positions"]:
unit["memory"]["visited_positions"].append((unit["x"], unit["y"]))
# 限制记忆大小
if len(unit["memory"]["visited_positions"]) > 20:
unit["memory"]["visited_positions"].pop(0)
# 更新记忆中的陷阱位置
for trap in self.defense_objects:
if trap["type"] == "trap":
trap_pos = (trap["x"], trap["y"])
if trap_pos not in unit["memory"]["trap_positions"]:
unit["memory"]["trap_positions"].append(trap_pos)
# 限制记忆大小
if len(unit["memory"]["trap_positions"]) > 15:
unit["memory"]["trap_positions"].pop(0)
# 更新记忆中的敌人位置
enemies = self.attackers if unit["role"] == "defender" else self.defenders
for enemy in enemies:
if enemy["health"] > 0:
enemy_pos = (enemy["x"], enemy["y"])
if enemy_pos not in unit["memory"]["enemy_positions"]:
unit["memory"]["enemy_positions"].append(enemy_pos)
# 限制记忆大小
if len(unit["memory"]["enemy_positions"]) > 15:
unit["memory"]["enemy_positions"].pop(0)
def avoid_traps(self, unit):
# 检查记忆中的陷阱位置并避开
if unit["memory"]["trap_positions"] and random.random() < 0.7:
# 检查是否靠近陷阱
for trap_pos in unit["memory"]["trap_positions"]:
trap = {"x": trap_pos[0], "y": trap_pos[1]}
if self.distance(unit, trap) < 50:
# 避开陷阱
avoid_dir_x = unit["x"] - trap["x"]
avoid_dir_y = unit["y"] - trap["y"]
distance = math.sqrt(avoid_dir_x*avoid_dir_x + avoid_dir_y*avoid_dir_y)
if distance > 0:
# 归一化方向向量
avoid_dir_x /= distance
avoid_dir_y /= distance
# 寻找避开陷阱的位置
safe_x = unit["x"] + avoid_dir_x * 100
safe_y = unit["y"] + avoid_dir_y * 100
# 确保安全位置不在障碍物内
if not self.is_in_obstacle(safe_x, safe_y):
# 生成新路径
unit["path"] = self.astar((unit["x"], unit["y"]), (safe_x, safe_y))
break
def check_trap_trigger(self, defender):
# 检查是否可以主动触发陷阱
for trap in self.defense_objects:
if trap["type"] == "trap" and not trap["triggered"]:
# 检查是否有敌人靠近陷阱
for attacker in self.attackers:
if attacker["health"] > 0 and self.distance(attacker, trap) < trap["trigger_radius"]:
# 有敌人靠近,触发陷阱
self.trigger_trap(trap, attacker)
break
def trigger_trap(self, trap, target):
# 触发陷阱
trap["triggered"] = True
# 造成伤害
target["health"] -= trap["damage"]
# 显示爆炸效果
explosion = self.canvas.create_oval(
trap["x"] - trap["trigger_radius"], trap["y"] - trap["trigger_radius"],
trap["x"] + trap["trigger_radius"], trap["y"] + trap["trigger_radius"],
fill="orange", outline="red"
)
# 显示伤害数字
damage_text = self.canvas.create_text(
target["x"], target["y"] - 20,
text=f"-{trap['damage']}",
fill="red",
font=(self.font_family, 12, "bold")
)
# 短暂显示爆炸效果后删除
self.root.after(500, lambda: self.canvas.delete(explosion))
self.root.after(1000, lambda: self.canvas.delete(damage_text))
# 检查目标是否死亡
if target["health"] <= 0:
# 从画布上删除目标
self.canvas.delete(target["canvas_obj"])
self.canvas.delete(target["canvas_text"])
if not target["is_player"]:
self.canvas.delete(target["state_indicator"])
self.canvas.delete(target["behavior_indicator"])
else:
self.canvas.delete(target["direction_line"])
# 从防御设施列表中删除陷阱
self.defense_objects.remove(trap)
def ai_behavior_pattern(self, unit):
# 根据AI行为模式调整行动
unit["behavior_timer"] += 1
# 每100帧(约1.6秒)随机改变行为
if unit["behavior_timer"] > 100:
unit["behavior_timer"] = 0
# 随机行为
if random.random() < 0.3: # 30%的概率改变当前行为
if unit["role"] == "defender":
# 防守方行为模式
patterns = ["aggressive", "defensive", "ambush", "support"]
unit["behavior_pattern"] = random.choice(patterns)
else:
# 进攻方行为模式
patterns = ["aggressive", "cautious", "flank", "support"]
unit["behavior_pattern"] = random.choice(patterns)
# 根据行为模式调整行为
if unit["role"] == "defender":
if unit["behavior_pattern"] == "aggressive":
# 激进型:主动寻找敌人并攻击
if unit["state"] == "patrol" and random.random() < 0.5:
# 随机选择一个可能的敌人位置
if unit["memory"]["enemy_positions"]:
target_pos = random.choice(unit["memory"]["enemy_positions"])
unit["path"] = self.astar((unit["x"], unit["y"]), target_pos)
elif unit["behavior_pattern"] == "defensive":
# 防御型:坚守炸弹点
if unit["state"] == "patrol" and random.random() < 0.5:
unit["path"] = self.astar((unit["x"], unit["y"]),
(self.bomb_site["x"], self.bomb_site["y"]))
elif unit["behavior_pattern"] == "ambush":
# 埋伏型:寻找好的埋伏点
if unit["state"] == "patrol" and random.random() < 0.5:
# 寻找可能的埋伏点
ambush_points = []
for cover in unit["cover_points"]:
# 检查是否是好的埋伏点
ambush_point = {"x": cover[0], "y": cover[1]}
if self.distance(ambush_point, self.bomb_site) < 200:
ambush_points.append(cover)
if ambush_points:
target_pos = random.choice(ambush_points)
unit["path"] = self.astar((unit["x"], unit["y"]), target_pos)
elif unit["behavior_pattern"] == "support":
# 支援型:跟随队友
if unit["state"] == "patrol" and random.random() < 0.5:
# 寻找最近的队友
nearest_teammate = None
min_dist = float('inf')
for teammate in self.defenders:
if teammate != unit and teammate["health"] > 0:
dist = self.distance(unit, teammate)
if dist < min_dist:
min_dist = dist
nearest_teammate = teammate
if nearest_teammate and min_dist > 100:
unit["path"] = self.astar((unit["x"], unit["y"]),
(nearest_teammate["x"], nearest_teammate["y"]))
else: # 进攻方
if unit["behavior_pattern"] == "aggressive":
# 激进型:直接冲向炸弹点
if unit["state"] == "patrol" and random.random() < 0.5:
unit["path"] = self.astar((unit["x"], unit["y"]),
(self.bomb_site["x"], self.bomb_site["y"]))
elif unit["behavior_pattern"] == "cautious":
# 谨慎型:缓慢推进,寻找掩体
if unit["state"] == "patrol" and random.random() < 0.5:
# 寻找靠近炸弹点的掩体
cover_points = []
for cover in unit["cover_points"]:
cover_pos = {"x": cover[0], "y": cover[1]}
if self.distance(cover_pos, self.bomb_site) < 300:
cover_points.append(cover)
if cover_points:
target_pos = random.choice(cover_points)
unit["path"] = self.astar((unit["x"], unit["y"]), target_pos)
elif unit["behavior_pattern"] == "flank":
# 包抄型:从侧面接近炸弹点
if unit["state"] == "patrol" and random.random() < 0.5:
# 计算从侧面接近的位置
bomb_x, bomb_y = self.bomb_site["x"], self.bomb_site["y"]
side_offset = random.choice([-150, 150])
# 随机选择左侧或右侧
if random.choice([True, False]):
target_x = bomb_x + side_offset
target_y = bomb_y
else:
target_x = bomb_x
target_y = bomb_y + side_offset
unit["path"] = self.astar((unit["x"], unit["y"]), (target_x, target_y))
elif unit["behavior_pattern"] == "support":
# 支援型:跟随携带炸弹的队友
if unit["state"] == "patrol" and random.random() < 0.5:
# 寻找最近的队友
nearest_teammate = None
min_dist = float('inf')
for teammate in self.attackers:
if teammate != unit and teammate["health"] > 0:
dist = self.distance(unit, teammate)
if dist < min_dist:
min_dist = dist
nearest_teammate = teammate
if nearest_teammate and min_dist > 100:
unit["path"] = self.astar((unit["x"], unit["y"]),
(nearest_teammate["x"], nearest_teammate["y"]))
def start_game(self):
# 开始游戏
self.create_game_screen()
self.initialize_defenders()
self.initialize_attackers()
self.start_attack_phase()
self.setup_player_controls()
self.update_player_position()
if name == "main": root = tk.Tk() game = Game(root) root.mainloop()