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

为何我的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 10:15:57