I have a problem in my block world with my mesh optimization. I have optimized my custom mesh generation to not add vertices for block faces that are not visible. In fact this was already done for me in the cubes framework. However that framework did not remove chunk boundary vertices, and in a 16x256x16 size chunk that makes a BIG difference especially since I am loading a 10 chunk radius around the player.
So I added the code to remove chunk boundaries and it was a big deal because my game was unplayable without it. However, when breaking a block on a chunk boundary, it will show a hole in the world since those face vertices are not there on the neighboring chunk. Well, no problem I just mark that neighbor chunk as dirty. This mostly works except now there is a frame where the hole is still visible.
One key here is that I submit a Callable to a ScheduledThreadPoolExecutor for building my mesh. There are no priorities on this scheduling. I thought that it might be a timing issue where the neighbor chunk was getting rebuilt after the current chunk so I tested that theory by making my chunk loads be threaded but my updates run on the main thread. This was slow but it did fix the problem so itās certainly a timing issue. Itās like I need both meshes rebuilt in a threaded manner but added to the scene at the same time. Not sure if thatās possible.
Whew, hope you are still reading. Are there any suggestions about what I am doing here? The game is mostly smooth and runs at 60 fps but breaking those border blocks and seeing a flicker is pretty annoying and greatly affects gameplay in my opinion.
Well itās a lot of code but Iāll try to post the important parts. The pictures are impossible because the problem is only visible for a frame or 2.
Here is my BlockTerrainControl
@Override
protected void controlUpdate(final float lastTimePerFrame)
{
world.update(lastTimePerFrame);
world.calculateLight();
updateSpatial();
}
public void updateSpatial()
{
profiler.startSection("ChunkSpatial");
//Update meshes
for(BlockChunkControl chunk : chunks.values())
{
chunk.updateSpatial();
}
//Add chunks
Chunk addedChunk = world.chunkRenderQueue.poll();
if(addedChunk != null)//Game runs smoother when we load 1 chunk per frame for some reason
{
BlockChunkControl control = new BlockChunkControl(this, addedChunk);
this.spatial.addControl(control);
chunks.put(ChunkCoordIntPair.chunkXZ2Int(addedChunk.location.x, addedChunk.location.z), control);
}
//Remove chunks
Chunk removedChunk = world.chunkUnloadQueue.poll();
if(removedChunk != null)
{
long chunkKey = ChunkCoordIntPair.chunkXZ2Int(removedChunk.location.x, removedChunk.location.z);
BlockChunkControl control = chunks.get(chunkKey);
if(control != null)
{
control.detachNode();
chunks.remove(chunkKey);
}
}
profiler.endSection();
}
I think generating both chunks together is really the only way around this. You ājustā (I know not trivial) change the unit upon which your background threads are operating⦠instead of single chunks, potentially sets of chunks.
@pspeed is my architecture just wrong here? Do you not thread mesh building in Mythruna? It seems like minecraft and other games of this sort must do this.
Sure I thread mesh building. But the only way to avoid gaps in your chunks is to build them both at the same time. This is not mutually exclusive with threading.
That being said, I do use a priority queue for mine and give neighbors an earlier priority than the chunk Iām in. I canāt remember if I cycle all chunks below a certain priority before updating the scene, though. As I recall, the most jarring blinks are when a block is removed so generating the neighbors first fixes that one.
My architecture is slightly difficult to unravel for this question because itās complicated by separate relighting and mesh generation⦠not to mention that I have two completely different engines now and my memory gets muddy between the two without diving in.
Thanks. A priority queue seems easier to implement than trying to make sets of chunks that get updated. Plus it seems like it might seem laggy if I only update sets of chunks at a time.
Side question if you donāt mind. Do you do junit tests on your ābusiness logicā? Or does threading make that pretty much impossible in most cases?
Well, somehow you are submitting a callable to a thread pool. One way is a chunk per callable One way is array of chunks per callable.
Either way, you will have to have both chunks built before being display or you will see a gap. So if there is going to be lag there will be lag and your choice would be between lag and gaps. But really, if you are seeing lag then chunk generation is taking too long anyway.
I believe unit tests have limited utility in general and especially in game development.
Hereās the math I use:
unit tests are best for determining regressions during refactoring. Itās like 95% of their reason for existing.
unit tests will never catch all regressions.
testing the game itself will find most regressions.
you have to test the game anyway.
a significant portion of the time, unit test regressions turn out to just be problems with the unit tests, ie: more work to no good end.
without 100% code coverage the gap in (2) is so huge to make the whole process nearly irrelevant.
Most of the real bugs you will have trouble with will not be caught by (2) or (3)⦠the time writing unit tests could have been better spent testing/debugging those gaps.
ā¦though I suppose it depends on your definition of āunit testā. I come from some formal software dev environments so I use the real meaning. Some people use āunit testā to mean āautomated tests that run with my buildā. I would call those automated tests that run with my build⦠which I also donāt do that often but there is a lot of utility there.
The unit testing we have been doing at my day job over the last two years has reaped significant measurable benefits. Although a business app is much more unit testable than a game app because of it seems game apps depend a lot on graphics rendering. I have refactored my code to try and separate logic like lighting and physics and overall world model to prepare for unit testing in the future if needed.
Not to get on a soap box about unit testing butā¦
I have found that unit testing causes me to catch more bugs while developing, and without having to run the app
I agree that when unit tests break, itās likely the test not the code. Though not always
Anyway, I have discovered the game dev community has a negative skew on unit testing and I am finding out why with my experience. I was just curious about your opinion.
Some of it is down to development style. I tend to prefer highly iterative approaches where I can have something running, build it in steps, and run each step as I go. I find bugs quickly. I also shun debuggers (personally) because I feel they dull my ability to see the bugs in the code before I run it. (This is also why I can often spot bugs in other peoplesā code just by glancing through it.) Each personās experience will vary and the tools they need to make themselves productive will also vary⦠but often we get in a mindset that something is good without really think about the costs.
Unit tests make a lot of sense when:
-the code is predictable
-100% code coverage is achievable
-others will be using your code and depending on its behavior.
Else, if itās just you developing then there are other strategies that can be just as, if not more, effective in the long run. Game development tends to be signified by pivots and wholesale cutting⦠all of those unit tests would have been wasted time.
I have open source libraries with 100% unit test coverage. Iām not against it but itās a heavy axe to wield and not suitable for a lot of problems. Itās an extremely expensive practice as often unit test development time meets or exceeds the cost of actual code development. Sometimes itās worth it. Often not.
Like, if I make a mathd OSS library then Iād consider unit testing it (even if those tests are 90% ādid this field really get set when I set itā⦠grr.) but like the IsoSurface stuff would be hard to unit test in any meaningful way. As with most libraries higher than the absolute lowest level, some thoughtful integration tests would be waaay better.
Itās also possible that I have a tapestry of āstrange practicesā that shield me from some of the benefits of unit tests. In large teams following the ārational unified processā, it was a convenient way to keep entry-level devs busy and learning, though.