0 votes

I am creating a procedurally generated voxel world, like minecraft, and I want to add loading screen while the world is loading. I think I should be able to do this with yield, but don't know how.
My project looks like this:

World script: extends Spatial, is on the root node, generates chunks(only the data, if its air, stone dirt or whatever), then after that, generates the mesh of the chunks, based on their and their neighbours data.

Chunk script: extends nothing, has "classname Chunk" on top, in "init()" function generates block data, that it stores, and in "drawchunk()" creates a SurfaceTool and calls "drawblock()" function on its blocks

Block script: extends nothing, "classname Block" on top, in "init()" function sets its position, and parentChunk, in" drawblock()" function adds vertices uvs and normals to SurfaceTool.

WorldData script: extends Resource, contains world data like chunk size etc. I load it as a resource to each script

Also, I appreciate any advice to restructure my code, I have a feeling there are better ways.

Edit: The "draw_chunk()" method on Chunk returns the MeshInstance created in it, so it can be added to the scene as child

in Engine by (146 points)
edited by

1 Answer

0 votes

How about having a fullscreen GUI node containing the loading screen shown by default, and hide it when enough chunks finished loading?

If you actually want to see an animation or some progress, I would recommend you structure your game so that chunks load asynchronously (i.e, not all at once stalling the CPU). One way to do this is to centralize the heavy chunk loading logic into one place, so you can put them in a queue and process a controlled amount of them per frame, or even sort them by distance to player (i.e keep loading chunks until 8 milliseconds elapsed). This allows to keep the game responsive, but might load a bit slower.

An evolved version of that technique is to use threads: instead of the queue, you'd have a worker thread taking chunk coordinate requests as input and chunk data and mesh as output. This can't live in the scene tree though because it's not thread-safe.
I worked a long time on a system like this in my voxel module for Godot and I have three threads: one for loading data, one to compute mesh from voxels (also used when editing the world), and one to save data. I even had to build the ArrayMesh object on the main thread still due to some perf issues I had with Godot's renderer.
There is a lot more to go through if you are going that route, and I don't have time to explain it all here, but you can have a look to my module if you search for ways to do this (main logic is here https://github.com/Zylann/godot_voxel/blob/master/terrain/voxel_terrain.cpp#L527).

by (27,595 points)
edited by

So far i did it like this: i start the generation in _ready() of a main script. It starts by crating a chunk. In the _init() of that chunk, the creation of its block data is happening. Every 'X' blocks, it yields waiting for "resumeBlockGen", jumping(or returns, whatever yield does is called), to the code at the generation of the chunk. It also yields there, waiting for signal "resumeChunkGen". Then, in the main script's _process() function the signal "resumeBlockGen" gets emitted, thus jumping back to the block generation in the current chunk. This goes on, and when the chunk is finished, it emits signal "resmumeBlockGen", so it can move on to the next chunk.
This is done in both generation of chunks/blocks, and drawing of them. It works perfectly fine so far.
Kind of another topic: any advice on how I should store data? Right now I have a global script that stores a dictionary of chunks (with unique key as their position), and they hold the data of their blocks, and have a MeshInstance on them.

You can store voxels in arrays and meshes sepatately in a dictionary indexed by position, that's what I do as well.

I just changed to chunks being in dictionary and block data being in arrays before you commented.
How much ram does it use for you? For me,9 chunks use close to 300MB and 25 chunks use almost 600 MB, where one chunk is 16 by 256 by 16 blocks. Isnt that too much?

It can surely be less than that. In my module, a 32x32 chunk area takes about 300 Mb, because of simple optimizations I did:
- Voxels are stored in channels of 8-bit numbers, so one chunk takes maximum 65,536 bytes. In GDScript, integers are 64-bit.
- Chunks are split into blocks of 16x16x16 voxels. When one of these is uniform (only air, only dirt...), I store a single value instead of allocating a full grid
- More can be done such as RLE compression but haven't done it yet because memory usage is already quite decent
For me meshes take the most space in the end, but I can't do much about this :p

I see you do ur stuff in c++, do you know any good tutorial to get started with c++ in godot? I know c++, and would actually prefer programming in it, just dont know how to start using it in godot. If i know well its called nativescript or gdnative or whatever, but I couldnt really understand the documentation about it that I found.

There are two ways:
- GDNative gives you only access to the script API but you can distribute your system as a library file and build it with the system of your choice, although it can be a bit tricky to run safely: https://docs.godotengine.org/en/3.1/tutorials/plugins/gdnative/index.html
- You can also write a module directly in Godot, which gives access to more internals and is relatively easier to do, but requires to use SCons and compile the engine with it: http://docs.godotengine.org/en/3.1/development/cpp/custom_modules_in_cpp.html

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.