You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 17:43:18