Scrolling Game Development Kit Forum

SGDK Version 2 => Help, Errors, FAQ => Topic started by: Vincent on 2010-12-12, 09:45:40 AM

Title: Bug and improvement report
Post by: Vincent on 2010-12-12, 09:45:40 AM
Hi! :)

That thread concerning load problems for sprites made me realized that there's a couple of things in SGDK2 that could be improved I think.  I worked with recent versions of SGDK2 for 2 years, so here are the things that annoy me a little:

1- Sorting dropdown lists:
I have a lot of sprites, maps, counters, you name it in my project.  I think it was a huge improvement to have all of them sorted in alphabetical order in the project tree.  It would be great if these elements were sorted in the same fashion in dropdown lists for rules, tileset counters, etc.  My dropdown lists are huge, and I use the letters to get to the right element faster, but since they are not sorted, it's still long to select the right one.

2- Frameset "del" button:
When I create a new frameset, the first thing I do is to give it a name.  So I focus the text field, select the "New " part of "New Frameset" and press "del" button.  And then "boing!" I get an error because no frames are selected.  It would be nice if SGDK2 noticed that I focused the name textbox and not a frame in the frame set to delete.

3- Global Counters can't be lower than 0:
Sometimes, I use global counters to store values of a sprite because I want other sprites to access a single sprite value.  For example, the sprites dx or dy.  If the sprite is going up or left, I can't store the value, because counters don't go below 0.  Since sprites' counter can go below 0 and you don't get an error for storing one property value in another property, it can be confusing.

4- Global counters and sprite counters are integer:
Many values in SGDK2 are floats rather than ints.  When I want to store speed, exact location, etc.  I have to round or truncate the decimal to get them stored into a counter.  I lose precision.  It would be nice if counters (both global and sprites) could be stored as floats.

5- Accessing sprites values from another sprite:
It would be great if rules within a sprite could allow access to values of another sprite in the same map.  Not easy to do I guess, since at design time, sprite rules don't care about which map they're in, so there's no real clean way to access another sprite.  But maybe there could be a "layer" or "map" property into the sprite that would be set to the current "map" or "layer" the sprite is in when activated and then, using this in the sprite rules, a rule could access the property, get the right sprite in the list of sprites of the layer and then reach the desired property.  Maybe a difficult feature to implement, but it would be so much easier to make sprites interact.  Of course, there must be a lot of exception handling done in this not to crash the game at runtime...

6- Graphic sheet design size:
I know I have big images in my game, but I don't really understand why the graphic sheet prevents to zoom in when the borders of the image can't fit into my screen.  It makes it real hard to edit images within SGDK2.  On a small laptop screen, it's not pleasant to modify big images if you can't zoom in.

7- Graphic sheet movement of windows:
The graphic sheet consists of 4 sub-windows or palettes: 1 the image you work on, 2 image at normal size, color palette and the graphic sheet navigator.  When I load a graphic sheet with big cells, palettes can get out the screen.  In relation with report #6  ;), i have to reduce the zoom, and it seems there is code to replace the palettes in a logical way.  But sometimes, the "replace windows" code gets a little erratic and when I want to replace them in a way that suits me best, they go crazy and move out where I don't want them to be.  It would be nice if this replace windows code was optional.  Maybe a checkbox in the palette window to turn it off.  That would be great! :)

8- Output text that is not debug nor message:
In my game, I want to show text in the menu and some places.  There are statistics to show.  I can find only 2 ways to output text in SGDK2: via the showmessage (but you get a frame around it and can't really place the text where you want) or via logdebuglabel (but it won't show in release mode and ignores spaces).  It would be nice to have a tool to write frameless text, with spaces where you want on the screen. :)

9- Global counter with dynamic max value:
It would be nice to be able to set the max value of a counter at runtime.  I did it in my game (it's a partial class after all), but maybe other people would find this interesting.  But why would they want to change the max value of a counter?  Well, I use it to show a lifebar.  The max amount of life a character has can change during the game.  If I set the value to max possible value at the beginning, it can be confusing for the player (why am I always damaged) or give him unwanted clues (I'm still missing life upgrades).

Hum...  I can't think of anything else at the moment.

Like I said, these things annoy me, but they aren't showstoppers at all.  I think it would make SGDK2 easier and a better experience. :)

Any one of these upgrades would be a great asset I think. :)

What do you think?

