Scrolling tile RPG

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

User avatar
burger2227
Veteran
Posts: 2466
Joined: Mon Aug 21, 2006 12:40 am
Location: Pittsburgh, PA

Post by burger2227 »

There is a problem using INP(&H60) with the Arrow keys. It seems that the arrow keys are designated as added keys and not only returns the correct scancode. but 224 also. Plus the numberlock mode may send back a 170 release code in either mode.

The number lock keyboard arrows do not have this problem however.

There are Assembly code routines that can help with multiple keypresses for 2 or more players.

Ted
Please acknowledge and thank members who answer your questions!
QB64 is a FREE QBasic compiler for WIN, MAC(OSX) and LINUX : https://www.qb64.org/forum/index.php
Get my Q-Basics demonstrator: https://www.dropbox.com/s/fdmgp91d6h8ps ... s.zip?dl=0
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

burger2227 wrote:There is a problem using INP(&H60) with the Arrow keys. It seems that the arrow keys are designated as added keys and not only returns the correct scancode. but 224 also. Plus the numberlock mode may send back a 170 release code in either mode.

The number lock keyboard arrows do not have this problem however.

There are Assembly code routines that can help with multiple keypresses for 2 or more players.

Ted


I've never seen the INP(&H60) before, but yeah, I have some routines at home that allow for multiple key presses. I just tossed this together really quick to give people an idea of what I'm going for.
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

MilesAway1980:
Very nice! Wow, I have never seen such long strings for the DRAW command.

Your code snippet worked well. A lot of flicker, due to the constant redrawing of the screen, whether a move required it or not. I added another, short DO/LOOP just after your first DO, that loops until either a + or - or an arrow key or Esc is pressed.

I also moved the CIRCLE and PAINT lines between the NEXT y and the NEXT x, to reduce its flicker.

All works pretty good. I am trying to see if I can just draw one screen's worth, or maybe a slight overlap, and create the new edges only as we move in that direction. If I succeed, that should allow faster movement. Also, making the xmove and ymove increase by increments of 2 or even 3, instead of the current 1, speeds up the movement considerably.
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

Yeah, those string commands are LONG. But they were the only way I could think of to draw fully detailed tiles to the screen and have the tiles be able to draw half on and half off the screen.

I did try just putting each in a 20x20 array and PSETing each and every pixel to the screen (even with the faster PEEK and POKE method), but that was definitely much slower.



In the full map program I have (where you can actually explore all the caves and towns I've drawn), I have it constantly redrawing the map for animation, such as candles flickering, or water moving. But its on a timer to go off every second, so it doesn't "flicker".
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

I still have a question. Do you draw ALL the map every time, or just a part? For the whole map, I would think that the time element would make movement very slow...
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

Ralph wrote:I still have a question. Do you draw ALL the map every time, or just a part? For the whole map, I would think that the time element would make movement very slow...

Oh, no. I definitely only draw what's on screen every time. I've worked extensively on that. :)

The map is 600 x 500 elements, with each element being a 20x20 tile. It would take (and DID take!) forever, to draw 300,000 tiles every time! So I figured out a way to keep track of which ones needed to be drawn, so that it only had to do the 16x10 of the screen.
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

Aha! Great! So, that's the secret for a scrolloing map. I'll give it a try, with your snippet. But, maybe in a few days, not today.
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

MilesAway1980:

Say, I just had a thought! Maybe one way of taking care of those pesky border-intruding tiles is this:

1. Draw the 8-pixel-wide outside, all-around black border once only!
2. Early on, GET an 8x8 black sprite.
3. Whenever a tile of the map is to intrude, let it, followed by PUTing the black sprite on top of it.

This just might do the trick! What do you think?
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
User avatar
Codemss
Veteran
Posts: 124
Joined: Sun Jun 24, 2007 6:49 am
Location: Utrecht, The Netherlands
Contact:

Post by Codemss »

The way I learnt to do is it is simple and fast.
(I you haven't already, I recommand btw using a assembly key handler, search for multikey if you don't know one)
I make a lookuptable for the biggest axe of the map. So if the map is 25x20 and the tiles 64x64, I make 2 lookuptables of the size 64*25 (cause the x axe is the biggest here), one with the texturecoordinates and one with the map coordinates. Then you just use a for...next loop for x and y, and in the inner loop:
u = texturelut(x + xoffset)
v = texturelut(y + yoffset)
mapx = maplut(x + xoffset)
mapy = maplut(y + yoffset)
c = tile(map(mapx, mapy), u, v)

