• Skip to main content
  • Skip to header right navigation
  • Skip to site footer
Retro Game Coders

Retro Game Coders

Retro computer/console game + dev community

  • Home
  • About
  • Blog
  • Retro Resources
    • Retro Gaming Timeline
    • Browser C64 Emulator
    • Best Retro YouTube Channels
    • New Retro Books
    • Raspberry Pi Amiga Emulation
    • MiSTer FPGA Tutorial
    • BMC64 C64 Pi
  • Contact
Home » Programming

Programming the Amiga with AMOS BASIC: Better Tile Scrolling

In the previous article, we created a tile map and did a simple scrolling screen using Screen Copy.

While it worked, and demonstrated some cool aspects of AMOS, it wouldn’t really be efficient for use in an action/arcade game, is there a better option? Also, can we load the map from disk?

There is also another option for scrolling the screen we haven’t investigated, let’s look at that first.

Amiga AMOS Basic Programming Tutorials:
  1. Installing AMOS
  2. Loading images and configuring AMOS screens
  3. “Dual Playfield”
  4. Bobs and Sprites
  5. Maps and Scrolling
  6. Better Tile Scrolling
  7. AMOS AMAL Animation Programming
  8. Playable Shoot-Em-Up Game

Def Scroll

AMOS has a neat feature where you can define scrollable areas, and then specify which area you wish to scroll.

When I think of parts of the screen scrolling, my mind immediately goes to the kind of parallax scrolling where the strip of background closest to you scrolls faster than the most distant.

Here is a simple example:

Here is the code to implement this effect:

' Set up the screen
Screen Open 1,320,200,32,Lowres
Screen Open 0,320,200,32,Lowres
Flash Off : Hide : Paper 0 : Pen 4 : Cls 
' Define the scrolling 
Def Scroll 1,0,0 To 320,8,-1,0
Def Scroll 2,0,8 To 320,16,-2,0
Def Scroll 3,0,16 To 320,24,-4,0
Def Scroll 4,0,24 To 320,32,-8,0
Def Scroll 5,0,32 To 320,56,-16,0
' Put something on screen
For LINE=1 To 15
   Print "Hello World! ";
Next LINE
' Scroll Loop  
While Inkey$=""
   Screen Copy 0,0,0,16,60 To 1,0,0
   Wait Vbl 
   Scroll 1
   Scroll 2
   Scroll 3
   Scroll 4
   Scroll 5
   Screen Copy 1,0,0,16,60 To 0,304,0
Wend 

The main part to take note of is where we “define the scrolling”:

Def Scroll 1,0,0 To 320,8,-1,0

This tells AMOS that we want to set up scrolling area 1, that it starts in the top, left, goes as far as the right of our screen, but only 8 lines down. The scroll direction is -1, so it will scroll left one pixel. On the vertical it doesn’t scroll at all.

We also set up another four zones, getting incrementally faster.

To actually activate the scroll we have to enter

Scroll 1

Where 1 is the number of the zone to scroll. Each time this is called, it will scroll that area of screen by the amount we set up in the definition.

You might also notice that to make it an infinite scroll, we copy the area of screen we are about to scroll off screen to a hidden screen and back again. Otherwise as the screen scrolls we would have a gap where the text used to be.

Under the hood, the Def Scroll approach is simply using the blitter to screen copy in a similar way to what we implemented in the previous tutorial.

Is there a better way?

I am no expert, I only got an Amiga for the first time in 2020, so I am still learning. That said, I have to believe copying large amounts of data on each frame is not going to be the best route.

So far my best results have come from mixing the first example we looked at, using screen offset, and the tile-based approach.

Scrolling Tiles with Offset

Before we get into the code, I need to drop some theory.

If you recall, setting the screen offset moves our viewport, rather than the screen data. It’s like moving your phone camera and seeing the updated view on your phone display. The scene didn’t move, your view of it did.

