How to slow this down?

If you have questions about any aspect of QBasic programming, or would like to help fellow programmers solve their problems, check out this board!

Moderators: Pete, Mods

DWolf
Coder
Posts: 10
Joined: Fri Aug 25, 2006 3:58 am

How to slow this down?

Post by DWolf » Wed Aug 30, 2006 3:32 am

I am creating a space shooter and I'm having a problem with my asteroids. They are moving to quickly. The whole program is in a loop so you can move the spaceship around and after I print the asteroid ASCII I have the line:

Code: Select all

asteroidx = asteroidx + 1
Anyway because the loop runs so fast the asteroid just speeds across the screen. How do I slow it down? I can't use sleep cause it would also stop the movement of the spaceship. Thanks for any help.

bungytheworm
Veteran
Posts: 288
Joined: Sat Feb 18, 2006 4:02 pm

Post by bungytheworm » Wed Aug 30, 2006 5:18 am

Check TIMER function.

Code: Select all

Delay = Timer
...
...
..
...
IF Timer - Delay > WhatDelayYouWant THEN 
    asteroidx = asteroidx + 1 
    Delay = Timer
END IF
Would be easier to help if any piece of source would been included to your post. But hope this helps a bit.

DWolf
Coder
Posts: 10
Joined: Fri Aug 25, 2006 3:58 am

Post by DWolf » Wed Aug 30, 2006 6:58 am

yea that works, But even if I put WhatDelayYouWant down to 1, it now moves too slowly lol. Any ideas?

bungytheworm
Veteran
Posts: 288
Joined: Sat Feb 18, 2006 4:02 pm

Post by bungytheworm » Wed Aug 30, 2006 7:16 am

DWolf wrote:yea that works, But even if I put WhatDelayYouWant down to 1, it now moves too slowly lol. Any ideas?
IF WhatDelayYouWans = 1 it waits for a second.
Timer returns an double. Here is simple example, hope it helps.

Code: Select all

CLS
DIM Delay, DelayCheck AS DOUBLE
      Delay = .1  ' 0.1 seconds now for value of wanted delay
      DelayCheck = TIMER ' this moment timer value
      PRINT "Start time"; TIMER
DO
LOOP UNTIL TIMER > (DelayCheck + Delay)

PRINT "Finished waiting for;"; Delay; " seconds."
PRINT "End time"; TIMER
SLEEP

User avatar
Quibbler
Coder
Posts: 16
Joined: Tue Jan 24, 2006 7:29 am
Location: Trinidad and Tobago

Post by Quibbler » Wed Aug 30, 2006 7:25 am

The easiest way is to use a delay loop

For i=1 to 10000:next i

but obviously the time delay will depend on the speed of the computer. Timer increments are about 50 ms so are often too long for many applications.

Z!re
Veteran
Posts: 887
Joined: Wed Aug 04, 2004 11:15 am

Post by Z!re » Wed Aug 30, 2006 7:35 am

DWolf wrote:yea that works, But even if I put WhatDelayYouWant down to 1, it now moves too slowly lol. Any ideas?
Try 0.1

And never use for/next as a delay unless you make it a dynamic for/next. So DONT USE FOR/NEXT AS A DELAY!
I have left this dump.

bungytheworm
Veteran
Posts: 288
Joined: Sat Feb 18, 2006 4:02 pm

Post by bungytheworm » Wed Aug 30, 2006 7:58 am

Z!re wrote:And never use for/next as a delay unless you make it a dynamic for/next. So DONT USE FOR/NEXT AS A DELAY!
Amen for that. Delay from FOR/NEXT loop depends then totally from cpu speed of your computer. On other computers, delay would be totally different.

User avatar
Quibbler
Coder
Posts: 16
Joined: Tue Jan 24, 2006 7:29 am
Location: Trinidad and Tobago

Post by Quibbler » Wed Aug 30, 2006 8:35 am

What!! anybody would think that this was a C++ forum get real this is qbasic.
If you're soooo fussy you could calibrate the delay loop against TIMER first.
There are other ways of getting short delays but they are much more involved like reading the screen refresh or you could even change the clock divider. You can even read the cpu clock (but again this is machine dependent).

User avatar
{Nathan}
Veteran
Posts: 1169
Joined: Thu Aug 19, 2004 6:08 pm
Location: The wetlands of central Ohio, USA
Contact:

Post by {Nathan} » Wed Aug 30, 2006 8:44 am

Or you could just put this line in right before your draw the screen.

