Superpowers Game Development Series #5

SUPER PACMAN

Chapter 6 : Scripting global game behavior

Global datas

The first thing we can add in our scripts is the datas we will use in our game, like the points, the game keys and others datas we will need to keep track of what happen in the game.

// This variable will be used to open a web url in a new window, it is not directly related to the game logic
declare var window;

// A global module
namespace Global {

  // points for each objects the pacman can eat
  export enum points {
        coin = 10,
        bigcoin = 50,
        fruit = 100,
        ghost = 200
       }

  // keyboard's keys the player will use in the game
  export const keys = {
        left: "LEFT",
        right: "RIGHT",
        up: "UP",
        down: "DOWN",
        space: "SPACE",
        exit: "ESCAPE"
       }

  // name of the menu screens
  export const menuScreens = {
    start: "Start",
    levels: "Levels",
    end: "End"
  }

  // number of coins small and big
  export let coins = {
        small: 0,
        big: 0
       }

  // list of all the coins in game, small and big
  export let coinsList = {
        small: [],
        big: []
       }

  // starting life
  export const lifesMax:number = 3;
  // life order indicator for HUD
  export const lifesOrder:boolean[][] = [[false, false, false], [true, false, false], [true, true, false]]

  // the game behavior
  export let game:GameBehavior;
  // the game HUD (game information displayed)
  export let HUD:Sup.Actor;
  // the pacman behavior
  export let pacman:PacmanBehavior;
  // the ghosts behavior in a list
  export let ghosts:GhostBehavior[];
  // the fruits behavior in a list
  export let fruits:FruitsBehavior[];
  // the game time
  export let time:string;
  // the game score
  export let score: number;
  // level chosen to play
  export let currentLevel:string;
  // boolean flag if the game is won or not
  export let won:boolean;
  // number of frame the game stay blocked before continue
  export let freeze: number;

  // the current pacman lifes
  export let pacmanLifes: number;
  // number of coins eaten
  export let coinsEatens: number;
  // number of ghosts eaten
  export let ghostsEaten:number;
  // number of fruits eaten
  export let fruitsEaten:number;
  // fruits status, each position in list is a fruit, if false the fruit is not eaten
  export let fruitsEatenByIndex: boolean[] = [false, false, false, false, false];


  // fruits positions in the level
  export let fruitsRandomPositions:Sup.Math.Vector2[];
  // current available fruits positions in the level
  export let fruitsAvailablePositions:Sup.Math.Vector2[];

Game behavior

The game behavior is the script which monitoring what is happening in the game behind the gameplay logic of movement and collision, like keeping track of the timer.

Initialize behavior

First we start the class (here we prefer the start method above the awake method because it give time to the other behavior to load).

class GameBehavior extends Sup.Behavior {
  // We initialize the tile map
  public tileMap: Sup.TileMap;
  // We initialize locally the timer variable
  private time: number; private second: number; private minute: number;

