Registered User
Join Date: Oct 2008
Location: Finland
Posts: 643
|
8-Way Scrolling Beta
Quote:
Originally Posted by earok
Excellent!
There was one more thing I wanted to bring up. You might have noticed that the screen height the example isn't exactly fullscreen, that's indeed due to complications around having the vertical wrap past line 255.
However, it's perfectly possible to do so, but it's a little more tricky. You do indeed need to use CopperEnd in that case (and ONLY when the wrap is beyond line 255). On top of that, you need to make sure you run at least two wait commands to get to the correct line (since the wait command uses bytes for Y position, you can't specify a line higher than 255 - you need to do two wait commands).
ALSO, it makes it even more complicated if you have a second copper display (say, a UI bar at the bottom). It may be a bit easier if you're exercising total control over the copper, but if you're using Blitz's copperlist commands it may be far more complicated to manage.
For that reason, with the Kiwi's Tale I opted to make the top display end just short of the 255 line, and use all of the space underneath that for UI.
|
Thanks, that's good to know! I will stick to a screen height like this to avoid complications, there's still plenty of room.
I have now almost managed to get 8-way tilemap scrolling to work using coppersplit+corkscrew by looking at http://aminet.net/package/dev/src/ScrollingTrick and the devnotes for Kiwi's Tale.
I would be super grateful if somebody else could have a look at what's wrong. The variable names are the same as in the ScrollingTrick examples and this demo is based on "Scroller_XYLimited". There are sources and html explanations in ScrollingTrick.lha.
The problem: When scrolling down-right there can be graphical errors of tile-wide lines. This is caused by corkscrew scrolling but for some reason I can't repair it. The repairing when switching from left-right or vice-versa is working.
Code:
; ****************************************************
;
; Single buffer 8-way tilemap scrolling demo (corkscrew+coppersplit)
; by MickGyver @ English Amiga Board 2019
; Based on http://aminet.net/package/dev/src/ScrollingTrick
; and earoks dev notes for Kiwi's Tale
;
; TODO: Fix graphic glitch when scrolling down-right (tile needs repairing)
; TODO: Implement double buffering
;
; In order to use the Bitmap newtype, include the resident file
; Blitzlibs:Bb2objtypes.res in the residents list in the compiler options.
;
; ****************************************************
;INCLUDE "helper_functions.bb2"
; Enable running from Workbench
WBStartup
; Set default type to word
DEFTYPE .w
; Set up interrupt 5 so we can cap to 25fps etc.
vb=0
SetInt 5
vb+1
End SetInt
; Macros
; Get a tile from the tilemap array
; The bitwise AND 255 is for reading the byte value as unsigned
Macro GetTile
(map(`1, `2) & 255)-1
End Macro
; Round down value to nearest tile width
; -16 is the same as ~(#TILE_W-1)
Macro RoundToBlockWidth
`1 & -16
End Macro
; Round down value to nearest tile width
Macro RoundToBlockHeight
`1 & -16
End Macro
; Frame (for debugging)
Macro Frame
Box `1+1,`2+1,`1+14,`2+14,`3
End Macro
; Constants
#SCR_W=320 ; Width of visible screen
#SCR_H=208 ; Height of visible screen
#HUD_H=40 ; Height of HUD
#TILE_W=16 ; Width of tiles
#TILE_H=16 ; Height of tiles
#TILE_XSHIFT=4 ; Set according to #TILE_W (16->4, 32->5)
#TILE_YSHIFT=4 ; Set according to #TILE_H (16->4, 32->5)
#MAP_W=82 ; Tilemap width (number of tiles wide)
#MAP_H=51 ; Tilemap height (number of tiles high)
#SCROLL_SPEED=1 ; Scroll speed, this equals the number of tiles that needs to be drawn every frame
; Calculated constants (from constants above)
#SCR_XCOUNT=10 ; Total number of screens horisontally in tilemap
#TILE_XPAD=#TILE_W ; The horisontal tile padding, for each side
#TILE_YPAD=#TILE_H ; The vertical tile padding
#TILE_XANDNOT=0-#TILE_W ; Used for inverted bitshift AND to get nearest multiple of TILE_W
#TILE_YANDNOT=0-#TILE_H ; Used for inverted bitshift AND to get nearest multiple of TILE_H
#BM_W=#SCR_W+#TILE_XPAD*2 ; Buffer bitmap width (screen width + padding)
#BM_H=#SCR_H+#TILE_YPAD*2 ; Buffer bitmap height (screen height + padding)
#BUFFER_H=#BM_H ;
#BM_TILES_X=#BM_W/#TILE_W ; Number of tiles that fits horisontally on the buffer bitmap
#BM_TILES_Y=#BM_H/#TILE_H ; Number of tiles that fits vertically on the buffer bitmap
#MAP_XMAX=#MAP_W*#TILE_W-#SCR_W-#TILE_W*2
#MAP_YMAX=#MAP_H*#TILE_H-#SCR_H-#TILE_H*2
#TWOBLOCKS=#BM_TILES_X-#TILE_H
#TWOBLOCKSTEP=#TWOBLOCKS
; Keyboard etc. constants
#KEY_UP=$4C
#KEY_DOWN=$4D
#KEY_LEFT=$4F
#KEY_RIGHT=$4E
#KEY_ESCAPE=$45
#KEY_BACKSPACE=$41
#DIR_NONE=0
#DIR_UP=1
#DIR_DOWN=2
#DIR_LEFT=3
#DIR_RIGHT=4
; Tiles bitmap
#TILECOUNT_X=19 ; The number of tiles horisontally in the tiles bitmap
#TILECOUNT_Y=12 ; The number of tiles vertically in the tiles bitmap
#TILE_BLUE=205
#TILE_YELLOW=207
#TILE_ORANGE=208
#WHITE=18
#YELLOW=19
#TURQOISE=20
#ORANGE=21
#PINK=22
; Global variables
db.b=0 ; Keeps track of double buffer bitmap used (not in use currently)
x=0
y=0
x2=0
y2=0
ymax=0
i=0
mapx=0
mapy=0
tile=0
map_w=0
map_h=0
mapposx=0 ; Map X position, increments infinitely
mapposy=0
blit_yoffset.b=0 ; Y offset for blitting due to corkscrew scroll
scroll_dir_prev.b=#DIR_NONE
bp.l=0
videoposx=0
videoposy=0
show_y=0
splitline=0
splitoffset=0
; Create two bitmaps for double buffered display
BitMap 0,#BM_W,#BUFFER_H+20,5
BitMap 1,#BM_W,#BUFFER_H+20,5
BitMap 2,#SCR_W,#HUD_H,5
; Create a bitmap for the tiles
BitMap 3,#TILECOUNT_X*#TILE_W,#TILECOUNT_Y*#TILE_H,5
; Load the tiles to bitmap 2
LoadBitMap 3,"tiles.iff"
; Load the palette from the same image
LoadPalette 0,"tiles.iff"
; Create shapes for all the tiles (increase the max number of shapes in compiler options to 255)
i = 0
For y=0 To #TILECOUNT_Y-1
For x=0 To #TILECOUNT_X-1
GetaShape i, x LSL #TILE_XSHIFT, y LSL #TILE_YSHIFT, #TILE_W, #TILE_H
i+1
Next
Next
; Helper tiles for debugging
;LoadBitMap 3,"tiles_helpers.iff"
;helpers_start=i
;For i=helpers_start To helpers_start+5
; GetaShape i, i LSL #TILE_XSHIFT, 0, #TILE_W, #TILE_H
;Next
; Free the bitmap used to load the tiles
Free BitMap 3
; Read the tile map
If ReadFile(0,"world.map")
; First two words are width and height of tilemap
ReadMem 0,&map_w,2
ReadMem 0,&map_h,2
; Create a two dimensional array for the tilemap
Dim map.b(map_w-1, map_h-1)
; Read the entire tilemap into the array
ReadMem 0,&map(0,0),map_w*map_h
; Close the tilemap file
CloseFile 0
EndIf
; Wait for disk activity to finish before going into Blitz mode
VWait 100
; Go into blitz mode and enable blitzkeys
BLITZ
BlitzKeys On
; Create copperlists for the display
InitCopList 0,44,#SCR_H,$15,8,32,12 ; y=44, lores, smoothscroll, 5 bitplanes, 8 sprites, 32 colours, 12 copper commands
InitCopList 1,46+#SCR_H,#HUD_H,$15,8,32,12 ; y=44, lores, smoothscroll, 5 bitplanes, 8 sprites, 32 colours, 12 copper commands
; Create a display using copperlist 0 and 1
CreateDisplay 0,1
; Use palette 0 (second parameter) for copperlist 0 (first parameter)
DisplayPalette 0,0
DisplayPalette 1,0
; Output text to bitmap 2 (HUD)
BitMapOutput 2
Colour 18,4
Use BitMap 2
Cls 4
; Use first bitmap
Use BitMap db
; Draw tiles to fill the whole bitmap
For y = 0 To #BM_TILES_Y-1
For x = 0 To #BM_TILES_X-1
; Map is exported from Tiled, so the value 0 means no tile, because of that the tile value is reduced by one
Block !GetTile{x,y}, x LSL #TILE_XSHIFT, y LSL #TILE_YSHIFT
Next
Next
; Copy bitmap 0 to bitmap 1 so both double buffer bitmaps are identical
CopyBitMap 0,1
; Display HUD bitmap on second copperlist
DisplayBitMap 1,2,0,0
; Get the pointer to the front buffer bitmap
*b.bitmap = Addr Bitmap(db)
*b\_xclip = $7FFF
block_videoposy=0
stepx=0
stepy=0
mapblocky=0
mapblockx=0
time_taken=0
time_max=0
timeq.q=0.0
time_dir_pressed=0
; The main loop
MainLoop:
Repeat
; Wait for vertical blank
VWait 1
vb=0
; Set screen position
show_y = (videoposy+#TILE_H) MOD #BM_H
splitline = #BM_H-show_y
; Show bitmap 0 on copperlist 0 at offset
DisplayBitMap 0,db,mapposx+#TILE_XPAD,show_y
; Switch to other double buffer bitmap
; db = 1-db
; Use BitMap db
;StartTimer{}
; Reset the copper, this is needed for the library to work
CopperReset 0, 0
; Update the copperlist
; Are we displaying over the bottom edge?
If splitline < #SCR_H
; Scroll to the correct position
splitoffset = (mapposx+#TILE_XPAD) / 8
; Wait until the bottom edge
CopperWait 0, splitline + 44
; Reset the display to the top of the bitmap
bp.l = *b\_data[0] + splitoffset
CopperMove $0e0, bp lsr 16
CopperMove $0e2, bp & $ffff
bp.l = *b\_data[1] + splitoffset
CopperMove $0e4, bp lsr 16
CopperMove $0e6, bp & $ffff
bp.l = *b\_data[2] + splitoffset
CopperMove $0e8, bp lsr 16
CopperMove $0ea, bp & $ffff
bp.l = *b\_data[3] + splitoffset
CopperMove $0ec, bp lsr 16
CopperMove $0ee, bp & $ffff
bp.l = *b\_data[4] + splitoffset
CopperMove $0f0, bp lsr 16
CopperMove $0f2, bp & $ffff
Else
; If we're not displaying over the bottom edge, just wipe out our copper commands
CopperMove $1fe, 0 : CopperMove $1fe, 0 : CopperMove $1fe, 0
CopperMove $1fe, 0 : CopperMove $1fe, 0 : CopperMove $1fe, 0
CopperMove $1fe, 0 : CopperMove $1fe, 0 : CopperMove $1fe, 0
CopperMove $1fe, 0 : CopperMove $1fe, 0 : CopperMove $1fe, 0
EndIf
; Reset the max value for the timer?
If RawStatus(#KEY_BACKSPACE)
time_max=0
EndIf
; Scrolling up?
If RawStatus(#KEY_UP)
BSR ScrollUp : BSR ScrollUp : BSR ScrollUp
Else
; Scrolling down?
If RawStatus(#KEY_DOWN)
BSR ScrollDown : BSR ScrollDown : BSR ScrollDown
EndIf
EndIf
; Scrolling left?
If RawStatus(#KEY_LEFT)
BSR ScrollLeft : BSR ScrollLeft : BSR ScrollLeft
Else
; Scrolling right?
If RawStatus(#KEY_RIGHT)
BSR ScrollRight : BSR ScrollRight : BSR ScrollRight
EndIf
EndIf
; Stop timer
;time_taken=EndTimer{}
; Check the result of the timer before printing
If time_taken>time_max
time_max=time_taken
timeq=time_max*time_constant
EndIf
; Print stuff to HUD
Locate 0,0 : Print "mapposx:",mapposx,",mapposy:",mapposy," "
Locate 0,1 : Print "mapblockx:",mapblockx,"mapblocky:",mapblocky," "
Locate 0,2 : Print "stepx:",stepx,",block_videoposy:",block_videoposy," "
Locate 0,3 : Print "timer:",0,",timer_max:",timeq,"us "
Until RawStatus(#KEY_ESCAPE)=-1
; Quit game
End
ScrollUp:
If mapposy > 0
; Increase position first
mapposy-1
mapblocky=mapposy LSR #TILE_YSHIFT
stepy=mapposy & (#TILE_H-1)
videoposy-1
If videoposy<0
videoposy+#BM_H
EndIf
If stepy = (#TILE_H-1)
block_videoposy-#TILE_H
If block_videoposy<0
block_videoposy+#BM_H
EndIf
If stepx > 0
; step 1: blit the 1st block in the fillup
mapx=mapblockx+#BM_TILES_X
mapy=mapblocky+1
x=!RoundToBlockWidth{videoposx}
y=(block_videoposy+#TILE_H) MOD #BM_H
Block !GetTile{mapx,mapy}, x+#BM_W, y
; step 2: remove the (former) bottommost fill up block
; we blit a 'left' block
If stepx+1 < #BM_TILES_Y
mapy=stepx+1
;Else
; mapy=#BM_TILES_Y-1
;EndIf
y = (block_videoposy+(mapy LSL #TILE_YSHIFT)) MOD #BM_H
mapx-#BM_TILES_X
mapy+mapblocky
Block !GetTile{mapx,mapy}, x, y
; Repair broken tile if needed
If scroll_dir_prev=#DIR_RIGHT ; OK!!!
If stepx+2 < #BM_TILES_Y
y2=y+#TILE_H
If y2>=#BM_H Then y2-#BM_H
Block !GetTile{mapx,mapy+1}, x, y2
EndIf
EndIf
; Set previous direction as left
scroll_dir_prev=#DIR_LEFT
EndIf
EndIf
EndIf
mapx=stepy
mapy=mapblocky
y=block_videoposy
If mapx >= #TWOBLOCKSTEP
; blit only one block
mapx+#TWOBLOCKSTEP
x=mapx LSL #TILE_XSHIFT + !RoundToBlockWidth{videoposx}
mapx+mapblockx
Block !GetTile{mapx,mapy}, x, y
Else
; Draw two tiles
mapx=mapx LSL 1
x=mapx LSL #TILE_XSHIFT + !RoundToBlockWidth{videoposx}
mapx+mapblockx
Block !GetTile{mapx,mapy}, x, y
Block !GetTile{mapx+1,mapy}, x+#TILE_W, y
EndIf
EndIf
RTS
ScrollDown:
If mapposy < #MAP_YMAX
; Get positions
mapx = stepy
mapy = mapblocky + #BM_TILES_Y
y = block_videoposy
If mapx >= #TWOBLOCKSTEP
; blit only one block
mapx+#TWOBLOCKSTEP
x=mapx LSL #TILE_XSHIFT + !RoundToBlockWidth{videoposx}
mapx+mapblockx
Block !GetTile{mapx,mapy}, x, y
Else
; Blit two blocks
mapx = mapx LSL 1
x = mapx LSL #TILE_XSHIFT + !RoundToBlockWidth{videoposx}
mapx+mapblockx
; Get tiles and draw them
Block !GetTile{mapx,mapy}, x, y
Block !GetTile{mapx+1,mapy}, x+#TILE_W, y
EndIf
; Increase position
mapposy+1
mapblocky=mapposy LSR #TILE_YSHIFT
stepy=mapposy & (#TILE_H-1)
videoposy+1
If videoposy>=#BM_H
videoposy-#BM_H
EndIf
; If stepy=0 then we need to fix the horisontal fillup row
If stepy=0
block_videoposy+#TILE_H
If block_videoposy>=#BM_H
block_videoposy-#BM_H
EndIf
If stepx > 0
; step 1: blit the 1st block in the fillup row (y)
mapx=mapblockx
mapy=mapblocky
x=!RoundToBlockWidth{videoposx}
y=block_videoposy ; should be this value already
Block !GetTile{mapx,mapy}, x, y
; step 2: blit the (new) bottommost fill up block
; we blit a 'right-block'
If stepx < #BM_TILES_Y
mapy=stepx
Else
mapy=#BM_TILES_Y-1
EndIf
x+#BM_W
y=(block_videoposy+(mapy LSL #TILE_YSHIFT)) MOD #BM_H
y2=(block_videoposy+((mapy+1) LSL #TILE_YSHIFT)) MOD #BM_H
mapx+#BM_TILES_X
mapy+mapblocky ; Can get out of range, does map need to be one tile higher?
; Repair broken tile if needed
; There's something wrong with down-right scrolling!
; If scroll_dir_prev=#DIR_LEFT
;If stepx < #BM_TILES_Y-1
; y2=y-#TILE_H
; If y2<0 Then y2+#BM_H
;Block !GetTile{mapx,mapy+1}, x, y2
;!Frame{x,y,#YELLOW}
;EndIf
; EndIf
Block !GetTile{mapx,mapy}, x, y
;!Frame{x,y,#WHITE}
; Set previous direction as right
scroll_dir_prev=#DIR_RIGHT
End If
EndIf
EndIf
RTS
ScrollLeft:
If mapposx > 0
; Decrease position
mapposx-1
mapblockx=mapposx LSR #TILE_XSHIFT
stepx=mapposx & (#TILE_W-1)
videoposx-1
If stepx = (#TILE_W-1)
; step 1: blit the block which came in at the left side,
; which might or might not be a fill up block
mapx=mapblockx
mapy=mapblocky
If stepy > 0
; there is a fill up block
mapy+#BM_TILES_Y
EndIf
x=!RoundToBlockWidth{videoposx}
y=block_videoposy
; Mostly ok
Block !GetTile{mapx,mapy}, x, y
; step 2: remove the (former) rightmost fillup-block
mapx=stepy
If mapx > 0
; there is a fill up block
If mapx >= #TWOBLOCKSTEP
mapx+#TWOBLOCKSTEP
Else
mapx=mapx LSL 1 ; mapx=mapx*2
EndIf
x=!RoundToBlockWidth{videoposx}+(mapx LSL #TILE_XSHIFT) ; mapx*#TILE_W
y=block_videoposy
mapx+mapblockx
mapy-#BM_TILES_Y
; Mostly ok
Block !GetTile{mapx,mapy}, x, y
EndIf
EndIf
mapx=mapblockx
mapy=stepx+1
x=!RoundToBlockWidth{videoposx}
y=(block_videoposy+(mapy LSL #TILE_YSHIFT)) MOD #BM_H
mapy+mapblocky
; Repair broken tile if needed
If scroll_dir_prev=#DIR_RIGHT ; OK!!!!
If stepx+2 < #BM_TILES_Y
y2=y+#TILE_H
If y2>=#BM_H Then y2-#BM_H
Block !GetTile{mapx,mapy+1}, x, y2
EndIf
EndIf
If stepx+1 < #BM_TILES_Y ; Don't draw tiles every step, it's not needed/wanted
Block !GetTile{mapx,mapy}, x, y
EndIf
If stepx > 0
scroll_dir_prev=#DIR_LEFT
Else
scroll_dir_prev=#DIR_NONE
EndIf
EndIf
RTS
ScrollRight:
If mapposx < #MAP_XMAX
mapx=mapblockx + #BM_TILES_X
mapy=stepx+1
x=!RoundToBlockWidth{videoposx}
y=(block_videoposy+(mapy LSL #TILE_YSHIFT)) MOD #BM_H
;y2=(block_videoposy+((mapy-1) LSL #TILE_YSHIFT)) MOD #BM_H
mapy+mapblocky
; Repair broken tile if needed
If scroll_dir_prev=#DIR_LEFT
If stepx > 0 AND stepx < #BM_TILES_Y-1
y2=y-#TILE_H
If y2<0 Then y2+#BM_H
Block !GetTile{mapx,mapy-1}, x+#BM_W, y2
EndIf
EndIf
; Blit one block
If stepx+1 < #BM_TILES_Y ; Don't draw tiles every step, it's not needed/wanted
Block !GetTile{mapx,mapy}, x+#BM_W, y
EndIf
; Increase position
mapposx+1
mapblockx=mapposx LSR #TILE_XSHIFT
stepx=mapposx & (#TILE_W-1)
videoposx+1
If stepx = 0
; step 1: blit the block which came in at the right side, ; which is never a fill up block
mapx=mapblockx+#BM_TILES_X-1
mapy=mapblocky
x=!RoundToBlockWidth{videoposx}+(#BM_TILES_X-1) LSL #TILE_XSHIFT
y=block_videoposy
; Seems ok
Block !GetTile{mapx,mapy}, x, y
; step 2: blit the (new) rightmost fillup-block
mapx=stepy
If mapx > 0
; there is a fill up block
If mapx >= #TWOBLOCKSTEP
mapx = mapx + (#TWOBLOCKSTEP-1)
Else
mapx = mapx LSL 1 - 1
EndIf
x=!RoundToBlockWidth{videoposx} + (mapx LSL #TILE_XSHIFT)
y=block_videoposy
mapx+mapblockx
; Seems ok
Block !GetTile{mapx,mapy+#BM_TILES_Y}, x, y
EndIf
EndIf
If stepx > 0
scroll_dir_prev=#DIR_RIGHT
Else
scroll_dir_prev=#DIR_NONE
EndIf
EndIf
RTS
EDIT: Removed attachment, new working version available.
Last edited by MickGyver; 11 March 2019 at 10:38.
Reason: Removed the attachment due to updated version
|