So we set up a screen much larger than the viewable screen on your monitor, then to get the scroll effect we move the viewport up this virtual screen. When we get to the top we start over at the bottom.

Obviously, like in the example at the top of the page, we need to take care of the gaps left behind, and make it so there doesn’t seem to be a jolting jump.

For now I take a full copy of the screen and paste it down at the bottom before starting the scroll area. Not the best but it’s progress!

I am not entirely sure if the screen copy approach I use in my example code below is better or if I should have tried pasting the tiles twice instead. Let me know if you have a better approach!

AMOS and Loading the Map Data File from Amiga Disk Drive

Before we get into the code, we should talk about loading the map and how AMOS handles text file access.

We set up a simple ASCII text file with our map data, that way for now we can use a text editor to edit our map.

MAPDATA$="vertical_map_2.txt"

Then later we open the file and start reading it, line by line

 
   ' Load the map file from disk  
   Open In 1,MAPDATA$
   
   ' Read the lines and fill the map array  
   MAP_TILE_ROW=0
   
   Print "Loading ..."
   While Not Eof(1)
      
      ' Read a line at a time
      Print MAP_TILE_ROW," ";
      Line Input #1,THIS_LINE$

As each line is read it, we interpret the string by grabbing characters using Mid$ (each tile number is 3 digits, eg. 004, followed by a space).

We increment the row counter and loop, until the end of the file in which case we close the file and continue.

   MAP(9,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,37,4))
      
      Inc MAP_TILE_ROW
   Wend 
   
   
   ' Close the file 
   Close 1

It’s not super fast, but it is convenient and human-readable. There are other options available to us, but it works!

Full Code

The code is still quite raw, proof of concept rather than a finished piece, but hopefully it gives you an idea as a starting point for how a vertically scrolling shooter can have backdrop implemented.

Rem ************************************** 
Rem  
Rem   Vertically scrolling shooter demo
Rem
Rem   by Maker Hacks 
Rem
Rem   2020 
Rem
Rem ***************************************
' Need a bigger buffer for a large map 
Set Buffer 150
' Make a double-height screen + 32px 
Screen Open 0,320,512+32,16,Lowres
Screen Hide : Curs Off : Flash Off : Cls 0
Colour Back $0 : Hide : Screen Show 
Screen Display 0,135,60,320,200
' Debug/Score info area
Screen Open 1,320,20,16,Lowres
Screen Display 1,135,40,320,20
Hide : Curs Off : Flash Off : Cls 13
Screen 0
' Load map tiles 
Load "tile.abk"
' Load sprites 
Load "invader.abk"
Get Sprite Palette 
' Map file (ascii for now) 
MAPDATA$="vertical_map_2.txt"
' Set up a list of colours 
Palette ,,,,,,,,,,,,,,,,Colour(0),Colour(1),Colour(2),Colour(3),Colour(4),Colour(5),Colour(6),Colour(7),Colour(8),Colour(9),Colour(10),Colour(11),Colour(12),Colour(13),Colour(14),Colour(15)
' DEBUG the palette
X=0
For I=0 To 15
   Ink I,I,I
   X=X+8
   Bar X,40 To X+8,48
Next 
' Background to black
Paper 0
' Print colours and numbers
For I=0 To 15
   Pen I
   Print I;
Next 
' See the bob on screen
Paste Bob 170,100,2
'----------------------
' Title screen pause 
Locate 15,12
Print "Press a key"
Wait Key 
Cls 
'----------------------
' Set up map in memory 
' Fixed map length for now 
MAP_HEIGHT=100
Dim MAP(9,MAP_HEIGHT)
' Globals - not elegant but works
Global MAP_HEIGHT,PLYER_X,PLYER_Y,ENEMY_X,ENEMY_Y,SCENERY_X,SCENERY_Y
Global _SCROLL_OFFSET,MAP(),MAP_TILE_ROW,MAPDATA$,THIS_PIXEL_ROW,ROW_COUNTER

