Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DarkBASIC Professional Discussion / Updating visuals while performing "intensive" operations

Author
Message
Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 8th Dec 2011 16:00
In most games there is some indicator given to the player that the game has not completely frozen up during process that make heavy or extended use of the CPU. A prime example would be load screens that have the word "Loading" fading in and out; if that ever stops fading for an extended period of time then the player will realize the game has frozen up.

How would I go about coding something like this? I looked into the "COROUTINE" commands that come with the Matrix1 dll and it might be what I'm looking for, but I don't have any experience using it.
Mobiius
Valued Member
23
Years of Service
User Offline
Joined: 27th Feb 2003
Location: The Cold North
Posted: 8th Dec 2011 16:10
I'd suggest using an animated gif or a video, and use the play animation commands. It uses a third party library to play the animation so will keep working when you load your game assets.

My signature is NOT a moderator plaything! Stop changing it!
Millenium7
21
Years of Service
User Offline
Joined: 13th Dec 2004
Location:
Posted: 8th Dec 2011 16:53 Edited at: 8th Dec 2011 17:06
I'm returning from a long absence of coding, but have recently started work on a new project. This is something I thought about, particularly in for things like AI where you don't want to the computer crawling to its knees if it's handling a large number of AI, or complex calculations, and instead you'd rather split the work load over several loops instead of all in 1

Here's how I worked it out (havn't coded it but it will work when written correctly)

The first step is defining a timeout value, you may need more if you are performing more than a single task in 1 go. Let's first concentrate on a singular task, loading multiple files. We want this to run, but have something like a flashing "Loading..." text, animation, whatever.

Now the simplest and most obvious way would be to simply make a list of all the files that needed to be loaded, and only load 1 file per game loop. But this is inefficient as you may have 300 files to load and this would require 300 frames. At 60fps that's a guaranteed minimum of 5 seconds to load, of course larger files will increase that time
So instead write something like...



I just quickly wrote this code and checked for syntax, I havn't actually compiled it but hopefully no bugs and it works as intended. If not there should be enough tags to work it out

Basically you define a requested framerate, 16fps should be enough for a loading screen. You can go higher or lower but the higher you longer it'll take to finish because there will be more 'sync' happening, creating extra time between the actual file loading

The idea is that the function will load as many objects as possible within that timeframe, then simply exit and continue on with the rest of your loop. Now on an awesome computer this could load in a single loop! that's a hell of a lot better than a mandatory 5 second wait, no matter how fast the computer is. And on slower computers it will take more loops, but at least they can see some progress happening, ideally at 16fps (minus overheads)

When executing again it will begin loading again, once again timing out after the defined value and continueing with the rest of the loop. Keep in mind that if there's a large file to be loaded which takes say 500ms, then it will still drop your framerate lower than 16 obviously. This is a fundamental design in the dbpro language and nothing you do will get around it.
Millenium7
21
Years of Service
User Offline
Joined: 13th Dec 2004
Location:
Posted: 8th Dec 2011 17:03
Now with AI for instance you might get a bit more complicated but the principle is the same, if you wanted to achieve uninterrupted play at 60fps, regardless of how many AI are in the game then you would set a 'master' timeout value to 16ms (maybe even a bit lower to account for overheads)
So that at the very most it won't take any more than 16ms to execute AI instructions, even if it's only 1 calculation per sync. Though on a faster computer it might manage to squeeze in many more calculations, resulting in smarter and more responsive AI, as they are able to 'think' more times per second the faster the CPU is

You would then determine so 'slave' timeout values, for instance while you don't want to take any more than 16ms to do AI calculations, you may want to ensure that no more than half of that time is taken up by Team1, half by Team2. And also half of that again goes to movement calculation, and half to shooting, so they don't only move around and never shoot on a slower computer. So set Team1MoveTimeout to 4ms, Team2MoveTimeout to 4ms, Team1ShootTimeout to 4ms and Team2ShootTimeout to 4ms.
This allows you to ensure that both movement and shooting for EACH team gets 'at least' 1 calculation to ensure fairness. One team might get more than the other if one is more complex than the other

write the function with many many many more checks in the actual function loop itself to see if any of the slave timeouts have been hit, and then move onto the next set of calculation. Again, it 'may' take more than the alloted time, resulting in a framerate lower than 60. Though its still more preferable to have slightly 'dumb' AI at 40fps than 'smart' AI at 2fps. Agreed?

