Smooth scrolling in 2D

BlitzPlus Forums/BlitzPlus Programming/Smooth scrolling in 2D

Foppy(Posted 2003) [#1]
Hello! I am trying to create a 2D racing game, in Blitz 2D. To get the idea of movement, it scrolls tiles across the screen from top to bottom. The problem is the scrolling is not really smooth. I know this is a well known problem, getting the frame rate right and the scrolling smooth without relying on some monitor refresh rate.

In a single-screen game I used the following solution to make sure a character moved say 40 times a second:

I know how long each frame should take: 1/40th of a second. So I measure how long it actually takes, and from these two figures I compute a correction_factor, to get from the actual to the desired duration.

Now, normally the player moves every single frame. So I would (theoretically) count the frames (adding 1 to this framecounter each frame), and the player would move if the count reaches 1 (and the framecounter is reset to 0). To get the correct animation speed however, I do not simply add 1 every frame, but instead I add 1.0 * correction_factor.

Notice that of course I am using floats here as otherwise the small differences would be lost. I also take care not to reset the frames counter to 0, but instead to frames_counter-1.0, so as to keep the remainder.

So I use this method in my racing game.

However... when I link my scroller's location to the location of the player (to get a scrolling screen when the player moves) it can be seen that movement is not really smooth. What happens is that most of the time, the player is "allowed" to move in every single frame, but sometimes, the player is not allowed to move (due to "correction", the player has to skip a turn) and this is seen in the scrolling as a slight delay.

Here's the code that says whether the player may move:

AItime = AItime + correct(g_fpsc,1.0)

If (AItime>=1.0) Then
					
   player_move()
					
   AItime = AItime - 1.0
					
   DebugLog(">>>>>>> ACTION!")
					
Else
				
   DebugLog("wait...")
				
End If


("AItime" counts the frames, "g_fpsc" is an object that computes the corrected value of the value that is passed in the function. So instead of counting 1 frame, the program counts, say, 0.9 frames if the game has to be slowed down.) I also print some debug info to show the effect:

>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
wait...
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
>>>>>>> ACTION!
wait...


And so on. Writing this all down it seems quite obvious that this would result in jerky scrolling, as whenever it says "wait" in the debuglog, at that moment the car does not move.

Does anyone have ideas or experience in creating smooth scrolling in Blitz 2D, or comments on what I am doing here?

The above method I got through advice in another post if I remember correctly, but it could be that I applied it differently.

A thing that I could try is to use the "correction" method to change the amount by which the car moves (also suggested earlier by others). Right now I use integer amounts, but changing this to floats I could apply the correction factor to the car's speed. Maybe that still results in jerky scrolling, as in the end, movement is by whole pixels, but maybe the problem will be smaller.


coffeedotbean(Posted 2003) [#2]
Try Delta Time, http://www.blitzcoder.com/cgi-bin/articles/show_article.pl?f=gamemorduun03212002.html


Foppy(Posted 2003) [#3]
Yes, so what I am doing now is Morduun's method, instead that I didn't apply it to the speed of the car, but instead to the movement "turns" the car is assigned. Thanks for the link, it is a very clear description by Morduun. So I will try to use his method the way it was meant to be used (I don't know why I did not do that in the first place, maybe because it involves adjusting every speed or timing value used in the game and I thought I could avoid this "extra processing").

Thinking some more about it, using it on the speed of the objects will probably give better results especially with higher resolutions, as in that case the possible amount of variation is bigger; the speed could for instance be changed from 6 pixels to 5 pixels in a given frame, and this would not be very noticable in scrolling.

Do you use Delta Time in your Alpha Breed game?


Dr Derek Doctors(Posted 2003) [#4]
Or just tell the user to set their DirectX refresh rate to 60fps using the DXDiag override option (every PC monitor in the world must be able to display 60fps so it's a safe rate) and set your game to 60fps. It's a trick I learned off of Simon Smith and it works a *treat*.


Réno(Posted 2003) [#5]
Hi,

Here is my method. Sorry, I can't comment it because my english is not very good. But for my next game, I'll allow the player to chose the normal timming, or setting himself the monitor refresh at 60hz ( for the best smooth, like console-games ).



AppTitle "Test"
;/////////////////////////////////////////////////////////////////////////
Const ResolutionX%=640;X
Const ResolutionY%=480;Y
Const FPS#=60;images par secondes du jeu
;/////////////////////////////////////////////////////////////////////////
Graphics ResolutionX,ResolutionY,16
;*****************************************************
;*****************************************************
;Routine pour gerer le nombre d'image par secondes **
;*****************************************************
Type Frame_Rate ;*****************
Field TargetFPS# ;*****************
Field FPS# ;*****************
Field TicksPerSecond# ;*****************
Field CurrentTicks# ;*****************
Field FrameDelay# ;*****************
Field SpeedFactor# ;*****************
Field AccumTicks# ;*****************
End Type ;*****************
Global Target_FPS#=FPS# ;*****************
Global FL.Frame_Rate=New Frame_rate ;*****************
Frame_Limit_Init(Target_FPS) ;*****************
;*****************************************************
;*****************************************************

;image
Image=CreateImage(64,64)

;variable
ImageX#=0;X
ImageY#=32;Y
Acceleration#=0;acceleration
AccelerationBis%=0;acceleration bis

;/////////////////////////////////////////////////////////////////////////
;*****************
;* boucle du jeu *
;*****************
SetBuffer BackBuffer()
While Not KeyDown(1);tant que "echappe" n'a pas ete presse
;******************************************************
;Routine pour gerer le nombre d'image par secondes ***
;******************************************************
Gosub Set_Speed_Factor; ***
;******************************************************
ClsColor 255,0,255
Cls;efface le tampon

;FL\SpeedFactor=1;a utiliser si rafraichissement moniteur=60hz

;acceleration
If AccelerationBis=1 ImageX=(ImageX-(4*FL\SpeedFactor));gauche
If AccelerationBis=2 ImageX=(ImageX+(4*FL\SpeedFactor));droite

;dessine l'image
DrawBlock Image,ImageX,ImageY

;textes
Text 00,100,"ImageX "+ImageX
Text 00,110,"ImageY "+ImageY
Text 00,120,"AccelerationBis "+AccelerationBis

;mise a jour
AccelerationBis=0

;/////////////////////////////////////////////////////////////////////////
Flip;basculement des tampons
;/////////////////////////////////////////////////////////////////////////

;controleur
If KeyDown(203) AccelerationBis=1;gauche
If KeyDown(205) AccelerationBis=2;droite

;/////////////////////////////////////////////////////////////////////////
Wend
End

;**************************************************************************************
;**************************************************************************************
;Routine pour gerer le nombre d'image par secondes ***********************************
;**************************************************************************************
.Set_Speed_Factor ;** ;**
FL\CurrentTicks=MilliSecs() ;**
FL\SpeedFactor=(FL\CurrentTicks-FL\FrameDelay)/(FL\TicksPerSecond/FL\TargetFPS) ;**
If FL\SpeedFactor<0 FL\SpeedFactor=0;ne doit pas etre negatif ;**
If FL\SpeedFactor>1 FL\SpeedFactor=1;ne permet pas au jeu de saccader ;**
FL\FPS=FL\TargetFPS/FL\SpeedFactor ;**
FL\FrameDelay=FL\CurrentTicks ;**
Return ;**
Function Frame_Limit_Init(target_FPS) ;**
FL\TargetFPS=target_FPS ;**
FL\TicksPerSecond=1000 ;**
FL\FrameDelay=MilliSecs() ;**
End Function ;**
;**************************************************************************************
;**************************************************************************************


Anthony Flack(Posted 2003) [#6]
What's that about tampons?


GfK(Posted 2003) [#7]
What's that about tampons?
French word for "plug", apparently. :)


Réno(Posted 2003) [#8]
"Tampon"=buffer

:)


Foppy(Posted 2003) [#9]
Thanks Réno. That looks similar to the "Delta Time" method. I will look at it some more.

OK, maybe I'd better just open up Blitz and put his in my game. I am *so* lazy.

Let's do it!


Foppy(Posted 2003) [#10]
I have added Delta Time to change the speed of the car, and now I do get quite smooth scrolling! Yippie. Thanks for pointing this out again.