如何在Ursina中为可拾取Entity添加重力实现物理下落?
为Ursina游戏中的箱子Entity添加重力下落效果
问题描述
我正在开发一款3D游戏,已实现拾取特定箱子类Entity并随玩家移动的功能,但放下箱子后它会悬浮在空中。请问如何为该Entity添加重力,使其放下后能像Unity中那样实现动态物理下落效果?
原始代码:
import ursina.application from ursina import * from ursina.prefabs.first_person_controller import FirstPersonController app = Ursina() window.fullscreen = True window.fps_counter.enabled = False window.color = color.black window.exit_button.enabled = False #=========================================== #from Player import _Player_ from Map import * #============================== player = FirstPersonController() player.jump_up_duration = 1 player.jump_height = 5 player.gravity = 0.8 player.speed = 15 player.cursor.color = color.blue player.y = 10 player.cursor.model = 'circle' #===================movables pbox = Entity(model = 'cube', scale = 1, position = (5, 1, 5), collider = 'box', name = 'box1') pbox.add_script() global pbox_hold pbox_hold = False #=============================== def exit_game(): app.userExit() def show_game_menu(): Exit.enabled = True game_menu_bd.enabled = True def hide_game_menu(): Exit.enabled = False game_menu_bd.enabled = False def pause_handler_input(key): if key == 'left mouse down' and mouse.hovered_entity == back_game: back_game.enabled = False hide_game_menu() application.paused = not application.paused mouse.locked = True if key == 'left mouse down' and mouse.hovered_entity == Exit: exit_game() game_menu_bd = Button(scale = 10) game_menu_bd.z = 0.1 game_menu_bd.enabled = False back_game = Button(scale = (0.15, 0.07), position = (0, 0), ignore_paused = True, color = color.azure, text = 'Resume') back_game.input = pause_handler_input back_game.enabled = False Exit = Button(model = 'quad' , scale = (0.05, 0.035), position = (window.exit_button.x-0.025, window.exit_button.y-0.015), color = color.red, ignore_paused = True, text = 'exit') Exit.input = pause_handler_input Exit.enabled = False global holding_box_E holding_box_E = pbox #=================== def input(key): global pbox_hold, holding_box_E if key == "c": mouse.locked = True if key == "escape" or key == "x": show_game_menu() mouse.locked = False back_game.enabled = True ursina.application.paused = True if key == 'left mouse down' and pbox_hold == False: cast = raycast(player.position+Vec3(0, 1.9, 0), direction=(player.camera_pivot.forward), distance=4, traverse_target=pbox, debug=True) #print(cast.hit) if cast.hit: pbox.color = color.blue pbox_hold = True holding_box_E = cast.entity elif key == 'left mouse up' and pbox_hold == True: pbox_hold = False def update(): global pbox_hold, holding_box_E if player.y < -20: player.position = (0, 2, 0) if pbox_hold == True: holding_box_E.position = player.position + Vec3(0, 1.5, 0) + player.camera_pivot.forward + Vec3(player.camera_pivot.forward.x, player.camera_pivot.forward.y, player.camera_pivot.forward.z)*2 app.run()
注:地图相关逻辑在单独的脚本中!
解决方案
这里提供两种可行方案,推荐使用内置物理组件实现更稳定的效果:
方案1:使用Ursina内置Rigidbody组件(推荐)
Ursina的Rigidbody会自动处理重力、碰撞和物理运动,只需在拾取/放下时切换物理状态即可:
- 首先导入
Rigidbody:
from ursina import Rigidbody
- 修改箱子初始化代码,添加Rigidbody组件:
pbox = Entity(model='cube', scale=1, position=(5,1,5), collider='box', name='box1') # 添加刚体组件,设置重力和质量 Rigidbody(pbox, gravity=0.8, mass=1)
- 修改
input函数,拾取时禁用刚体物理,放下时重新启用:
def input(key): global pbox_hold, holding_box_E # ... 原有代码不变 ... if key == 'left mouse down' and pbox_hold == False: cast = raycast(player.position+Vec3(0,1.9,0), direction=player.camera_pivot.forward, distance=4, traverse_target=pbox, debug=True) if cast.hit: pbox.color = color.blue pbox_hold = True holding_box_E = cast.entity # 拾取时禁用刚体,避免重力影响 holding_box_E.rigidbody.enabled = False elif key == 'left mouse up' and pbox_hold == True: pbox_hold = False # 放下时重新启用刚体,恢复重力 holding_box_E.rigidbody.enabled = True
- 简化
update中的位置计算(可选):
if pbox_hold == True: # 简化向量运算,效果一致 holding_box_E.position = player.position + Vec3(0,1.5,0) + player.camera_pivot.forward * 3
方案2:手动模拟重力与碰撞
如果不想依赖内置物理系统,可以手动在update中计算重力和碰撞检测:
- 给箱子添加速度属性:
pbox = Entity(model='cube', scale=1, position=(5,1,5), collider='box', name='box1') pbox.velocity_y = 0 # 存储竖直方向速度 pbox.gravity = 0.8 # 重力加速度
- 修改
update函数,添加重力逻辑:
def update(): global pbox_hold, holding_box_E # ... 原有代码不变 ... if not pbox_hold: # 应用重力加速度 holding_box_E.velocity_y -= holding_box_E.gravity * time.dt # 更新竖直位置 holding_box_E.y += holding_box_E.velocity_y * time.dt # 检测下方碰撞(防止穿过地面) hit = raycast(holding_box_E.position, Vec3(0,-1,0), distance=holding_box_E.scale_y/2 + 0.01) if hit: # 修正箱子位置到地面上方 holding_box_E.y = hit.world_point.y + holding_box_E.scale_y/2 holding_box_E.velocity_y = 0 # 落地后速度清零
关键注意事项
- 确保地图中的地面/障碍物都添加了
collider组件,否则箱子无法检测碰撞会直接穿过 - 使用Rigidbody时,可通过调整
mass、friction、bounciness等参数优化物理表现 - 手动模拟方案需要额外处理复杂场景(比如箱子落在其他物体上),适合简单需求
内容的提问来源于stack exchange,提问作者Alex Krakowiak
相关产品推荐
相关产品推荐