Code: Select all

wait &H3DA
This rids your program of any flicker and will slow it down.
Image

Z!re
Veteran
Posts: 887
Joined: Wed Aug 04, 2004 11:15 am

Post by Z!re » Wed Aug 30, 2006 9:42 am

Quibbler wrote:What!! anybody would think that this was a C++ forum get real this is qbasic.
If you're soooo fussy you could calibrate the delay loop against TIMER first.
There are other ways of getting short delays but they are much more involved like reading the screen refresh or you could even change the clock divider. You can even read the cpu clock (but again this is machine dependent).
So, just because it's QBASIC we should take the simplest most corrupt way of doing things? You "get real" :roll:

Like Nathan said, although that is still dpendent on the refreshrate of the monitor.

Whats wrong with using timer? If you need higher resolution than it can offer, you really shouldnt be coding in QBASIC, try freebasic instead. www.freebasic.net
I have left this dump.

bungytheworm
Veteran
Posts: 288
Joined: Sat Feb 18, 2006 4:02 pm

Post by bungytheworm » Wed Aug 30, 2006 11:37 am

@Quibbler

How about

Code: Select all

INPUT "Press <ENTER> when you think you have delayd meteors moving enough";variablehere
MeteorX = MeteorX + 1

User avatar
Stoves
Veteran
Posts: 101
Joined: Fri Feb 10, 2006 12:24 am
Location: Nashville, TN

Gauging Computer Speed

Post by Stoves » Wed Aug 30, 2006 12:38 pm

I've written a crude program that creates and plays videos in qbasic (no sound yet). I wanted to adjust for the cpu speed, so I created a SUB to gauge the computer's speed. The code determines the maximum frames per second the cpu can handle, the number of empty FOR/NEXT loops the computer can run in one second for delay purposes, and a number that can be used to enforce a particular frame rate on the current cpu. (See comments for explanation in code below.)

The most relevant code to the asteroid game is the part that calculates the number of empty FOR/NEXT loops the cpu can cycle through in one second. Here's the essential steps for determining the magic number:

1. Cycle through a FOR/NEXT loop a certain number of times. (The larger the number of cycles, the more accurate the results.)
2. Get the current time. (Time1 = Time$)
3. Run the test. (FOR x = 0 TO cyclesToTest : NEXT)
4. Get the current time again. (Time2 = Time$)
5. Calculate the difference (in seconds) between the test's start and end times to find the total amount of time it took to run the test. (secsForTestRun = Time2 - Time1)
6. If it took the cpu more than a second to run the test, calculate the cycles per second by dividing the total number of FOR/NEXT loop cycles tested by the duration of the test in seconds. (cps = cyclesToTest / secsForTestRun)
(7. If it took the cpu less than a second to run the test, consider running the test for a much larger number of cycles.)

Since you don't want to pause the entire program while the delay occurs, you'll need to create a counter variable based on running a loop test that includes all program functions except the move asteroids function. For example, if you replace the code below about loading and displaying the movie frame with your game code (ship moving, anything other than the asteroids moving), that should be able to give you a pretty good estimate of what you need to set your asteroid delay counter to. Then you can just have your asteroid delay counter count down to zero, and call the move asteroids function when the counter runs down and reset the counter.

So, let's say you want the asteroids to move once every fifth of a second. After gauging the speed of your computer using code similar to what I have below, you will have a number that says how many times cycling through your program loop equals one second. Let's call that number progLoopsPerSec. Now you can just set a variable like asteroidCounter to equal 1/5 * progLoopsPerSec (because we want to move every fifth of a second, not every whole second) and decrease the counter variable each time through your program loop. Finally, create an IF/THEN statement in your main program loop that checks asteroidCounter to see if it has reached zero. If so, call the move asteroids function, and reset asteroidCounter to 1/5 * progLoopsPerSec again.

If you think this might be helpful, but it's too confusing, let me know, and I'll be glad to try and clarify or help implement this in your code.

Code: Select all

'This function gauges the speed of the current machine. The most cpu-intensive part of frame processing in this video program is the loading of the palette and the video frame to the screen. Gauge the speed of the current cpu by repeatedly processing a single frame. Note that other programs running in the background can affect the speed of loading from the test file. For best results, run this test without other programs running in tandem.
SUB getSpeed (file$)