tile is an 3dimensinal array that saves all tiles.
Well you should figure out the code.
A simple demo of this runs at 100 fps compiled on my computer. I will look if I have the code somewhere.
Check out my site: <a href="http://members.lycos.nl/rubynl">Click here</a>
Hope you like it. Send some feedback if you want: <a href="mailto:basicallybest@live.nl">Mail me</a>
Codemss, before known as RubyNL
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

Ralph wrote:MilesAway1980:

Say, I just had a thought! Maybe one way of taking care of those pesky border-intruding tiles is this:

1. Draw the 8-pixel-wide outside, all-around black border once only!
2. Early on, GET an 8x8 black sprite.
3. Whenever a tile of the map is to intrude, let it, followed by PUTing the black sprite on top of it.

This just might do the trick! What do you think?

Gave this a try, as it was a great idea. It definitely made it look better, but didn't quite do it. The center of the screen where the map was looked great, but there was still little flickers and sprinkles of drawn then covered tiles coming in and out around the edges. :cry: Was hoping that would have done it.
Last edited by MilesAway1980 on Mon Aug 11, 2008 4:17 pm, edited 1 time in total.
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

Codemss wrote:The way I learnt to do is it is simple and fast.
(I you haven't already, I recommand btw using a assembly key handler, search for multikey if you don't know one)
I make a lookuptable for the biggest axe of the map. So if the map is 25x20 and the tiles 64x64, I make 2 lookuptables of the size 64*25 (cause the x axe is the biggest here), one with the texturecoordinates and one with the map coordinates. Then you just use a for...next loop for x and y, and in the inner loop:
u = texturelut(x + xoffset)
v = texturelut(y + yoffset)
mapx = maplut(x + xoffset)
mapy = maplut(y + yoffset)
c = tile(map(mapx, mapy), u, v)

tile is an 3dimensinal array that saves all tiles.
Well you should figure out the code.
A simple demo of this runs at 100 fps compiled on my computer. I will look if I have the code somewhere.

So if I get this right, when its all said and done, the variable 'c' is the color of the pixel to be drawn? So when I draw the map, if I'm using 20x20 tiles, it's:

for x = 0 to 15 (screen is 16 tiles across)
for y = 0 to 9 (screen is 10 tiles tall)
for u = 0 to 19 (tiles are 20 wide)
for v = 0 to 19 (tiles are 20 tall)
mapx = map(x + xoffset)
mapy = map(y + yoffset)
c = tile(map(mapx, mapy), u, v)
pset(x,y),c
next
next
next
next

Makes sense.

However, when I did a benchmark test with just some random info, the method I have using the DRAW command was nearly 250% faster. Or did I not understand you fully and there's something I can do with yours to make it faster?



PS: I tried this with both PSET and the DEF SEG POKE method that puts pixels directly into the video memory.
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

Miles Away:

I wrote a program to time various methods of drawing in QuickBASIC 4.5, SCREN mode 12. The DRAW method seems to take a time almost directly proportional to te number of CR commands, as in c15r1. So, if you can manage to substitute some of the "almost equal" colors, perhpas those with index numbers that differ by 1 or 2, as in c109r1, c110r1, and c111r1. and that follow each other with only one of those, giving, say, c110r3, with enough of these, you might reduce the time between moves by a significant amoung! What do you think? Here is the program, with approximate results included:

Code: Select all

'Timing for various methods of drawing in QuickBASIC 4.5, SCREEN mode 12.

SCREEN 12
m = 78700 'number of times to run each drawing method

'Method 1 through 4 use the DRAW graphics statement, Method 5 uses PSET, and
'Method 6 uses GET and PUT statements.

'1. String use r8 to DRAW eight cosecutive points in one stroke.
Z8$ = "bm1,1 c15r8"
t0 = TIMER: FOR i = 1 TO m: DRAW Z8$: NEXT i
LOCATE 1, 10: PRINT "Time is"; : PRINT TIMER - t0 '''Result: takes 1.2 seconds, +/-0.05

'2. String use r4 twice to DRAW eight cosecutive points in two strokes.
z4$ = "bm1,17 c15r4c11r4"
t0 = TIMER: FOR i = 1 TO m: DRAW z4$: NEXT i
LOCATE 2, 10: PRINT "Time is"; : PRINT TIMER - t0 '''Result: takes 2.2 seconds, +/-0.05

