Superpowers Game Development Series #5

SUPER PACMAN

Chapter 7 : Scripting menu behavior

Initializing

The menu awake first when we start the menu scene (which is the startup scene).

class MenuBehavior extends Sup.Behavior {
  // Create a ray caster used to check collision between the mouse and objects of the screen
  private ray = new Sup.Math.Ray;
  // The current screen displayed
  private screen:string;
  // Init The button actor
  private button:Sup.Actor;
  // Init The level buttons actors in a list
  private levelsList:Sup.Actor[];

  awake() {
    // Set the current screen with the start screen
    this.updateScreen(Global.menuScreens.start);
    // Set the button actor to local button variable
    this.button = Sup.getActor("Button");
    // Set the level button actors to an empty list
    this.levelsList = [];
    // Call the function that add all levels buttons actors to the list
    this.getLevelsList();
    // Set the opacity of the levels buttons to default
    this.setLevelOpacityDefault();
  }
[...]
Update the screen method

This method display the current screen.

[...]
  updateScreen(screenName:string){
    // Set the screenName parameter as the current screen
    this.screen = screenName;
    // Get the Screens actor in a variable
    let menuScreens = Sup.getActor("Screens");
    // Loop through all the menu Screen
    for ( let screen in Global.menuScreens){
      // Check if the screen from loop is the same than the current screen
      if (Global.menuScreens[screen] === this.screen){
        // if yes, set the screen visibility to true
        Sup.getActor("Screens").getChild(Global.menuScreens[screen]).setVisible(true);
      }
      else{
        // else, set the screen visibility to false
        Sup.getActor("Screens").getChild(Global.menuScreens[screen]).setVisible(false);
      }
    }
  }
[...]
Level buttons methods

Differents methods are used for the levels buttons behavior.

  • getLevelsList() will add the the levelsList all the level button actors
  • setLevelOpacityDefault() will set down the sprite opacity of all the level buttons
  • setLevelOpacityBright() will set up the sprite opacity of the level button currently hovered by the mouse
[...]
  getLevelsList(){
    // Loop through all the levels name
    for(let level in Level.levels){
      // Get the actor of the level from the loop and add it to the levelsList 
      this.levelsList.push(Sup.getActor("Screens").getChild("Levels").getChild(Level.levels[level]));
    }
  }

  setLevelOpacityDefault(){
    // Loop through all the levels actors from the list
    for (let level of this.levelsList){
      // Set the opacity of the sprite to half
      level.spriteRenderer.setOpacity(0.5);
    }
  }