' Initialise environment 
INIT
' Where the magic happens
MAIN

Rem Initial setup  
Procedure INIT
   
   ' Load and Draw the initial map  
   MAP_INIT
   
   ' Initial coords for the player sprite 
   PLYER_X=150 : PLYER_Y=200
   
   ' Initial coords for enemy sprite  
   ENEMY_X=100 : ENEMY_Y=10
   ' Start line to draw at
   THIS_PIXEL_ROW=288
   ' Set the screen to start conditions 
   RESTART_SCROLL
   
   ' Uncomment to turn off automatic Bob updates for speed          
   'Bob Update Off  
   
End Proc

Rem Tile painting routine
Procedure _PAINT_ROW
   
   ' Paint a full row of tiles
   Paste Icon 0,THIS_PIXEL_ROW,MAP(0,MAP_TILE_ROW)
   Paste Icon 32,THIS_PIXEL_ROW,MAP(1,MAP_TILE_ROW)
   Paste Icon 64,THIS_PIXEL_ROW,MAP(2,MAP_TILE_ROW)
   Paste Icon 96,THIS_PIXEL_ROW,MAP(3,MAP_TILE_ROW)
   Paste Icon 128,THIS_PIXEL_ROW,MAP(4,MAP_TILE_ROW)
   Paste Icon 160,THIS_PIXEL_ROW,MAP(5,MAP_TILE_ROW)
   Paste Icon 192,THIS_PIXEL_ROW,MAP(6,MAP_TILE_ROW)
   Paste Icon 224,THIS_PIXEL_ROW,MAP(7,MAP_TILE_ROW)
   Paste Icon 256,THIS_PIXEL_ROW,MAP(8,MAP_TILE_ROW)
   Paste Icon 288,THIS_PIXEL_ROW,MAP(9,MAP_TILE_ROW)
   
End Proc

Rem Map loading and drawing routine
Procedure MAP_INIT
   
   
   ' Load the map file from disk  
   Open In 1,MAPDATA$
   
   ' Read the lines and fill the map array  
   MAP_TILE_ROW=0
   
   Print "Loading ..."
   While Not Eof(1)
      
      ' Read a line at a time
      Print MAP_TILE_ROW," ";
      Line Input #1,THIS_LINE$
      
      ' Put the file data into the array 
      MAP(0,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,1,4))
      MAP(1,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,5,4))
      MAP(2,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,9,4))
      MAP(3,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,13,4))
      MAP(4,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,17,4))
      MAP(5,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,21,4))
      MAP(6,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,25,4))
      MAP(7,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,29,4))
      MAP(8,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,33,4))
      MAP(9,MAP_TILE_ROW)=Val(Mid$(THIS_LINE$,37,4))
      
      Inc MAP_TILE_ROW
   Wend 
   
   
   ' Close the file 
   Close 1
   
   ' LOADED LINES = NEW MAP HEIGHT
   MAP_HEIGHT=MAP_TILE_ROW
   
   ' Select the start tile row to put on screen 
   MAP_TILE_ROW=MAP_TILE_ROW-16
   
   ' Screen full of tiles 
   For THIS_PIXEL_ROW=1 To 512 Step 32
      
      ' Paint the row of tiles 
      '_PAINT_ROW
      
      ' Increment the tile row 
      Inc MAP_TILE_ROW
      
   Next THIS_PIXEL_ROW
   
End Proc