'3. String use r2 four times, to DRAW eight cosecutive points in four strokes.
z2$ = "bm1,33 c15r2c12r2c12r2c12r2"
t0 = TIMER: FOR i = 1 TO m: DRAW z2$: NEXT i
LOCATE 3, 10: PRINT "Time is"; : PRINT TIMER - t0 '''Result: takes 4.2 seconds, +/-0.25

'4. String use r1 to DRAW eight cosecutive points in eight strokes.
z1$ = "bm1,49 c15r1c15r1c15r1c15r1c15r1c15r1c15r1c15r1"
t0 = TIMER: FOR i = 1 TO m: DRAW z1$: NEXT i
LOCATE 4, 10: PRINT "Time is"; : PRINT TIMER - t0 '''Result: takes 8.1 seconds, +/-0.05

'5. This method uses the PSET statement to draw eight consecutive points, one at a time.
t0 = TIMER: FOR i = 1 TO m: FOR x = 1 TO 8: PSET (x, 65), 15: NEXT x: NEXT i
LOCATE 5, 10: PRINT "Time is"; TIMER - t0 '''Result: takes 3.3 seconds, +/-0.15

'6. Method uses the GET and PUT statements to draw eight consecutive points at one time.
DIM arr(7, 7): GET (1, 1)-(8, 1), arr
t0 = TIMER: FOR i = 1 TO m: PUT (1, 81), arr: NEXT i
LOCATE 6, 10: PRINT "Time is"; TIMER - t0 '''Result: takes 4.7 seconds, +/-0.05

system
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

Yeah, this is something I've noticed greatly. When the character is walking through grass and water tiles, which are probably 5 colors each, and then suddenly mountains come in to view, the scrolling noticeably slows down. (Grass tiles are around .5 kb whereas mountain tiles are probably 2.5kb)
Need to figure out how to make them draw the same pictures with shorter draw commands. Maybe create two or three different algorithms, instead of just one? My current one draws right to left, down, left to right, down, right to left, down, etc.... Maybe create one that goes top to bottom, right, bottom to top, right, etc... and have it pick between the two as to which is a shorter string.
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

Your idea sounds good, but, you will have totest, not only the string length, but, the actual time it takes to DRAW. I might be that going up-and-down takes longer than left-to-right. I still feel that sacrificing a little bit of precision coloring would pay off...

In any case, the search for etter speed should be concentrated on the screen background, since you DRAW the bckground tile to just about cover the screen, or some 640/20 x 480/20, or 828 times, as compared to one castle tile,

Based on my results, above, I think that you have to concentrate on getting as many as possible color-moves with moves greater than 3, in order to speed up your process.

I made a program that converts a sprite into DRAW strings. For instnnce, with the statement,
PRINT "T"
as a first PRINT statement, I get the "T" as a sprite in the top-left-most area, the rectangle (0,0)-(7,15). Using that "T", I get the following 16 lines:
0 c000R1c000R1c000R1c000R1c000R1c000R1c000R1c000R1d1BL8
1 c000R1c000R1c000R1c000R1c000R1c000R1c000R1c000R1d1BL8
2 c000R1c015R1c015R1c015R1c015R1c015R1c015R1c000R1d1BL8
3 c000R1c015R1c000R1c015R1c015R1c000R1c015R1c000R1d1BL8
4 c000R1c000R1c000R1c015R1c015R1c000R1c000R1c000R1d1BL8
5 c000R1c000R1c000R1c015R1c015R1c000R1c000R1c000R1d1BL8
6 c000R1c000R1c000R1c015R1c015R1c000R1c000R1c000R1d1BL8
7 c000R1c000R1c000R1c015R1c015R1c000R1c000R1c000R1d1BL8
8 c000R1c000R1c000R1c015R1c015R1c000R1c000R1c000R1d1BL8
9 c000R1c000R1c000R1c015R1c015R1c000R1c000R1c000R1d1BL8
10 c000R1c000R1c000R1c015R1c015R1c000R1c000R1c000R1d1BL8
11 c000R1c000R1c015R1c015R1c015R1c015R1c000R1c000R1d1BL8
12 c000R1c000R1c000R1c000R1c000R1c000R1c000R1c000R1d1BL8
13 c000R1c000R1c000R1c000R1c000R1c000R1c000R1c000R1d1BL8
14 c000R1c000R1c000R1c000R1c000R1c000R1c000R1c000R1d1BL8
15 c000R1c000R1c000R1c000R1c000R1c000R1c000R1c000R1d1BML8

