为何我的Top-Down Controller无法检测上方与右侧碰撞?
Ursina俯视角控制器碰撞穿透问题修复
问题根源分析
你的代码存在几个关键问题导致碰撞失效:
- 碰撞修正逻辑错误:右侧和上方的碰撞计算用了错误的极值(
max),导致修正方向完全反向;硬编码的0.51偏移值也和角色实际尺寸不匹配。 - 移动与碰撞检测顺序颠倒:先直接修改角色位置,再做碰撞修正,容易让角色先穿透方块再尝试拉回,最终导致碰撞规则失效。
- 未排除自身碰撞:射线检测没有把角色自身加入忽略列表,可能误检测到自身
collider干扰判断。
修复后的代码
from ursina import * class top_down_controller(Entity): def __init__(self, model, color, scale=1, texture=None, collider='box', ignore=None): # 初始化时把自身加入忽略列表,避免射线检测到自身 if ignore is None: ignore = [] ignore.append(self) super().__init__( model=model, color=color, scale=scale, texture=texture, collider=collider, ignore=ignore ) # 存储角色半尺寸,统一碰撞计算标准 self.half_size = scale * 0.5 def update(self): # 先计算预期移动量,而非直接修改位置 move_x = (held_keys['d'] - held_keys['a']) * 0.05 move_y = (held_keys['w'] - held_keys['s']) * 0.05 # 水平方向碰撞处理 if move_x != 0: ray_dir = Vec3(1,0,0) if move_x > 0 else Vec3(-1,0,0) # 从角色上下边缘发射射线,覆盖整个高度避免角落穿透 ray_top = raycast(self.world_position + Vec3(0, self.half_size, 0), ray_dir, distance=self.half_size + 0.01, ignore=self.ignore) ray_bottom = raycast(self.world_position + Vec3(0, -self.half_size, 0), ray_dir, distance=self.half_size + 0.01, ignore=self.ignore) if ray_top.hit or ray_bottom.hit: # 取有效碰撞点,修正角色位置至碰撞边缘 hit_point = ray_top.world_point if ray_top.hit else ray_bottom.world_point self.x = hit_point.x - ray_dir.x * self.half_size else: self.x += move_x # 垂直方向碰撞处理 if move_y != 0: ray_dir = Vec3(0,1,0) if move_y > 0 else Vec3(0,-1,0) # 从角色左右边缘发射射线,覆盖整个宽度避免角落穿透 ray_right = raycast(self.world_position + Vec3(self.half_size, 0, 0), ray_dir, distance=self.half_size + 0.01, ignore=self.ignore) ray_left = raycast(self.world_position + Vec3(-self.half_size, 0, 0), ray_dir, distance=self.half_size + 0.01, ignore=self.ignore) if ray_right.hit or ray_left.hit: hit_point = ray_right.world_point if ray_right.hit else ray_left.world_point self.y = hit_point.y - ray_dir.y * self.half_size else: self.y += move_y app = Ursina() e = top_down_controller(model='quad', color=color.blue) e1 = Entity(model='quad', color=color.green, collider='box', position=(2,0,0)) # 给方块设置初始位置方便测试 app.run()
关键修改说明
- 调整移动逻辑:先计算移动量,检测碰撞后再决定是否移动,从根源避免穿透问题。
- 修正碰撞计算:根据移动方向取正确的碰撞点,用角色半尺寸做偏移,确保碰撞后角色刚好贴住方块。
- 排除自身碰撞:初始化时把角色加入忽略列表,防止射线误判自身。
- 优化射线发射点:从角色边缘发射射线,覆盖角色整个范围,解决角落位置的穿透问题。
内容的提问来源于stack exchange,提问作者Phước Thắng Đinh
相关产品推荐
相关产品推荐

