Dynamic Sprites

Anything QL Software or Programming Related.
User avatar
dilwyn
Mr QL
Posts: 2753
Joined: Wed Dec 01, 2010 10:39 pm

Re: Dynamic Sprites

Post by dilwyn »

Tinyfpga wrote:Thank you for your short, simple and informative answer. I have been trying to write some "crap" games for the 2021 competition, mostly to learn how to program in SBASIC.

My best, so far, is a bat and ball game in which the animation consists of repeatedly writing characters to the screen. My main problem with this process is that I have use the keyword PAUSE, with a minimum pause of 20msecs, to control the program flow. My animations either run flat out (no pause and too fast), or too slowly (20 msec pause).

Is there a fine grained way of controlling the flow of an SBASIC program? I know this is off-topic but my question seems
too trivial for a new topic.
From BASIC, the use of 20ms PAUSE units is probably the best the QL can do. AFAIK, there is only a 20ms timer at best. Plus, if you press a key, the pause is cut short and things speed up.

An alternative would be to use a SUSPEND type extension, e.g. http://www.dilwyn.me.uk/tk/suspend.zip. This 'suspends' BASIC for 20ms ticks, but unlike PAUSE doesn't get cut short by a keypress, so animation can be smoother.

The only other practical way of introducing pauses shorter than 20ms is to use time-wasting loops such as FOR/END FOR loops, but it has two disadvantages.
(1) it puts a load on processing during the pauses (affects multitasking), and
(2) it varies from system to system, so you need a method of working out how long any delays or time-wastes need to be. You could probably do this by waiting for the clock to change to the next second, then run a loop until the clock changes again to see how many loops are needed to fill one second on this particular system, then use this information to introduce suitable PAUSE or SUSPEND statements. Clumsy and extremely approximate.

A possible third way of slowing things down is for the game to be in a loop and you can set a variable to control every how many iterations of the loop are executed as game, and how many as time wasters, by prompting the user to enter a speed factor. I did something like this in the game I submitted to the crap games competition. It is clumsy and fiddly to program, admittedly, but relieves you of timing responsibility by letting the user enter a "percentage" slowdown. In this example, you enter a slow-down factor from 0 to 1000 with 0 being fastest game, 1000 being slowest (on a very fast PC with QPC you may need even higher than 1000 to slow things right down!).

To keep things simpler for the user, you could change the first line to enter a percentage factor and multiply it into the range you want, e.g. INPUT"Enter % slowdown ';speed : LET speed = 10*speed

A little demo routine to illustrate this (untested!)

Code: Select all

CLS : INPUT"Speed (0-fast to 1000-slow) ?";speed
counter=0
REPeat game
  IF INKEY$ = CHR$(27) THEN EXIT game :REM ESC to quit
  counter = counter+1
  IF counter > speed THEN NEXT game :REMark waste some time
  counter = 0 : REMark reset time waster
  REMark main game code here
  PRINT"*"; : REMark just something to show demo speed
END REPeat game


User avatar
NormanDunbar
Forum Moderator
Posts: 2251
Joined: Tue Dec 14, 2010 9:04 am
Location: Leeds, West Yorkshire, UK
Contact:

Re: Dynamic Sprites

Post by NormanDunbar »

Dilwyn wrote:An alternative would be to use a SUSPEND type extension, e.g. http://www.dilwyn.me.uk/tk/suspend.zip. This 'suspends' BASIC for 20ms ticks, but unlike PAUSE doesn't get cut short by a keypress, so animation can be smoother.
Interesting, very interesting.

When I wrote the code for Langton's Ant, back in Issue 5 of my somewhat irregular eMagazine, https://github.com/NormanDunbar/QLAssem ... ag/Issue_5, I tried suspending the task for 1 frame after drawing the screen. This made the animation too jerky and actually, quite slow. I ended up with a delay loop running 4 * 65536 executions of NOP NOP and a couple of DBFs. This gave a much smother animation.

I don't know for sure, but I suspect that switching tasks for one frame is probably the readon, and had I tried a longer delay, it might have been better.

It might be different in SuperBASIC, of course, mine was in assembly.

Cheers,
Norm.


Why do they put lightning conductors on churches?
Author of Arduino Software Internals
Author of Arduino Interrupts

No longer on Twitter, find me on https://mastodon.scot/@NormanDunbar.
User avatar
tofro
Font of All Knowledge
Posts: 2685
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Dynamic Sprites

Post by tofro »