asecs = 0
'More test cycles = more accurate results.
cyclesToTest = 800
'Get the current time.
a$ = TIME$
'Break down the current time into total seconds for the current day to avoid issues with gauging near the top of the hour or top of the minute.
ot = INT(VAL(MID$(a$, 1, 2)) * 360) + INT(VAL(MID$(a$, 4, 2)) * 60) + INT(VAL(MID$(a$, 7, 2)))

'Load in the test frame and its palette for as many cycles as previously indicated.
FOR x = 0 TO cyclesToTest
  'Call a function that loads in the palette from the test file.
  loadPalette file$
  'Load the frame to the screen.
  DEF SEG = &HA000
  BLOAD file$ + sshExt$, 0
  DEF SEG
NEXT

'Check the time again to see how long it took to run the test cycles.
'Get current time.
a$ = TIME$
'Break down time into seconds again.
t = INT(VAL(MID$(a$, 1, 2)) * 360) + INT(VAL(MID$(a$, 4, 2)) * 60) + INT(VAL(MID$(a$, 7, 2)))
'Check the difference to get the total number of seconds it took to run the test.
secs = t - ot

'Calculate this cpu's max frame rate by dividing the cycles tested by the amount of time it took to run the test cycles.
maxFps = INT(cyclesToTest / secs)

'Now find out how many empty FOR/NEXT loops this cpu can run in a second.
'Initialize the seconds variable.
secs = 0
'Test for about 10 million cycles; the higher the number of cycles, the more accurate the results.
cyclesToTest = 9999999
'Get the current time.
a$ = TIME$
'Break down the time into seconds.
ot = INT(VAL(MID$(a$, 1, 2)) * 360) + INT(VAL(MID$(a$, 4, 2)) * 60) + INT(VAL(MID$(a$, 7, 2)))

'Run the test.
FOR x = 0 TO cyclesToTest
NEXT

'Get the current time.
a$ = TIME$
'Break down the time into seconds.
t = INT(VAL(MID$(a$, 1, 2)) * 360) + INT(VAL(MID$(a$, 4, 2)) * 60) + INT(VAL(MID$(a$, 7, 2)))
'Calculate the difference to find the total amount of time it took to run the test (in seconds).
bsecs = t - ot

'If it took the cpu more than a second to run the test, calculate the cycles per second.
IF secs > 0 THEN
  'Calculate the number of cycles the cpu has to run to delay one second.
  cps = INT(cyclesToTest / secs)
ELSE
  'This cpu is crazy fast. Just assume a cycles per second rate of about 10 million.
  cps = 9999999
END IF

'Since we want to be able to adjust the frame rate of our video, calculate cycles per frame for this cpu.
'This number can be used to accurately enforce a user-designated frame rate.
cpf = INT(cps / maxFps)

END SUB

DWolf
Coder
Posts: 10
Joined: Fri Aug 25, 2006 3:58 am

Post by DWolf » Thu Aug 31, 2006 1:01 am

