
在Ursina中,敌人实体不会自动移动——必须在update()函数中主动更新其位置;否则即使成功创建并显示,敌人也将静止不动。本文详解如何为敌人添加平滑、可控的运动逻辑,并提供可直接运行的完整示例。
在ursina中,敌人实体不会自动移动——必须在update()函数中主动更新其位置;否则即使成功创建并显示,敌人也将静止不动。本文详解如何为敌人添加平滑、可控的运动逻辑,并提供可直接运行的完整示例。
在Ursina中,所有实时行为(如移动、旋转、动画播放)都必须放在 update() 函数内——这是引擎每帧自动调用的核心钩子。你当前的代码仅在初始化时创建了敌人,但未定义任何持续更新逻辑,因此敌人虽可能渲染出来,却完全“冻结”。
✅ 正确做法:将移动逻辑写入 update()
首先,确保每个敌人实体是可访问、可更新的全局或类成员变量(例如存入 enemies = [] 列表),然后在 update() 中遍历并修改其 .position 属性。
以下是一个最小可运行示例(已修复原代码中的常见问题,如路径错误、重复模型加载、坐标维度缺失等):
from ursina import *
app = Ursina()
# 简化资源路径示例(请替换为你本地存在的模型/贴图)
enemy_model = 'cube' # 临时用内置模型测试,避免路径错误
enemy_texture = None
# 创建敌人列表
enemies = []
for i in range(4):
enemy = Entity(
model=enemy_model,
texture=enemy_texture,
scale=(0.5, 0.5, 0.5),
position=(random.uniform(-5, 5), 0.5, random.uniform(10, 20)), # Z轴前置,面向玩家
color=color.red,
collider='box'
)
enemies.append(enemy)
# 主角(用于追击逻辑参考)
player = FirstPersonController()
# --- 核心:敌人移动逻辑写在 update() 中 ---
def update():
for enemy in enemies:
# 方式1:匀速向玩家水平方向逼近(X/Z平面)
direction = (player.position - enemy.position).xz # 忽略Y轴,只在地面平面移动
if direction.length() > 1:
direction = direction.normalized() * time.dt * 3 # 速度=3单位/秒
enemy.position += Vec3(direction.x, 0, direction.z)
# 方式2(可选):沿固定路径循环移动(如左右横移)
# enemy.x += math.sin(time.time * 2 + enemy.id) * time.dt * 1.5
# 可选:添加简单碰撞检测(当敌人碰到玩家时触发)
def input(key):
if key == 'escape':
quit()
app.run()⚠️ 关键注意事项
-
position是三维向量:原代码中position=(0, -4)缺少 Z 坐标(应为(x, y, z)),会导致 Z 默认为 0,可能与其他物体重叠或不可见; -
避免重复加载模型/贴图:
FrameAnimation3d和子Entity同时加载相同.obj文件易引发崩溃或性能问题;推荐仅用一个 Entity,无需嵌套; -
纹理路径需正确:确保
'Jowhareh_galleries_...png'在assets/目录下,或使用绝对路径;否则贴图为空,模型可能不可见; -
动画与移动可共存:若需播放行走动画,可在
update()中同步调用enemy.animate_position(...)或控制FrameAnimation3d.frame; -
性能优化:大量敌人时,考虑使用
invoke()实现分帧更新,或启用enemy.eternal = True配合对象池复用。
✅ 总结
让敌人动起来 ≠ 创建它,而是持续驱动它的状态变化。牢记三要素:
① 敌人实体必须可被 update() 访问(如存入列表);
② 所有位移、旋转、缩放操作必须在 update() 内执行;
③ 运动逻辑应基于 time.dt 实现帧率无关的平滑移动。
掌握这一模式后,你可轻松扩展为巡逻、追逐、躲避、路径点导航等高级AI行为——真正的游戏逻辑,从此刻的 update() 开始。

