Rem Main loop
Procedure MAIN
   
   ' Loop forever unless told otherwise ... 
   Repeat 
      
      ' Debug info 
      Screen 1 : Pen 2 : Paper 0
      Locate 0,0 : Print "                                 "
      Locate 0,0 : Print "OFFSET: ",_SCROLL_OFFSET," ROW: ",MAP_TILE_ROW
      Screen 0
      
      ' Get player input 
      _PLAYER_INPUT
      
      ' Decrement the scroll offset
      Add _SCROLL_OFFSET,-2
      
      
      ' If we get to zero then reset the scroll, otherwise 
      ' scroll by setting the screen offset  
      If _SCROLL_OFFSET>0
         
         ' Paint row of tiles above viewable area 
         Add ROW_COUNTER,2
         If ROW_COUNTER>=32
            ROW_COUNTER=0
            Dec MAP_TILE_ROW
            THIS_PIXEL_ROW=_SCROLL_OFFSET-32
            _PAINT_ROW
         End If 
         
         ' The offset is how we get the scrolling 
         Screen Offset 0,0,_SCROLL_OFFSET
         
      Else 

         ' We got to the top of the virtual screen
         ' So we need to start over at the bottom 
         Bob Off 
         Screen Copy 0,0,-32,320,200 To 0,0,256
         RESTART_SCROLL
         Dec MAP_TILE_ROW
         THIS_PIXEL_ROW=_SCROLL_OFFSET-32
          _PAINT_ROW
         Screen Offset 0,0,_SCROLL_OFFSET
      End If 
      
      
      
      ' Draw player and enemy sprites  
      SPR_DRAW
      
      ' Wait for vertical blank
      Wait Vbl 
      
      
      ' If mouse pressed then quit, otherwise back to the loop!
   Until Mouse Key
   
End Proc
' Reset the screen offset  
Procedure RESTART_SCROLL
   ' Screen offset counter
   _SCROLL_OFFSET=288
   
   ' Reset screen offset to screen bottom 
   Screen Offset 0,0,_SCROLL_OFFSET
   
   ' Set the row in MAP array to count from 
   If MAP_TILE_ROW=<10 Then MAP_TILE_ROW=MAP_HEIGHT-1
   
   ' Reset the pixel row counter  
   ROW_COUNTER=0
   
End Proc
Rem Get player input 
Procedure _PLAYER_INPUT
   
   
   ' If cursor left or right then move player 
   If Key State(79)=True and PLYER_X>112 Then PLYER_X=PLYER_X-4
   If Key State(78)=True and PLYER_X<418 Then PLYER_X=PLYER_X+4
   
   ' Fire button
   If Key State(64)=True Then Shoot 
   
End Proc
' Draw player and enemy sprites  
Procedure SPR_DRAW

   ' Set the obstacle x, y
   SCENERY_Y=512-_SCROLL_OFFSET+4
   If SCENERY_Y>512 Then SCENERY_X=Rnd(288)
   
   ' Bobs are drawn onto the screen memory
   Bob 10,SCENERY_X,SCENERY_Y,4
   Bob 20,SCENERY_X,SCENERY_Y+32,5
   Bob 30,SCENERY_X+32,SCENERY_Y,6
   Bob 40,SCENERY_X+32,SCENERY_Y+32,7
   
   ' Sprites are on a layer above the regular 
   ' viewable screen so are much easier 
   Sprite 0,PLYER_X,PLYER_Y,2
   
   ' Sprite 8 upwards is a special "computed" sprite
   Sprite 8,ENEMY_X+100,ENEMY_Y,1
   
   ' Move enemy down the screen 
   Add ENEMY_Y,6
   
   ' If the enemy drops off bottom
   ' start at a new random X coord
   If ENEMY_Y>300
      ENEMY_X=Rnd(300)+30
      ENEMY_Y=30
   End If 
   
End Proc

If you like it, share it! (Please - because it really helps)

  • Tweet
Category: ProgrammingTag: amiga, amos, basic programming, imported, retro, retrocomputing
Previous Post: « Programming the Amiga with AMOS BASIC: Maps & Scrolling
Next Post: Programming the Amiga with AMOS BASIC: Animation & Scrolling with AMAL »

Retro Game Coders

Retro computer/console game + dev community by Chris Garrett

  • Facebook
  • Twitter
  • Instagram
  • YouTube

Maker Hacks ・ Geeky Game Master

© Copyright 2021 Chris Garrett

Privacy ﹒ Terms of Service

Return to top