GameHandler

Chapter Twenty-Five

Hello, and welcome to Chapter 25!

Hello, and welcome to Chapter 25!

This is a big day! Congratulations if you've made it this far.

For today's tutorial, we're going to transform our game from "Almost" to "Wow!"

We've got a lot to do, but I'll be making this as painless as I possibly can.

Here's what we need to do to take this from "Almost" to "Wow!"

1. "End Game"

Remember that EndGame

Remember that EndGame "Too Bad" line that we added in Chapter 23?

Sure would be nice if our game wrote "Too Bad" to the screen when we died.

That way, as well as "just blowing up", they get a big Game Over style text.

2. Record the Highscores

We should keep the previous high-scores, make a table.

We should keep the previous high-scores, make a table.

What about saving those scores, too?

Store them in the browser's cookie the same way it stores our program, so that the next time you run the game, all of the highscores are still there.

GotoJSE has a lot of functions for loading and saving a certain amount of "Game" information along with our programs, and highscore tables are one of those things.

3. A Better Titlescreen

We're going to need a better titlescreen to show all the highscores.

We're going to need a better titlescreen to show all the highscores.

A nice new sidebar on the titlescreen with the highscores listed, alongside a list of the last 10 player's scores.

We'll add a circular menu to choose between Starting a game, and opening the Options screen.

Ooh, do we need an Option's Screen?

4. Options Screen

We should probably add a little volume slider, and maybe even add one for Music, so that later on if we add Music (!), the player can change that volume too.

We should probably add a little volume slider, and maybe even add one for Music, so that later on if we add Music (!), the player can change that volume too.

That sounds like something that could be useful.

5. Ready to go?

We've a giant todo list of things we're going to add to our game.

We've a giant todo list of things we're going to add to our game.

We've made a list, and checked it twice.

It's time for the big event.

Our game, so far.

// My Game
// by Mr Green
// Created 2025/12/16

Symbol 0,"2__0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0P?0_0/P?0!P0,P0_0/P0,P0?P,00P0_0/P00P,0.P,00P0_0/P00P,0.P,00P0_0/P00P,0.P,00P!0!P!00P,0_0?P@0_0_0_0_0_0/P!0.P!0_0_0_0_0_0.P?0PP0P?0_0_0!PP0_0_0/P.0PP0P.0_0_0/PP0_0_0_0,PP0_0@P_P,0PP0P_P,0.P0_0_0@P0.P0_0_0@P0.P0_0_0@P0.P0@P0@P0@P0.PP0/P0@P0/PP0?P0/P0P!0P0/P0!P?0,P0@P0,P?0@P,0,P0@P0,P,0_0?P,0@P,";
Symbol 1,"2__0_0_0_0_0_0_0_0_0_0_0_0_0_0_00P,0_0_0@P_P,0_0?P_P,0?P0_0?P0?P,0@P0_0?P0_0/P0_0?P0_0/P?0!P?0_0_00P0!P0_0_0!P@0_0_0_0_0_0/P!0.P!0_0_0_0_0_0.P?0PP0P?0_0_0!PP0_0_0/P.0PP0P.0_0_0/PP0_0_0_0,PP0_0!P_P?0PP0P_P!0_0_0_0,PP0_0_0_0,PP0_0_0_0,PP0_0P0@P0_0PP0_0P0@P0_0PP0_0P0P!0P0_0PP0_0P0P0.P0P0_0PP0_0P0P0.P0P0_0P,0/P,0P!0P,0/P.0_0_0_0P.0_0_0_0PP";
Symbol 2,"2__0_0_0_0_0_0_0_0_0_0_0_0_0NN00NN0_0_0/NPQNNQPN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0!NPQ.PN0_0_0?NPPQ.PPN0_0_0.NPPQ.PPN0_0_0.NPPQ.PPN0_0_0.NPPQ.PPN0_0_0.NPQ!PN0_0_0.NPQ!PN0_0_0.NPQ!PN0_0_0,NPPQ!PPN0_0_00NPPQ!PPN0_0_00NPPQ!PPN0_0_00NPPQ!PPN0_0,P_P_P_P0,PS_S_S_SP0PS_S_S_S,PPSSR,S,R,S,RRS,R,S,R,SSP0S_S_S_S,0,S_S_S_S";
Symbol 3,"2__P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P_P/";
Symbol 4,"2__0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_z0_0_0_0020,20_0_0_00;0_0_0_0z0;1;0z0_0_0_0;0_0_0_0020,20_0_0_00z";
Symbol 5,"2__0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0_0?Q_Q_Q_0?Q_Q_Q_";

AntiAlias Off // Turn off AntiAlias, for Crispy Invaders


// Arrays
Dim EnemyAlive(20)
Dim EnemyBullet(50,2)

// Particle Setup

// Clear out settings
ResetParticle

// Setting for Particle type
SetParticleImage 5
SetParticleDirection 0,360
SetParticleSpeed 0.1,0.3

// Save particle style to Variable
StoreParticle ExplodingPiece

// And don't forget to reset, afterwards
ResetParticle

// Main Loop
GameRun=0
Repeat
  If GameRun==0 then Gosub .StartScreen
  If GameRun==1 then Gosub .InGame
Flip
Forever


// This is our Titlescreen
.StartScreen
CLS
ResetDraw

  ShowInvader=1
  If Wrap(Frames,0,20)<10 Then ShowInvader=0
  DrawImg 320,160,ShowInvader

  SetFontSize 32
  Text 320,240,"Titlescreen!",1
  SetFontSize 16
  Text 320,360,"Press Fire to Play",1
  
  If GamePad(ButtonA)
    Gosub StartGame
    GameRun=1
  Endif
Return



// This resets the current level
.NextLevel
  Level=Level+1
  DropDown=480
  For BadGuy=1 to 20
    EnemyAlive(BadGuy)=1
  Next

  // New variables to multiply the EnemyX and Y Angles
  WaveX=Rand(1,3)
  WaveY=Rand(1,3)
  Gap=Rand(3,15)
  
Return


// This prepares our game when the A Button is hit
.StartGame
  Score=0
  Dead=0
  Level=0
  
  PlayerX=320 // Middle of the screen
  PlayerY=440 // Bottom of the screen
  
  BulletX=0  // Reset player's bullet
  BulletY=0


  XPosition=1
  YPosition=2

  For b=0 to 50
    EnemyBullet(b,1)=0
    EnemyBullet(b,2)=1000
  Next

  EnemyBulletTo=0  // We increase this when an enemy fires a bullet.
  EnemyBulletDelay=0 // We'll have a delay between every bullet drop.
  
  Gosub NextLevel
Return



