This is an archived, read-only copy of the United-TI subforum , including posts and topic from May 2003 to April 2012. If you would like to discuss any of the topics in this forum, you can visit Cemetech's z80 & ez80 Assembly subforum. Some of these topics may also be directly-linked to active Cemetech topics. If you are a Cemetech member with a linked United-TI account, you can link United-TI topics here with your current Cemetech topics.

This forum is locked: you cannot post, reply to, or edit topics. Z80 & 68k Assembly => z80 & ez80 Assembly
Author Message
WikiGuru
ADOS (Attention deficit... Oh! Shiny!)


Elite


Joined: 15 Sep 2005
Posts: 923

Posted: 17 Aug 2008 01:22:13 pm    Post subject:

I was wondering if someone could go over the concept of a smooth-scrolling tilemap?

I have a few ideas on how they would work, but i'm not sure which way would be the best way to implement.

Idea 1: Have a screen buffer slightly larger than the display screen. To scroll up/down, the draw pointer is moved up/down 1 bytes. To scroll left/right, data is shifted right/left 1 bit.

Idea 2: Shift the current screen. Then, depending on which way the screen was shifted, draw the missing parts as defined by the tilemap.

edit:

after looking at this, it looks like he used idea 1 (i think, i can't tell). Out of curiosity, would idea 2 work better at all (in terms of memory, speed, or both)?


Last edited by Guest on 17 Aug 2008 01:29:05 pm; edited 1 time in total
Back to top
asdf


Advanced Newbie


Joined: 17 Aug 2008
Posts: 73

Posted: 17 Aug 2008 08:12:59 pm    Post subject:

Well, idea 1 requires that you update the entire buffer each time you have scrolled a complete row or column, in addition to a few other things that aren't very efficient. I just checked it real quick, but it seems like what he did was have two variables, one to show the current map's aligned position, and one to keep track of how many pixels off it is from being aligned. When it shifts, he shifts the screen accordingly then fills in the gap, like your idea 2. The "temp" variable is just where he rotates the bytes to match the x/y offset. To shift horizontally, he fills the temp variable again with the offscreen sprite data then checks the bit that needs to be shifted in. If it is zero, reset the carry flag, if it is one, set the carry flag. The carry flag is then shifted into the screen.

There are certainly different ways to handle it all (for example, you can have the map routine handle the shifting if you want and then just update the x/y coordinates when moving about the screen). Really, just figure out what works best with how you want your character (or whatever is interacting with the tilemap) to navigate the tilemap.


Code:
scrolldown:
ld a,(mapy)
or a
jr nz,scrdy
ld a,(mapyp)
or a
ret z            ;if map is at the very top y value can't scroll any more
scrdy: ld hl,buffer+(63*12)-1
ld de,buffer+(64*12)-1
ld bc,768-12
lddr              ;shift the screen down, with the top row needing to be filled in
ld hl,mapyp  ;this holds a value between 0 and 7, saying how many bytes to cut off the top row
ld a,(hl)
dec a
ld b,a
and %00000111  ;limit it to a value between 0 and 7. if a was 0 it now equals 7
ld (hl),a
push af
bit 7,b          ;if bit 7 is set then a wrapped around (0 to $FF)
jr z,scrds    ;if it is not set, don't bother updating the maps y position
ld hl,mapy
dec (hl)       ;if bit 7 was set that means we need to start drawing a new row in the map
scrds: call getscreenorigin  ;i am guessing this finds the topleft onscreen byte in the map?
dec hl      ;move pointer left one byte
ld de,temp  ;14 (maybe more) byte space
ld b,14
pop af;a=mapyp
scrdL: push bc
push hl
call findtexture   ;this returns the current sprite? multiplied by 8 with y offset added. and i think loads the pointer to tile data in hl
ld c,a
ld b,0
add hl,bc      ;add that to tiledata pointer to get correct byte in tile
ldi           ;store that byte into data to work with it
pop hl
inc hl    ;check next tile in map
pop bc
djnz scrdL
ld a,(mapxp)
or a
jr z,scrdsd    ;if the map is x aligned, we don't need to shift anything
scrdSO: ld b,14
ld hl,temp+13
scrdSL: rl (hl)
dec hl
djnz scrdSL
dec a
jr nz,scrdSO     ;rotate each byte left
scrdsd: ld hl,temp+1
ld de,buffer
ld bc,12
ldir             ;load it to the graph buffer
ret
Back to top
cjgone
Aw3s0m3


Active Member


Joined: 24 May 2006
Posts: 693

Posted: 18 Aug 2008 12:46:40 am    Post subject:

didn't read the above post but theres a few ways (srry just came back from some other place)

simplest way is to draw the tile map from an x and y coordinate that is at the top left of the screen. everything is drawn relative 2 this point

tiles are 8 pixels by 8 pixels, so an alligned tile map, there is 12 by 8 sqaures on the screen... if you scroll left and right, you'll get some more..

the top corner starts at 0,0 and you draw the tile map based on the top corner.. as you go right you increment the x cor and dec it going left... same for up \ down..

okay, so lets look at actually drawing the tile map.... if we have a tile map thats like 8 by 33 ( 8 down, 33 across... forget up and down scrolls atm cuz its the same concept as left\right), if the top coordinate was at 0,0 you would draw an 8x12 map onto the screen, all the tiles will be alligned, which means every 8 pixels, there is another tile and no tiles not fully draw on the LCD. So when is the tile map alligned? At 0,0 (x,y)... 8,0 16,0 ...so on and on... the pattern is 0,8,16,24,32... which is infact the width of each tile..

at 0,0, the tile on the top of the map is the 0th tile (aka first tile) at 8,0 the tile in the top corner is the first tile (aKa the 2nd tile).

i really cant explain the idea atm, but the tile in the top corner is the x coordinate divided by 8. so if the coor is 1,0 the top tile is the 0th, if its 5,0 its still the 0th,,, if its 10,0 its the first tile... in z80 division
0/8 = 0
4/8 = 0
7/8 = 0
8/8=1
12 /8 = 1 ... now we got an alligned map.. by using the top coordinate and diving it by 8 you can find what collumn the first tile is, which is the starting point for all the other tiles.

now we gotta do the offset.'

we use modulus to find the "offset", which is how far the tiles on the screen are shifted. at 0,0 the tile map has a 0 offset (all tiles alligned), at 1,0 the tile map is offset by 1 pixel... 2,0 = a shift of 2 pixels.... 8,0 = 0 shift.... so on so forth...

If you continue you'll see a pattern offset of 0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7... which is infact modulus 8.... To do modulus 8 in z80 quickly, you;d do "AND 07".... not explain why tho

So if each tile is a seperate tile drawn by putspriteCLIPXOR, you'd do something on the idea of "putspriteclip( x - OFFSET, y)".... IF there is an offset on the left or right not only do you need the 8x12 matrix, but their is infact a 13th collumn which must be accounted for when the map is unallgined.

generally, this is a slower way but way easier anyway. srry if this explanation sucked but oh well.


Last edited by Guest on 18 Aug 2008 12:48:29 am; edited 1 time in total
Back to top
Cryzbl


Newbie


Joined: 20 Jun 2008
Posts: 46

Posted: 18 Aug 2008 01:48:30 pm    Post subject:

asdf wrote:
Well, idea 1 requires that you update the entire buffer each time you have scrolled a complete row or column, in addition to a few other things that aren't very efficient. I just checked it real quick, but it seems like what he did was have two variables, one to show the current map's aligned position, and one to keep track of how many pixels off it is from being aligned. When it shifts, he shifts the screen accordingly then fills in the gap, like your idea 2. The "temp" variable is just where he rotates the bytes to match the x/y offset. To shift horizontally, he fills the temp variable again with the offscreen sprite data then checks the bit that needs to be shifted in. If it is zero, reset the carry flag, if it is one, set the carry flag. The carry flag is then shifted into the screen.
[post="126097"]<{POST_SNAPBACK}>[/post]

This is indeed the fastest way to scroll a tilemap. However keep in mind that you can only shift it pixel per pixel.
Another way of thinking about it is "Try to reuse as much as possible from the graph buffer and fill in the missing gap". In the case you are shifting horizontal with 8 or more pixels you can shift it much faster. jim e has worked a tilemapper based on this idea, all should be available at revsoft.

cjgone:
Unfortunately just redrawing the whole tilemap every frame is VERY slow, you should read the part of asdf's post I quoted. As you can see shifting/copying the bytes in the graph buffer will save a lot of time.


Last edited by Guest on 18 Aug 2008 01:56:49 pm; edited 1 time in total
Back to top
Liazon
title goes here


Bandwidth Hog


Joined: 01 Nov 2005
Posts: 2007

Posted: 18 Aug 2008 02:51:56 pm    Post subject:

the major annoyance of this method is that you do need an extra buffer to hold the map, otherwise the sprites will leave artifacts because the buffer you're shifting will still have them. you then modify this buffer with your mapper and copy to the main buffer where you draw sprites.

extra rows in the buffer can be useful, because as you said, you can just change the starting row for your vertical shifts. I think Jim'e mappers do this too. extra columns not so much because you have to do multiple (ranging from 0 to 4) shifts for every byte in addition to the copy operation to the main buffer. without the extra column, you might just need to do 1 shift per byte, but like Cryzbl pointed out, this isn't too flexible unless you modify to allow more shift from the current position. extra columns force you to use the aligned positions to shift from. and if you shift 1 pixel per frame, each shift in one direction takes more time with extra columns.


Last edited by Guest on 18 Aug 2008 03:09:47 pm; edited 1 time in total
Back to top
calc84maniac


Elite


Joined: 22 Jan 2007
Posts: 770

Posted: 18 Aug 2008 05:52:23 pm    Post subject:

Actually, redrawing every frame is by no means slow. It allows you to have animated tiles, plus you don't have to worry about sprites messing it up for the next frame. I mean, just look at Project M. The tilemapper got over 40 fps there.
Back to top
Liazon
title goes here


Bandwidth Hog


Joined: 01 Nov 2005
Posts: 2007

Posted: 18 Aug 2008 07:16:20 pm    Post subject:

i have a hunch, a tile scrolling (messuptiles) method?
Back to top
cjgone
Aw3s0m3


Active Member


Joined: 24 May 2006
Posts: 693

Posted: 18 Aug 2008 07:52:54 pm    Post subject:

I'm using the fill-scroll method for my blobby project thing where i fill in the extra pixels on the edges after a shift and I don't think i'm getting anymore speed then calc84maniac up there with a tile by tile method.... not to mention the benefits of having a tile by tile drawing >.>
Back to top
Liazon
title goes here


Bandwidth Hog


Joined: 01 Nov 2005
Posts: 2007

Posted: 18 Aug 2008 08:48:06 pm    Post subject:

one of the biggest bottlenecks will probably be the fastcopy. the delay/waits between outs is a lot of wasted clocks unfortunately. hence, the interlacing of LCD writing and buffer drawing in crash's and one of Jim's earlier mappers.
Back to top
asdf


Advanced Newbie


Joined: 17 Aug 2008
Posts: 73

Posted: 18 Aug 2008 10:34:40 pm    Post subject:

Redrawing the tilemap every frame is indeed slower, as you are either clipping every tile individually then drawing it on screen or drawing them all to a buffer and shifting the entire buffer, and, unless you are using a timer to keep things consistent or something else to maintain consistency, it will look choppy as you move because the screen will display much more quickly when it is aligned.

If you are planning on having a lot of animated tiles, however, redrawing the map each frame is a good way to keep the map speed consistent no matter how many animated tiles are onscreen.
Back to top
Iambian


Advanced Member


Joined: 13 Mar 2004
Posts: 423

Posted: 19 Aug 2008 12:06:14 am    Post subject:

From experience, trying to fill in the gaps after a shift proved to be a lot slower than just redrawing the whole map from scratch. With what little I've done with regards to smooth scrolling tile maps, I've found that if the redraw is done by AND-ing two halves to form a tile, there's very little shifting involved in making the entire screen seem like it's moving. In fact, I've written a little routine that buffers all the shift and mask possibilities in a 16K chunk of that "extra" ram in the TI-83 Plus SE / 84+ calcs so that there is literally NO shifting beyond the initial buffering. Frame rates went through the roof with that method :)

Still. I'm against filling in the extra bits during a shift. It just uses way too much logic to be any faster than a total screen redraw.
Back to top
tr1p1ea


Elite


Joined: 03 Aug 2003
Posts: 870

Posted: 19 Aug 2008 01:43:08 am    Post subject:

I prefer to redraw the map each frame as well, mainly because of the animated tile payoff. Pre-rotating tiles (or just using a LUT) can give you a constant (and fast) frame rate no matter the xoffset.
Back to top
Cryzbl


Newbie


Joined: 20 Jun 2008
Posts: 46

Posted: 19 Aug 2008 06:04:48 am    Post subject:

tr1p1ea wrote:
I prefer to redraw the map each frame as well, mainly because of the animated tile payoff. Pre-rotating tiles (or just using a LUT) can give you a constant (and fast) frame rate no matter the xoffset.
[post="126133"]<{POST_SNAPBACK}>[/post]

Animated tiles are perfectly possible with smoothscrolling tilemaps: Just redraw only that sprite that you want to animate and then abusing the artefact effect that will cause the drawn sprite to become part of the buffer.

My smoothscroller scrolls a tilemap horizontally at 20000 cycles (which gives 85 fps with fastcopy (+50000 cycles)).


Last edited by Guest on 19 Aug 2008 06:05:52 am; edited 1 time in total
Back to top
tr1p1ea


Elite


Joined: 03 Aug 2003
Posts: 870

Posted: 19 Aug 2008 06:24:02 am    Post subject:

I never really like that approach to animated tiles for some reason. Have you thought about integrating fastcopy into your mapper?

Last edited by Guest on 19 Aug 2008 06:26:32 am; edited 1 time in total
Back to top
Cryzbl


Newbie


Joined: 20 Jun 2008
Posts: 46

Posted: 19 Aug 2008 01:09:34 pm    Post subject:

tr1p1ea wrote:
I never really like that approach to animated tiles for some reason. Have you thought about integrating fastcopy into your mapper?
[post="126135"]<{POST_SNAPBACK}>[/post]

I'm not sure what you mean, what is there to integrate?
Back to top
calc84maniac


Elite


Joined: 22 Jan 2007
Posts: 770

Posted: 19 Aug 2008 01:17:46 pm    Post subject:

As in, let the tilemap drawing code also output the previous contents of the buffer to the screen.
Back to top
Liazon
title goes here


Bandwidth Hog


Joined: 01 Nov 2005
Posts: 2007

Posted: 19 Aug 2008 06:26:09 pm    Post subject:

basically, you send one byte of the buffer to the LCD, and then during the wait, you write one byte new data to the buffer. instead of wasting time waiting, you can use that time to do a consistent operation for ever one of those 768 bytes.

Jim did this, but I'm guessing tr1p1ea that you've probably optimized a lot?

btw tr1p1ea, do u integrate the LCD write and the buffer draw in Crystal Elements?


Last edited by Guest on 19 Aug 2008 06:26:29 pm; edited 1 time in total
Back to top
tr1p1ea


Elite


Joined: 03 Aug 2003
Posts: 870

Posted: 19 Aug 2008 10:45:53 pm    Post subject:

CrASH_Man's mapper (the one being used in sonic iirc) is a really good example of a highly optimised mapper. Jims rewrite of dwedits mapper is also a good example.

Liazon: Since mario is a grayscale game all the LCD interaction is required to be part of the interrupt and not the mapper.
Back to top
Cryzbl


Newbie


Joined: 20 Jun 2008
Posts: 46

Posted: 20 Aug 2008 09:45:58 am    Post subject:

calc84maniac wrote:
As in, let the tilemap drawing code also output the previous contents of the buffer to the screen.
[post="126152"]<{POST_SNAPBACK}>[/post]

Hmm, that would be perfectly suited if you redraw the tilemap every frame, but the shifting the buffer method doesn't allow this trick.

On a second thought... It would be possible... Saving you the "copy buffer" part. Hmm.


Last edited by Guest on 20 Aug 2008 11:07:50 am; edited 1 time in total
Back to top
Liazon
title goes here


Bandwidth Hog


Joined: 01 Nov 2005
Posts: 2007

Posted: 20 Aug 2008 06:17:42 pm    Post subject:

tr1p1ea wrote:
CrASH_Man's mapper (the one being used in sonic iirc) is a really good example of a highly optimised mapper. Jims rewrite of dwedits mapper is also a good example.

Liazon: Since mario is a grayscale game all the LCD interaction is required to be part of the interrupt and not the mapper.
[post="126183"]<{POST_SNAPBACK}>[/post]


ya, that's what Jim said awhile back, so it made me wonder where you were optimizing in crystal elements and SSB. the speed (assuming it's 6mhz) is really impressive. too bad MC is down.
Back to top
Display posts from previous:   
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
    » Goto page 1, 2  Next
» View previous topic :: View next topic  
Page 1 of 2 » All times are UTC - 5 Hours

 

Advertisement