The program can further reduce those lines to:
0 c000R8 1 d1BL8
1 c000R8 1 d1BL8
2 c000R1c015R6c000R1d1BL8
3 c000R1c015R1c000R1c015R2c000R1c015R1c000R1d1BL8
4 c000R3c015R2cc000R3d1BL8
5 c000R3c015R2cc000R3d1BL8
6 c000R3c015R2cc000R3d1BL8
7 c000R3c015R2cc000R3d1BL8
8 c000R3c015R2cc000R3d1BL8
9 c000R3c015R2cc000R3d1BL8
10 c000R3c015R2cc000R3d1BL8
11 c000R2c015R4c000R2d1BL8
12 c000R8 d1BL8
13 c000R8 d1BL8
14 cc00R8 d1BL8
15 c000R8 d1BL8

Since the first two and the last four lines would convey no useful information on a black bacground, those six lines can be deleted, to leave only the lines with information, lines 2 through 11. And, since columns 0(the first column of c000R1) and 7 (the last column with c000R1) also can be dispensed with, we end up with a much reduced set of useful strings, as follows:
2 c015R6d1BL8
3 c015R1c000R1c015R2c000R1c015R1d1BL8
4 cc000R2c015R2cooR2d1BL8
5 c015R2cc000R2d1BL8
6 c015R2cc000R2d1BL8
7 c015R2cc000R2d1BL8
8 c015R2cc000R2d1BL8
9 c015R2cc000R2d1BL8
10 c015R2cc000R2d1BL8
11 c00R1c015R4d1BL8

The final lines 2-11 will print in about 40% the time as the original 16 lines, due to the elimination of six line. Plus, the reduction of the remaining lines' length to an approximate average of 20 characters vs 52, should result in ann overall time reduction of some 60%*40%, or around 24%, say we have reduced the printing time from about 4 to 1.

So, I think that the solution for speeding up your screen printing every time a move is required is going to depend on how many new color groupings you can produce. To me, this means doing a little bit of sacrificing of presicion coloring.

By the way, unless your vertical colors match frequently, you will probably not get a speed improvement with your proposed top-to-bottom R1, bottom-to-top R1, and so on, proposed method. It is interesting to see if you can, in fact, devise a method of increasing the speed, based on color groopings...

It might still turn out that PSETting would be faster... Have you given it a try?
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

MilesAway1980:

I remember you had a problem with tiles hitting theedges and creating errors. Have been thinking on that problem, and have a solution. Just use the DRAW statement for your tiles. For instance, in your snipet, the castle has no problem disappearing gently into the edges, nor does the background.

If using DRAW for tiles would give you more lengthy times, then just use the DRAW when your tiel is goingto crash into the edge.

What do you think?
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

Ralph wrote:MilesAway1980:

I remember you had a problem with tiles hitting theedges and creating errors. Have been thinking on that problem, and have a solution. Just use the DRAW statement for your tiles. For instance, in your snipet, the castle has no problem disappearing gently into the edges, nor does the background.

If using DRAW for tiles would give you more lengthy times, then just use the DRAW when your tiel is goingto crash into the edge.

What do you think?

Hrm....using the DRAW statement for only the edges is a fantastic idea. I'll have to make two versions of each tile, (one a BSAVE and the other a DRAW), but oh well. :) I will give that a try and let you know how it goes!

If it works, I'll have my tile saving program save two versions of each whenever it saves.
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

That works GREAT!

I love it. :)

The draw was pretty fast after I made that optimizing routine, but that really made it glide smoothly. Now I just need to make two versions of every tile, unless someone knows how to put a BSAVE and a DRAW statement in the same file.

Thanks! Clever thinking.
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

Hey, I'm just happy I could shed some light on your problem. Real glad you got it to work, even though it will require doublein of your tiele storage.
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
MilesAway1980
Coder
Posts: 25
Joined: Tue Jul 29, 2008 12:30 pm

Post by MilesAway1980 »

Ralph wrote:Hey, I'm just happy I could shed some light on your problem. Real glad you got it to work, even though it will require doublein of your tiele storage.
Thanks for the help. Definitely. :)

No biggie on the tile storage. The game's about 3mb, and the doubling the tiles added another 40kb, so I'm not worried about it at all. Although, by the time I get done, there'll probably be a whole lot more tiles than there are now. :)
Ralph
Veteran
Posts: 148
Joined: Fri Feb 09, 2007 3:10 pm
Location: Katy, Texas

Post by Ralph »

One more thing. If PUT works faster then DRAW for the background tile, perhaps you could use PUT for ALL tiles, including the background, and just use DRAW for all the edge tiles? Maybe you already have done this!
Ralph, with QuickBASIC 4.5, operating under Windows XP, wiht anHP LaserJet 4L Printer. Bilingual in English/Spanish
Post Reply