PhysicsServer3D
2026/4/14大约 4 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — PhysicsServer3D
PhysicsServer3D
定义
PhysicsServer3D 是 Godot 物理引擎的底层接口。前面介绍的射线检测、碰撞检测等都是高层 API,而 PhysicsServer3D 提供了更底层的控制能力。大部分情况下你不需要直接用它,但如果需要高级物理功能(如程序化创建物理体、自定义碰撞回调),就需要用到它。
打个比方:如果 Area2D、RigidBody 等 SceneTree 节点是"傻瓜相机"模式,PhysicsServer3D 就是"专业手动模式"——功能更强大,但需要你了解更多细节。
常用方法
| 方法 | 返回值 | 说明 |
|---|---|---|
BodyCreate() | RID | 创建一个物理体 |
BodySetSpace(body, space) | void | 将物理体放入物理空间 |
BodySetShape(body, idx, shape) | void | 为物理体设置碰撞形状 |
BodySetState(body, state, value) | void | 设置物理体状态(位置、速度等) |
BodyGetState(body, state) | Variant | 获取物理体状态 |
ShapeCreate(type) | RID | 创建碰撞形状 |
SpaceCreate() | RID | 创建物理空间 |
SpaceGetDirectState(space) | PhysicsDirectSpaceState3D | 获取空间的直接访问接口 |
FreeRid(rid) | void | 释放 RID 资源 |
代码示例
基础用法:获取物理空间状态
C#
using Godot;
public partial class PhysicsExample : Node3D
{
public override void _PhysicsProcess(double delta)
{
// 获取当前世界的物理空间
var spaceRid = GetWorld3D().Space;
var spaceState = PhysicsServer3D.SpaceGetDirectState(spaceRid);
// 运行结果: 获取到物理空间的直接访问接口
GD.Print("物理空间状态已获取");
}
}GDScript
extends Node3D
func _physics_process(delta):
# 获取当前世界的物理空间
var space_rid = get_world_3d().space
var space_state = PhysicsServer3D.space_get_direct_state(space_rid)
# 运行结果: 获取到物理空间的直接访问接口
print("物理空间状态已获取")实际场景:程序化创建静态碰撞体
C#
using Godot;
public partial class ProceduralPhysics : Node3D
{
private Rid _bodyRid;
private Rid _shapeRid;
public override void _Ready()
{
// 创建碰撞形状(一个球体)
_shapeRid = PhysicsServer3D.ShapeCreate(PhysicsServer3D.ShapeType.Sphere);
PhysicsServer3D.ShapeSetData(_shapeRid, 5.0f); // 半径 5
// 创建静态物理体
_bodyRid = PhysicsServer3D.BodyCreate();
PhysicsServer3D.BodySetMode(_bodyRid, PhysicsServer3D.BodyMode.Static);
// 设置形状
PhysicsServer3D.BodyAddShape(_bodyRid, _shapeRid);
// 放入当前物理空间
var spaceRid = GetWorld3D().Space;
PhysicsServer3D.BodySetSpace(_bodyRid, spaceRid);
// 设置位置
var transform = Transform3D.Identity;
transform = transform.Translated(new Vector3(0, 0, 0));
PhysicsServer3D.BodySetState(_bodyRid, PhysicsServer3D.BodyState.Transform, transform);
// 运行结果: 在原点创建了一个半径 5 的隐形碰撞球体
GD.Print("程序化碰撞体已创建");
}
public override void _ExitTree()
{
// 清理资源
PhysicsServer3D.FreeRid(_bodyRid);
PhysicsServer3D.FreeRid(_shapeRid);
// 运行结果: 物理资源已释放
}
}GDScript
extends Node3D
var _body_rid: RID
var _shape_rid: RID
func _ready():
# 创建碰撞形状(一个球体)
_shape_rid = PhysicsServer3D.shape_create(PhysicsServer3D.SHAPE_SPHERE)
PhysicsServer3D.shape_set_data(_shape_rid, 5.0) # 半径 5
# 创建静态物理体
_body_rid = PhysicsServer3D.body_create()
PhysicsServer3D.body_set_mode(_body_rid, PhysicsServer3D.BODY_MODE_STATIC)
# 设置形状
PhysicsServer3D.body_add_shape(_body_rid, _shape_rid)
# 放入当前物理空间
var space_rid = get_world_3d().space
PhysicsServer3D.body_set_space(_body_rid, space_rid)
# 设置位置
PhysicsServer3D.body_set_state(_body_rid, PhysicsServer3D.BODY_STATE_TRANSFORM, Transform3D())
# 运行结果: 在原点创建了一个半径 5 的隐形碰撞球体
print("程序化碰撞体已创建")
func _exit_tree():
# 清理资源
PhysicsServer3D.free_rid(_body_rid)
PhysicsServer3D.free_rid(_shape_rid)
# 运行结果: 物理资源已释放进阶用法:批量查询多个物理体状态
C#
using Godot;
using System.Collections.Generic;
public partial class PhysicsQuery : Node3D
{
private List<Rid> _bodyRids = new();
public void CreateBodies()
{
var spaceRid = GetWorld3D().Space;
for (int i = 0; i < 5; i++)
{
var shapeRid = PhysicsServer3D.ShapeCreate(PhysicsServer3D.ShapeType.Box);
PhysicsServer3D.ShapeSetData(shapeRid, new Vector3(1, 1, 1));
var bodyRid = PhysicsServer3D.BodyCreate();
PhysicsServer3D.BodySetMode(bodyRid, PhysicsServer3D.BodyMode.Static);
PhysicsServer3D.BodyAddShape(bodyRid, shapeRid);
PhysicsServer3D.BodySetSpace(bodyRid, spaceRid);
var transform = Transform3D.Identity.Translated(new Vector3(i * 3, 0, 0));
PhysicsServer3D.BodySetState(bodyRid, PhysicsServer3D.BodyState.Transform, transform);
_bodyRids.Add(bodyRid);
}
// 运行结果: 创建了 5 个间距 3 米的静态碰撞体
GD.Print($"创建了 {_bodyRids.Count} 个物理体");
}
public void PrintBodyPositions()
{
foreach (var rid in _bodyRids)
{
var transform = (Transform3D)PhysicsServer3D.BodyGetState(rid, PhysicsServer3D.BodyState.Transform);
GD.Print($"物理体位置:{transform.Origin}");
// 运行结果: 打印每个物理体的位置
}
}
}GDScript
extends Node3D
var _body_rids: Array[RID] = []
func create_bodies():
var space_rid = get_world_3d().space
for i in range(5):
var shape_rid = PhysicsServer3D.shape_create(PhysicsServer3D.SHAPE_BOX)
PhysicsServer3D.shape_set_data(shape_rid, Vector3(1, 1, 1))
var body_rid = PhysicsServer3D.body_create()
PhysicsServer3D.body_set_mode(body_rid, PhysicsServer3D.BODY_MODE_STATIC)
PhysicsServer3D.body_add_shape(body_rid, shape_rid)
PhysicsServer3D.body_set_space(body_rid, space_rid)
var transform = Transform3D().translated(Vector3(i * 3, 0, 0))
PhysicsServer3D.body_set_state(body_rid, PhysicsServer3D.BODY_STATE_TRANSFORM, transform)
_body_rids.append(body_rid)
# 运行结果: 创建了 5 个间距 3 米的静态碰撞体
print("创建了 %d 个物理体" % _body_rids.size())
func print_body_positions():
for rid in _body_rids:
var transform = PhysicsServer3D.body_get_state(rid, PhysicsServer3D.BODY_STATE_TRANSFORM)
print("物理体位置:%s" % transform.origin)
# 运行结果: 打印每个物理体的位置注意事项
- 使用 RID 管理资源:PhysicsServer 使用 RID(资源 ID)来引用物理对象。创建后必须手动释放,否则会内存泄漏。
- 大部分情况下不需要直接使用:Godot 的高层节点(RigidBody3D、StaticBody3D、Area3D 等)已经封装了 PhysicsServer 的功能。只有在特殊需求时才需要直接使用 PhysicsServer。
- 必须在场景树中:创建物理体后需要设置到物理空间中才能生效。使用
BodySetSpace将其放入当前世界的物理空间。 - 性能优势:直接使用 PhysicsServer 可以避免 SceneTree 节点的开销,适合需要创建大量简单碰撞体的场景。
