What is a simple, reliable way to get a simple object like a ball or letter to move through a window at a reasonably constant speed that can be set by the program?
The following sample code is too fast if I take out the DELAY and too herky-jerky if I use DELAY:
DIM gWinWide%
DIM gWinHigh%
DIM gRect.8
END GLOBALS
CLEAR LOCAL
DIM x
DIM y
DIM dy
DIM radius
LOCAL FN movingBall
gWinWide% = 300
gWinHigh% = 400
WINDOW#1, "Moving Ball", (0,0) - (gWinWide%,gWinHigh%), _docNoGrow
COORDINATE WINDOW
TEXT _geneva, _textHeight, 0
COLOR = _zBlack
radius = 3 'radius of ball
x = gWinWide%\2.0 'horizontal center of ball at middle of window
y = radius 'vertical center of ball near top of window
CALL SETRECT(gRect,x-radius,y-radius,x+radius,y+radius)' ball
dy = 1 'pixels dropped each cycle
CALL FILLOVAL(gRect,#REGISTER(A5)+_black) 'draw ball black
DO
DELAY 25 'try to slow ball down
CALL ERASEOVAL(gRect)
CALL OFFSETRECT(gRect, 0, dy)
CALL FILLOVAL(gRect,#REGISTER(A5)+_black)
y = y + dy
UNTIL y = gWinHigh% - radius 'stop at bottom of window
END FN
Just a couple of possibilities.
The screen is only updated once per tick (60 times a second). It's possible that without the delay, your ball is being drawn and erased more than once per screen refresh. Usually 30 times per second is adequate for smooth animation. You could try this to make sure your drawing happens once every two ticks:
DO
nextTime& = FN TICKCOUNT + 2 'Schedule next loop
CALL ERASEOVAL(gRect)
CALL OFFSETRECT(gRect, 0, dy)
CALL FILLOVAL(gRect,#REGISTER(A5)+_black)
y = y + dy
WHILE FN TICKCOUNT < nextTime& 'Delay until tickcount changes twice
WEND
UNTIL y = gWinHigh% - radius 'stop at bottom of window
If your ball moves too slowly at this rate, adjust the size of the moves. It will still appear smooth as long as the drawing happens regularly every 2 ticks. It may even mean fractional moves (i.e. it may drop 2 pixels on one move and 3 the next). You should have plenty of time for a little floating point math if you need to store fractional positions.
Another (unlikely) problem might be that, with the code you posted, screen refreshes could conceiveably happen between
CALL ERASEOVAL(gRect)
and
CALL FILLOVAL(gRect,#REGISTER(A5)+_black)
causing an occasional flash. Offsetrect is quick enough that I doubt it could ever be a problem, especially if you use the code above so it's done right after a tickcount change, but I would probably choose to use two different rects so I had nothing between the erase and the redraw. There are sprite routines available to allow the erase and redraw to happen (on screen) simultaneously, but I doubt this is a major part of your problem.
If you'll permit me one other observation about your code:
x = gWinWide%\2.0 'horizontal center of ball at middle of window
Both "2.0" and "\" are ways of forcing floating point calculation. Once you store the result in your integer variable x, it reverts to an integer. The result will be the same as:
x = gWinWide% / 2 '(much faster)
or
x = gWinWide% >> 1 '(even faster yet)
Not that it matters in this case, but I try to cultivate the habit of avoiding FP calculation except where necessary (as it might be for your y calculations if you decide dy--or dy! in this case--needs to be something like 2.2 pixels per redraw).