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

如何获取Ursina Entity所有顶点坐标并实现均匀重力系统?

问题:自定义Ursina重力系统与顶点获取问题

用户提供的代码:

from ursina import *

bodies = set()
G = 0.002

app = Ursina(render_mode='colliders')

ground = Entity(model='plane', color=rgb(230,230,230), scale=10, collider='mesh')
ground.collider.visible = True

cube = Entity(model='cube', color=rgb(100, 100, 100), scale=2, position=Vec3(0, 5,0), collider='mesh', rotation=Vec3(45, 45, 45))
cube.collider.visible = True
bodies.add((cube, 0))

def update():
    global bodies
    new_bodies = set()
    for body, v in bodies:    
        hit_info = raycast(Vec3(cube.X, cube.Y, cube.Z) , Vec3(0, -1, 0), ignore=(body,), distance=body.scale.y, debug=True)
        
        if not hit_info.hit:
            v += G
            body.y -= v
        else:
            v = 0
        new_bodies.add((body, v))
    bodies = new_bodies

EditorCamera()
DirectionalLight(parent=Entity(), y=2, z=3, shadows=True, rotation=(45, -45, 45))

app.run()

需求与问题:
我希望在update函数中实现均匀作用于物体的重力系统,而非Ursina内置FirstPersonController的重力效果。我想到的方法是从每个顶点向下进行射线检测(以适配非立方体物体),但尝试使用body.model.vertices获取顶点时出现错误:AttributeError: 'panda3d.core.NodePath' object has no attribute 'vertices'。

请问是否有获取Ursina Entity所有顶点坐标的方法?若没有,是否有更简便的重力实现方式?现有方法会导致立方体以顶点着地的问题,实际应平稳落至地面。


获取Ursina Entity顶点坐标的方法

Ursina的Entity.model加载后是Panda3D的NodePath对象,无法直接通过vertices属性访问顶点数据,正确的获取方式是通过模型的mesh属性:

  • 对于内置模型(如cube、plane)或自定义模型,可通过body.model.mesh.vertices获取局部空间下的原始顶点坐标。
  • 若要用于世界空间的射线检测,需要将局部顶点转换为世界坐标:
    # 获取局部顶点并转换为世界坐标
    local_vertices = body.model.mesh.vertices
    world_vertices = [body.world_position + body.world_rotation * Vec3(v) for v in local_vertices]
    

注意:逐顶点射线检测性能开销较高,复杂模型不建议使用这种方式实现重力。


更简便的重力实现方式(解决顶点着地问题)

现有代码的核心问题是仅从物体中心发射射线,旋转后的立方体顶点先接触地面但中心仍悬空,导致顶点卡地。以下两种方案更高效且能解决该问题:

方案1:基于碰撞盒多点射线检测

针对带碰撞盒的物体,从碰撞盒底部的多个关键点(如四个角和中心)发射射线,只要有一个射线检测到地面,就判定物体着地,并调整位置避免穿透:

from ursina import *

bodies = set()
G = 0.002

app = Ursina(render_mode='colliders')

ground = Entity(model='plane', color=rgb(230,230,230), scale=10, collider='mesh')
ground.collider.visible = True

cube = Entity(model='cube', color=rgb(100, 100, 100), scale=2, position=Vec3(0, 5,0), collider='mesh', rotation=Vec3(45, 45, 45))
cube.collider.visible = True
bodies.add((cube, 0))

def update():
    global bodies
    new_bodies = set()
    for body, v in bodies:
        # 获取碰撞盒的局部边界范围
        bounds = body.collider.bounds
        # 定义碰撞盒底部的多个检测点(局部坐标)
        bottom_local_points = [
            Vec3(bounds[0][0], bounds[1][0], bounds[2][0]),  # 左下后
            Vec3(bounds[0][1], bounds[1][0], bounds[2][0]),  # 右下后
            Vec3(bounds[0][0], bounds[1][0], bounds[2][1]),  # 左下前
            Vec3(bounds[0][1], bounds[1][0], bounds[2][1]),  # 右下前
            Vec3((bounds[0][0]+bounds[0][1])/2, bounds[1][0], (bounds[2][0]+bounds[2][1])/2)  # 底部中心
        ]
        # 转换为世界坐标
        bottom_world_points = [body.world_position + body.world_rotation * p for p in bottom_local_points]
        
        on_ground = False
        min_ground_distance = float('inf')
        # 遍历所有底部点检测地面
        for point in bottom_world_points:
            hit_info = raycast(point, Vec3(0, -1, 0), ignore=(body,), distance=0.1, debug=False)
            if hit_info.hit:
                on_ground = True
                if hit_info.distance < min_ground_distance:
                    min_ground_distance = hit_info.distance
        
        if not on_ground:
            v += G
            body.y -= v
        else:
            # 调整物体位置至刚好接触地面
            body.y += min_ground_distance - 0.01  # 微小偏移防止穿透
            v = 0
        new_bodies.add((body, v))
    bodies = new_bodies

EditorCamera()
DirectionalLight(parent=Entity(), y=2, z=3, shadows=True, rotation=(45, -45, 45))

app.run()

方案2:使用intersects直接检测碰撞

Ursina内置的Entity.intersects()方法可快速检测物体与其他碰撞体的交集,无需手动发射射线,代码更简洁:

from ursina import *

bodies = set()
G = 0.002

app = Ursina(render_mode='colliders')

ground = Entity(model='plane', color=rgb(230,230,230), scale=10, collider='mesh')
ground.collider.visible = True

cube = Entity(model='cube', color=rgb(100, 100, 100), scale=2, position=Vec3(0, 5,0), collider='mesh', rotation=Vec3(45, 45, 45))
cube.collider.visible = True
bodies.add((cube, 0))

def update():
    global bodies
    new_bodies = set()
    for body, v in bodies:
        # 先应用速度让物体下落
        body.y -= v
        # 检测是否与地面碰撞
        hit_info = body.intersects(ground)
        if hit_info.hit:
            # 回退位置避免穿透地面
            body.y += hit_info.distance
            v = 0
        else:
            # 未接触地面则继续加速
            v += G
        new_bodies.add((body, v))
    bodies = new_bodies

EditorCamera()
DirectionalLight(parent=Entity(), y=2, z=3, shadows=True, rotation=(45, -45, 45))

app.run()

这种方式自动适配各种碰撞体形状,性能更优,且能完美解决旋转物体顶点着地的问题。


内容的提问来源于stack exchange,提问作者AG-88301

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:55:01