I knocked this out to test the two different methods. Variable frame time scale (meaning it is calculated per frame in this case just using GetFrameTime) and Constant frame time scale which is pre-calculated.
And honestly, I can see no difference in stability. Both methods are completely stable smooth movement on my laptop.
Granted, it is late and I am ready for sleep so I may have made a mistake somewhere but it *seems* correct at this point.
I think the whole key is being sure to enable both vsync and the frame rate independent time scale multiplier. Unfortunately, I am pretty sure users can override the vsync setting in their graphics properties. lol But the way I see that is if they do well just the way it is. They did it. Can't waste forever on something like this. Just make a solid effort.
// show all errors
SetErrorMode(2)
#constant TRUE 1
#constant FALSE 0
#constant KEY_ESC 27
#constant KEY_1 49
#constant KEY_2 50
#constant STATE_MAIN 1
#constant STATE_TEST1 2
#constant STATE_TEST2 3
#constant SECONDS_PER_TEST 10
#constant TARGET_FRAME_RATE 120
type TObject
id as integer
xp as float
yp as float
xv as float
endtype
global state as integer
global startmillis as integer
global stopmillis as integer
global millisPerFrame as float
global FTS as float
global objects as TObject[4]
InitDisplay("Smooth Variable Frame-rate Movement", 1280, 720, 0, TRUE)
// allow display to settle
sleep(500)
sync()
sync()
// calculate millis per frame
i as integer
sync()
startmillis = GetMilliseconds()
for i = 1 to 30
sync()
next i
stopmillis = GetMilliseconds()
millisPerFrame = (stopmillis - startmillis) / 30.0
state = STATE_MAIN
repeat
select state
case STATE_MAIN
StateMain()
endcase
case STATE_TEST1
StateTest1()
endcase
case STATE_TEST2
StateTest2()
endcase
endselect
Sync()
until GetRawKeyPressed(KEY_ESC)
end
function InitDisplay(title as string, width as integer, height as integer, maxFrameRate as float, fullscreen as integer)
// set window properties
SetWindowTitle(title)
SetWindowSize(width, height, fullscreen)
// set display properties
SetVirtualResolution(width, height)
SetScissor(0,0,0,0)
UseNewDefaultFonts(TRUE)
SetVSync(1)
endfunction
function StateMain()
Print(" 1 - VSync and Variable Frame Time Scale")
Print(" 2 - VSync and Constant Frame Time Scale")
print("")
Print(" Esc to Exit")
print("")
print(" Milliseconds per frame: " + str(millisPerFrame))
// Variable Frame Time Scale
if GetRawKeyPressed(KEY_1)
ObjectsInit()
state = STATE_TEST1
startmillis = GetMilliseconds()
// Constant Pre-Calculated Time Scale
elseif GetRawKeyPressed(KEY_2)
ObjectsInit()
FTS = TARGET_FRAME_RATE / (1000.0 / millisPerFrame) / TARGET_FRAME_RATE
startmillis = GetMilliseconds()
state = STATE_TEST2
endif
endfunction
function StateTest1()
Print(" VSync and Variable Frame Time Scale ... " + str(SECONDS_PER_TEST - ((GetMilliseconds() - startmillis) / 1000)))
FTS = GetFrameTime()
ObjectsMove()
if (GetMilliseconds() - startmillis) < (SECONDS_PER_TEST * 1000) then exitfunction
ObjectsHide()
state = STATE_MAIN
endfunction
function StateTest2()
Print(" VSync and Constant Frame Time Scale ... " + str(SECONDS_PER_TEST - ((GetMilliseconds() - startmillis) / 1000)))
ObjectsMove()
if (GetMilliseconds() - startmillis) < (SECONDS_PER_TEST * 1000) then exitfunction
ObjectsHide()
state = STATE_MAIN
endfunction
function ObjectsInit()
i as integer
for i = 0 to 4
if objects[i].id = 0
objects[i].id = CreateSprite(0)
endif
objects[i].xp = 0
objects[i].yp = i * 144
objects[i].xv = (i + 1) * 128
SetSpriteSize(objects[i].id, (i + 1) * 32, (i + 1) * 32)
SetSpritePosition(objects[i].id, objects[i].xp, objects[i].yp)
SetSpriteVisible(objects[i].id, TRUE)
select i
case 0
SetSpriteColor(objects[i].id, 255,255,255, 255)
endcase
case 1
SetSpriteColor(objects[i].id, 255,0,0, 255)
endcase
case 2
SetSpriteColor(objects[i].id, 0,255,0, 255)
endcase
case 3
SetSpriteColor(objects[i].id, 0,0,255, 255)
endcase
case 4
SetSpriteColor(objects[i].id, 255,255,0, 255)
endcase
endselect
next i
endfunction
function ObjectsHide()
i as integer
for i = 0 to 4
if objects[i].id > 0
SetSpriteVisible(objects[i].id, FALSE)
endif
next i
endfunction
function ObjectsMove()
i as integer
for i = 0 to 4
inc objects[i].xp, objects[i].xv * FTS
if objects[i].xp > 1279
objects[i].xp = 0 - GetSpriteWidth(objects[i].id)
endif
SetSpritePosition(objects[i].id, objects[i].xp, objects[i].yp)
next i
endfunction
TI/994a (BASIC) -> C64 (BASIC/PASCAL/ASM/Others) -> Amiga (AMOS/BLITZ/ASM/C/Gamesmith) -> DOS (C/C++/Allegro) -> Windows (C++/C#/Monkey X/GL Basic/Unity/Others)