View Single Post
Old 26 February 2019, 21:52   #9
MickGyver
Registered User
 
MickGyver's Avatar
 
Join Date: Oct 2008
Location: Finland
Posts: 643
8-Way Scrolling Beta

Quote:
Originally Posted by earok View Post
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
MickGyver is offline  
 
Page generated in 0.04771 seconds with 11 queries