Hopefully the above 2 posts have made some sense
Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 8th Dec 2011 17:32
Thanks for the quick replies!


@Mobiius

Wow, this is a really simple yet elegant solution. I can't think of any reason this wouldn't work for what I'm trying to do (since it's simply text fading in and out), though it probably wouldn't be an option if people were trying to display up to date information on the process (like a completion percentage). I'll definitely give this some more thought and try it out.


@Millenium7

This is a much more flexible solution, but sadly I'm not sure it would work for what I'm trying to do. The main place I'd use a system like this is loading/generating my worlds, and the data for those worlds is all saved on one or two files (all game assets will be loaded into memory when the game is started). For your solution I would need a way to store what position I stopped at in the specific file, and then be able to pick up loading at that position once I reentered the second function. This might be possible with the DATAFILE POSITION/SET DATAFILE POSITION commands that come with the Matrix1 dll, but I don't think so since it would try to load the data into the array as if it were an empty array.

Might as well give co-routines a try since they should be able to pause whatever action is taking place and resume it automatically. I'll report back what I find out
Mobiius
Valued Member
23
Years of Service
User Offline
Joined: 27th Feb 2003
Location: The Cold North
Posted: 8th Dec 2011 19:59
Millenium7's method will work if you handle all the loading with a stream reader style streaming media loader.
For load object/load image and things, it won't be as elegant.

Granted, my suggestion isn't perfect, but you could combine it with the loading code occasionally updating the screen with the loading percentage if you really want to. (Most games I play now don't have a loading percentage, only the loading message)

My signature is NOT a moderator plaything! Stop changing it!
Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 8th Dec 2011 20:34 Edited at: 8th Dec 2011 20:36
Well, I've been playing around with co-routines and I'm pretty confident that they'll work for what I need, if I can figure out a solution to the following problem.

I'm currently trying to use the co-routines for world generation, which is all done procedurally before the player enters the map. It's currently set up as a list of steps that take place one after another.


As Millenium7 showed in their example, you need to update the screen at regular intervals, and the best way to do this is by measuring the time that's elapsed. I thought this would be easy enough to implement with a WHILE/ENDWHILE loop with the program leaving the co-routine once that loop was exited.


After getting that all in place I realized this won't work at all. The reason is that when I reenter the co-routine it's not going to start back where the program was during the WHILE/ENDWHILE, it'll start back up with whatever comes after ENDWHILE (which happens to be the command to switch back to the main co-routine again; infinite loops anyone?).

It looks like I'd need an additional COROUTINE function that would activate specific co-routines while a set of parameters were being met; seems like something only IanM would be able to add to his dll. If anyone has ideas for other workarounds I'd be happy to hear them!
IanM
Retired Moderator
23
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 8th Dec 2011 22:59
Coroutines are indeed the easiest way to go about this.

For example:


Pro's are that there's no need to save state in global variables, no code needed to restore that state and no need to make your code more complex to handle stop/start of the process.

I've played around with this technique myself, and the steps I used were:
- Create a multi-limb plain and exclude each limb
- Load the terrain data into a bank
- Apply the terrain data to the plain in chunks of 'n' vertices, un-excluding the limb if it is complete
- Go back to the start and wait for another 'request' for a terrain object to be placed.

There would be a 'yield' between each step, and a yield between each chunk of vertices loaded and applied - if your chunk size coincides with the number of vertices per limb, you can have a limb appearing every frame if loading dynamically during your game.

I didn't link any of this to timing, but there's no reason that wouldn't work (maybe link it with a ticker):


Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 9th Dec 2011 01:27
@IanM

Was hoping I'd see you in this topic haha; thanks for your reply!

I think you're right, co-routines are probably the way to go. That said, I was hoping to avoid having timer checks sprinkled throughout the entire function. Doesn't really look like there's any way around it though. The main reason I wanted to avoid it is that each step of the terrain generator will take a different length of time, even though they have to work through the same number of tiles (Perlin Noise functions will take longer than simple multiplicative functions). I can't just break up every step with a timer check. Each step of the process has its own FOR/NEXT loop that goes through all the terrain tiles, so I guess I could just add a timer check like in your second code box within each of those.

Going to give that a try; will let you all know what comes of it.
Millenium7
21
Years of Service
User Offline
Joined: 13th Dec 2004
Location:
Posted: 9th Dec 2011 16:35 Edited at: 9th Dec 2011 16:37
I've never used coroutines myself so I may be missing something. But from what I understand it may be an easier implementation, but is less effective with the time used. Since you'll be running more loops than is necessary on faster computers, and have larger framerate variance with lots of smaller/faster files to load.

