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

Ursina中FirstPersonController角色模型穿模问题求助

Ursina第三人称角色碰撞失效问题修复指南

问题根源拆解

  1. 控制器与视觉模型脱节:直接修改camera.z = -5实现第三人称时,FirstPersonController的碰撞检测逻辑仍绑定自身,但你设置的角色模型(model="cube")未与控制器的碰撞体关联,导致视觉模型位置和实际碰撞检测区域错位,看起来像穿模。
  2. 自定义模型碰撞体生成异常:使用collider="mesh"加载walls_starting_area.obj时,若模型存在非闭合面、原点偏移或顶点数据错误,会导致碰撞体无法正确生成,失去阻挡作用。
  3. 模型原点与碰撞体未同步:仅调整模型原点而未同步偏移碰撞体,会导致碰撞检测区域与视觉模型位置不匹配,无法彻底解决穿模。

针对性修复方案

1. 绑定角色模型与控制器碰撞体

放弃直接修改全局相机位置,将角色模型设为FirstPersonController的子实体,确保模型移动与碰撞检测完全同步:

# 初始化控制器,优先用capsule碰撞体贴合人形
player = FirstPersonController(collider="capsule", speed=20)
# 创建第三人称角色模型,作为控制器子节点
player_model = Entity(parent=player, model="cube", scale=(1, 2, 1))
# 绑定相机到控制器,设置第三人称视角
camera.parent = player
camera.position = (0, 2, -5)
camera.look_at(player_model)
  • capsule碰撞体比box更适合角色移动,能减少边角穿模概率。
  • 相机作为控制器子节点,视角跟随角色移动更稳定,不会出现视角与角色错位。

2. 修复墙体碰撞体

针对自定义OBJ模型的碰撞问题,两种可行方案:

  • 方案一:替换为简单碰撞体
    若墙体是规则形状,改用box碰撞体,避免复杂mesh碰撞体的检测异常:
    walls = Entity(
        model="walls_starting_area.obj",
        position=(0,-30,0),
        collider="box",
        texture="brick"
    )
    
  • 方案二:重新烘焙模型碰撞体
    用Blender打开OBJ模型,检查并修复非闭合面,重新导出后,在Ursina中手动生成碰撞体:
    walls.collider = MeshCollider(walls, mesh=walls.model)
    

3. 同步模型原点与碰撞体偏移

若需自定义模型原点,必须同步调整碰撞体的位置偏移:

# 模型原点在底部,向上偏移1单位对齐角色重心
player_model = Entity(parent=player, model="cube", position=(0, 1, 0))
# 碰撞体同步偏移,确保检测区域与模型重合
player.collider = CapsuleCollider(player, center=(0, 1, 0))

4. 优化碰撞检测逻辑

简化碰撞检测代码,避免遍历所有实体提升性能:

def update():
    if player.intersects(ground).hit:
        print("触发地面碰撞")
    if player.intersects(walls).hit or player.intersects(wall).hit:
        print("触发墙体碰撞")

完整修正代码

from ursina import *
from ursina.prefabs.first_person_controller import *
from perlin_noise import PerlinNoise
import random

noise = PerlinNoise(octaves=3, seed=random.randint(1,10000))
app = Ursina()

ground = Entity(
    model="cube",
    scale=(200,20,200),
    texture="grass",
    position=(0,-30,0),
    collider="box"
)
walls = Entity(
    model="walls_starting_area.obj",
    position=(0,-30,0),
    collider="box",
    texture="brick"
)

Sky()

class character(Entity):
    def __init__(self, position=(0,-20,0), model="cube", speed=20, collider="box",scale_x=1,scale_y=1):
        super().__init__(
            model=model,
            position=position,
            speed=speed,
            collider=collider,
            scale_x=scale_x,
            scale_y=scale_y
        )

wall = character(position=(0,-20,3), scale_x=30, scale_y=20,collider="box")

# 绑定角色模型与控制器
player = FirstPersonController(collider="capsule", speed=20)
player_model = Entity(parent=player, model="cube", scale=(1,2,1))

# 设置第三人称相机
camera.parent = player
camera.position = (0, 2, -5)
camera.look_at(player_model)

def update():
    if player.intersects(ground).hit:
        print("撞到地面")
    if player.intersects(walls).hit or player.intersects(wall).hit:
        print("撞到墙体")

app.run()

额外注意事项

  • 禁止直接修改全局相机位置,必须将相机绑定到玩家控制器,确保视角与碰撞体同步。
  • 复杂模型优先使用简单碰撞体组合,mesh碰撞体性能低且易出现检测漏洞。
  • 调整角色模型缩放时,同步修改碰撞体尺寸,例如player.collider.scale = (1,2,1)。

内容的提问来源于stack exchange,提问作者Beckett

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 09:15:31