用 tkinter 来实现一个模拟星际争霸微操的图形界面程序,核心是通过鼠标/键盘操作界面中的单位,模拟游戏中的移动、选中、攻击等微操行为。
下面构建一个简化版的星际争霸微操模拟器,包含最核心的微操功能:单位选中、移动、攻击和框选,代码可以直接运行。

import tkinter as tkimport randomfrom tkinter import messagebox# 单位基类classUnit:def__init__(self, canvas, x, y, unit_type, color, hp, damage, speed): self.canvas = canvas self.x = x self.y = y self.unit_type = unit_type # 'marine' 机枪兵, 'zergling' 小狗 self.color = color self.hp = hp self.max_hp = hp self.damage = damage self.speed = speed self.selected = False self.target = None# 绘制单位 self.body = self.canvas.create_oval( x-10, y-10, x+10, y+10, fill=self.color, outline='black', width=2 )# 血量条背景 self.hp_bg = self.canvas.create_rectangle( x-10, y-15, x+10, y-12, fill='black' )# 血量条 self.hp_bar = self.canvas.create_rectangle( x-10, y-15, x+10, y-12, fill='green' )# 单位标签 self.label = self.canvas.create_text( x, y+15, text=self.unit_type[:3].upper(), fill='white', font=('Arial', 8, 'bold') )defupdate_position(self):"""更新单位位置(移动逻辑)"""if self.target and isinstance(self.target, (int, float)):# 移动到指定坐标 target_x, target_y = self.target dx = target_x - self.x dy = target_y - self.y distance = (dx**2 + dy**2)**0.5if distance > self.speed:# 计算移动步长 step_x = (dx / distance) * self.speed step_y = (dy / distance) * self.speed self.x += step_x self.y += step_yelse:# 到达目标点 self.x = target_x self.y = target_y self.target = None# 更新图形位置 self.canvas.coords( self.body, self.x-10, self.y-10, self.x+10, self.y+10 ) self.canvas.coords( self.hp_bg, self.x-10, self.y-15, self.x+10, self.y-12 )# 更新血量条 hp_ratio = self.hp / self.max_hp self.canvas.coords( self.hp_bar, self.x-10, self.y-15, self.x-10 + (20 * hp_ratio), self.y-12 ) self.canvas.coords( self.label, self.x, self.y+15 )# 更新选中状态的视觉效果if self.selected: self.canvas.itemconfig(self.body, outline='yellow', width=3)else: self.canvas.itemconfig(self.body, outline='black', width=2)defattack(self, target_unit):"""攻击目标单位"""if target_unit.hp > 0: target_unit.hp -= self.damage# 血量条颜色变化 hp_ratio = target_unit.hp / target_unit.max_hpif hp_ratio > 0.5: color = 'green'elif hp_ratio > 0.2: color = 'orange'else: color = 'red' self.canvas.itemconfig(target_unit.hp_bar, fill=color)# 单位死亡处理if target_unit.hp <= 0: target_unit.destroy()defdestroy(self):"""销毁单位""" self.canvas.delete(self.body) self.canvas.delete(self.hp_bg) self.canvas.delete(self.hp_bar) self.canvas.delete(self.label)# 主游戏类classStarcraftMicroSim:def__init__(self, root): self.root = root self.root.title("星际争霸微操模拟器")# 设置窗口大小 self.width = 800 self.height = 600# 创建画布 self.canvas = tk.Canvas(root, width=self.width, height=self.height, bg='#2c3e50') self.canvas.pack()# 单位列表 self.units = []# 选中的单位 self.selected_units = []# 框选相关变量 self.dragging = False self.select_rect = None self.select_start_x = 0 self.select_start_y = 0# Shift键状态(用于攻击) self.shift_pressed = False# 生成初始单位 self.spawn_units()# 绑定事件 self.bind_events()# 游戏循环 self.game_loop()defspawn_units(self):"""生成初始单位"""# 生成5个机枪兵(人族)for _ in range(5): x = random.randint(50, 300) y = random.randint(50, 550) marine = Unit(self.canvas, x, y, 'marine', '#3498db', 40, 6, 2) self.units.append(marine)# 生成5个小狗(虫族)for _ in range(5): x = random.randint(500, 750) y = random.randint(50, 550) zergling = Unit(self.canvas, x, y, 'zergling', '#e74c3c', 35, 5, 3) self.units.append(zergling)defbind_events(self):"""绑定鼠标和键盘事件"""# 鼠标左键按下 - 开始框选 self.canvas.bind('<Button-1>', self.on_left_click)# 鼠标拖拽 - 绘制框选矩形 self.canvas.bind('<B1-Motion>', self.on_drag)# 鼠标左键释放 - 完成框选 self.canvas.bind('<ButtonRelease-1>', self.on_left_release)# 鼠标右键 - 移动/攻击指令 self.canvas.bind('<Button-3>', self.on_right_click)# Shift键按下/释放 self.root.bind('<Shift_L>', self.on_shift_press) self.root.bind('<KeyRelease-Shift_L>', self.on_shift_release)defon_left_click(self, event):"""左键点击事件"""# 开始框选 self.dragging = True self.select_start_x = event.x self.select_start_y = event.y# 先取消所有选中for unit in self.selected_units: unit.selected = False self.selected_units = []# 检查是否点击到单个单位 clicked_unit = Nonefor unit in self.units:if (abs(unit.x - event.x) < 10and abs(unit.y - event.y) < 10): clicked_unit = unitbreakif clicked_unit:# 选中单个单位 clicked_unit.selected = True self.selected_units.append(clicked_unit)else:# 开始绘制框选矩形 self.select_rect = self.canvas.create_rectangle( event.x, event.y, event.x, event.y, outline='white', dash=(2, 2) )defon_drag(self, event):"""鼠标拖拽事件"""if self.dragging and self.select_rect:# 更新框选矩形 self.canvas.coords( self.select_rect, self.select_start_x, self.select_start_y, event.x, event.y )defon_left_release(self, event):"""左键释放事件""" self.dragging = False# 删除框选矩形if self.select_rect: self.canvas.delete(self.select_rect) self.select_rect = None# 框选范围内的单位 min_x = min(self.select_start_x, event.x) min_y = min(self.select_start_y, event.y) max_x = max(self.select_start_x, event.x) max_y = max(self.select_start_y, event.y)# 选中框选范围内的所有单位for unit in self.units:if (min_x < unit.x < max_x) and (min_y < unit.y < max_y): unit.selected = True self.selected_units.append(unit)defon_right_click(self, event):"""右键点击事件 - 移动/攻击"""ifnot self.selected_units:return# 检查右键是否点击到敌方单位 target_unit = Nonefor unit in self.units:if (abs(unit.x - event.x) < 10and abs(unit.y - event.y) < 10): target_unit = unitbreakif self.shift_pressed and target_unit:# Shift+右键 - 攻击目标for unit in self.selected_units:# 不能攻击自己if unit != target_unit: unit.attack(target_unit)else:# 普通右键 - 移动到目标点 target_x, target_y = event.x, event.yfor unit in self.selected_units: unit.target = (target_x, target_y)defon_shift_press(self, event):"""Shift键按下""" self.shift_pressed = Truedefon_shift_release(self, event):"""Shift键释放""" self.shift_pressed = Falsedefgame_loop(self):"""游戏主循环"""# 更新所有单位位置for unit in self.units[:]: # 使用切片避免遍历时列表变化if unit.hp > 0: unit.update_position()else:# 移除死亡单位 self.units.remove(unit)if unit in self.selected_units: self.selected_units.remove(unit)# 检查游戏结束条件 marines = [u for u in self.units if u.unit_type == 'marine'] zerglings = [u for u in self.units if u.unit_type == 'zergling']ifnot marines ornot zerglings: winner = "人族"if marines else"虫族" messagebox.showinfo("游戏结束", f"{winner}胜利!") self.root.quit()# 循环调用 self.root.after(30, self.game_loop)# 运行程序if __name__ == "__main__": root = tk.Tk() app = StarcraftMicroSim(root) root.mainloop()Unit类:封装了单位的所有属性和行为,包括:
事件绑定:
游戏循环:通过after()方法实现帧率控制(约33帧/秒),持续更新单位位置和状态。
你可以在此基础上扩展更多功能,比如: