Jump to content

Recommended Posts

Hi everyone,

 

I'm going to list some tricks, tips and examples for using 7800basic. Many have also been generously offered by Mike @RevEng and Lewis @Muddyfunster. I'll provide a Table of Contents so they will be easy to find as I add them or have questions about them.

 

Display, Sprites and Images

Clear Palette

Clear Palette (Double-Buffer)

Sprite Animation

TallSprite [Updated]

Quick Score [Updated]

Centering Text

Singlewide Text in Doublewide Mode [Updated]

 

Sound

Copy TIA Sound to Ram

 

Controls

Read Controller

 

Data/Tables

Large Data Table > 256 Bytes

 

 

  • Like 7
  • Thanks 4
Link to comment
https://forums.atariage.com/topic/308806-7800basic-tips-tricks-and-examples/
Share on other sites

Clear Palette

I have found particularly as you change screen between banks it's a great idea to total black the display BEFORE changing banks otherwise you can have screen and color artefacting issues as you switching to the next screen.  For example your Title Screen in bank 7 and the user presses the button to start the game.  This may result in you returning to bank 1 to initialise your game and begin the gameplay (either in bank 1 or another bank).   A call to this function (I keep this in the last shared bank) will ensure screen artefacting is kept to a minimum before your next screen is built.

ClearPalette
 rem background
 BACKGRND = $00
 rem palettes
 P0C1 = $00 : P0C2 = $00 : P0C3 = $00
 P1C1 = $00 : P1C2 = $00 : P1C3 = $00
 P2C1 = $00 : P2C2 = $00 : P2C3 = $00
 P3C1 = $00 : P3C2 = $00 : P3C3 = $00
 P4C1 = $00 : P4C2 = $00 : P4C3 = $00
 P5C1 = $00 : P5C2 = $00 : P5C3 = $00
 P6C1 = $00 : P6C2 = $00 : P6C3 = $00
 P7C1 = $00 : P7C2 = $00 : P7C3 = $00
 rem screen
 clearscreen : savescreen : drawscreen
 return

 

  • Like 4

Read Controller

Keeping track of what controllers you are accessing can depending on the game be a little tricky at times. 7800basic offers access to various controllers such as 1 or 2 button joysticks, paddles, driving wheels and mice and these can be easily configured. But what about tracking things such as debounce, button press/held states or multiple controls where might be easier to do a single check? 

 

As I developed Arkanoid I needed to to find ways to track all these things so I came up with a single function I call at the start of each frame.  I can then check the status of some bits to do most of the work for me.  You can expand these functions with additional checks for things such as paddle buttons or for 1 button joysticks tag both buttons as pressed as I have done in Arkanoid to allow the use of both the laser and slower vaus speeds. 

 

Basic

 rem controller dirs
 dim controllerState = var0
 dim controller_debounce_bit0 = var0
 dim controller_left_bit1 = var0
 dim controller_right_bit2 = var0
 dim controller_up_bit3 = var0
 dim controller_down_bit4 = var0
 rem controller buttons
 dim controllerButtonState = var1
 dim controller_button_debounce_bit0 = var1
 dim controller_button1_press_bit1 = var1
 dim controller_button2_press_bit4 = var1
 
 ReadController
  rem clear (don't touch debounce state)
  controllerState = controllerState&%00000001
  controllerButtonState = controllerButtonState&%00000001
 
  rem joystick
  if !joy0any then goto _readControllerSkipJoystickDirectionCheck
  if joy0left then controller_left_bit1{1} = 1
  if joy0right then controller_right_bit2{2} = 1
  if joy0up then controller_up_bit3{3} = 1
  if joy0down then controller_down_bit4{4} = 1
 
 _readControllerSkipJoystickDirectionCheck
  rem left port
  rem button 1 press
  if joy0fire1 then controller_button1_press_bit1{1} = 1
  rem button 2 press
  if joy0fire0 then controller_button2_press_bit4{4} = 1
 
  rem clear debounce? (nothing chosen)
  if controllerState = %00000001 then controller_debounce_bit0{0} = 0
  if controllerButtonState = %00000001 then controller_button_debounce_bit0{0} = 0
 
  rem exit
  return

 

Advanced

This advanced function adds additional tracking for determining whether a player is quickly tapping a button, pressing a button (this is your general check) or holding down a button. I needed to determine in Millie and Molly whether the player was tapping the button (switch between Millie and Molly) or holding the button (activate the rewind feature).

 rem controller dirs
 dim controllerState = var0
 dim controller_debounce_bit0 = var0
 dim controller_left_bit1 = var0
 dim controller_right_bit2 = var0
 dim controller_up_bit3 = var0
 dim controller_down_bit4 = var0
 rem controller buttons
 dim controllerButtonState = var1
 dim controller_button_debounce_bit0 = var1
 dim controller_button1_press_bit1 = var1
 dim controller_button1_tap_bit2 = var1
 dim controller_button1_hold_bit3 = var1
 dim controller_button2_press_bit4 = var1
 dim controller_button2_tap_bit5 = var1
 dim controller_button2_hold_bit6 = var1
 rem controller button held counter
 dim controller_button1_counter = var2
 dim controller_button2_counter = var3
 
 ReadController
  rem clear (don't touch debounce state)
  controllerState = controllerState&%00000001
  controllerButtonState = controllerButtonState&%00000001
 
  rem joystick
  if !joy0any then goto _readControllerSkipJoystickDirectionCheck
  if joy0left then controller_left_bit1{1} = 1
  if joy0right then controller_right_bit2{2} = 1
  if joy0up then controller_up_bit3{3} = 1
  if joy0down then controller_down_bit4{4} = 1
 
 _readControllerSkipJoystickDirectionCheck
  rem left port
  rem button 1 press
  if joy0fire1 then controller_button1_press_bit1{1} = 1
  rem button 2 press
  if joy0fire0 then controller_button2_press_bit4{4} = 1
 
  rem increment button counters?
  if controller_button1_press_bit1{1} then controller_button1_counter = controller_button1_counter+1 : if controller_button1_counter > 99 then controller_button1_counter = 99
  if controller_button2_press_bit4{4} then controller_button2_counter = controller_button2_counter+1 : if controller_button2_counter > 99 then controller_button2_counter = 99
  
  rem change the value 15 to lengthen or shorten the tap/hold 
  rem button hold?
  if controller_button1_counter > 15 then controller_button1_hold_bit3{3} = 1
  if controller_button2_counter > 15 then controller_button2_hold_bit6{6} = 1
  rem button tap? 
  if !controller_button1_press_bit1{1} && controller_button1_counter > 0 && controller_button1_counter <= 15 then controller_button1_tap_bit2{2} = 1
  if !controller_button2_press_bit4{4} && controller_button2_counter > 0 && controller_button2_counter <= 15 then controller_button2_tap_bit5{5} = 1
 
  rem clear counters
  if !controller_button1_press_bit1{1} then controller_button1_counter = 0
  if !controller_button2_press_bit4{4} then controller_button2_counter = 0
 
  rem clear debounce? (nothing chosen)
  if controllerState = %00000001 then controller_debounce_bit0{0} = 0
  if controllerButtonState = %00000001 then controller_button_debounce_bit0{0} = 0
 
  rem exit
  return

 

Example

Here is a basic Main Loop example where a player could move left/right and press a button. Using the debounce flags they will need to release the joystick to register before they can register another direction or button change (signified by the sound effect)

 set romsize 48k
 
 rem controller dirs
 dim controllerState = var0
 dim controller_debounce_bit0 = var0
 dim controller_left_bit1 = var0
 dim controller_right_bit2 = var0
 dim controller_up_bit3 = var0
 dim controller_down_bit4 = var0
 rem controller buttons
 dim controllerButtonState = var1
 dim controller_button_debounce_bit0 = var1
 dim controller_button1_press_bit1 = var1
 dim controller_button2_press_bit4 = var1

MainLoopInitialise
 rem set debounce states
 controller_debounce_bit0{0} = 1 : controller_button_debounce_bit0{0} = 1
 
MainLoop
 rem controls
 gosub ReadController
 
 rem check joystick
 rem is the player currently moving in a direction or pressing a button? if so skip
 if controller_debounce_bit0{0} || controller_button_debounce_bit0{0} then goto _mainLoopSkipJoystickCheck
 
 rem check directions
 rem if the player is moving in a direction set the debounce flag so they need release the joystick to continue move again
 if controller_left_bit1{1} then controller_debounce_bit0{0} = 1 : playsfx sfx_ding 7 : BACKGRND = rand
 if controller_right_bit2{2} then controller_debounce_bit0{0} = 1 : playsfx sfx_ding 9 : BACKGRND = rand
 if controller_up_bit3{3} then controller_debounce_bit0{0} = 1 : playsfx sfx_ding 8 : BACKGRND = rand
 if controller_down_bit4{4} then controller_debounce_bit0{0} = 1 : playsfx sfx_ding 6 : BACKGRND = rand

 rem check buttons
 rem if the player is pressing a button set the debounce flag so they need to release the button to fire again
 rem button?
 if controller_button1_press_bit1{1} then controller_button_debounce_bit0{0} = 1 : playsfx sfx_fire 6 : BACKGRND = rand
 
_mainLoopSkipJoystickCheck
 rem draw
 clearscreen
 rem ...
 drawscreen
 
 rem continue
 goto MainLoop


ReadController
 rem clear (don't touch debounce state)
 controllerState = controllerState&%00000001
 controllerButtonState = controllerButtonState&%00000001
 
 rem joystick
 if !joy0any then goto _readControllerSkipJoystickDirectionCheck
 if joy0left then controller_left_bit1{1} = 1
 if joy0right then controller_right_bit2{2} = 1
 if joy0up then controller_up_bit3{3} = 1
 if joy0down then controller_down_bit4{4} = 1
 
_readControllerSkipJoystickDirectionCheck
 rem left port
 rem button 1 press
 if joy0fire1 then controller_button1_press_bit1{1} = 1
 rem button 2 press
 if joy0fire0 then controller_button2_press_bit4{4} = 1
  
 rem clear debounce? (nothing chosen)
 if controllerState = %00000001 then controller_debounce_bit0{0} = 0
 if controllerButtonState = %00000001 then controller_button_debounce_bit0{0} = 0
 
 rem exit
 return
 
 data sfx_ding
 16, 0, 1; version, priority, frames per chunk
 $01,$04,$08 ; 1st chunk of freq,channel,volume data
 $01,$04,$04; 2nd chunk
 $01,$04,$07 ; 1st chunk of freq,channel,volume data
 $01,$04,$03;
 $01,$04,$06 ; 3rd chunk
 $01,$04,$02 ; 4th chunk
 $01,$04,$05 ; 1st chunk of freq,channel,volume data
 $01,$04,$01; 

 $01,$04,$04 ; 5th chunk
 $01,$04,$00
 $01,$04,$03 ; 1st chunk of freq,channel,volume data
 $01,$04,$00;
 $01,$04,$02 ; 1st chunk of freq,channel,volume data
 $01,$04,$00;
 $01,$04,$01 ; 1st chunk of freq,channel,volume data
 $01,$04,$00;

 $00,$00,$00 ; End Of Sound marker
end
 data sfx_fire
 16, 1, 1; version, priority, frames per chunk
 $01,$0c,$08 ; 1st chunk of freq,channel,volume data
 $07,$0c,$08; 2nd chunk
 $09,$0c,$08 ; 1st chunk of freq,channel,volume data
 $0b,$0c,$08
   
 $01,$0c,$04; 1st chunk of freq,channel,volume data
 $07,$0c,$04; 2nd chunk
 $09,$0c,$04
 $0b,$0c,$04
  
 $01,$0c,$02 ; 1st chunk of freq,channel,volume data
 $07,$0c,$02; 2nd chunk
 $09,$0c,$02   
 $0b,$0c,$02
   
 $00,$00,$00
end

 

Tips

Before entering a new screen such as a title or game over screen where you check for a button press, set the debounce flags before starting your screen loop. That way you don't skip through the screen if the player is still holding down the button from the prior screen.

controller_debounce_bit0{0} = 1 : controller_button_debounce_bit0{0} = 1

 

  • Like 4

Copy TIA sound into RAM

Sound effects can add a lot your game but as your game grows you may require the same sound to be played from within a number of banks. There are a couple of options:

  • Store the sound effect data in your last shared bank so it can be called from anywhere
  • Duplicate the sound effect data in each of the required banks (using different names to identify them ie. sfx_vaus_laser_bank1, sfx_vaus_laser_bank2)

Depending on the size of your game one or both of these options may not be available.  They way I lay my games out with Bank 1 being the entry bank it doesn't really get used for much other than initialisation tasks and as a returning point when switching between screens such as the splash, title, intro and game.  I often store sound effects in the last shared bank (Bank 8 ) but when you have quite a few things in there to share it quickly becomes full.  Whilst I have started duplicating code in banks (another great trick!) I try where possible to not duplicate stuff such as sound effects. 

 

Example

Recently I have started using Bank 1 to store things I can copy into shared RAM leaving me with valuable space in Bank 8 but still having it accessible from anywhere.

 set romsize 128k
 
 rem vars
 dim buttonDebounce = var0
 
 rem configure RAM location for sound
 dim SFXVAUSLASERADR = $2300
 
 rem copy TIA sound to RAM
 gosub CopyTIASoundToRamBank1 
 
 rem set
 buttonDebounce = 0
 
 rem start
 goto MainLoopBank2 bank2
 

CopyTIASoundToRamBank1
  memcpy SFXVAUSLASERADR sfx_vaus_laser $2a
  return
  
  data sfx_vaus_laser
  16, 1, 1; version, priority, frames per chunk
  $01,$0c,$08 ; 1st chunk of freq,channel,volume data
  $07,$0c,$08; 2nd chunk
  $09,$0c,$08 ; 1st chunk of freq,channel,volume data
  $0b,$0c,$08
    
  $01,$0c,$04; 1st chunk of freq,channel,volume data
  $07,$0c,$04; 2nd chunk
  $09,$0c,$04
  $0b,$0c,$04
   
  $01,$0c,$02 ; 1st chunk of freq,channel,volume data
  $07,$0c,$02; 2nd chunk
  $09,$0c,$02   
  $0b,$0c,$02
    
  $00,$00,$00
end

 rem --------------------------------------------------------------------------
 rem BANK2
 rem --------------------------------------------------------------------------
 bank 2
 
MainLoopBank2
 rem button?
 if buttonDebounce then goto _mainLoopBank2SkipButtonCheck
 if joy0fire1 then buttonDebounce = 1 : playsfx SFXVAUSLASERADR 6 : BACKGRND = rand
 
_mainLoopBank2SkipButtonCheck
 rem clear debounce
 if !joy0fire1 then buttonDebounce = 0
 
 rem draw
 clearscreen
 rem ...
 drawscreen
 
 rem continue
 goto MainLoopBank2

 

Dev Notes

  • This is an advanced technique which should only be used where you are using a multi-bank ROM and understand the appropriate RAM location to store your data.
  • Like 6
2 hours ago, Karl G said:

Thanks for sharing these tips! One question though: why or how would switching banks mess up the display or colors?

The 7800basic game code runs during the visible screen, to maximize the number of cycles available to the game. If you include your graphics into a switchable bank, then bank-switching to another bank will cause the graphics rom to be replaced with whatever is at your bankswitch destination. Maria doesn't understand bank-switching, so it has no way to get to your pre-bankswitch graphics. (outside of exotic bank-switching schemes, anyway)

 

To avoid glitching in these circumstances, you can either:

  • place your graphics in a perma-bank (which has limited space)
  • put a copy of the graphics in your source bank at the destination bank
  • call "drawwait" before your bankswitch and be sure to return before the next visible screen begins (which wastes the visible cycles)
  • If you're swapping between different kinds of levels or screens, you can just black all the palettes so no graphics are visible (as Matt has demonstrated) as you build the new screen.

As a minor bit of compensation, you can also use this bankswitch graphics-swap effect to skin levels.

  • Like 3
  • Thanks 3

@RevEng Thanks for the explanation on the colour artifacts I figured it was something like " "Maria says so!".  :)

 

I was using something similar to Matt's technique to stop artifacts in EXO (dimming down when changing screen across banks to avoid that splash of colour from the previous screen),  but I found when I moved to double buffering it doesn't work, I guess due to how double buffing builds the screen.

 

I've not yet figured a workaround, but that's a lower priority fix for me. 

 

@mksmith This is great Matt, assume it's ok to share code snips as well for the thread? Would be nice to build up the kind of knowledge base we have for Bb.

Edited by Muddyfunster
  • Like 4
7 hours ago, SlidellMan said:

Hey, thanks for the clever tricks. Maybe you could give tips on how to make bosses that are comprised of several sprites?

Definitely can put something together there - I intend on adding some tips around sprites, tallsprites and animations ?

 

@Karl G @RevEng thanks for explaining that Mike - I was struggling to explain that one in a way that was clear enough to understand. That is the general use case I have seen the artefacting.

 

@Muddyfunster of course mate ?

 

  • Like 3
  • Thanks 1

Sprite Animation

Most games will require some sort of sprite animations eg. players, enemies, bullets, explosions etc.  Depending on your requirements there are many ways to achieve this.

 

Example 1 (Simple increment)

Simple increment of a frame tracking index will update the animation each frame. To repeat the animation we check for the last frame and reset the index back to 0. 

 rem vars
 dim frameIndex = var0

 rem increment frame
 frameIndex = frameIndex + 1 : if frameIndex > 12 then frameIndex = 0
 
 rem draw
 plotsprite frame0 0 70 94 frameIndex

Example 2 (Advanced increment and speed control)

What if we want to control the speed of the animation? There are 2 options do this:

 

Example 2A - Using a delay variable

Say you want to change the animation every x number of frames rather than every frame as in example 1? A delay counter introduces the ability to determine the speed of the animation:

 rem vars
 dim frameIndex = var0
 dim frameDelayCounter = var1
 
 rem increment frame (speed)
 frameDelayCounter = frameDelayCounter + 1 : if frameDelayCounter > 9 then frameDelayCounter = 0 : frameIndex = frameIndex + 1
 if frameIndex > 12 then frameIndex = 0

 rem draw
 plotsprite frame0 0 70 94 frameIndex

Example 2B - Using fixed point variables

Rather than using a delay counter you can instead simplify the delay by using a fixed point variable:

 rem vars
 dim frameIndex = var0.var1

 rem increment frame (speed)
 frameIndex = frameIndex + 0.1 : if frameIndex > 12 then frameIndex = 0

 rem draw
 plotsprite frame0 0 70 94 frameIndex

Depending on your requirements either of these options can be used and each has potential advantages and disadvantages.

 

Example 3 (Animation list)

There are also times you may want a finer control over your animation rather than using a simple repeating loop.  What if we wanted a ping-pong style loop or to use only specific frames in our animation?  We can use a lookup list to determine which animation frame we want shown:

 rem vars
 dim frameIndex = var0.var1
 dim animationFrame = var2

 rem list of frames to animate
 data frameAnimationList
 1,2,3,4,5,6,7,8,9,10,11,12,11,10,9,8,7,6,5,4,3,2
end

 rem increment frame (speed)
 frameIndex = frameIndex + 0.25 : if frameIndex > 21 then frameIndex = 0

 rem lookup frame from animation list
 animationFrame = frameAnimationList[frameIndex]

 rem draw
 plotsprite frame0 0 70 94 animationFrame

 

Full examples

The following attachment contains full working examples for the above: 

SpriteAnimation.zip

 

Dev Notes

  • The sprite_graphic property in the plotsprite command should refer to the first frame in the animation (it is recommended you incgraphic your frames in the base order of the animation)
  • When using the frame property in the plotsprite command you must ensure all frames within that animation exist in the same GFX Block otherwise the animation will display the incorrect frame.  Additional consideration is also required where you are using TallSprites.

 

PLOTSPRITE command

 plotsprite sprite_graphic palette_# x y [frame] [tallheight]
    
 sprite_graphic - name of the included graphic
 palette_# - index of palette (0-7)
 x - x screen co-ordinate
 y - y screen co-ordinate
 frame (optional) - index of the frame to display
 tallheight (optional) - number of zones each tallsprite occupies

 

Edited by mksmith
Updated for changes in 7800basic
  • Like 6

Tall Sprites

Continuing on from the sprite animation, tall sprites work effectively the same (with some minor changes as you will see) but allow you to automatically display sprites much larger than your base zone-height (8 or 16 pixels) taking away a lot of complexity. The key changes here is setting the tallsprite flag and then when plotting the sprite confirming the zone-height of your sprite and calculating an offset to the start of the next frame. All of these are very simple into implement.

 

Update (7800basic v0.16 onwards)

From this version onwards you no longer need to calculate the offset to the next frame as 7800basic now does this for you!  If you wish to keep compatibility with your existing source but utilise newer versions of 7800basic, add the following line to the configuration at the top of your source:

set deprecated frameheight

Configuring tall sprites 

To use tallsprites all you need to do is activate the setting in your configuration at the top of your source.  This will inform 7800basic to join all incgraphic images greater than the zone-height together.

 set tallsprite on

Example 1 (7800basic v0.15 or earlier)

The additional change over our base animation is determining the frame animation offset.  To do this we need to know how tall our sprite is in relation to the number of zones we have configured.  In our example below we have set an 8 zone-height and our sprite is 24 pixels high - thus our count is 24/8 = 3. 

 

So to calculate our offset we need to multiply the current frameIndex by 3 and in the plotsprite function set frame property to our offset and set the count property to 3 (the number of actual sprites that make up our image).

 set zoneheight 8
 set tallsprite on

 rem vars
 dim frameIndex = var0.var1
 dim frameAnimationOffset = var2

 rem increment frame (speed)
 frameIndex = frameIndex + 0.20 : if frameIndex > 5 then frameIndex = 0

 rem calculate next frame offset
 frameAnimationOffset = frameIndex*3

 rem draw
 plotsprite frame0 0 70 88 frameAnimationOffset 3

Full example

The following attachment contains a fully working example:

tallsprite.zip

 

Example 2 (7800basic v0.16 or later)

As we no longer need to calculate the frame offset, the process is simplified:

 set zoneheight 8
 set tallsprite on

 rem vars
 dim frameIndex = var0.var1

 rem increment frame (speed)
 frameIndex = frameIndex + 0.20 : if frameIndex > 5 then frameIndex = 0

 rem draw
 plotsprite frame0 0 70 88 frameIndex

Full example

The following attachment contains a fully working example:

tallsprite_new.zip

 

Dev Notes

  • When using tallsprites the height of your sprite should always follow the expected zone-height boundaries.  So for a zone-height of 8 your sprite height should follow 8,16,24,32 etc pixels whilst for a 16 zone-height sprite 16,32,48,64 pixels etc
  • As with standard height sprites when using the frame property in plotsprite you must ensure all frames within that animation exist in the same GFX Block otherwise the animation will display the incorrect frame
  • One way to determine if the tallsprite feature is active is to review your GFX Block allocations in the compile log.  Each tallsprite imported will be tagged with the _tallsprite_xx allocation.

 

PLOTSPRITE command

 plotsprite sprite_graphic palette_# x y [frame] [tallheight]
    
 sprite_graphic - name of the included graphic
 palette_# - index of palette (0-7)
 x - x screen co-ordinate
 y - y screen co-ordinate
 frame (optional) - index of the frame to display
 tallheight (optional) - number of zones each tallsprite occupies

 

Edited by mksmith
Updated for changes in 7800basic
  • Like 6

Quick Score (Advanced)

This is an advanced feature and it's use may vary depending on the type of game you are creating.

 

This is one shared to me by @RevEng and allows you to update your score display without having to continually render the output with plotchars/plotvalue each frame, which can be quite costly to your display time.  In effect we end up modifying already-plotted DL objects (by baking the object into our screen with a "savescreen") from RAM. Some further general information about this type of process is available in Mike's 7800basic, under the hood post. 

 

Update

An additional example has been added which caters for doublewide on mode where characters as 8 pixels wide (2x4) rather than the standard 4 pixels.   Thanks again to Mike for the required changes.

 

Configuration

For a six-digit display we create some RAM variables (scoreText vars) to store the score digits and then use the textdata parameter in the plotchars command to draw those into our display (starting at the first variable).  The scoreVar variables are used to split the score into bytes allowing us to quickly update the RAM store.

 rem vars
 dim scoreText0 = var0                        
 dim scoreText1 = var1
 dim scoreText2 = var2
 dim scoreText3 = var3
 dim scoreText4 = var4
 dim scoreText5 = var5
 dim scoreVar0 = score0
 dim scoreVar1 = score0+1
 dim scoreVar2 = score0+2

InitialiseScreen
 clearscreen
 
 rem bake score display into buffer
 plotchars '1up' 1 20 0 3
 plotchars scoreText0 0 12 1 6 

 savescreen
 return

Once your display is configured it's a matter of copying the score in the RAM variables either every frame (recommended) or as the score is updated.  Depending on whether you are using standard or doublewide character modes, use one of the following:

 

Standard character mode (4 pixel characters)

UpdateScore
 rem replace chars with score
 scoreText5 = scoreVar2&$0F
 scoreText4 = scoreVar2/16
 scoreText3 = scoreVar1&$0F
 scoreText2 = scoreVar1/16
 scoreText1 = scoreVar0&$0F
 scoreText0 = scoreVar0/16
 return

Doublewide character mode (8 pixel characters - 2x4)

UpdateScore
 rem replace chars with score
 scoreText5 = (scoreVar2&$0F)*2
 scoreText4 = (scoreVar2/16)*2
 scoreText3 = (scoreVar1&$0F)*2
 scoreText2 = (scoreVar1/16)*2
 scoreText1 = (scoreVar0&$0F)*2
 scoreText0 = (scoreVar0/16)*2
 return

Updating the score

To change the score you just update your score0 variable as per normal:

score0 = score0 + 250

Full examples

The following attachment contains a fully working examples:

QuickScore.zip

QuickScore_DoubleWide.zip

 

PLOTCHARS command

plotchars textdata palette_# x y [chars|extrawide]

textdata - RAM, ROM or literal string 'message' to plot onto the screen 
palette_# - index of palette (0-7)
x - x screen co-ordinate
y - y line (zone) co-ordinate
chars (optional) - number of characters to display 
extrawide (optional) - display chars twice the width of the character size

 

  • Like 5
On 7/13/2020 at 8:13 AM, SlidellMan said:

I wonder how many TIA Sound effects can be stored in each bank?

Hi! That certainly depends on your code requirements. I certainly had no chance to include any in Millie and Molly and am finding not likely for Arkanoid - one of the reasons I needed this feature for this game. There is definitely a real balance in your main game bank ?

  • Like 2
On 7/6/2020 at 12:41 PM, mksmith said:

Clear Palette

I have found particularly as you change screen between banks it's a great idea to total black the display BEFORE changing banks otherwise you can have screen and color artefacting issues as you switching to the next screen.  For example your Title Screen in bank 7 and the user presses the button to start the game.  This may result in you returning to bank 1 to initialise your game and begin the gameplay (either in bank 1 or another bank).   A call to this function (I keep this in the last shared bank) will ensure screen artefacting is kept to a minimum before your next screen is built.


ClearPalette
 rem background
 BACKGRND = $00
 rem palettes
 P0C1 = $00 : P0C2 = $00 : P0C3 = $00
 P1C1 = $00 : P1C2 = $00 : P1C3 = $00
 P2C1 = $00 : P2C2 = $00 : P2C3 = $00
 P3C1 = $00 : P3C2 = $00 : P3C3 = $00
 P4C1 = $00 : P4C2 = $00 : P4C3 = $00
 P5C1 = $00 : P5C2 = $00 : P5C3 = $00
 P6C1 = $00 : P6C2 = $00 : P6C3 = $00
 P7C1 = $00 : P7C2 = $00 : P7C3 = $00
 rem screen
 clearscreen : savescreen
 return

 

You can use almost the same code if you are doublebuffering the display.

 

I found forcing a restorescreen and then a doublebuffer flip worked well and prevented artifacts in exactly the same way as Matt's code. In EXO I'm retrieving screens stored in another bank so I'm going between banks on each screen change. This method eliminates artifacts and colour ghosts (where the colours remain plotted for a frame the same as the previous screen before Maria catches up - it looks "rough" if you have contrasting colours without a dimming routine).

Clear_Palette
 BACKGRND = $00
 P0C1 = $00 : P0C2 = $00 : P0C3 = $00
 P1C1 = $00 : P1C2 = $00 : P1C3 = $00
 P2C1 = $00 : P2C2 = $00 : P2C3 = $00
 P3C1 = $00 : P3C2 = $00 : P3C3 = $00
 P4C1 = $00 : P4C2 = $00 : P4C3 = $00
 P5C1 = $00 : P5C2 = $00 : P5C3 = $00
 P6C1 = $00 : P6C2 = $00 : P6C3 = $00
 P7C1 = $00 : P7C2 = $00 : P7C3 = $00
 clearscreen
 savescreen
 restorescreen
 doublebuffer flip
return

 

 

 

  • Like 3

Easy centering of text

 

Sometimes you want to have centered text rather than left justified, like on a menu or something.

 

An easy way to work out the PLOTCHAR co-ordinates :

  • Step 1 - calculate the length of text you want to plot including spaces
  • Step 2 - multiply by the font type (x4 for a 4 pixel wide font, x8 for an 8 pixel wide font)
  • Step 3 - divide this by 2
  • Step 4 - take this remaining number away from 80 (80 being the middle of the screen)
  • result is your x co-ordinate to center the plotchar.

Example. I want to center the text "GAME MENU"

  • Step 1 - length = 9 (8 letters + 1 space)
  • Step 2 - 8 pixel font so 9*8 = 72
  • Step 3 - 72 / 2 = 36
  • Step 4 - 80-36 = 44
  • result : our x co-ordinate to horizontally center "GAME MENU" is 44.

plotchar 'GAME MENU' 0 44 0

 

I've also added an excel and opendoc file that does the calculation for you. These are provided "as-is".

 

Hope this is useful !

 

plotchar calc.ods plotchar calc.xlsx

Edited by Muddyfunster
  • Like 5
  • 2 months later...
3 hours ago, Lillapojkenpåön said:

I copied quickscore to my code, what am I doing wrong if

 

 plotchars '1up' 3 20 3 3    ;shows up as 23  ]&

 plotchars scoreText0 3 12 3 6    ;doesn't show up at all?

 

The quickscore code was a complete sample program.  If you copied the entire thing to your own code there's possibly conflicts in the dims and characterset/imports going on?  Compiling the example.78b on it's own gives a proper output.  If you're using your own font, it would have to be designed similarly to what's in the example (digits first in particular.)

 

I believe the example also requires the digits/fonts to be added to the bank first so the math will hold up, if you have other graphics being added.  In the example there's only the 1 graphic/font being included anyway.

 

 

 

 

 

  • Like 1
  • Thanks 1
8 hours ago, Mord said:

 

The quickscore code was a complete sample program.  If you copied the entire thing to your own code there's possibly conflicts in the dims and characterset/imports going on?  Compiling the example.78b on it's own gives a proper output.  If you're using your own font, it would have to be designed similarly to what's in the example (digits first in particular.)

Thanks, I'm using the same font, at first the screen was completely scrambled, so I did the characterset right before when they were needed

 

 characterset testsprite

     plotmap tiledMap4colors 0 0 0 20 12 0 0 40

     plotmap tiledMap4colors 0 80 0 20 12 20 0 40

 

 characterset font

 plotchars '1up' 3 20 3 3

 plotchars scoreText0 3 12 4 6 

 

That made the background good but not the font apparently, do I need to re-draw the font in my tileset and only do one characterset or can I use it like I do?

 

8 hours ago, Mord said:

I believe the example also requires the digits/fonts to be added to the bank first so the math will hold up, if you have other graphics being added.  In the example there's only the 1 graphic/font being included anyway.

I don't understand what you mean, "added to the bank" especially? Could you try to explain in other words?

 

I tried doing the

 incgraphic font.png 160A

first thing in my code

 

that made the 6 digits show up, 010101, 

then I changed 160A to 160B in the incgraphic, and that made the text say 1up but now only shows the first 3 digits, 000,

 

So it's allmost working now, except only 3 digits?

I tried to change 1up to "score" but it also only shows first 3 letters, I both changed the number_of_chars parameter and removed it, no luck.

 

 

 

 

2 hours ago, Lillapojkenpåön said:

I don't understand what you mean, "added to the bank" especially? Could you try to explain in other words?

Any time you're adding a graphic to your program with incgraphic, you're "adding it to the bank". IE: a graphics bank. So by adding it to the bank first I just mean make sure it's the first incgraphic command you add.

 

You shouldn't need to be switching it to 160B given the font being used in the example is expecting 160A sized sprites - 160B sprites are basically half the size of a 160A sprite. (a 2 byte 160A sprite is 8 pixels, a 2 byte 160B sprite is 4 pixels)  What you should be seeing, instead of 3 digits, is 6 half-digits (the left hand side of each digit). The 1's might not be showing anything if the left hand is blank, or perhaps they're looking like the rest of the zero from the digit before them?

 

For the characterset, if both font.png and testsprite.png are in the same graphics bank then you should be able to just use "characterset font".  I'm not sure what the effect of using multiple characterset commands in the program would be or if it's even suppose to be possible.

 

Also, perhaps change it back to 160A first, then take a look at what values you're giving to palette P3.  Maybe it's displaying but with the palette is set to the same color as the background color?

 

 

 

  • Like 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...