Alright thats sorted now, thanks everyone. One last thing, I've made it so my Spaceship can't move through an asteroid or any "0". Anyway after It moves along ther screen the asteroid leaves behind an invisible trail o "0"'s. I thought I printed over them with nothing, but I can't move my spaceshi[p through anywhere the asteroid has been. Any ideas?

nkk_kan
Veteran
Posts: 57
Joined: Thu Jun 01, 2006 10:45 am
Location: Gujrat,India,Asia
Contact:

Post by nkk_kan » Thu Aug 31, 2006 2:53 am

hmmm.. How about this one?

Code: Select all

sub delay(dlay!)
stime! = timer
Do
loop until Timer - stime! > dlay!
[/code]

DWolf
Coder
Posts: 10
Joined: Fri Aug 25, 2006 3:58 am

Post by DWolf » Thu Aug 31, 2006 4:04 am

nkk_kan wrote:hmmm.. How about this one?

Code: Select all

sub delay(dlay!)
stime! = timer
Do
loop until Timer - stime! > dlay!
[/code]
urr hello? can you read? I said I got it working...

nkk_kan
Veteran
Posts: 57
Joined: Thu Jun 01, 2006 10:45 am
Location: Gujrat,India,Asia
Contact:

Post by nkk_kan » Thu Aug 31, 2006 4:07 am

oh yeah sorry :oops:

User avatar
Stoves
Veteran
Posts: 101
Joined: Fri Feb 10, 2006 12:24 am
Location: Nashville, TN

Still need help with this?

Post by Stoves » Fri Sep 01, 2006 2:09 pm

DWolf wrote:Alright thats sorted now, thanks everyone. One last thing, I've made it so my Spaceship can't move through an asteroid or any "0". Anyway after It moves along ther screen the asteroid leaves behind an invisible trail o "0"'s. I thought I printed over them with nothing, but I can't move my spaceshi[p through anywhere the asteroid has been. Any ideas?
Need more information. Could you display the lines of code that do the following:

1. displays the asteroid
2. changes the asteroid's position
3. prints over the asteroid with nothing
4. checks whether the spaceship can move
5. displays the spaceship

If you could post that info, it should be possible to help identify the problem.

DWolf
Coder
Posts: 10
Joined: Fri Aug 25, 2006 3:58 am

Post by DWolf » Fri Sep 01, 2006 5:16 pm

Heres the whole thing, pretty messy, but oh well.

Code: Select all

CLS

'Set spaceship and meteor position
y = 2
x = 1

meteorx = 38
meteory = 2

Delay = TIMER

'Map
COLOR 15
PRINT "0000000000000000000000000000000000000000"
PRINT "                                        "
PRINT "                                        "
PRINT "                                        "
PRINT "                                        "
PRINT "0000000000000000000000000000000000000000"

'Check for spaceship moving
DO
  a$ = INKEY$
  checky = y
  checkx = x
  checkmx = meteorx
  checkmy = meteory

  LOCATE y, x
  COLOR 0
  PRINT ">"
  IF a$ = "w" AND y - 1 > 0 THEN checky = y - 1
  IF a$ = "a" AND x - 1 > 0 THEN checkx = x - 1
  IF a$ = "s" THEN checky = y + 1
  IF a$ = "d" THEN checkx = x + 1

'Print Meteor
  LOCATE meteory, meteorx
  COLOR 0
  PRINT "00"

'Move Meteor
  IF TIMER - Delay > .1 THEN
    meteorx = meteorx - 1
    Delay = TIMER
  END IF

  IF SCREEN(checky, checkx) <> 48 THEN
    y = checky
    x = checkx
  END IF
 
'Make sure the spaceship can't leave the area
  IF x > 40 THEN x = 40
    LOCATE y, x
    COLOR 15
    PRINT ">"
    LOCATE 10, 10

'Resets meteor position
  IF meteorx < 1 THEN meteorx = 40
    LOCATE meteory, meteorx
    COLOR 15
    PRINT "00"

LOOP UNTIL a$ = "q"


User avatar
Stoves
Veteran
Posts: 101
Joined: Fri Feb 10, 2006 12:24 am
Location: Nashville, TN

Aaah

Post by Stoves » Fri Sep 01, 2006 5:40 pm

Here's the problem:

Code: Select all

'Print Meteor 
LOCATE meteory, meteorx 
COLOR 0 
PRINT "00"
PRINT "00" with color 0 WILL make the asteroid disappear on the screen, but the ascii value for that position will still be for the "0" character. Just because you redrew the asteroid with color 0 doesn't mean the character at that position has been changed. So, when your code checks the position later to see if there's an asteroid there...

Code: Select all

IF SCREEN(checky, checkx) <> 48 THEN 
    y = checky 
    x = checkx 
END IF
... it still sees the invisible "0". All you have to do to fix this is print spaces instead of "00" when you're clearing the asteroid. That way you won't have to change the color either, and then your program should work better.

So, to clear the asteroid, you could use any of the following:

Code: Select all

LOCATE meteory, meteorx 
PRINT "  "
OR

Code: Select all

LOCATE meteory, meteorx 
PRINT SPACE$(2)
OR

Code: Select all

LOCATE meteory, meteorx 
COLOR 0 
PRINT "**"
OR

Code: Select all

LOCATE meteory, meteorx 
COLOR 0 
PRINT ">>"
ETC...

The comment that says "'Print Meteor " is a little misleading because actually, that's where you clear the meteor. Hope this helps.

DWolf
Coder
Posts: 10
Joined: Fri Aug 25, 2006 3:58 am

Post by DWolf » Fri Sep 01, 2006 7:18 pm

wow thanks. that was so much help. I don't actually hav that commenting in my program I addeed it in when I posted that message, and it was rushed lol. Anyway one other question if you're happy to keep to keep helping me.

Is there anyway I can use different characters for the meteor? At the moment I used the same as the edges of the level because I've set it so my spaceship can't move into 0's, but thats only because it can' have the co-ords 0, 0. Is there anyway to check for collissions with other characters?

Also, I know all the characters flash because they are being erased and redrawn, but is there anyway to stop it? Thanks.

Post Reply