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

如何在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会自动处理重力、碰撞和物理运动,只需在拾取/放下时切换物理状态即可:

  1. 首先导入Rigidbody:
from ursina import Rigidbody
  1. 修改箱子初始化代码,添加Rigidbody组件:
pbox = Entity(model='cube', scale=1, position=(5,1,5), collider='box', name='box1')
# 添加刚体组件,设置重力和质量
Rigidbody(pbox, gravity=0.8, mass=1)
  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
  1. 简化update中的位置计算(可选):
if pbox_hold == True:
    # 简化向量运算,效果一致
    holding_box_E.position = player.position + Vec3(0,1.5,0) + player.camera_pivot.forward * 3

方案2:手动模拟重力与碰撞

如果不想依赖内置物理系统,可以手动在update中计算重力和碰撞检测:

  1. 给箱子添加速度属性:
pbox = Entity(model='cube', scale=1, position=(5,1,5), collider='box', name='box1')
pbox.velocity_y = 0  # 存储竖直方向速度
pbox.gravity = 0.8  # 重力加速度
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:25:19