  start() {
    // We set the game behavior globally
    Global.game = this;
    //  We reset the game timer globally and locally
    Global.time = "0"; this.time = 0; this.second = 0; this.minute = 0;
    // We set the tilemap to the current game level
    this.tileMap = Sup.getActor("Level").tileMapRenderer.getTileMap();
    // We prepare the level with the function Level.set()
    Level.set();
  }
[...]

Note : the Level.set() function will be writen in the chapter 8, for now we can comment it to avoid errors.

And then we add different methods we will use to update and display game informations.

update score

A method who receive points to update the score when needed.

[...]
  updateScore(points:number){
        // Add the points to the game score
        Global.score += points;
        // Update the HUD score display
        Global.HUD.getChild("Score").textRenderer.setText("SCORE:"+Global.score.toString());
  }
[...]
display new score

A method who display the new score won in the current position when a ghost or a fruit is eaten by the player. It take as parameters a position and points.

[...]
  displayNewScore(position:Sup.Math.Vector3, points:number){
      // Create a new actor score
      let score = new Sup.Actor("score");
      // Add a new component text renderer to the actor with the points as text
      new Sup.TextRenderer(score, points.toString());
      // Add the font Font to the component text renderer
      score.textRenderer.setFont("Font");
      // Give the current position to the score (+0.5 to adapt to the centered origin)
      score.setPosition(position.x+0.5, position.y+0.5, position.z);
      // Destroy the actor score after 1 second
      Sup.setTimeout(1000, function(){score.destroy();});
    }
[...]
update life

A method that update and display the Pacman lifes. How it works is than when the pacman loose one life, the method is called, it compare the life sprite from the HUD with the pattern of boolean flag lifesOrder related to the current life of the Pacman. If the life is true, then the sprite is full, else, the sprite is empty.

[...]
  updateLife(){
      // Loop the number of maximum lifes the pacman got (3 times) and give the current value to index
      for (let index = 0; index < Global.lifesMax; index++){
        // Get the sprite Renderer component from HUD/Lifes/index actor
        let sprite = Global.HUD.getChild("Lifes").getChild(index.toString()).spriteRenderer;
        // Check the boolean flag from the lifesOrder pattern of the pacman current lifes and current index
        if(Global.lifesOrder[Global.pacmanLifes][index] === true){
          // If the flag is true, set the sprite animation to full
          sprite.setAnimation("full", false);
        }
        else{
          // If the flag is false, set the sprite animation to empty
          sprite.setAnimation("empty", false);
        }
      }
    }
[...]
update timer

A method that store the local time globally and display it to the HUD.

[...]
  updateTimer(){
    // convert minute and second to string and set them to variables
    let minute = this.minute.toString(); let second = this.second.toString();
    // If the minutes or seconds are inferior to 10, then add a 0 to the string to keep display consistency 
    if (this.minute < 10){
      minute = "0"+minute;
    }
    if (this.second < 10){
      second = "0"+second;
    }
    // Build the complete string for the current time
    Global.time = minute+':'+second;
    // Display it with the HUD/Timer text renderer
    Global.HUD.getChild('Timer').textRenderer.setText("TIME:"+Global.time);
  }  
[...]
update() game loop

The update loop go 60 frames per second and check the differents flag of the game to see if something happen globally.

[...]
  update() {
    // If the freeze counter is on, decrease it from 1 and return to pass the block and repeat 
    if(Global.freeze > 0){
      Global.freeze--
      return;
    }

    // Check if the game is won or not (when the Global.won is not undefined anymore)
    if(Global.won === false || Global.won === true){
      // Load the menu scene and destroy the game scene
      Sup.loadScene("Menu/Scene");
      // Call the function that will load the victory or gameover end screen
      Sup.getActor("Menu").getBehavior(MenuBehavior).setEndscreen();
    }

    // Increase the game timer by one
    this.time++;
    // When the time got 60 frames add 1 second (the game is default set as 60 frames = 1 second)
    if(this.time%60 === 0){
      this.second++;
      // When the second is 60, add 1 minute and reset second to 0 
      if(this.second%60 === 0){
        this.minute++; this.second = 0;
      }
      // Call the updateTimer method every second
      this.updateTimer();
    }

    // Check if the exit key is pressed
    if(Sup.Input.wasKeyJustPressed(Global.keys.exit)){
      // If yes, load the menu scene and destroy the game scene
      Sup.loadScene("Menu/Scene");
    }

    // Check if there is still coins left, if not, the game is won
    if(Global.coins.small === 0 && Global.coins.big === 0){
      // Set the won flag to true
      Global.won = true;
      // Set frames number freeze counter
      Global.freeze = 100;
    }

    // Check if there is still lifes for pacman, if not, the game is lost
    if (Global.pacmanLifes === 0){
      // Set the won flag to false
      Global.won = false;
    }
  }
}
Sup.registerBehavior(GameBehavior);

Note : the .setEndscreen() method will be writen in the chapter 7 with the menu, for now we can comment it to avoid errors.

We can now start to script the menu logic.

We can download the superpowers project v6 from this chapter here.

<-- go to chapter 5 -- go to chapter 7 -->