fenomas / noa

Experimental voxel game engine.
MIT License
608 stars 86 forks source link

Changing the world results in chunks being loaded in a bad way #168

Closed MCArth closed 2 years ago

MCArth commented 2 years ago

Hi, I've found that something is behaving differently now I've upgraded. If you change the world and stay in the same position, it is possible (and, in my game, happens a lot) that chunks far away from you will load before the chunks near you - which isn't great on machines with limited processing power.

This is as a result of markAllChunksForRemoval now not clearing world._chunksKnown where it did before. Then these chunks close to the player are skipped within findNewChunksInRange due to the following code

if (known.includes(i, j, k)) {
    if (toRemove.includes(i, j, k)) removalsFound = true
    return false
}
fenomas commented 2 years ago

Hmm, I can't reproduce this. Are you using the current #develop branch, and does it happen for you in the testbed?

I'm testing by: running npm test in the noa-examples repo, and then using the 'O' key shortcut to swap worlds. When I do this, the existing chunks get removed from the player position outwards, and then new chunks load in the same order.

MCArth commented 2 years ago

Yep!

If you change the interval for generating world chunks from 10 to 50 in worldgen.js and increase the chunkadddistance you will see that chunks that aren't the player's chunk are loaded first

    chunkAddDistance: [5, 5],     // [horiz, vert]

(in this code:

    // process the worldgen request queue:
    setInterval(function () {
        if (requestQueue.length === 0) return
        var req = requestQueue.shift()
        if (chunkIsStored(req.id)) {
            retrieveChunk(req.id, req.array)
        } else {
            generateChunk(req.array, req.x, req.y, req.z, req.worldName)
        }
        // pass the finished data back to the game engine
        noa.world.setChunkData(req.id, req.array)
    }, 50)

Took a video: https://streamable.com/totrqq

fenomas commented 2 years ago

Ah, I see what you mean. I think I know what's going on - when searching for old/new chunks it scans out by layers, splitting up the process over multiple ticks so as not to block rendering. I bet that it's skipping the nearest layer or two because they're still being removed, and then when the cleanup was finished it starts meshing wherever it happens to be in the scanning process.

Definitely fixable, but it's fidgety code so let me have a look!

fenomas commented 2 years ago

Hi, can you check this in current develop? I revisited the way queues are handled a fair bit. It now loads chunks more reliably, and I found a bug where it was basically waiting until the removal queue was empty before loading any new chunks.

Also, unrelated, you can now define a custom order for how you want chunks to be loaded:

noa.world.chunkSortingDistFn = (i, j, k) => {
    // i, j, k are a chunk's separation from the player's chunk
    // return a number that's lower for chunks to be loaded first
}

I don't use world switching very often, so it'd be useful if you can let me know if this works for you.

fenomas commented 2 years ago

Hi @MCArth, just wondering if you've checked if this resolved world loading for you? It seems sound for me so I'll probably do an engine release one of these days.

MCArth commented 2 years ago

Hi Andy, sorry for the delay - the git wrangling when I merge into my own fork is a bit of a pain - but I am getting quicker!

I've tested it out, it works well. I haven't found a need for chunkSortingDistFn so can't give feedback on that - though I imagine it could be useful for someone who has chunks that contain important structures in the world

Thanks for having a look into this.

fenomas commented 2 years ago

Thanks for checking!

The order sort function has a few use cases - one is if you have a strong preference between "load horizontally first so the player sees the distant horizon sooner" and "load locally first so the player can see things above and below sooner". The other is, in my game I do some costly init for each X/Z column, so it's beneficial to batch them together by column.

I'll push a release soon, thanks!