  setLevelOpacityBright(actor:Sup.Actor){
    // Loop through all the levels actors from the list
    for (let level of this.levelsList){
      // if the actor name is the same than the level name from the loop
      if(actor.getName() === level.getName()){
        // Set the opacity of this level button to full
        level.spriteRenderer.setOpacity(1);
      }
      else{
        // Else, set the opacity of this level button to half
        level.spriteRenderer.setOpacity(0.5);
      }
    }
  }
[...]
End screen method

This method set the end screen related to the result of won (victory or game over)

[...]
  setEndscreen(){
    // Update the screen display with the end screen
    this.updateScreen(Global.menuScreens.end);
    // Get the actor of the end screen
    let endScreen: Sup.Actor = Sup.getActor('Screens').getChild("End");
    // If the game won flag is true
    if(Global.won){
      // Set the animation victory of the sprite (without looping the animation)
      endScreen.spriteRenderer.setAnimation("victory", false);
    }
    else{
      // Set the animation game over of the sprite (without looping the animation)
      endScreen.spriteRenderer.setAnimation("gameover", false);
    }

    // Set the statistic datas to update the text display in the end screen
    endScreen.getChild("Score").textRenderer.setText("Score:"+Global.score);
    endScreen.getChild("Time").textRenderer.setText("Time:"+Global.time);
    endScreen.getChild("Ghosts").textRenderer.setText("Ghosts eaten:"+Global.ghostsEaten);
    endScreen.getChild("Coins").textRenderer.setText("Coins eaten:"+Global.coinsEatens);
    endScreen.getChild("Fruits").textRenderer.setText("Fruits eaten:"+Global.fruitsEaten);
    endScreen.getChild("Lifes").textRenderer.setText("Lifes left:"+Global.pacmanLifes);
  }
[...]
Update loop
[...]
  update() {
    // Update the position of the raycaster of the mouse inside the camera zone
    this.ray.setFromCamera(Sup.getActor("Camera").camera, Sup.Input.getMousePosition());
    // Give data related to the collision between the mouse and the button to a variable
    let hitButton = this.ray.intersectActor(Sup.getActor("Button"));

    // if the hitButton variable got datas, it is hovered
    if(hitButton.length > 0){
      // If the current screen is the start screen
      if(this.screen === Global.menuScreens.start){
        // Change the animation of the button to hoverPlay
        this.button.spriteRenderer.setAnimation("hoverPlay", false);
        // Change the animation of the start screen
        Sup.getActor("Screens").getChild("Start").spriteRenderer.setAnimation("hover", false);
      }
      // If the current screen is the level selection or the end screen
      else {
        // Change the animation of the button to hoverReturn
        this.button.spriteRenderer.setAnimation("hoverReturn", false);
      }
      // If the button is pressed while hovering it
      if(Sup.Input.wasMouseButtonJustPressed(0)){
        // call the updateScreen() function with a parameter depending of the current screen
        switch(this.screen){
          case "Start":
            // If the current screen was start screen, go to levels screen
            this.updateScreen(Global.menuScreens.levels);
            break;
          case "Levels":
            // If the current screen was levels screen, go to start screen
            this.updateScreen(Global.menuScreens.start);
            break;
          case "End":
            // If the current screen was end screen, go to start screen
            this.updateScreen(Global.menuScreens.start);
            break;
        }
      }
    }
    else{
      // If the button is not hovered, reset animation to unhoverPlay is the current screen is Start
      if(this.screen === "Start"){
        this.button.spriteRenderer.setAnimation("unhoverPlay");
        // Change the animation of the start screen
        Sup.getActor("Screens").getChild("Start").spriteRenderer.setAnimation("unhover", false);
      }
      // Else reset animation to unhoverReturn for the others screens
      else {
        this.button.spriteRenderer.setAnimation("unhoverReturn");
      }
    }

    // If the current screen is levels screen
    if (this.screen === "Levels"){
    // Give data related to the collision between the mouse and all the levels buttons to a variable
      let hitLevel = this.ray.intersectActors(this.levelsList);
      if (hitLevel.length > 0){
        // if there is hitLevel data, a button level is hovered, call funtion to set a light opacity
        this.setLevelOpacityBright(hitLevel[0].actor);
        // if the current button is clicked
        if(Sup.Input.wasMouseButtonJustPressed(0)){
          // If the level clicked is the level 6 (which is a tutorial link than we will change later)
          if (hitLevel[0].actor.getName() === "Level6"){
            // Open a new window with the url
            window.open("http://mseyne.github.io/");
          }
          // Else, for all others levels
          else{
            // Set the current level to the level which have been clicked
            Global.currentLevel =  hitLevel[0].actor.getName();
            // Start a new game
            Global.startNewGame();
          }
        }
      }
      // if no one is hovered set opacity false to all levels buttons
      else{
        this.setLevelOpacityDefault();
      }
    }
  }
}
Sup.registerBehavior(MenuBehavior);

To make all this work without error, we can add in the Global script the Level namespace with this datas, we will complete this module in the next chapter.

[...]
namespace Level {
  export const levels = {
               1:"Level1",
               2:"Level2",
               3:"Level3",
               4:"Level4",
               5:"Level5",
               6:"Level6"
              }

  export function set(){}

  export const endStats = ["Score", "Time", "Ghosts", "Coins", "Fruits", "Lifes"];
}

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

<-- go to chapter 6 -- go to chapter 8 -->