You can even suspend the job for 0 ticks, and this will already add some sort of delay to your program, as it forces QDOS to re-schedule and probably end up in a task switch (which can very well end up in your job waiting for its next time slice for longer than 20ms, depending on what else is going on in the system).

A busy-wait loop, however, actually signals the system "oh, this job must be doing something very important, so allocate more slices of CPU to it" - QDOS simply can't decide whether your program is busy eating up cycles for nothing but idling or instead calculating what holds together the universe - so it will try and put the job into the front of the waiting queue.

So, giving up time slices with mt_susjb actually tells QDOS "I have nothing important to do right now" and if there are higher-priority jobs running, you might as well end up with even more jerky animation and longer delays than what you actually specified in the trap.

Multitasking on the QL has a lot to do with fairness (don't grab more than you need) - but if you want smooth animation, you cannot always be fair ;)


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
dilwyn
Mr QL
Posts: 2753
Joined: Wed Dec 01, 2010 10:39 pm

Re: Dynamic Sprites

Post by dilwyn »

NormanDunbar wrote:
Dilwyn wrote:An alternative would be to use a SUSPEND type extension, e.g. http://www.dilwyn.me.uk/tk/suspend.zip. This 'suspends' BASIC for 20ms ticks, but unlike PAUSE doesn't get cut short by a keypress, so animation can be smoother.
Interesting, very interesting.

When I wrote the code for Langton's Ant, back in Issue 5 of my somewhat irregular eMagazine, https://github.com/NormanDunbar/QLAssem ... ag/Issue_5, I tried suspending the task for 1 frame after drawing the screen. This made the animation too jerky and actually, quite slow. I ended up with a delay loop running 4 * 65536 executions of NOP NOP and a couple of DBFs. This gave a much smother animation.

I don't know for sure, but I suspect that switching tasks for one frame is probably the readon, and had I tried a longer delay, it might have been better.

It might be different in SuperBASIC, of course, mine was in assembly.

Cheers,
Norm.
Yes of course, although I didn't want to over-complicate things with the description.

Getting back onto the thread topic, this becomes quite obvious when you try to animate the converted GIF files output by my program say, by passing a channel ID from your program to another job to do the writing of animation frames in parallel with a main program, e.g. an sbasic job writes the individual frames to the channel ID of your game's #1 or whatever. The more you play with it, the trickier and more uneven things become, it seems simpler in many ways to keep things simple and just accept a bit of jerkiness. In some ways, we're going a bit beyond what the system is capable of doing.

Don't know if it would be possible to synchronise animation a little with the video frame/line outputs to try to smooth things a bit? I don't know if this is even possible, although there are related utilities out there which do something similar to switch between video modes at a certain point down the picture so you get a split mode 4 and mode 8 strips on a QL screen. Some adventures did something similar to allow the graphics to be in 8 colours and the text to be csize 0,0.


Tinyfpga
Gold Card
Posts: 252
Joined: Thu Sep 27, 2018 1:59 am

Re: Dynamic Sprites

Post by Tinyfpga »

I have played around with dilwyn's delaying code and it worked once I had changed line 6 to IF counter < speed but like all
my animation experiments I get odd results on SMSQE at high resolutions. I appreciate that QDOS was not designed to support real-time animation but I enjoy trying. The subject seems to interest others so I will start a new thread where
I can list my programming problems.


User avatar
dilwyn
Mr QL
Posts: 2753
Joined: Wed Dec 01, 2010 10:39 pm

Re: Dynamic Sprites

Post by dilwyn »

Tinyfpga wrote:I have played around with dilwyn's delaying code and it worked once I had changed line 6 to IF counter < speed but like all
my animation experiments I get odd results on SMSQE at high resolutions. I appreciate that QDOS was not designed to support real-time animation but I enjoy trying. The subject seems to interest others so I will start a new thread where
I can list my programming problems.
Well spotted (told you it was untested!).

Should make for an interesting new thread.


User avatar
dilwyn
Mr QL
Posts: 2753
Joined: Wed Dec 01, 2010 10:39 pm

Re: Dynamic Sprites

Post by dilwyn »

Just for fun, here's the simplest of animation programs for you to tinker with, which just moves an asterisk around the screen under cursor key control, complete with the speed control routine.

Runs in monitor mode window #1 (that's just to allow the fixed limits to go to the edges of the window).

If no key press is read, the asterisk moves a little bit at random.

Have fun spotting the bugs and typos in this routine :oops: :D

Code: Select all

100 REMark very simple animation with delay
110 REMark runs in a monitor mode #1 (e.g. after WMON 4 command)
120 :
130 RANDOMISE
140 :
150 CLS:INPUT'Speed (0-fast to 1000-slow)?';speed
160 CLS
170 counter = 0 : REMark speed delay counter
180 :
190 REMark start somewhere within a WMON #1 window
200 x = RND(0 TO 246) : y = RND(0 TO 190)
210 CURSOR x,y : PRINT '*'; : REMark initial position
220 :
230 REMark remember where it was to erase it when it moves
240 oldx = x : oldy = y
250 :
260 REPeat game
270   REMark speed delay routine
280   counter = counter + 1
290   IF counter < speed THEN NEXT game
300   counter = 0 : REMark reset delay counter
310   :
320   k$ = INKEY$ : IF k$ = CHR$(27) THEN EXIT game :REMark Esc to quit
330   :
340   IF oldx <> x OR oldy <> y THEN
350     REMark moved, need to print object
360     CURSOR oldx,oldy : PRINT ' '; : REMark erase old position
370     CURSOR x,y       : PRINT '*'; : REMark print at new position
380   END IF
390   :
400   oldx = x : oldy = y : REMark remember old position
410   IF k$ <> '' THEN
420     REMark move about under cursor key control
430     x = x - (k$=CHR$(192)) + (k$=CHR$(200))
440     y = y - (k$=CHR$(208)) + (k$=CHR$(216))
450   ELSE
460     REMark random movement if no keys pressed
470     x = x + RND(-1 TO 1) : REMark horizontal movement
480     y = y + RND(-1 TO 1) : REMark vertical movement
490   END IF
500   :
510   REMark check window limits
520   IF x < 0 THEN x = 0
530   IF x > 246 THEN x = 246
540   IF y < 0 THEN y = 0
550   IF y > 190 THEN y = 190
560 END REPeat game


stevepoole
Super Gold Card
Posts: 712
Joined: Mon Nov 24, 2014 2:03 pm

Re: Dynamic Sprites

Post by stevepoole »

Hi Dilwyn,

On QPC2, a delay factor of 100,000 is necessary to slow the animation, (at 1.9Ghz).

Line 320 k$=INKEY$ is a 'bug', in that the keyboard queue is not cleared, so better use KEYROW().....

X and y randomness is smoother using RND(-1 , 1) instead of RND(-1 to 1).

Best wishes,
Steve.
_________________


User avatar
dilwyn
Mr QL
Posts: 2753
Joined: Wed Dec 01, 2010 10:39 pm

Re: Dynamic Sprites

Post by dilwyn »

stevepoole wrote:Hi Dilwyn,

On QPC2, a delay factor of 100,000 is necessary to slow the animation, (at 1.9Ghz).

Line 320 k$=INKEY$ is a 'bug', in that the keyboard queue is not cleared, so better use KEYROW().....

X and y randomness is smoother using RND(-1 , 1) instead of RND(-1 to 1).

Best wishes,
Steve.
_________________
Thanks for the feedback. Nice to see someone is tinkering with it :geek:

Delay factor - wow! I just tried it on my other PC which is faster than this one, you are right, stunning speed.

KEYROW - yes, of course. Plus by using KEYROW you can read more than one key e.g. to allow diagonal movements. In case anyone is unfamiliar with what Steve said, KEYROW seems to clear the keyboard buffer, so that for example if there are already keys pressed, or you hold your finger on a key causing it to auto-repeat, you won't have to wait for the program to catch up with them. I used INKEY$ partly to encourage users to tinker, and partly because I wasn't sure if KEYROW works on all emulators.

RND(-1,1) - interesting, both produce the same range of random numbers (-1, 0, and 1) on my QPC2 - the 0 allows for the occasional 'no movement' randomness in either direction. Wonder why the separator makes a difference to the smoothness?


stevepoole
Super Gold Card
Posts: 712
Joined: Mon Nov 24, 2014 2:03 pm

Re: Dynamic Sprites

Post by stevepoole »

Hi Dilwyn,
You are right about the comma in RND(-1,1) acting as though it were a TO.... (Not on JS ROMs ? -- I do not have a 128ko to test on).

To overcome this : xx=RND(1): x=x-(xx=0)+(xx=1): yy=RND(1): y=y-(yy=0)+(yy=1) : REM gets rid of zeroes for smoothness.

The difference is light, but noticeable.

Steve.
________________


Post Reply