Did all your units needed updated same time?
you can make chunks and update near player.
Make from enemies groups and process them as single unit?
You can extend time between physic process.
You can skip physic process and collect delta time from skipped updates.
start multi threading.
ObjectPooling is not recommended in Godot.
To be more precise, it is not needed for simple objects. It still makes sense to pool complex “objects” that have noticeable setup time. E.g. complex nodes that do a lot of performance expensive stuff on creation.
Objectpooling for simple objects makes no sense in Godot due to its core C++ engine as long as you stay within that core (e.g. GDSCript or C++). The Godot core does not suffer from the same problems as other engines with a garbage collected language (e.g. C#) that adds mandatory game stutters when not pooling. Hence why pooling in certain game engines like Unity is seen mandatory for everything but makes no sense by default in Godot.
That said if you are using C# with Godot you might want to do pooling for certain things as otherwise the C# garbage collector will kill your games performance at some point.
This is too little context because the physics process increase can be a lot of things.
The agent avoidance runs in physics as well as the navigation map sync. Also CharacterBody nodes are notorious physics performance killers when used in large quantities.
If you have a noticeable performance drop from pathsearch when the target is not reachable that is a sign that your navigation mesh is too big and unwieldy and your world needs to be chunked and/or the pathfinding needs a hierarchy layer.
If a position is not reachable the pathfinding searches all available polygons to confirm that the position can not be reached. That does not matter on a “normal” sized map but on a very big map with an excessive amount of polygons that matters a lot for performance. As long as a path can be found quick the pathsearch will do an early exit so this can hide a too large map for a long time but performance hits back hard when the position can not be reached.
Each enemy is a CharacterBody2D. Started trying to use servers for optimization. If you take the task, just to reach the player, then before at 300 enemies was less than 60 fps, and with PhysicsServer2D number of enemies at a stable fps increased to 2000. This pleased me very much, but it forced every second to create a new ShapeParameters to handle collision (perhaps you can do otherwise, but tutorials less than I have fingers on one hand) because of what were jumps on objects 30000-80000-30000, etc. Although it did not eat frame time, but raises doubts.
About pooling, I work in C# and my objects are components of a composition, all entities have plus or minus similar structure:
I wouldn’t say they are complex objects, they are quite primitive, they just take up a lot of nodes. I plan to start replacing all components to use servers (except visual ones). If you know a good tutorial on how to create top-down entities on servers, I’d love to see it. If you can just show sample code, then all I need is this:
Handling movement (along with the visuals represented by nodes, not RenderingServers),
Creating bodies and areas (I know about Body and Area in PhysicsServers, but I don’t understand how they work).
Receive and send signals (to pass information between Attack/HitboxComponent)
If I take my current code:
using System;
using Godot;
public partial class TestEnemy : Node2D
{
[Export(PropertyHint.Layers2DPhysics)] private uint collisionLayer = 0;
[Export(PropertyHint.Layers2DPhysics)] private uint collisionMask = 0;
[Export] private float shapeRadius = 0f;
private CharacterBody2D player;
private Rid shapeRID;
private Rid bodyRID;
public override void _Ready()
{
player = GetNode("/root/Main/Place/Player") as CharacterBody2D;
shapeRID = PhysicsServer2D.CircleShapeCreate();
PhysicsServer2D.ShapeSetData(shapeRID, shapeRadius);
bodyRID = PhysicsServer2D.BodyCreate();
PhysicsServer2D.BodySetParam(bodyRID, PhysicsServer2D.BodyParameter.Friction, 0);
PhysicsServer2D.BodySetMode(bodyRID, PhysicsServer2D.BodyMode.RigidLinear);
PhysicsServer2D.BodySetState(bodyRID, PhysicsServer2D.BodyState.Transform, Transform);
PhysicsServer2D.BodyAddShape(bodyRID, shapeRID);
PhysicsServer2D.BodySetCollisionLayer(bodyRID, collisionLayer);
PhysicsServer2D.BodySetCollisionMask(bodyRID, collisionMask);
PhysicsServer2D.BodySetSpace(bodyRID, GetWorld2D().Space);
}
public override void _PhysicsProcess(double delta)
{
Vector2 direction = (player.GlobalPosition - this.GlobalPosition).Normalized();
Vector2 moveVector = direction * 100 * Convert.ToSingle(delta);
PhysicsTestMotionParameters2D Parameters = new PhysicsTestMotionParameters2D();
//Parameters.CollideSeparationRay = true;
Parameters.From = Transform;
Parameters.Motion = moveVector;
if (!PhysicsServer2D.BodyTestMotion(bodyRID, Parameters))
{
PhysicsServer2D.BodySetState(bodyRID, PhysicsServer2D.BodyState.Transform, Transform);
this.Translate(moveVector);
}
}
}