Tutorial game: General structure

A TRSE game is typically structured around raster interrupts. Sure, you can just go ahead and have a busy while-loop as your main loop, but in C64 terms this is not considered a particularly elegant solution. The reason for this is that the busy while-loop steals all your valuable CPU cycles – just waiting. You want to use those valuable cycles in your game to move sprites, render screens etc.

The raster on the C64 updates 50 times per second (PAL), and you can control what happens when the raster line hits different lines of the screen (there are 320 raster lines on a PAL screen). In other words, you basically “hook” the raster to perform actions at specific positions, while the main program just halts/loops infinitely. When you decided to switch to another part of the program (such as going from menu to game level) etc, you just hook a new raster interrupt routine. Here’s an example of the “wrong” way of doing a game loop on the C64:

procedure Part1();
   InitPart1();
    while(done=0) do begin
       waitforraster(0);
       UpdateSprites();
       MoveSprites();
       if newLevel<>0 then RenderNewLevel(newLevel);
    end;
end.

So the correct way of doing things is by using raster interrupts (see tutorial 13 as an example):

procedure InitMainMenu(); // pre-define
@define useKernal 0

procedure InitGame(); 
begin 
    // ...initialize game stuff, draw levels etc
end;

interrupt RasterMainMenu(); // Forward declare 
interrupt RasterGame();
begin
     startIrq(@useKernal);
     UpdateGameStuff();
     if (gameOver=1) then begin
        InitMainMenu();
        RasterIRQ(RasterMainMenu(), 0, @usekernal);

     end;
     closeirq();
end;

procedure InitMainMenu();
begin
    //... init menu stuff
end;

interrupt RasterMainMenu();
begin
     startIrq(@useKernal);
     UpdateMenuStuff();
     if (startGame=1) then begin
        InitGame();
        RasterIRQ(RasterGame(), 0, @usekernal);
     end;

     closeIrq();
end;

// Main block
begin
	setmemoryconfig(1,@usekernal,0);
	DisableCIAInterrupts();
	InitMainMenu();
	RasterIRQ(RasterMainMenu(), 0, @usekernal);
	enableirq();
	enablerasterirq();
	Loop(); // Halt 
end.

Here, be advised that the main block just turns off kernal stuff, initializes the main menu, hooks the raster irq to the main menu and finally loops the system. When raster line 0 is hit, RasterMainMenu will be executed and the main menu loop progresses. This interrupt will be called 1 time for each frame, unless you start doing lots of fancy stuff within the routine. But as long as you keep the cycle times lower than the maximum for each frame, you are guaranteed to get a timed interrupt.

When starting a game, we just hook the game raster routine and initialize the main game. Now the game will update for each time the raster hits line 0, and will continue doing so until the gameOver flag is set, which sends the raster irq back to the main menu.

A note on include files

As source files tend to grow rather large, it is useful to think of each raster part of a game + the corresponding methods for that part as a class of methods. Moving these methods into a new file and including from the main file is a nice practice.

 

program MyPerfectGame;

@define usekernal 0 // don't use kernal

@include "definitions.ras" // Variables and stuff

@include "mainmenu.ras" // Very important procedures

@include "gamelevel.ras" // My cat is nagging me

begin
     // hook rasters etc
      ...
     Loop();
end.

Keeping it nice and tidy is part of the job!

Next tutorial