Thanks a lot!
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2010-12-12, 10:35:26 AM
It's good to hear suggestions from someone who has probably used SGDK2 even more than myself, and definitely used it more rigorously.  My little projects don't really test the limits of SGDK2's capabilities and usability as much as yours does.  Let me try to respond to a few items that may help you even without any changes to SGDK2:

5. There is a function "SelectTargetSprite" that you can use to store a global reference to a sprite as the "selected sprite".  It is based on a sprite collection and an index.  It's handy if you just collided with a sprite and want to interact with it.  I am actually working with it just this morning.  But I discovered there are not a lot of functions that work with the selected sprite.  Mostly just SetTargetParameter.  It's a powerful function, but doesn't provide any means to retrieve information about the sprite.  So I added this to a partial class for GeneralRules this morning (only for my project):
Code: [Select]
   /// <summary>
   /// Determine if the selected target sprite (selected with SelectTargetSprite)
   /// is of the specified type.
   /// </summary>
   /// <returns>
   /// True if the selected target is of the specified type, false otherwise.
   /// </returns>
   [Description("Determines if the sprite selected with SelectTargetSprite is of the specified type.")]
   public bool IsTargetSpriteType([Editor("SpriteDefinition", "UITypeEditor")] System.Type SpriteDefinition)
   {
      return SpriteDefinition.IsInstanceOfType(selectedTarget);
   }

   /// <summary>
   /// Deactivate the sprite selected with SelectTargetSprite
   /// </summary>
   [Description("Deactivate the sprite selected with SelectTargetSprite")]
   public void DeactivateTargetSprite()
   {
      if (selectedTarget != null)
         selectedTarget.Deactivate();
   }
Of course it would also be possible, and probably useful, to implement GetTargetParameter, but I have not done that yet.

6. The integrated graphics editor was not intended for heavy duty or large-scale graphics work.  It's mainly intended to be "good enough" for simple graphics in simple games, and to import more complex graphics from external sources.  Of course it would be nice if the graphics editor got even better, but for now, just be aware that it is possible to import and export graphics from external sources.

8. Have you seen the function for Plans called "DrawCounterWithLabel"?  It's probably very similar to what you want if not exactly what you want.  I think the reason it is a plan function and not a sprite function is because the plan is used to determine where the text is drawn (so you don't have to provide x and y coordinates manually).
Title: Re: Bug and improvement report
Post by: Vincent on 2010-12-12, 10:53:24 AM
I'm glad you like to hear suggestions. :)

5. I already made something similar too.  Yeah, GetParameter would be great.

6. Yeah, that's what I do most of the time: import and export graphics and work with photoshop.  But sometimes, I realize I made a little mistake (one or two pixels) and it's troublesome to reexport, load PS, change, save and reimport.  I think the graphic editor is great as it is!  There's not much missing for it too be a wonderful stand alone graphic tool.  Anyway, I guess it's a lot of work to modify and it doesn't change the end result for a game.  The idea is to have a game making software, not a graphic making software I get that. :)

8.  Ohh!!!  I have to take a look at that!  It seems like exactly what I need.

What about the other issues?  You didn't comment because you have no opinion on them?

Thanks bluemonkmn! :)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2010-12-12, 11:14:39 AM
The other issues are something I will have to investigate and possibly work on in my spare time.  I would look at them today, but I'm working on my game again so I'm focusing on things that affect my game :).
Title: Re: Bug and improvement report
Post by: Vincent on 2010-12-12, 11:20:38 AM
Great!

Then I'm going back to work on my game too! :)
Title: Re: Bug and improvement report
Post by: Vincent on 2010-12-20, 03:35:00 PM
Hi guys!

I had some other ideas:
1- It would be nice if there was a way to change the default icon of the program.  I can do it by compiling the project, open it in Visual Studio and set the icon in the project's properties, but for those who do not have VS, it would be easier to allow it right in SGDK2.

2- It would be great to link Sprite Categories and Sprite Definitions in the IDE.  For example, you open the sprite category window with all the sprites in the project with the checkboxes and then you can click on the box to add the sprite definition to the category (current behavior until now).  It would be a nice feature to allow double-click on the sprite definition and it would open the sprite definition window.  Not very useful for small projects I guess, but very useful for one such as mine. :)

