如何在Tkinter窗口中创建阻挡玩家的实体墙壁?
实现墙壁碰撞与反弹的解决方案
1. 添加墙壁实体
先在代码中创建可阻挡的墙壁,并将所有墙壁ID存入列表,方便后续统一检测碰撞:
# 在创建玩家之后添加以下代码 walls = [] # 边界墙壁 wall_top = c.create_rectangle(0, 0, Width, 50, fill='saddle brown') walls.append(wall_top) wall_bottom = c.create_rectangle(0, Height-50, Width, Height, fill='saddle brown') walls.append(wall_bottom) wall_left = c.create_rectangle(0, 0, 50, Height, fill='saddle brown') walls.append(wall_left) wall_right = c.create_rectangle(Width-50, 0, Width, Height, fill='saddle brown') walls.append(wall_right) # 中间障碍物(可按需添加更多) wall_mid = c.create_rectangle(400, 200, 600, 600, fill='saddle brown') walls.append(wall_mid)
2. 核心碰撞检测与反弹逻辑
修改玩家移动函数,通过「移动前记录位置 → 执行移动 → 检测碰撞 → 碰撞则回退」的逻辑实现墙壁阻挡效果:
player_move = 50 def player_movement(event): # 记录玩家移动前的位置 prev_pos = c.coords(player_id) prev_x1, prev_y1, prev_x2, prev_y2 = prev_pos # 执行移动操作 if event.keysym == 'Up': c.move(player_id, 0, -player_move) elif event.keysym == 'Down': c.move(player_id, 0, player_move) elif event.keysym == 'Left': c.move(player_id, -player_move, 0) elif event.keysym == 'Right': c.move(player_id, player_move, 0) else: return # 非方向键直接返回 # 检测移动后是否与墙壁碰撞 current_pos = c.coords(player_id) x1, y1, x2, y2 = current_pos # 找出所有与玩家重叠的画布元素 overlapping_items = c.find_overlapping(x1, y1, x2, y2) # 检查是否碰撞到墙壁 for wall in walls: if wall in overlapping_items: # 碰撞触发,将玩家移回移动前的位置 c.coords(player_id, prev_x1, prev_y1, prev_x2, prev_y2) break
3. 优化:自定义碰撞阈值(精确触发)
如果需要玩家距离墙壁一定距离时就触发阻挡(而非完全重叠),可以手动计算距离实现阈值控制:
player_move = 50 threshold = 3 # 触发碰撞的最小距离(像素) def player_movement(event): player_pos = c.coords(player_id) px1, py1, px2, py2 = player_pos # 计算玩家中心坐标 p_center_x = (px1 + px2) / 2 p_center_y = (py1 + py2) / 2 # 确定移动方向 dx, dy = 0, 0 if event.keysym == 'Up': dy = -player_move elif event.keysym == 'Down': dy = player_move elif event.keysym == 'Left': dx = -player_move elif event.keysym == 'Right': dx = player_move else: return # 预判移动后的玩家位置 new_px1 = px1 + dx new_py1 = py1 + dy new_px2 = px2 + dx new_py2 = py2 + dy new_center_x = p_center_x + dx new_center_y = p_center_y + dy # 检测是否触发阈值碰撞 collision_triggered = False for wall in walls: wx1, wy1, wx2, wy2 = c.coords(wall) # 计算玩家与墙壁的水平/垂直距离 if new_center_x < wx1: dist_x = wx1 - new_px2 elif new_center_x > wx2: dist_x = new_px1 - wx2 else: dist_x = 0 if new_center_y < wy1: dist_y = wy1 - new_py2 elif new_center_y > wy2: dist_y = new_py1 - wy2 else: dist_y = 0 # 距离小于阈值则触发碰撞 if dist_x < threshold and dist_y < threshold: collision_triggered = True break # 无碰撞则执行移动,否则不移动 if not collision_triggered: c.move(player_id, dx, dy)
完整修改后的代码
整合所有逻辑后的完整代码:
from tkinter import * window = Tk() window.title('Rook Map') Width = 1200 Height = 800 c = Canvas(window, width=Width, height=Height, bg='midnight blue') c.pack() c_middle = [int(c['width']) / 2, int(c['height']) / 2] window.resizable(False, False) screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() x_cordinate = int((screen_width/2) - (Width/2)) y_cordinate = int((screen_height/2) - (Height/2)) window.geometry("{}x{}+{}+{}".format(Width, Height, x_cordinate, y_cordinate)) player_width = 20 player_height = 20 player_id = c.create_rectangle( c_middle[0] - (player_width / 2), c_middle[1] - (player_height / 2), c_middle[0] + (player_width / 2), c_middle[1] + (player_height / 2), fill='grey' ) c.move(player_id, 0, 320) # 创建墙壁 walls = [] wall_top = c.create_rectangle(0, 0, Width, 50, fill='saddle brown') walls.append(wall_top) wall_bottom = c.create_rectangle(0, Height-50, Width, Height, fill='saddle brown') walls.append(wall_bottom) wall_left = c.create_rectangle(0, 0, 50, Height, fill='saddle brown') walls.append(wall_left) wall_right = c.create_rectangle(Width-50, 0, Width, Height, fill='saddle brown') walls.append(wall_right) wall_mid = c.create_rectangle(400, 200, 600, 600, fill='saddle brown') walls.append(wall_mid) player_move = 50 def player_movement(event): prev_pos = c.coords(player_id) prev_x1, prev_y1, prev_x2, prev_y2 = prev_pos if event.keysym == 'Up': c.move(player_id, 0, -player_move) elif event.keysym == 'Down': c.move(player_id, 0, player_move) elif event.keysym == 'Left': c.move(player_id, -player_move, 0) elif event.keysym == 'Right': c.move(player_id, player_move, 0) else: return current_pos = c.coords(player_id) x1, y1, x2, y2 = current_pos overlapping_items = c.find_overlapping(x1, y1, x2, y2) for wall in walls: if wall in overlapping_items: c.coords(player_id, prev_x1, prev_y1, prev_x2, prev_y2) break c.bind_all('<Key>', player_movement) window.mainloop()
关键说明
- 使用tkinter内置的
c.coords()获取元素位置,c.find_overlapping()检测矩形重叠,简单高效适配2D碰撞场景。 - 反弹逻辑通过「移动回退」实现,确保玩家无法穿透墙壁;若需要更平滑的碰撞反馈,可基于距离计算调整移动幅度。
内容的提问来源于stack exchange,提问作者NullVoid
相关产品推荐
相关产品推荐

