✅ World generation optimization
Hey guys, i'm having issues with world generation for my game, do you guys know how i could optimize it? it takes 15s for the window to load rn
61 Replies
tried to shorten the time of each individual image to load, but it changed nothing
anyone? please i need help
I have a really hard time understanding why half this code is the way it is
Dispatchers? 2 second timers? Why the hell do these things exist in a world generator?
I would expect an asynchronous method that builds the world in the background. You'd then delegate the result to the UI thread using a Dispatcher which does STRICTLY the visualization
What is this anyway? Please tell me it's not Winforms?
Dispatcher is for it to be async ig, and the timer is for tick
wpf
this is the game

Dispatchers and timers are not async
A Dispatcher just delegates stuff to the UI thread
oh, ok
how should i do it then
Make it async, use an async Task
thats why it takes so much for the window to load...
Just make sure it doesn't involve UI modification because WPF doesn't like that
okey, could u send me a example/tutorial to Task?
You have to use Dispatcher when you want to delegate calls to the UI thread for modifying the UI after the task ran
whats the async alternative to timer?
There's an async timer variant
dont understand
You don't really need to modify the timer though
Here's an explanation about async https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
Asynchronous programming - C#
An overview of the C# language support for asynchronous programming using async, await, Task, and Task
You can also use a thread, which kind of works the same but is more lower level. I'd use
async Task
thoughi dont understand ;-;
async in
js
for example is so much simplerIt's not hard
Idk what you want me to say
Even in javascript you have to explicitly define an async function and await it
That's also the case here, but you can work with a return type called
Task
which basically defines the internal workings on asynchronous programming
That's nothing you have to worry about, you just have to understand that Task
means asychronous. This means you can switch between asynchronous and synchronous in C#like i get the await part, but what if i just want to make it not wait to generate the world?
Then call the method but don't await it
Note the method executed regardless if you wait for it
A good idea is to wrap it in
Task.Run
, due to the way asynchronous works with threads
It doesn't always run asynchronous because it efficiently determines if it has to
Task.Run
ensures it guarantees that it's run in the background
like this?
Just
GenerateWorld()
. You don't need the return type
But it does absolutely nothing because GenerateWorld
is not asynchronous
You have to understand you can't just wrap this in an asynchronous method and get an improvement. Your code is not set up in a way where it will work properly for it
A big ass Dispatcher method prevents it from reaching half the code because it's all moved to the UI thread
You have to write code that generates the world asynchronously in the background in full, and when it's done it has to tell the UI to update
Alternatively, even better, is to generate parts and move each resulting part to the UI thread for it to be stored and rendered
I can't/won't help you with it because I literally can't wrap my head around half this code and what you try to accomplish
You should really look into asynchronous programming thook, so like push all the tiles to a list and move the list to a dispatcher?
@Foosed am i doing it right?:
No, you should really learn asynchronous programming using a more basic example
But maybe take a step back. Figure out what specifically takes an ass load of time to happen in your code. Maybe it's simpler to fix when it comes to speeding things up
i tried, but i just dont see how it translates to my project
I dont think the answer would be to make it async here, atleast not at first. I think theres some issues with the loops in the first place, depending on the size of WorldHeight and WorldWidth.
In the first loop of GenerateWorld you loop over every tile (WorldHeight * WorldWidth iterations) and create a new tile. Inside the Tile constructor, you then loop over the Display.Children twice to find a tile with the same type and null source. It would be much faster to initialize your image sources first, and then look them up in a dictionary.
Then in the MaxTrees and MaxMushrooms loops of GenerateWorld you iterate and over all the tiles on every iteration, but never change any property on the tile in either of the loops. Pull these lines out of the for loops:
Also in the MaxTrees and MaxMushrooms loops, you perform a check on the type of the bgTile every loop, but the type can never be DestructableTile because you havent added any of type GrassA at any point before that. I think you can remove that check but double check before committing to that.
thanks man, when it comes to the tile images ill try it, but with the other stuff i already coded a solution and got the world to generate in about 3s
this is my new
GenerateWorld()
functionEh, you know the whole async thing was something I mentioned because it does generally improve the application by avoiding blocking calls if done right
But if world generationt akes a long time that's not something asynchronous programming just fixes
3 seconds is still much, but it sounds like a reasonable number when crunching through data to generate it
Scriptbin - Share Your Code Easily
Use Scriptbin to share your code with others quickly and easily.
sure
Scriptbin - Share Your Code Easily
Use Scriptbin to share your code with others quickly and easily.
Took 1312ms to generate world damn
added a Stopwatch to measure
started it in mainwindow() and stopped at end of worlgen
Considering there are only 2160 tiles (I think) 3s is a lot of time for generating the world. I think the change to the time images will help, but probably not solve the speed issue.
AddTilesToUIThread wont really help much I dont think because GenerateWorld is already running on the UI thread, so they will be dispatched instantly. A quick and dirty way to get GenerateWorld to run in the background would be to change
to
Id recommend you split GenerateWorld up into multiple methods too, theres a lot going on in there.
.
without AddTilesToUIThread it took a lot more time for the game to load
this is good advice, already thinking of how to do so
Looking over the code I cant see anywhere that jumps out as a performance bottleneck, not at 2k items in the collection.
Worst case this line will have to loop through all 2k tiles for every tile you attempt to place a mushroom or tree at
If you instead looped over a shuffled list of tiles instead of their positions, you wouldnt have to do a lookup at all
Not what I expected but sure. Try the Task.Run thing anyway, the window should pop up almost immediately while GenerateWorld runs in the background
the window didnt load and returned errors
Youll have to change all your Display.Children.Add to call AddTilesToUIThread though
lines 139, 140 & 147 in the scriptbin you sent
right
i changed:
The .Any will still loop over the children until it matches, so you have a loop within a loop of the same collection making it O(n^2) rather than O(n)
but again, at 2k elements total the issue is likely somewhere else
so how do i optimize it
Can you share the results and also where specifically bottlenecks are?
no bottlenecks rn i think, current ms is
Took 1122ms to generate world
current version of code after some changes:
https://scriptbin.xyz/zuqubejewe.cs
Scriptbin - Share Your Code Easily
Use Scriptbin to share your code with others quickly and easily.
1kms to generate 2k tiles feels quite high to me. If youre ok with that though then is there still an issue you need help with?
maybe the bottleneck is image generation?
Wont know without benchmarking.
how to make it faster?
Try putting stopwatch intervals in the code to see when it jumps quite high
The total is less relevant here
Like I said, loop over the tiles directly rather than their positions
But you need to use the stopwatch to work out what is taking the longest, I dont see anything off the top of my head that jumps out as an issue
best way to solve performance issues is to measure, measure, measure
WorldHeight: 34, WorldWidth: 60
Checkpoint: 922ms
Checkpoint: 1221ms
Checkpoint: 1228ms
Grass tiles generation complete.
Checkpoint: 1230ms
Checkpoint: 1231ms
Checkpoint: 1396ms
Tree and mushroom generation complete.
Took 1397ms to generate world
Looks like it takes a whole 922ms for the function to even get called
sure
Put a checkpoint before and after the
TileImages=
line
Might be just how long it takes to load the imagesLooks like theres a ~1s delay between the component being constructed and the Loaded event being triggered. Im not sure what would be causing that, but it might just be how long it takes for the framework to initialize
ig, is there a way to shorten that time?
No clue off the top of my head, but again would be a case of adding logs and timestamps to other areas of your application to see if theres anything else which is getting called during that period. Pretty sure that Loaded is one of the last events to get called, so the framework will have been doing a lot in the meantime. Also seeing as this is called MainWindow, im guessing this is the window that shows up as the first thing after you started the application? Probably not surprising that it takes a second or two to warm up
where should i move it then?
this is the current way i call the function
the function gets called in 70-150ms
BlazeBin - ljcdcarukqbf
A tool for sharing your source code with the world!