Godot Version
Godot 4.4
Question
I detailed the issue on Github: Integrate forces in physics3d takes up frame time · Issue #741 · Dimensionfall/Dimensionfall · GitHub. Basically, when a mob spawns, it will add about 25ms to integrate_forces. When multiple mobs spawn, it will quickly add up to 100ms in a frame, causing stuttering.
The game is a top-down 3d game made up of chunks. I already moved the static and physics furniture to the physicsserver api which works great. However, the mob is still instantiated as a scene. I tried different approaches to solve this issue, like pooling collision objects, pooling mobs, disabling collision shape, moving using the physicsserver3d instead of mob.global_position
This is my latest code, which works partially but only spreads out the frame time across frames:
extends Node3D
@onready var projectiles_container : Node = $Projectiles
const POOL_SIZE: int = 100
var pool: Array[Mob] = []
func _init():
Helper.signal_broker.projectile_spawned.connect(on_projectile_spawned)
func _ready():
initialize_pool()
func on_projectile_spawned(projectile: Node, instigator: RID):
projectiles_container.add_child(projectile)
func initialize_pool() -> void:
if pool.size() > 0:
return # Pool already initialized
var dummy_data: Dictionary = {"id": "bone_thrower"} # Example mob ID for initialization
for i in range(POOL_SIZE):
var mob: Mob = Mob.new(Vector3.ZERO, dummy_data)
# Add to scene and initialize physics objects
add_child(mob)
mob.visible = false
mob.set_physics_process(false)
mob.state_machine.set_physics_process(false)
mob.detection.set_physics_process(false)
if mob.collision_shape_3d:
mob.collision_shape_3d.disabled = true
pool.append(mob) # ✅ Add to pool
await get_tree().process_frame # Allow deferred collisions/nav setup to complete
func spawn_mob(at_pos: Vector3, mob_json: Dictionary) -> CharacterBody3D:
if pool.is_empty():
push_error("Mob pool exhausted! Increase POOL_SIZE.")
return null
# Activate a mob from the pool
var mob: Mob = pool.pop_back()
# remember layers
var old_layer = mob.collision_layer
var old_mask = mob.collision_mask
# turn collisions off
mob.collision_layer = 0
mob.collision_mask = 0
#mob.set_physics_process(false)
#mob.state_machine.set_physics_process(false)
#mob.detection.set_physics_process(false)
#var body_rid := mob.get_rid()
#var new_transform := Transform3D(Basis(), at_pos)
#PhysicsServer3D.body_set_state(body_rid, PhysicsServer3D.BODY_STATE_TRANSFORM, new_transform)
#mob.global_position = at_pos
# before spawn:
var rid = mob.get_rid()
var xform = Transform3D(Basis(), at_pos)
PhysicsServer3D.body_set_state(rid, PhysicsServer3D.BODY_STATE_TRANSFORM, xform)
# now sync the visual Node too (without re‐triggering physics):
mob.global_transform = xform
# restore
mob.collision_layer = old_layer
mob.collision_mask = old_mask
await get_tree().physics_frame # ← key: wait until transform is stable
mob.apply_mob_data(mob_json)
_reactivate_mob.call_deferred(mob)
#mob.visible = true
#mob.set_physics_process(true)
#mob.state_machine.set_physics_process(true)
#mob.detection.set_physics_process(true)
#if mob.collision_shape_3d:
#mob.collision_shape_3d.disabled = false
Helper.signal_broker.mob_spawned.emit(mob)
return mob
func _reactivate_mob(mob):
mob.visible = true
mob.set_physics_process(true)
mob.state_machine.set_physics_process(true)
mob.detection.set_physics_process(true)
if mob.collision_shape_3d:
mob.collision_shape_3d.disabled = false
func despawn_mob(mob: CharacterBody3D, emit_signal: bool = true) -> void:
# Deactivate and hide the mob
mob.set_physics_process(false)
mob.state_machine.set_physics_process(true)
mob.detection.set_physics_process(true)
mob.visible = false
if mob.collision_shape_3d:
mob.collision_shape_3d.disabled = true
mob.remove_from_group("mobs")
if emit_signal:
Helper.signal_broker.mob_killed.emit(mob)
# Reset any transient state (if needed)
mob.is_blinking = false
mob.terminated = false
# Return to pool
pool.append(mob)
I was never able to find out why the mob adds 25ms of frame time to the integrate forces. Godot Engine should easily be able to spawn a mob, right? Once the mob spawns, it will land on the ground, which is one or more static collision objects and connect with it’s navigation. The integrate_forces frame time disappears when I add the collision shape to a StaticBody3D and parent that to the mob. However, then the mob no longer collides with walls and mobs.
Please help me figure out where this frame time comes from. In the code above, it happens when mob.global_transform = xform
is called and the mob starts to move. The mob itself is a characterbody3d: Dimensionfall/Scripts/Mob/Mob.gd at main · Dimensionfall/Dimensionfall · GitHub