Currently, I only found the TileMap.SetCell() method in the documentation. I currently need to update approximately 260,000 cells in 1Tick, which is far too slow. I would like to know if there is any way to optimize it?
Currently there is not. TileMap is not made for this use case. Best you can do is put all the needed updates in an array and only do so many each frame, leave the rest for the next frame, so it doesn’t block the ui, but if you want to do it in one tick it’s gonna block and be slow. The other alternative is not using TileMap at all, which is possible, faster, but loses all the nicer features, obviously. Pick your poison, there’s no magic solution.
private Vector2I _size;
private Texture2D _texture2D;
private Sprite2D[] _sprites;
private Node2D[] _chunks;
public override void _Ready() {
Node2D node2D = new Node2D();
CanvasGroup canvasGroup = new CanvasGroup();
_sprites = new Sprite2D[_size.X * _size.Y];
_chunks = new Node2D[_size.X / 16];
for (int i = 0; i < _sprites.Length; i++) {
_sprites[i] = new Sprite2D();
_sprites[i].Texture = _texture2D;
_sprites[i].Position = new Vector2I(i / _size.X * 32, i % _size.Y * 32);
}
int spriteIndex = 0;
for (int i = 0; i < _chunks.Length; i++) {
_chunks[i] = new Node2D();
for (int j = 0; j < (_sprites.Length / _chunks.Length); j++) {
_chunks[i].AddChild(_sprites[spriteIndex]);
spriteIndex++;
}
canvasGroup.AddChild(_chunks[i]);
}
node2D.ProcessThreadGroup = ProcessThreadGroupEnum.SubThread;
node2D.AddChild(canvasGroup);
AddChild(node2D);
}
public override void _PhysicsProcess(double delta) {
// _world.Progress();
for (int i = 0; i < _chunks.Length; i++) {
Vector2 globalPosition = _chunks[i].GlobalTransform.Origin;
Rect2 cameraRect = this.GetViewport().GetCamera2D().GetViewportRect();
if (globalPosition.X >= cameraRect.Position.X && globalPosition.X <= cameraRect.End.X &&
globalPosition.Y >= cameraRect.Position.Y && globalPosition.Y <= cameraRect.End.Y) {
_chunks[i].Visible = true;
} else {
_chunks[i].Visible = false;
}
}
}
Currently, I use sprite2d for rendering. If there are approximately 260,000 nodes to be rendered, the delay will be very large. I have written a rudimentary culling system.I don’t know how to write it optimally.
You can render it as a single quad using the texture atlas and a shader only. You just upload the tilemap array as a data texture and use that to figure out what tile each pixel belongs to. If you can’t figure it out, I have a test project I can dig around for, I think.
After saying I went try make an example and realized I had done this on my own Racket-based engine, so I tried to recreate it in godot and found out godot does not yet support buffer textures… which makes this unreasonably hard to do, so I am afraid you are stuck with either regular TileMap or the barely more efficient RenderServer approach. You’re just gonna have to figure out how to deal with the slow process without making it faster. Sorry.
I used MeshInstance2D to implement it. It renders 100,000 meshes well, but if it renders 250,000 meshes, it performs poorly. I’m wondering if there’s any way to optimize it
RenderingServer functions might be better for 2D than meshes, but if you’re rendering 250k quads at once on screen you’re asking a lot of the engine. It’d work fine it it was a batched mesh, I guess? Hard to tell without trying.
canvas_item_add_texture_rect_region()
So in your case it’d be
RenderingServer.CanvasItemAddTextureRectRegion(
//Canvas RID
_rids[i],
//Position in the world of the tile
new Rect2(world_position, world_size),
//Texture atlas RID
_texture1.GetRid(),
//Position of the region in the atlas
new Rect2(atlas_tile_position, atlas_tile_size));
I think that’s all you’d need? This lets you draw the tiles from one atlas. If it’s more than one atlas, you could merge them or have them in an array/dict.