// This is our ingame loop
.InGame
CLS
ResetDraw

  // Starfield using Symbol 4
  Starfield 1,0,4

  // Player
  If Dead==0
    PlayerCollide=DrawImg(PlayerX,PlayerY,2)
    Speed=4
    If GamePad(ButtonLeft)>0.5 then PlayerX=PlayerX-Speed
    If GamePad(ButtonRight)>0.5 then PlayerX=PlayerX+Speed
    // Using >0.5 allows players to use a thumbstick, too!
    
    PlayerX=Limit(PlayerX,32,640-32)
    
    
    // Player's Bullet
    // Keep the Bullet going upwards
    BulletSpeed=16
    BulletY=Limit(BulletY-BulletSpeed, -100,480)
  EndIf
  
  // Draw the Bullet
  SetScale 0.2,0.5

  BulletCollide=DrawImg(BulletX,BulletY,3)

  // Handle Enemy Bullets
  For b=1 to 50
    If EnemyBullet(b, YPosition)<480
      EnemyBullet(b, YPosition)=EnemyBullet(b, YPosition)+6
                    
      ThisCollide=DrawImg(EnemyBullet(b, XPosition),EnemyBullet(b, YPosition),3)
      If Collide(ThisCollide,PlayerCollide) and Dead==0 then Dead=1
    EndIf
  Next

  // And reset things afterwards
  ResetDraw
  
  If BulletY<0 And GamePad(ButtonA) and Dead==0
    BulletX=PlayerX
    BulletY=PlayerY
    PlaySFX("Lazer_2")
  EndIf

  
  // Enemy Section

  // Reuse this to keep the invaders animated
  ShowInvader=1
  if Wrap(Frames,0,20)<10 then ShowInvader=0
  
  CircleSize=160
  // New Variable for Circle Position
  CircleX=320+Sin(Frames)*160
  CircleY=200-DropDown
  DropDown=Limit( DropDown-5 ,0,480)
    // Why not try changing this value, too.
      // The higher the number the faster the drop.
      // Make it slower to have the invaders creep into view!
  
  // Bullet Delay workings
  EnemyBulletDelay=Limit(EnemyBulletDelay-Level, 0,200)


  BaddyCount=0
  // A loop for 20 Bad Guys
  For BadGuy=1 to 20
    // If Alive
    If EnemyAlive(BadGuy)==1
    
      BaddyCount=BaddyCount+1

      // Each enemy should be further around the loop.
      PlusAngle=BadGuy*Gap
      Angle=Wrap(Frames + PlusAngle,0,360)
      EnemyX=CircleX+Cos(Angle*WaveX)*CircleSize
      EnemyY=CircleY+Sin(Angle*WaveY)*CircleSize
      EnemyColour=Wrap(BadGuy,0,7)
      
      // New collision code
      EnemyCollide=DrawImg(EnemyX,EnemyY,ShowInvader,EnemyColour)
      If Collide(BulletCollide,EnemyCollide) And BulletY>0
        // If the bullet hits the Enemy

        // Turn off the Enemy
        EnemyAlive(BadGuy)=0
        // Make a Bang sound
        PlaySFX("Explode_Low_2")  
        // Gimme 5 points!
        Gimme 5,EnemyX,EnemyY      
        SpawnParticles EnemyX,EnemyY,ExplodingPiece,10
        
        // Move the Bullet off the screen
        BulletY=-100
        
        
      EndIf // Collision
      
      // Dropping Bullets
      If Rand(0,10)==1 and EnemyY<240 and EnemyBulletDelay==0
        EnemyBulletTo=Wrap(EnemyBulletTo+1,1,50)
        // Wrap the value between 1 and 50
    
        // Then position the Bullet
        EnemyBullet(EnemyBulletTo, XPosition)=EnemyX
        EnemyBullet(EnemyBulletTo, YPosition)=EnemyY
    
        // Reset the Delay
        EnemyBulletDelay=100
        
        // And go "peow!"
        PlaySFX("Lazer_1")

      EndIf
      
    EndIf // Alive
    
  Next // Each BadGuy

  If BaddyCount<1 then Gosub NextLevel

  
  // Quit button (Return)
  If GamePad(ButtonStart) then GameRun=0
    // We can hit the Return key to quit
  
  If Dead>0
    If Dead==1
      PlaySFX("Explode_High_4")
      
      SpawnParticles PlayerX,PlayerY,ExplodingPiece,100
    EndIf
    Dead=Dead+1
    
    EndGame "Too Bad"
      // We'll talk about this in Chapter 25!
    
    If Dead>120 Then GameRun=0
  EndIf
  
  SetFontSize 16
  Text 320,16,Score,1
Return

Adding "GameHandler"

Head to the space just above our original // Main Loop, and place the following line of code

GameHandler "My Space Game"

GameHandler will take over from our Main Loop, and do everything I just described.

.. and that'll do.

Good work, today!
You've transformed a "working" game into a "lovely" game.
Take the rest of the day off, and have a lovely day.

 

It would be great to add Lives, or health, so the player doesn't instantly die on the first hit.
Enemies could also have hit-points.
 
We can add Music, too.
And we never got around to adding proper backgrounds, either.

 

We could even draw a whole bunch of different enemies, have waves of different colours, and more.
Player Power-ups for shields, or different weapons.
 
There's a ton of things we can do with this game as our starting point.
Why don't you try out a number of things yourself, and see what you can come up with.
 
Have a great day!