That's it for now. :)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2010-12-21, 04:43:41 AM
You mean double-click on a sprite definition from the sprite categories window to open the sprite definition window?  Why is that so useful?  Isn't it the same list as you see in the project tree?
Title: Re: Bug and improvement report
Post by: Vincent on 2010-12-21, 07:55:36 AM
Well, you see, there are almost 240 different sprite definitions in my project.  I never expected my project to grow so much, so I didn't normalize the names of my sprites.  But I have much less sprite categories.  Let's say I think of a modification I want to apply to all the sprites in the "projectileSpriteCategory" or the "EnemySpriteCategoery", I have to scroll through all the list of sprites and hope not to forget one of them in the list (which can happen because some of the names aren't so great). 

Since, all sprites already are in the correct sprite category, it would be faster for me to open the sprite category, check the box "only show checked items" and double click on each sprite in the category.  This way I know I won't forget any.  Of course, that's almost what I do, but instead of a double click in the sprite category, I use the category as a reference and go through all the list of sprites to find the right ones.

So, it's not a super-must-have feature, but it would be a nice shortcut. :)
Title: Re: Bug and improvement report
Post by: durnurd on 2010-12-24, 12:27:33 AM
My thinking in places like this is: Currently, X does nothing?  What would I expect to happen if I do X?  Would I expect it to do something in a standard application?  If so, make it do something, otherwise, not.  In this case, double-clicking on an item in a list makes sense that it would open that item.
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-16, 03:23:55 PM
I fixed 1, 2 and 3 locally.  On #4, can't you store things like that in other variables instead of counters?  For example, add "static float tempFloat;" to SpriteBase (or a partial class of SpriteBase) and then you can always put decimal values in there?
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-16, 05:06:24 PM
Thanks bluemonkmn!  That will be very helpful.

Yes, I could create float fields in SpriteBase partial class.  That will do nicely too. :)

When will you release these changes?

Thanks again! 
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-17, 07:51:47 AM
When I am done making all the changes that I think are appropriate from your list and Durnurd's (the changes that don't require too much fundamental change), I'll probably do another release then.  But I could release earlier or compile a build just for you if you're eager for these changes.  Maybe it'll be a couple weeks before the next release.
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-17, 07:54:35 AM
Oh, there's no rush, I can wait a couple of weeks.  I was just curious. :)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-17, 10:47:59 AM
I can't find durnurd's list... did I already do everything that durnurd suggested... or is there some list I missed and forgot?  I thought there was still some list I had to work on from durnurd.

Also, this morning I did some significant work on #5, and checked in the changes to SVN source control (in case anybody wants to compile it and use it before I release).  Here's the list of changes since the last check-in:
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-17, 10:50:52 AM
Wow!  Great work!  All those "For" functions will be very helpful. :)
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-20, 11:30:01 AM
I think I found a glitch concerning LimitFrameRate in SGDK2 version 2.1.5.  :o

When I use LimitFrameRate(60) on my computer at home (5+ years old CPU) my fps counter shows 60 fps more or less steady.

When I run it on my computer at work (brand new desktop, very powerful) if I remove the LimitFrameRate, it runs at about 1500 fps.  But when I run it with a LimitFrameRate(60) rule it runs at a very very steady 50 fps.  Never goes higher or lower...  The game is quite slower because of that.  I looked at the LimitFrameRate code, but the maths there elude me...  Can someone explain the math here?  The frequency with the timestamps (standing for frames) are confusing me.

I don't see a good reason for the game to run slower on the high end computer, I think there might be a glitch in this function.

Thanks! :)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-21, 06:20:24 AM
Can you reproduce the problem in a simpler project?
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-21, 11:18:38 AM
Sure thing!

See the attached zip file for a very light fps test project.  It behaves exactly like my full game.  It runs at 50 fps at work on the powerful computer and at 60 fps at home on my old computer.

Also, it would be very useful to set a game fps in the project properties rather than in a plan rule or sprite rule... But I like that fact that we can change the fps at runtime also so keeping this woul be good too.

I hope it helps. :)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-22, 07:18:58 AM
I don't know that I will be able to find a system that can reproduce the problem here.  Oh, my wife says her project had the same problem on my laptop, so maybe I can look into it too when she's done playing games on the laptop :).  Meanwhile here are some ideas for debugging on the environment where you can reproduce it.  I think it will help to use the simpler project.

