import tkinter as tk
from tkinter import messagebox, font
import random
import time
import threading
import math
# ==================== 游戏常量 ====================
WORLD_WIDTH = 4000 # 世界地图宽度
WORLD_HEIGHT = 4000 # 世界地图高度
CANVAS_WIDTH = 1024 # 视口宽度
CANVAS_HEIGHT = 668 # 视口高度
VIEW_RADIUS = 450 # 玩家视野半径
VIEW_ANGLE = math.pi / 2 # 90°扇形视野
PLAYER_RADIUS = 16 # 玩家碰撞半径
AI_RADIUS = 15 # AI碰撞半径
PLAYER_SPEED = 3 # 玩家移动速度
AI_SPEED = 3 # AI移动速度
BULLET_DAMAGE = 34 # 子弹伤害
HIT_CHANCE = 0.7 # 基础命中率
BULLET_LIFETIME = 100 # 子弹显示时长(ms)
AI_UPDATE_INTERVAL = 100 # AI决策间隔(ms)
class Game:
def __init__(self, root):
self.root = root
self.root.title("Rainbow Six: Pioneer")
self.root.geometry("1024x768")
self.root.resizable(False, False)
# 中文字体
self.font_family = "SimHei"
# 游戏核心状态
self.game_active = False
self.defender_wins = False
self.attacker_wins = False
# 计时器
self.game_timer = 300 # 5分钟对局
self.timer_running = False
# 玩家与AI数量
self.player_count = 1
self.ai_count = 4
self.total_players = self.player_count + self.ai_count
# 游戏实体
self.defenders = []
self.attackers = []
self.bullets = []
self.damage_texts = []
self.obstacles = []
# 相机系统
self.camera_x = 0
self.camera_y = 0
# 玩家输入
self.key_pressed = {"w": False, "a": False, "s": False, "d": False}
self.mouse_x = CANVAS_WIDTH // 2
self.mouse_y = CANVAS_HEIGHT // 2
# AI更新计时器
self.ai_update_timer = 0
# 创建开始界面
self.create_start_screen()
# ==================== 界面系统 ====================
def create_start_screen(self):
for widget in self.root.winfo_children():
widget.destroy()
start_frame = tk.Frame(self.root, bg="#1a1a2e")
start_frame.pack(fill=tk.BOTH, expand=True)
title_font = font.Font(family=self.font_family, size=36, weight="bold")
title_label = tk.Label(start_frame, text="Rainbow Six: Pioneer",
font=title_font, fg="#e94560", bg="#1a1a2e")
title_label.pack(pady=50)
copyright_font = font.Font(family=self.font_family, size=12)
copyright_label = tk.Label(start_frame, text="Ubisoft&BCZOZ70371",
font=copyright_font, fg="#e94560", bg="#1a1a2e")
copyright_label.pack(pady=10)
controls_font = font.Font(family=self.font_family, size=14)
controls_label = tk.Label(start_frame, text="WASD移动 | 鼠标瞄准 | 左键射击",
font=controls_font, fg="white", bg="#1a1a2e")
controls_label.pack(pady=20)
button_frame = tk.Frame(start_frame, bg="#1a1a2e")
button_frame.pack(pady=100)
attacker_btn = tk.Button(button_frame, text="选择进攻方", command=self.select_attacker,
font=(self.font_family, 16), bg="#F44336", fg="white",
width=15, height=2, cursor="hand2")
attacker_btn.pack(side=tk.LEFT, padx=20)
def select_attacker(self):
self.create_game_screen()
self.initialize_attackers()
self.initialize_defenders()
self.start_attack_phase()
self.setup_player_controls()
def create_game_screen(self):
for widget in self.root.winfo_children():
widget.destroy()
self.game_frame = tk.Frame(self.root, bg="#0f3460")
self.game_frame.pack(fill=tk.BOTH, expand=True)
self.canvas = tk.Canvas(self.game_frame, bg="#16213e",
width=CANVAS_WIDTH, height=CANVAS_HEIGHT)
self.canvas.pack()
# 底部状态栏
self.status_frame = tk.Frame(self.game_frame, bg="#0f3460", height=100)
self.status_frame.pack(fill=tk.X)
self.timer_label = tk.Label(self.status_frame, text="05:00",
font=(self.font_family, 24), fg="white", bg="#0f3460")
self.timer_label.pack(side=tk.LEFT, padx=20)
self.player_status_label = tk.Label(self.status_frame, text="进攻方 | 存活",
font=(self.font_family, 14), fg="white", bg="#0f3460")
self.player_status_label.pack(side=tk.LEFT, padx=20)
self.game_status_label = tk.Label(self.status_frame, text="战斗阶段",
font=(self.font_family, 14), fg="white", bg="#0f3460")
self.game_status_label.pack(side=tk.RIGHT, padx=20)
# 生成地图障碍物
self.generate_world_obstacles()
# ==================== 地图与碰撞 ====================
def generate_world_obstacles(self):
self.obstacles = []
# 地图边界墙体
self.obstacles.append((0, 0, WORLD_WIDTH, 20))
self.obstacles.append((0, WORLD_HEIGHT - 20, WORLD_WIDTH, WORLD_HEIGHT))
self.obstacles.append((0, 0, 20, WORLD_HEIGHT))
self.obstacles.append((WORLD_WIDTH - 20, 0, WORLD_WIDTH, WORLD_HEIGHT))
# 随机生成室内障碍物
random.seed(42) # 固定种子保证地图一致
for _ in range(150):
w = random.randint(80, 200)
h = random.randint(80, 200)
x = random.randint(100, WORLD_WIDTH - 100 - w)
y = random.randint(100, WORLD_HEIGHT - 100 - h)
self.obstacles.append((x, y, x + w, y + h))
def check_wall_collision(self, x, y, radius):
"""圆形单位与墙体/障碍物碰撞检测"""
# 地图边界
if x - radius < 0 or x + radius > WORLD_WIDTH:
return True
if y - radius < 0 or y + radius > WORLD_HEIGHT:
return True
# 障碍物碰撞(圆形-矩形)
for obs in self.obstacles:
ox1, oy1, ox2, oy2 = obs
closest_x = max(ox1, min(x, ox2))
closest_y = max(oy1, min(y, oy2))
dist_sq = (x - closest_x) ** 2 + (y - closest_y) ** 2
if dist_sq < radius ** 2:
return True
return False
def line_intersects_wall(self, x1, y1, x2, y2):
"""子弹线段与墙体碰撞检测"""
for obs in self.obstacles:
if self._line_rect_intersect(x1, y1, x2, y2, obs):
return True
return False
def _line_rect_intersect(self, x1, y1, x2, y2, rect):
"""线段与矩形相交检测(CCW算法)"""
rx1, ry1, rx2, ry2 = rect
def point_in_rect(px, py):
return rx1 <= px <= rx2 and ry1 <= py <= ry2
if point_in_rect(x1, y1) or point_in_rect(x2, y2):
return True
def ccw(ax, ay, bx, by, cx, cy):
return (cy - ay) * (bx - ax) > (by - ay) * (cx - ax)
def seg_intersect(sx1, sy1, sx2, sy2, sx3, sy3, sx4, sy4):
return (ccw(sx1,sy1,sx3,sy3,sx4,sy4) != ccw(sx2,sy2,sx3,sy3,sx4,sy4) and
ccw(sx1,sy1,sx2,sy2,sx3,sy3) != ccw(sx1,sy1,sx2,sy2,sx4,sy4))
edges = [
(rx1, ry1, rx2, ry1), (rx2, ry1, rx2, ry2),
(rx2, ry2, rx1, ry2), (rx1, ry2, rx1, ry1)
]
for edge in edges:
if seg_intersect(x1, y1, x2, y2, edge[0], edge[1], edge[2], edge[3]):
return True
return False
# ==================== 单位初始化 ====================
def initialize_defenders(self):
self.defenders = []
for i in range(self.total_players * 2):
x = random.randint(WORLD_WIDTH//3, WORLD_WIDTH*2//3)
y = random.randint(WORLD_HEIGHT//3, WORLD_HEIGHT*2//3)
self.defenders.append({
"id": i, "x": x, "y": y,
"health": 100, "is_player": False, "role": "defender",
"target_pos": (x, y)
})
def initialize_attackers(self):
self.attackers = []
# 玩家单位
self.attackers.append({
"id": 0, "x": 100, "y": WORLD_HEIGHT//2,
"health": 100, "is_player": True, "role": "attacker",
"direction": 0
})
# AI队友
for i in range(1, self.total_players):
if random.choice([True, False]):
x, y = 100, random.randint(100, WORLD_HEIGHT - 100)
else:
x, y = WORLD_WIDTH - 100, random.randint(100, WORLD_HEIGHT - 100)
self.attackers.append({
"id": i, "x": x, "y": y,
"health": 100, "is_player": False, "role": "attacker",
"state": "find_cover", "state_timer": 0,
"cover": None, "target_pos": (x, y), "peek_target": None
})
# ==================== 游戏流程 ====================
def start_attack_phase(self):
self.game_active = True
self.game_status_label.config(text="战斗阶段")
self.game_timer = 300
self.timer_label.config(text="{:02d}:{:02d}".format(self.game_timer // 60, self.game_timer % 60))
self.timer_running = True
# 启动计时线程
threading.Thread(target=self.run_game_timer, daemon=True).start()
# 启动主循环
self.game_loop()
def run_game_timer(self):
while self.game_timer > 0 and self.timer_running and not self.defender_wins and not self.attacker_wins:
time.sleep(1)
self.game_timer -= 1
self.root.after(0, lambda: self.timer_label.config(
text="{:02d}:{:02d}".format(self.game_timer // 60, self.game_timer % 60)))
if self.timer_running and not self.defender_wins and not self.attacker_wins:
self.root.after(0, self.check_win_conditions)
def game_loop(self):
if not self.game_active:
self.root.after(16, self.game_loop)
return
# 更新玩家与相机
self.update_player_position()
self.update_camera()
# 更新AI移动(每帧)
self.update_ai_movement()
# 更新子弹与伤害文字
self.update_effects()
# AI决策(间隔更新)
self.ai_update_timer += 16
if self.ai_update_timer >= AI_UPDATE_INTERVAL:
self.ai_update_timer = 0
self.update_ai_defense_actions()
self.update_ai_attack_actions()
# 渲染画面
self.render()
self.root.after(16, self.game_loop)
# ==================== 玩家控制 ====================
def setup_player_controls(self):
self.root.bind("<KeyPress>", self.on_key_press)
self.root.bind("<KeyRelease>", self.on_key_release)
self.root.bind("<Motion>", self.on_mouse_move)
self.root.bind("<Button-1>", self.on_mouse_click)
def on_key_press(self, event):
key = event.keysym.lower()
if key in self.key_pressed:
self.key_pressed[key] = True
def on_key_release(self, event):
key = event.keysym.lower()
if key in self.key_pressed:
self.key_pressed[key] = False
def on_mouse_move(self, event):
self.mouse_x = event.x
self.mouse_y = event.y
player = self.get_player()
if player and player["health"] > 0:
# 屏幕坐标转世界坐标
world_mx = event.x + self.camera_x
world_my = event.y + self.camera_y
player["direction"] = math.atan2(world_my - player["y"], world_mx - player["x"])
def on_mouse_click(self, event):
player = self.get_player()
if player and player["health"] > 0:
world_mx = event.x + self.camera_x
world_my = event.y + self.camera_y
angle = math.atan2(world_my - player["y"], world_mx - player["x"])
target = self.find_enemy_in_direction(player, angle)
if target:
self.shoot_at(player, target, angle)
def get_player(self):
return next((a for a in self.attackers if a["is_player"]), None)
def update_player_position(self):
player = self.get_player()
if not player or player["health"] <= 0:
return
dx, dy = 0, 0
if self.key_pressed["w"]: dy -= PLAYER_SPEED
if self.key_pressed["s"]: dy += PLAYER_SPEED
if self.key_pressed["a"]: dx -= PLAYER_SPEED
if self.key_pressed["d"]: dx += PLAYER_SPEED
# 对角线速度归一化
if dx != 0 and dy != 0:
norm = 1 / math.sqrt(2)
dx *= norm
dy *= norm
# 分轴碰撞检测(贴墙滑行)
new_x = player["x"] + dx
if not self.check_wall_collision(new_x, player["y"], PLAYER_RADIUS):
player["x"] = new_x
new_y = player["y"] + dy
if not self.check_wall_collision(player["x"], new_y, PLAYER_RADIUS):
player["y"] = new_y
def update_camera(self):
player = self.get_player()
if not player:
return
# 相机中心跟随玩家
target_x = player["x"] - CANVAS_WIDTH / 2
target_y = player["y"] - CANVAS_HEIGHT / 2
# 限制相机不超出地图边界
target_x = max(0, min(target_x, WORLD_WIDTH - CANVAS_WIDTH))
target_y = max(0, min(target_y, WORLD_HEIGHT - CANVAS_HEIGHT))
self.camera_x = target_x
self.camera_y = target_y
# ==================== AI系统 ====================
def update_ai_movement(self):
"""每帧更新所有AI的位置(平滑移动)"""
# 防守方AI
for defender in self.defenders:
if defender["health"] > 0 and defender["target_pos"]:
self.move_unit(defender, defender["target_pos"][0], defender["target_pos"][1])
# 进攻方AI
for attacker in self.attackers:
if attacker["is_player"] or attacker["health"] <= 0:
continue
# 闪身状态使用闪身目标,否则使用常规目标
if attacker["state"] == "peek" and attacker["peek_target"]:
self.move_unit(attacker, attacker["peek_target"][0], attacker["peek_target"][1])
elif attacker["target_pos"]:
self.move_unit(attacker, attacker["target_pos"][0], attacker["target_pos"][1])
def update_ai_defense_actions(self):
"""防守方AI决策(保留原有巡逻+追击逻辑)"""
for defender in self.defenders:
if defender["health"] <= 0:
continue
nearest_attacker = self.find_nearest_enemy(defender, self.attackers)
if nearest_attacker and self.distance(defender, nearest_attacker) < 400:
# 发现敌人,追击并射击
defender["target_pos"] = (
nearest_attacker["x"] + random.randint(-20, 20),
nearest_attacker["y"] + random.randint(-20, 20)
)
angle = math.atan2(nearest_attacker["y"] - defender["y"],
nearest_attacker["x"] - defender["x"])
if random.random() < 0.3:
self.shoot_at(defender, nearest_attacker, angle)
else:
# 随机巡逻
if random.random() < 0.1:
defender["target_pos"] = (
random.randint(200, WORLD_WIDTH - 200),
random.randint(200, WORLD_HEIGHT - 200)
)
def update_ai_attack_actions(self):
"""进攻方AI决策:找掩体 + 闪身枪"""
for attacker in self.attackers:
if attacker["is_player"] or attacker["health"] <= 0:
continue
nearest_defender = self.find_nearest_enemy(attacker, self.defenders)
dt = AI_UPDATE_INTERVAL / 1000 # 时间增量
if attacker["state"] == "find_cover":
# 寻找最佳掩体
cover = self.find_best_cover(attacker, nearest_defender)
if cover:
attacker["cover"] = cover
cover_pos = self.get_cover_position(attacker, cover, nearest_defender)
attacker["target_pos"] = cover_pos
# 到达掩体后进入待命状态
if self.distance(attacker, {"x": cover_pos[0], "y": cover_pos[1]}) < 10:
attacker["state"] = "cover"
attacker["state_timer"] = random.uniform(1.5, 3.0)
else:
# 无掩体则直接推进
if nearest_defender:
attacker["target_pos"] = (
nearest_defender["x"] + random.randint(-30, 30),
nearest_defender["y"] + random.randint(-30, 30)
)
elif attacker["state"] == "cover":
attacker["state_timer"] -= dt
if attacker["state_timer"] <= 0 and nearest_defender:
# 进入闪身射击状态
attacker["state"] = "peek"
attacker["state_timer"] = 0.8
# 计算闪身方向(垂直于敌人方向)
angle = math.atan2(nearest_defender["y"] - attacker["y"],
nearest_defender["x"] - attacker["x"])
peek_side = 1 if random.random() > 0.5 else -1
peek_angle = angle + peek_side * math.pi / 2
attacker["peek_target"] = (
attacker["x"] + math.cos(peek_angle) * 35,
attacker["y"] + math.sin(peek_angle) * 35
)
elif attacker["state"] == "peek":
# 闪身期间尝试射击
if nearest_defender and random.random() < 0.55:
angle = math.atan2(nearest_defender["y"] - attacker["y"],
nearest_defender["x"] - attacker["x"])
self.shoot_at(attacker, nearest_defender, angle)
attacker["state_timer"] -= dt
if attacker["state_timer"] <= 0:
# 退回掩体
attacker["state"] = "cover"
attacker["state_timer"] = random.uniform(2.0, 4.0)
attacker["peek_target"] = None
def find_best_cover(self, unit, enemy):
"""寻找最优掩体(在单位与敌人之间)"""
if not enemy:
return None
best_cover = None
best_score = float('inf')
ex, ey = enemy["x"], enemy["y"]
ux, uy = unit["x"], unit["y"]
for obs in self.obstacles:
cx = (obs[0] + obs[2]) / 2
cy = (obs[1] + obs[3]) / 2
# 向量:单位→敌人
dx_enemy = ex - ux
dy_enemy = ey - uy
dist_enemy = math.sqrt(dx_enemy**2 + dy_enemy**2)
if dist_enemy < 60:
continue
# 点积判断掩体是否在敌人方向上
dx_cover = cx - ux
dy_cover = cy - uy
dot = dx_enemy * dx_cover + dy_enemy * dy_cover
if dot <= 0:
continue # 掩体在身后
# 距离评分
dist_cover = math.sqrt(dx_cover**2 + dy_cover**2)
if dist_cover < 40 or dist_cover > 350:
continue
if dist_cover < best_score:
best_score = dist_cover
best_cover = obs
return best_cover
def get_cover_position(self, unit, cover, enemy):
"""计算掩体后方的站位"""
cx = (cover[0] + cover[2]) / 2
cy = (cover[1] + cover[3]) / 2
# 敌人→掩体 的反方向
dx = cx - enemy["x"]
dy = cy - enemy["y"]
dist = math.sqrt(dx**2 + dy**2)
if dist == 0:
return (cx, cy)
# 向后偏移30像素
offset = 30
pos_x = cx + (dx / dist) * offset
pos_y = cy + (dy / dist) * offset
if not self.check_wall_collision(pos_x, pos_y, AI_RADIUS):
return (pos_x, pos_y)
return (cx + 30, cy)
# ==================== 战斗系统 ====================
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 distance(self, u1, u2):
return math.sqrt((u1["x"] - u2["x"]) ** 2 + (u1["y"] - u2["y"]) ** 2)
def find_enemy_in_direction(self, shooter, angle):
"""查找瞄准方向上的敌人(带穿墙检测)"""
max_dist = 300
nearest = None
min_dist = float('inf')
enemies = self.attackers if shooter["role"] == "defender" else self.defenders
for enemy in enemies:
if enemy["health"] <= 0:
continue
enemy_angle = math.atan2(enemy["y"] - shooter["y"], enemy["x"] - shooter["x"])
angle_diff = abs(angle - enemy_angle)
if angle_diff > math.pi:
angle_diff = 2 * math.pi - angle_diff
if angle_diff < math.radians(30):
dist = self.distance(shooter, enemy)
if dist < max_dist and dist < min_dist:
# 子弹不能穿墙
if not self.line_intersects_wall(shooter["x"], shooter["y"],
enemy["x"], enemy["y"]):
min_dist = dist
nearest = enemy
return nearest
def shoot_at(self, shooter, target, angle):
# 最终穿墙校验
if self.line_intersects_wall(shooter["x"], shooter["y"], target["x"], target["y"]):
return
# 添加子弹特效
self.bullets.append({
"x1": shooter["x"], "y1": shooter["y"],
"x2": target["x"], "y2": target["y"],
"time": BULLET_LIFETIME
})
# 命中判定
if random.random() < HIT_CHANCE:
target["health"] -= BULLET_DAMAGE
if target["health"] <= 0:
self.check_win_conditions()
else:
self.show_damage(target)
def show_damage(self, unit):
self.damage_texts.append({
"x": unit["x"], "y": unit["y"] - 20,
"time": 2000
})
def move_unit(self, unit, target_x, target_y):
"""单位移动(带碰撞检测)"""
dx = target_x - unit["x"]
dy = target_y - unit["y"]
dist = math.sqrt(dx * dx + dy * dy)
if dist > 2:
speed = PLAYER_SPEED if unit["is_player"] else AI_SPEED
move_x = (dx / dist) * speed
move_y = (dy / dist) * speed
radius = PLAYER_RADIUS if unit["is_player"] else AI_RADIUS
# 分轴碰撞
new_x = unit["x"] + move_x
if not self.check_wall_collision(new_x, unit["y"], radius):
unit["x"] = new_x
new_y = unit["y"] + move_y
if not self.check_wall_collision(unit["x"], new_y, radius):
unit["y"] = new_y
# ==================== 渲染系统 ====================
def update_effects(self):
"""更新子弹与伤害文字生命周期"""
# 子弹
new_bullets = []
for b in self.bullets:
b["time"] -= 16
if b["time"] > 0:
new_bullets.append(b)
self.bullets = new_bullets
# 伤害文字(向上飘动)
new_damage = []
for dt in self.damage_texts:
dt["time"] -= 16
dt["y"] -= 0.5
if dt["time"] > 0:
new_damage.append(dt)
self.damage_texts = new_damage
def render(self):
self.canvas.delete("all")
self.canvas.create_rectangle(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, fill="#16213e")
# 绘制障碍物(视口剔除)
for obs in self.obstacles:
x1 = obs[0] - self.camera_x
y1 = obs[1] - self.camera_y
x2 = obs[2] - self.camera_x
y2 = obs[3] - self.camera_y
if x2 < 0 or x1 > CANVAS_WIDTH or y2 < 0 or y1 > CANVAS_HEIGHT:
continue
self.canvas.create_rectangle(x1, y1, x2, y2, fill="#1a1a2e", outline="#e94560", width=1)
# 绘制防守方(蓝色)
for d in self.defenders:
if d["health"] <= 0:
continue
sx = d["x"] - self.camera_x
sy = d["y"] - self.camera_y
if sx < -50 or sx > CANVAS_WIDTH + 50 or sy < -50 or sy > CANVAS_HEIGHT + 50:
continue
self.canvas.create_oval(sx-AI_RADIUS, sy-AI_RADIUS,
sx+AI_RADIUS, sy+AI_RADIUS, fill="blue")
self.canvas.create_text(sx, sy, text=str(d["id"]), fill="white", font=(self.font_family, 10))
# 绘制进攻方(红色)
for a in self.attackers:
if a["health"] <= 0:
continue
sx = a["x"] - self.camera_x
sy = a["y"] - self.camera_y
if sx < -50 or sx > CANVAS_WIDTH + 50 or sy < -50 or sy > CANVAS_HEIGHT + 50:
continue
r = PLAYER_RADIUS if a["is_player"] else AI_RADIUS
self.canvas.create_oval(sx-r, sy-r, sx+r, sy+r, fill="red")
self.canvas.create_text(sx, sy, text=str(a["id"]), fill="white", font=(self.font_family, 10))
# 玩家朝向指示器
if a["is_player"]:
end_x = sx + math.cos(a["direction"]) * 30
end_y = sy + math.sin(a["direction"]) * 30
self.canvas.create_line(sx, sy, end_x, end_y, fill="yellow", width=2)
# 绘制子弹
for b in self.bullets:
x1 = b["x1"] - self.camera_x
y1 = b["y1"] - self.camera_y
x2 = b["x2"] - self.camera_x
y2 = b["y2"] - self.camera_y
self.canvas.create_line(x1, y1, x2, y2, fill="yellow", width=2)
# 绘制伤害数字
for dt in self.damage_texts:
sx = dt["x"] - self.camera_x
sy = dt["y"] - self.camera_y
self.canvas.create_text(sx, sy, text=str(BULLET_DAMAGE), fill="red", font=(self.font_family, 12))
# 绘制玩家视野遮罩
self.draw_vision_mask()
def draw_vision_mask(self):
"""绘制90°扇形视野遮罩,视野外全黑"""
player = self.get_player()
if not player or player["health"] <= 0:
self.canvas.create_rectangle(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, fill="black")
return
px = player["x"] - self.camera_x
py = player["y"] - self.camera_y
angle = player["direction"]
start_a = angle - VIEW_ANGLE / 2
end_a = angle + VIEW_ANGLE / 2
# 生成弧线顶点(30段平滑近似)
arc_points = []
for i in range(31):
a = start_a + (end_a - start_a) * i / 30
arc_points.append((px + math.cos(a) * VIEW_RADIUS,
py + math.sin(a) * VIEW_RADIUS))
# 构建遮罩多边形:屏幕边框 + 弧线 + 玩家位置
mask_pts = [0, 0, CANVAS_WIDTH, 0, CANVAS_WIDTH, CANVAS_HEIGHT, 0, CANVAS_HEIGHT]
# 逆序添加弧线点
for pt in reversed(arc_points):
mask_pts.extend([pt[0], pt[1]])
mask_pts.extend([px, py])
self.canvas.create_polygon(mask_pts, fill="black", outline="")
# ==================== 胜负判定 ====================
def check_win_conditions(self):
all_attackers_dead = all(a["health"] <= 0 for a in self.attackers)
all_defenders_dead = all(d["health"] <= 0 for d in self.defenders)
if all_attackers_dead:
self.defender_wins_game()
elif all_defenders_dead:
self.attacker_wins_game()
elif self.game_timer <= 0:
self.defender_wins_game()
def defender_wins_game(self):
self.timer_running = False
self.defender_wins = True
self.game_status_label.config(text="防守方胜利!")
messagebox.showinfo("游戏结束", "防守方胜利!")
self.root.after(3000, self.create_start_screen)
def attacker_wins_game(self):
self.timer_running = False
self.attacker_wins = True
self.game_status_label.config(text="进攻方胜利!")
messagebox.showinfo("游戏结束", "进攻方胜利!")
self.root.after(3000, self.create_start_screen)
if __name__ == "__main__":
root = tk.Tk()
game = Game(root)
root.mainloop()