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

如何阻止Ursina 2D迷宫游戏玩家穿过墙体?

Ursina 2D迷宫玩家穿墙问题解决

我正在使用Ursina引擎开发一款2D迷宫游戏,但无法实现玩家不能穿过墙体的效果。我尝试添加了collider但没有作用,因为需要检测碰撞后执行对应逻辑;添加raycast后也未解决该问题,恳请提供帮助。

用户原代码:

from ursina import *
from ursina import physics
class top_down_controller(Entity):
    def __init__(self, model, color, scale = 1, texture = None, collider = 'box', ignore=[], position=[0,0]):
        super().__init__(
            model = model,
            color = color,
            scale = scale,
            texture = texture,
            collider = collider,
            ignore = ignore,
            vleft = -0.1,
            vright = 0.1,
            vup = 0.1,
            vdown = -0.1,
            position= position
            )
    def update(self):
        
        self.x += held_keys['a'] * self.vleft
        self.x += held_keys["d"] * self.vright
        self.y += held_keys['w'] * self.vup
        self.y += held_keys['s'] * self.vdown
        above = raycast(self.world_position, self.up, 0.51, ignore=[self], debug = True)
        above_left = raycast(self.world_position+Vec3(-0.5,0,0), self.up, 0.51, ignore = [self], debug = True)
        above_right = raycast(self.world_position+Vec3(0.5,0,0), self.up, 0.51, ignore = [self], debug = True)
        below = raycast(self.world_position, self.down, 0.51, ignore = [self], debug = True)
        below_left = raycast(self.world_position+Vec3(-0.5,0,0), self.down, 0.51, ignore = [self], debug = True)
        below_right = raycast(self.world_position+Vec3(0.5,0,0), self.down, 0.51, ignore = [self], debug = True)
        left = raycast(self.world_position, self.left, 0.51, ignore = [self], debug = True)
        left_above = raycast(self.world_position+Vec3(0,0.5,0), self.left, 0.51, ignore = [self], debug = True)
        left_down = raycast(self.world_position+Vec3(0,-0.5,0), self.left, 0.51, ignore = [self], debug = True)
        right = raycast(self.world_position, self.right, 0.51, ignore = [self], debug=True)
        right_above = raycast(self.world_position+Vec3(0,0.5,0), self.right, 0.51, ignore = [self], debug = True)
        right_down = raycast(self.world_position+Vec3(0,-0.5,0), self.right, 0.51, ignore = [self], debug = True)
        if any((above.hit, above_left.hit, above_right.hit)):
            self.vup = 0
        elif any((left.hit, left_above.hit, left_down.hit)):
            self.vleft = 0
        elif any((right.hit, right_above.hit, right_down.hit)):
            self.vright = 0
        elif any((below_right.hit, below.hit, below_left.hit)):
            self.vdown = 0
app  = Ursina()
e1 = Entity(model = 'quad', color = color.green, collider = 'box')
e = top_down_controller(model = 'quad', color = color.blue, position = [2,2], ignore = [e1])

app.run()

问题分析与修复方案

核心问题点

  1. elif逻辑冲突:四个方向的碰撞判断用elif串联,导致仅第一个满足条件的方向生效,其他方向碰撞被忽略(比如同时碰左墙和上墙时,仅左墙限制生效)。
  2. 速度未恢复:碰撞时把对应方向速度设为0,但没有在碰撞消失后恢复,导致玩家碰到墙后永久失去该方向移动能力。
  3. ignore参数未传递:自定义控制器的ignore列表没有传入raycast,导致raycast可能误检测自身或其他对象。
  4. 移动顺序错误:先移动玩家再检测碰撞,导致玩家已经穿墙后才触发限制。

修复后的Raycast版本代码

from ursina import *