Inside the LimitFrameRate function it retrieves a "freq" value and calculates a "sleepTime" value.  Can you use LogDebugValue to display the values of these variables (only display sleepTime when it's not equal to zero) on the problem system?
The problem is that "freq" is a "long" and LogDebugValue expects an "int".  I think you can just change the definition of LogDebugValue to accept a "long" instead... it's just a couple functions down from LimitFrameRate.
Perhaps the behavior of the "Sleep" function is different on the problem system's OS.  The other thing to try (as a test only) would be to delete or comment out the "System.Threading.Thread.Sleep" line.  This would cause the game to take 100% of the cpu instead of allowing the OS to use the CPU that the game doesn't need while waiting for the next frame.  If that makes it work better, maybe it could be fixed by adding a condition like:
Code: [Select]
if (sleepTime > 2) System.Threading.Thread.Sleep(sleepTime - 2);

Strange, I thought the LimitFrameRate function was more complicated... I thought it had some complicated problems that I fixed... I wonder if I put the wrong version in.
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-24, 07:33:30 AM
Hi bluemonkmn!

I made the adjustments in the test program.  I tested it and got these results:

At home where it works:
freq: 1869890000
sleeptime: around 15 and 16

At work (where it fails):
freq: 2857783
sleeptime: jumps between 11-12 and 15-16

Is that helpful?
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-25, 03:38:10 AM
Sorry, I keep forgetting to debug it on my laptop here.  Strange that freq is so much lower at work.  But still it should be good enough.  It seems to be calculating about the same value.  But maybe the sleep function isn't performing as well.  Maybe you should try the other test -- what happens if you comment out or delete the "Sleep" line?  If that works, what happens if you replace the "sleep" line with by suggested code?
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-25, 11:34:34 AM
In the test project, if I remove "System.Threading.Thread.Sleep(sleepTime);" it runs at 60 fps!  If I replace the same line with the code you suggested, it runs at 50 fps...

If tried it in my game and I get the same results.

So it would seem removing the sleep line solves the problem!

Thanks bluemonkmn! :)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-25, 05:58:23 PM
That's not a good solution because it causes the game to use 100% of the CPU.  Sleep is required to give up some CPU time.
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-26, 07:23:43 AM
Hum...  Then what should be done?  ???
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-26, 03:17:19 PM
I tried reproducing the problem on my laptop, but I couldn't get the laptop to behave as well as your experiment.  Deleting the "Sleep" line did not make the game run at the correct FPS.  So I guess it will be back to you to help figure this out by testing my ideas.  My first idea is to compare your current solution (see that it is using 100% CPU, or maybe 50% CPU if you have a dual core processor) to one where you use Sleep(0) instead of Sleep(sleepTime).  If that doesn't work try only using sleep(0) when sleepTime is greater than 10. See how that affects the frame rate and the CPU usage.  My next idea is to change both instances of the number "2" in my suggested alternative to a larger number.  Since sleepTime was always a number greater than 10, you could try using something as high as 10 instead of 2, and still expect some extra CPU time back.  Translating from code to English, that line would then be saying something like this:
"If the number of milliseconds before the end of this frame's time is greater than 2, then wait for the calculated number of milliseconds minus 2.".  The intention is to sleep less if the Sleep function is having trouble returning control at the right time.  We calculated that we should sleep for 11 milliseconds after this frame is done processing in order to wait for achieving 60 FPS, but apparently when we tell the system to sleep for 11 milliseconds (or even 9 milliseconds, apparently) it waits longer.

So I don't know if you have any other way to verify that or any other ideas to test.  I know sleep(0) is supposedly a special value that yields the CPU only for the remainder of this process' time slice.  Not entirely sure what that means, but that's why I suggested that as a test too.

Aha -- it looks like there are some answers here: http://social.msdn.microsoft.com/Forums/en/clr/thread/facc2b57-9a27-4049-bb32-ef093fbf4c29 (http://social.msdn.microsoft.com/Forums/en/clr/thread/facc2b57-9a27-4049-bb32-ef093fbf4c29).  I will have to read that article and see if there is another solution.

Edit: I wonder if Thread.SpinWait would give CPU back to the system.  It's hard to calculate what parameter value to pass to spinwait because apparently it depends on the speed of the processor.  But if that does yield the CPU, maybe we could just use SpinWait(1000) and see if that is precise enough.  (Surely 1000 CPU cycles is less than 1 millisecond... but I'm not sure if SpinWait counts CPU cycles.)