I believe my method is the most effective as it can potentially load everything in a single loop/execution, providing the computer is fast enough of course. The key is using repeat/until loops inside the function, rather than inside your main loop. The function will then process as much information as possible within its allocated time, rather than just a set number of actions.

edit: btw just using an animation won't work either. As it's tied to your program which is busy loading stuff. the animation will appear frozen as well. If DBP was multithreaded it would be possible to dedicate 1 core to the animation and light loading tasks, whilst the other goes flat out loading the heavier stuff. But alas it is not
Mobiius
Valued Member
23
Years of Service
User Offline
Joined: 27th Feb 2003
Location: The Cold North
Posted: 10th Dec 2011 10:09
Quote: "As it's tied to your program which is busy loading stuff. the animation will appear frozen as well"

Wrong.

Quote: "It uses a third party library to play the animation so will keep working when you load your game assets."


My signature is NOT a moderator plaything! Stop changing it!
Millenium7
21
Years of Service
User Offline
Joined: 13th Dec 2004
Location:
Posted: 10th Dec 2011 17:39
Quote: "Wrong."


Sorry to burst your bubble but unless you call the sync command, you won't get any visual update within your program. And it's not possible to execute a sync command whilst stuff is loading

The animation is still 'working', but without any visual update it's about as useful as a lightbulb without a power source
Mobiius
Valued Member
23
Years of Service
User Offline
Joined: 27th Feb 2003
Location: The Cold North
Posted: 10th Dec 2011 18:16
*sigh* It does work as I've done it before. It doesn't require the sync command as the drawing is being handled by a non DBPro plugin. It uses activemovie to play the animations, and hides the window.

My signature is NOT a moderator plaything! Stop changing it!
Green Gandalf
VIP Member
21
Years of Service
User Offline
Joined: 3rd Jan 2005
Playing: Malevolence:Sword of Ahkranox, Skyrim, Civ6.
Posted: 10th Dec 2011 19:07
Is there any guarantee that the DBPro loading part hasn't got stuck while the video or whatever is playing? The only way that I trust is to insert regular printout from within the code - if you don't see the printout then it's stuck at an earlier point or very slow.
Millenium7
21
Years of Service
User Offline
Joined: 13th Dec 2004
Location:
Posted: 11th Dec 2011 03:25 Edited at: 11th Dec 2011 03:25
Quote: "*sigh* It does work as I've done it before. It doesn't require the sync command as the drawing is being handled by a non DBPro plugin. It uses activemovie to play the animations, and hides the window."


please show a code example

The project i'm working on at the moment uses animations for the menu's. The animation freezes on the last frame until loading is complete

If you can show me otherwise please do, but in all the time i've ever used db/dbpro animations have never once updated without a sync call, unless I was in the ungodly realms of 'sync off'

And if you can't i'll show you why



place any video and call it animation.avi and run that code. Now what is the framerate on the playback? it's not smooth and is only updating once per second, because its relying on the sync command, until that sync command happens (once per second) it will not update the screen
And likewise, whilst loading a file, no sync is taking place, ergo no visual update on the animation
Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 11th Dec 2011 04:45 Edited at: 11th Dec 2011 04:48
@Millenium7

Quote: "I've never used coroutines myself so I may be missing something. But from what I understand it may be an easier implementation, but is less effective with the time used. Since you'll be running more loops than is necessary on faster computers, and have larger framerate variance with lots of smaller/faster files to load."


What I ended up doing was actually a mix of what you and IanM recommended; it ended up being very similar to Ian's second code box since I use his TICKER functions. As I said a bit earlier, every step of world generation goes through a FOR/NEXT loop across the entire world map. All I had to do was add a bit of code within each of those loops to check if a certain amount of time has elapsed. If it has then it leaves that co-routine, and then jumps right back in where it left off after the screen's been updated. The main difference is that I'm using the COROUTINE functions instead of a REPEAT/UNTIL loop. I really wish there was a COROUTINE function that had the functionality of a loop so it'd exit once a certain argument was true since it'd make the code *a lot* cleaner.

Thanks to everyone who replied with their input; I've got it working reasonably well for now!

Login to post a reply

Server time is: 2026-07-11 02:13:13
Your offset time is: 2026-07-11 02:13:13