class top_down_controller(Entity):
    def __init__(self, model, color, scale=1, texture=None, collider='box', ignore=[], position=[0,0]):
        super().__init__(
            model=model,
            color=color,
            scale=scale,
            texture=texture,
            collider=collider,
            position=position
        )
        # 单独定义基础速度,方便碰撞后恢复
        self.base_speed = 0.1
        self.vleft = -self.base_speed
        self.vright = self.base_speed
        self.vup = self.base_speed
        self.vdown = -self.base_speed
        # 合并忽略列表,包含自身和指定对象
        self.ignore_list = ignore + [self]

    def update(self):
        # 每次更新先恢复基础速度,解决碰撞后无法移动的问题
        self.vleft = -self.base_speed
        self.vright = self.base_speed
        self.vup = self.base_speed
        self.vdown = -self.base_speed

        # 各方向碰撞检测
        above = raycast(self.world_position, self.up, 0.51, ignore=self.ignore_list)
        above_left = raycast(self.world_position + Vec3(-0.5, 0, 0), self.up, 0.51, ignore=self.ignore_list)
        above_right = raycast(self.world_position + Vec3(0.5, 0, 0), self.up, 0.51, ignore=self.ignore_list)
        
        below = raycast(self.world_position, self.down, 0.51, ignore=self.ignore_list)
        below_left = raycast(self.world_position + Vec3(-0.5, 0, 0), self.down, 0.51, ignore=self.ignore_list)
        below_right = raycast(self.world_position + Vec3(0.5, 0, 0), self.down, 0.51, ignore=self.ignore_list)
        
        left = raycast(self.world_position, self.left, 0.51, ignore=self.ignore_list)
        left_above = raycast(self.world_position + Vec3(0, 0.5, 0), self.left, 0.51, ignore=self.ignore_list)
        left_down = raycast(self.world_position + Vec3(0, -0.5, 0), self.left, 0.51, ignore=self.ignore_list)
        
        right = raycast(self.world_position, self.right, 0.51, ignore=self.ignore_list)
        right_above = raycast(self.world_position + Vec3(0, 0.5, 0), self.right, 0.51, ignore=self.ignore_list)
        right_down = raycast(self.world_position + Vec3(0, -0.5, 0), self.right, 0.51, ignore=self.ignore_list)

        # 用独立if判断每个方向,避免相互干扰
        if any((above.hit, above_left.hit, above_right.hit)):
            self.vup = 0
        if any((left.hit, left_above.hit, left_down.hit)):
            self.vleft = 0
        if any((right.hit, right_above.hit, right_down.hit)):
            self.vright = 0
        if any((below_right.hit, below.hit, below_left.hit)):
            self.vdown = 0

        # 最后执行移动
        self.x += held_keys['a'] * self.vleft
        self.x += held_keys["d"] * self.vright
        self.y += held_keys['w'] * self.vup
        self.y += held_keys['s'] * self.vdown

app = Ursina()
# 给墙体设置合适大小,避免默认尺寸导致初始碰撞
e1 = Entity(model='quad', color=color.green, collider='box', scale=(2,2))
e = top_down_controller(model='quad', color=color.blue, position=[2,2], ignore=[e1])

app.run()

更简洁的2D物理系统方案

直接使用Ursina内置的Physics2dComponent,无需手动编写raycast,自动处理碰撞限制:

from ursina import *
from ursina.physics_2d import Physics2dComponent

class top_down_controller(Entity):
    def __init__(self, model, color, scale=1, texture=None, collider='box', ignore=[], position=[0,0]):
        super().__init__(
            model=model,
            color=color,
            scale=scale,
            texture=texture,
            collider=collider,
            position=position
        )
        # 关闭重力,适配俯视视角2D游戏
        self.physics = Physics2dComponent(self, gravity=0)
        self.base_speed = 10

    def update(self):
        # 计算移动方向并归一化
        direction = Vec2(
            held_keys['d'] - held_keys['a'],
            held_keys['w'] - held_keys['s']
        ).normalized()
        # 设置物理速度
        self.physics.velocity = direction * self.base_speed

app = Ursina()
e1 = Entity(model='quad', color=color.green, collider='box', scale=(2,2))
e = top_down_controller(model='quad', color=color.blue, position=[2,2])

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 12:42:06