Edit 2: SpinWait does not return any CPU time.  Maybe simply removing the Sleep is the best solution.  People generally do not do other things at the same time they play games anyway.  And if you de-activate the window do do something else, some CPU time is returned while the game is paused anyway.  And most computers have dual core CPUs anyway so it will only be taking 1 core, and the other is available for other tasks.  I guess it's alright to simply remove Sleep if you want reliable frame rates.
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-27, 11:24:33 AM
Hi bluemonkmn!

I tried many of your ideas, but there's only one thing that makes the fps go back to 60 (where it should be).  If I always do Sleep(0), then it works.  I guess it seems pretty useless but since you said it does free some marginal CPU time, I think I'm going to let it in there.  I'm going to delete the whole sleeptime calculation, it's not useful anymore.

Thanks again! :)
Title: Re: Bug and improvement report
Post by: durnurd on 2011-01-27, 08:13:57 PM
It seems odd that the framerate stays at exactly 60 even when you're not limiting the framerate.  Maybe the framerate is otherwise limited via a vsync setting on your graphics card or something, in which case extra sleeping could cause the decreased framerate.  I wonder if there's a way to determine if the graphics card or something else is forcing a vsync at runtime and not manually limit the framerate in that case (unless you want to decrease the framerate, but then how would we know what number to use to decrease it?)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-28, 06:18:21 AM
I don't think anybody was running without LimitFrameRate.  We were just running without the Sleep in LimitFrameRate.
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-28, 07:38:15 AM
Just like bluemonkmn said, I'm always calling LimitFrameRate,  I simply eliminated the sleeptime calculation.  If I don't call LimitFrameRate, it goes up near 1500 fps.
Title: Re: Bug and improvement report
Post by: durnurd on 2011-01-28, 12:25:05 PM
Wait, so how does it stay at 60 fps if you only call sleep(0)?
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-28, 12:45:53 PM
Beats me!  But it does.
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-28, 02:42:23 PM
Wait, so how does it stay at 60 fps if you only call sleep(0)?

Because it does it over and over until the stopwatch says that it's time to go to the next frame.  The hope is that instead of simply looping and checking, adding a call to Sleep(0) inside the loop would give a little more time back to the CPU.
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-29, 09:33:12 AM
Vincent, can you test this fix for me and tell me if it limits the frame rate better?  I tried to make it use sleep to sleep for the correct time to average out correctly over 5 frames instead of just since the last frame.  Here are the changes in GeneralRules.cs:

Line 18 had a declaration for previousFrame.  Change it to this:

Code: [Select]
   protected static long[] previousFrames = new long[] { 0, 0, 0, 0, 0 };
   protected static int previousFrameIdx = 0;

Change the LimitFrameRate function to this:
Code: [Select]
   public virtual void LimitFrameRate(int fps)
   {
      long freq;
      long frame;
      freq = System.Diagnostics.Stopwatch.Frequency;
      frame = System.Diagnostics.Stopwatch.GetTimestamp();
      long oldFrameTime = previousFrames[previousFrameIdx];
      while ((frame - oldFrameTime) * fps < freq * previousFrames.Length)
      {
         int sleepTime = (int)((oldFrameTime * fps + freq * previousFrames.Length - frame * fps) * 1000 / (freq * fps));
         if (sleepTime > 0) System.Threading.Thread.Sleep(sleepTime);
         frame = System.Diagnostics.Stopwatch.GetTimestamp();
      }
      previousFrames[previousFrameIdx] = frame;
      previousFrameIdx = (previousFrameIdx + 1) % previousFrames.Length;
   }
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-29, 10:54:32 AM
Okay, I'll try this at work on monday. :)
Title: Re: Bug and improvement report
Post by: Vincent on 2011-01-31, 11:20:44 AM
Okay, with your test code, in my FpsTester program I get a frame rate that shifts between 56 and 58 fps (still requesting 60 fps).

I tried it in my game also, to see if get the same result, and I've got the same results but when I play the game, it looks like the rate isn't very fluid.  It's kinda stroboscopic a little.  It's a little jumpy for the eyes.  With sleep(0) it runs better.

Does that help?

Thanks! :)
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-01-31, 02:59:05 PM
After an experiment, I think sleep(0) is not really better than leaving sleep out completely.  It ends up taking all the CPU it can get on 1 thread.

