01-31-2023, 02:14 PM
Recently, I added a level editor to my game project. In this level editor, I included a "playhead" tool, similar to what you would see in a video or audio editing program - so when you hit "play level" from the editor, with the playhead set, it would invisibly simulate the level up to that point as fast as possible, then you'd start playing from the playhead. This is a very useful thing in my level design process, since it means I don't have to watch minutes of level go by while I'm tweaking a later part.
However, I noticed the simulation was taking almost as long as actually playing the level up to where the playhead was. I ran a visible frame counter during the simulation and discovered it was only about 30% faster.
At the time I chalked this up to poor design on my part, maybe some inefficiencies in my code, and looked into it a little bit, but didn't find anything conclusive. To boost the simulation speed a bit, I copied the main gameplay loop and stripped it down to bare essentials, only what the simulation would need. To my surprise, it was now running EVEN SLOWER than before.
So I started commenting out pieces of the simulation loop, to see how it affected the time. Nothing made a difference - the position updates, the level script check, collision detection, background scrolling, very mysterious. So I took the loop, with everything but the actual frame counter commented out, and still, no difference, it was running just as slowly. Good news - those chunks of code are actually super fast! But how could the frame counter be causing so much slowdown that it was running under 60 loops per second? It's just a PRINT statement with a variable plugged in.
Well, when you've eliminated every other possibility, whatever remains, however unlikely, must be true. Below the PRINT statement was a SUB call: display_screen. Here it is:
I had recently added a screen scaling option to the option menu. Players can choose to run the game at x1, x2, or x3; since it's a pixelart game, the window size options are doubling or tripling the pixels. The x1 resolution is 640x360 (16:9 ratio), so x2 is 1280x720 and x3 is 1920x1080.
So it's that PUTIMAGE statement; more specifically, the scaling. Apparently using PUTIMAGE, in any situation where the output size and input size are different, is a massive resource hog. I was running the playhead 1643 frames into the level in each case... at x2 scaling, this took 37.14 seconds, which is about 44 frames per second. Running x1 scaling took 4.56 seconds, or 360 fps. Removing the multiplier from the PUTIMAGE statement, curiously, took 7.8 seconds, or 210 fps. And finally, commenting out the display_screen call entirely (including the DISPLAY statement) caused it to take 1.42 seconds, which is 1157 fps.
I have some workarounds in mind, such as pre-processing all source images into three size versions, and toggling between them based on the player's chosen size option. But that's a lot of work, so first I have to know, is there a way to speed this up without such a huge overhaul to the code? I've seen games do this exact kind of window size option before, although they weren't made in QB64.
However, I noticed the simulation was taking almost as long as actually playing the level up to where the playhead was. I ran a visible frame counter during the simulation and discovered it was only about 30% faster.
At the time I chalked this up to poor design on my part, maybe some inefficiencies in my code, and looked into it a little bit, but didn't find anything conclusive. To boost the simulation speed a bit, I copied the main gameplay loop and stripped it down to bare essentials, only what the simulation would need. To my surprise, it was now running EVEN SLOWER than before.
So I started commenting out pieces of the simulation loop, to see how it affected the time. Nothing made a difference - the position updates, the level script check, collision detection, background scrolling, very mysterious. So I took the loop, with everything but the actual frame counter commented out, and still, no difference, it was running just as slowly. Good news - those chunks of code are actually super fast! But how could the frame counter be causing so much slowdown that it was running under 60 loops per second? It's just a PRINT statement with a variable plugged in.
Well, when you've eliminated every other possibility, whatever remains, however unlikely, must be true. Below the PRINT statement was a SUB call: display_screen. Here it is:
Code: (Select All)
sub display_screen
putimage(0, 0)-((screenw * option_window_size) - 1, (screenh * option_window_size) - 1), full_screen, scaled_screen(option_window_size), (0, 0)-(screenw - 1, screenh - 1)
display
end sub
I had recently added a screen scaling option to the option menu. Players can choose to run the game at x1, x2, or x3; since it's a pixelart game, the window size options are doubling or tripling the pixels. The x1 resolution is 640x360 (16:9 ratio), so x2 is 1280x720 and x3 is 1920x1080.
So it's that PUTIMAGE statement; more specifically, the scaling. Apparently using PUTIMAGE, in any situation where the output size and input size are different, is a massive resource hog. I was running the playhead 1643 frames into the level in each case... at x2 scaling, this took 37.14 seconds, which is about 44 frames per second. Running x1 scaling took 4.56 seconds, or 360 fps. Removing the multiplier from the PUTIMAGE statement, curiously, took 7.8 seconds, or 210 fps. And finally, commenting out the display_screen call entirely (including the DISPLAY statement) caused it to take 1.42 seconds, which is 1157 fps.
I have some workarounds in mind, such as pre-processing all source images into three size versions, and toggling between them based on the player's chosen size option. But that's a lot of work, so first I have to know, is there a way to speed this up without such a huge overhaul to the code? I've seen games do this exact kind of window size option before, although they weren't made in QB64.