My question now is this.  Is it better to have the original behavior where LimitFrameRate resulted in a frame rate lower than what you requested, or the new behavior where you get closer to the requested frame rate, but it's "kinda stroboscopic"?  Which behavior do you think SGDK2 should deliver by default?  Or should I have multiple LimitFrameRate functions:
1. LimitFrameRate (same behavior as now)
2. LimitFrameRateExact (same implementation without Sleep)
3. LimitFrameRateAvg (The new implementation from above that's "kinda stroboscopic")?

How does it behave if you change "protected static long[] previousFrames = new long[] { 0, 0, 0, 0, 0 };" to "protected static long[] previousFrames = new long[20]"?

Edit: Someone else from stackoverflow.com has been discussing the problem with me and has predicted this behavior.  Hopefully that person can also propose a better solution.  You can see the discussion at http://stackoverflow.com/questions/4837416/high-precision-sleep-or-how-to-yield-cpu-and-maintain-precise-frame-rate (http://stackoverflow.com/questions/4837416/high-precision-sleep-or-how-to-yield-cpu-and-maintain-precise-frame-rate)
Title: Re: Bug and improvement report
Post by: Vincent on 2011-02-01, 09:48:42 AM
I tried with the new array.  The FPS is around 60 fps this time, it is more precise.  But it is still very jumpy.  I move my character around and it's very bad.  He walks fast, stops, walks fast, stops again, continuously like this.  Very annoying, it ruins the whole game I think.

When it comes to fps, I don't like to compromise.  When I play a game, I make sure the graphics are not high enough to slow the game.  So, in this case, of course I would say drop the sleep method altogether.  The stroboscopic version is the definitely the worst.  I prefer the game to be a little slower but constant and fluid.

I think the best version if the sleepless one.  Yeah, it does drain all the CPU, but unfocus the game window and you're okay.  You won't get problems by changing computers too.

With the actual version, if someone develop a game on a computer where the lower fps problem occurs, he might boost the fps parameter in the LimitFrameRate call and then the game will become much too fast for other computers.  It's okay if the game is meant to created and played on the same computer, but otherwise it's risky.


The "stroboscopic version" that I would call the "unstable version", is just painful.  I would avoid it at all costs.

I don't know if providing different versions of the behavior is a good idea.  It should be explained what each version of the method implies.  I would definitely go with the sleepless one.

But then again, you have to take my word for it, since it would seem I'm the only one with this issue...   :P
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-02-01, 02:22:34 PM
I have another idea that will probably be better than the one above.  I think the stroboscopic effect happened because the first 5 frames were done instantly, then the next frame waits for 5 frames (or N frames) worth of time, and the following 4 frames run instantly again.  I was worried about this problem and tried to think about it without anything to write on, so I thought about it wrong, but now I see the problem.  Now I think it might work better to keep track of 2 things:
1. What was the timer on the first frame?
2. How many frames have passed since the first frame?

From that we can calculate whether we are ahead or behind the target FPS.  Now that I think about it, I think that's what the person from stackoverflow was suggesting.  But the word "decaying" threw me.  I think it makes sense now, though, to reset the "first frame" every once in a while in case something like a message box or switching to another window (pausing the game) causes one of these values to get out of sync.

I will try coming up with the code for that when I get home.
Title: Re: Bug and improvement report
Post by: bluemonkmn on 2011-02-01, 05:50:18 PM
OK, try this.

Replace the first block with:
Code: [Select]
  protected static long fpsStartTime;
   protected static long fpsFrameCount;

and the second block with:
Code: [Select]
  public virtual void LimitFrameRate(int fps)
   {
      long freq;
      long frame;
      freq = System.Diagnostics.Stopwatch.Frequency;
      frame = System.Diagnostics.Stopwatch.GetTimestamp();
      while ((frame - fpsStartTime) * fps < freq * fpsFrameCount)
      {
         int sleepTime = (int)((fpsStartTime * fps + freq * fpsFrameCount - frame * fps) * 1000 / (freq * fps));
         if (sleepTime > 0) System.Threading.Thread.Sleep(sleepTime);
         frame = System.Diagnostics.Stopwatch.GetTimestamp();
      }
      if ((++fpsFrameCount > fps) || (fpsStartTime == 0))
      {
         fpsFrameCount = 1;
         fpsStartTime = frame;
      }
   }

Edit: I just tried this one on my laptop where even sleep(0) didn't work (if I recall), and this was really solid.  The frame rate was almost always exactly what I requested.  Hopefully it works as well for you.
Title: Re: Bug and improvement report
Post by: Vincent on 2011-02-02, 11:47:24 AM
Wow, this one is very good!  Yup, you have a winner. :)