Wiki

Clone wiki

SpecGine / Pause

Adding pause menu

In this section we want to add option to pause game and select whether we want to continue it, start new game or return to main menu.

Pause Menu state

We start by creating empty state representing pause menu in file core/src/main/scala/PauseMenu.scala.

#!scala
package com.specdevs.ping

import com.specdevs.specgine.states.MenuState

class PauseMenu extends MenuState {
  def initialize() {}

  def create() {}

  def dispose() {}
}
Next we need to add new state into initialize method of Ping class.

#!scala
  def initialize() {
    //...
    addState(new PauseMenu, "PauseMenu", "Menus")
  }

Note that we add state called "PauseMenu" to "Menus" state group, so we are sure that we can safely use "defaultSkin".

Implementing Pause Menu

Like in MainMenu implementation section, we start from creating screen for PauseMenu.

Creating empty Pause screen

We create empty pause screen in file core/src/main/scala/PauseScreen.scala.

#!scala
package com.specdevs.ping

import com.specdevs.specgine.states.gdx.MenuScreen

class PauseScreen extends MenuScreen(width=Some(800), height=Some(480)) {
  def initialize() {}

  def create() {}

  def dispose() {}
}
Please note that, similarly to MainScreen, we set width and height in constructor of PauseScreen. Now we add new screen to initialize method of PauseMenu.

#!scala
  def initialize() {
    addMenuScreen("Pause", new PauseScreen)
  }

Adding buttons to Pause screen

We want to have three buttons in pause menu. "Resume" button which will jump back to the currently played game, "New game" which will start completely new game and "Quit to main menu" button for returning to MainMenu. We place code implementation in create method in of PauseScreen.

#!scala
//...
import com.specdevs.specgine.assets.Asset
import com.specdevs.specgine.assets.gdx.ImplicitSkinAsset

import com.badlogic.gdx.scenes.scene2d.{Actor=>GdxActor}
import com.badlogic.gdx.scenes.scene2d.ui.{Skin=>GdxSkin,TextButton=>GdxTextButton}
import com.badlogic.gdx.scenes.scene2d.utils.{ChangeListener=>GdxChangeListener}
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.{ChangeEvent=>GdxChangeEvent}
//...
  def create() {
    val skin = get[Asset,GdxSkin]("defaultSkin")

    val button = new GdxTextButton("Resume", skin)
    table.add(button).width(120).pad(10)
    button.addListener(new GdxChangeListener {
      def changed(event: GdxChangeEvent, actor: GdxActor) {
        popState()
      }
    })

    table.row()

    val button1 = new GdxTextButton("New game", skin)
    table.add(button1).width(120).pad(10)
    button1.addListener(new GdxChangeListener {
      def changed(event: GdxChangeEvent, actor: GdxActor) {
        pushState("MainMenu")
        pushState("Play")
      }
    })

    table.row()

    val button0 = new GdxTextButton("Quit to menu", skin)
    table.add(button0).width(120).pad(10)
    button0.addListener(new GdxChangeListener {
      def changed(event: GdxChangeEvent, actor: GdxActor) {
        pushState("MainMenu")
      }
    })

    ()
  }
  //...

Similar to MainMenu, we first obtain defaultSkin from "Menus" group and next we add three buttons. First button returns to currently played game by executing popStat method. This will remove PauseState from state stack and the previous state, game state in this case, will be the current one. The second button will start completely new game by executing pushState method twice, firstly jumping to MainMenu state and then to GameState itself. Because MainMenu state is already on stack, executing pushState method will pop all states that are on top of MainMenu state until it becomes active. Than we push GameState on stack. Finally, the third button will move us to MainMenu by executing pushState method only once.

At this point we have nice PauseMenu with some functionality, but we are not able to access it. Now we have to inform Play class that we want to jump to PauseMenu if user click escape key. For any other device user can set any other key which will pause the game.

Pasuing the game

We need to adjust InputSystem to process escape button from keyboard and back button from Android. To do so we firstly override backUp method and change end method of InputSystem so after all other keys were processed, but before key queue was cleared we check whether escape key was clicked.

#!scala
import com.badlogic.gdx.Input.{Keys=>GdxKeys}
import com.specdevs.specgine.input.BackReceiver
//...
class InputSystem extends SingleEntitySystem with KeyReceiver with PointerReceiver with BackReceiver with Processor {
  //...
  override def backUp() = {
    keysQueue.enqueue((GdxKeys.ESCAPE, false))
    true
  }

  override def end(dt: Float) {
    for ((key, state) <- keysQueue; if key==GdxKeys.ESCAPE && !state) {
      pushState("PauseMenu")
    }
  }
}
Now, by clicking escape button on keyboard or back button on Android, we can pause the game. At this stage PauseMenu is a black screen with three buttons. We now want to improve the rendering system, so PauseMenu will be displayed on current game screen, but table and paddles will be rendered in gray colors.

Rendering frozen state

To render frozen game state we need to inform RenderSystem that we are pausing the game. To do so first we need to change RenderSystem and Play class. We first add public variable to RenderSystem which will indicate whether we want to render frozen state and set it to false. We also need to override resizeFrozen method, because we render frozen scene and we need to make sure it was correctly resized.

#!scala
//...
class RenderSystem extends SingleEntitySystem with Renderer {
  //...
  private var frozen = false
  //...
  def renderFrozen(alpha: Float) {
    frozen = true
    renderAll(alpha)
    frozen = false
  }

  override def resizeFrozen(x: Int, y: Int) {
    resize(x, y)
  }
}

Next we change RenderSystem in Play class to be a variable and later we override renderFrozen method.

#!scala
//...
class Play extends GameState {
  private var rs: RenderSystem = null
  //... 
  def intialize() {
    //...
    rs = new RenderSystem
    addSystem(rs)
    //...
  }

  override def renderFrozen(alpha: Float, simulationAlpha: Float) {
    rs.renderFrozen(simulationAlpha)
  }
}
What we need now is to override renderBackground of PasueMenu state. This method will inform StateManager that it wants to render frozen game state as its background.

#!scala
  override def renderBackground(alpha: Float) {
    renderFrozenState(alpha)
  }

Now when we pause the game, the background of PasueMenu will be the current image of played game. To indicate that we are pausing the game, we will change rendered colors of table and paddles to be gray. We do so by changing renderPaddle and renderTable methods.

Rendering frozen paddle

#!scala
  private def renderPaddle(pos: Position, p: Paddle, c: Option[Color]) {
    //...
    if (frozen) {
      val gray = 0.21f*col.r + 0.71f*col.g + 0.07f*col.b
      renderer.setColor(gray, gray, gray, 1f)
    } else {
      renderer.setColor(col.r, col.g, col.b, 1f)
    }
    //...
  }

Rendering frozen table

#!scala
  private def renderTable(pos: Position, t: Table) {
    //...
    val tableColour = if (frozen) {
      Color(0.81f, 0.81f, 0.81f)
    } else {
      Color(0.5f, 1.0f, 0.5f)
    }
    //...
  }
Now when we pause the game our PauseMenu is displayed on grayed table and with gray paddles. In next section we are going to add scores and winning screen to our game to make it fully playable.

Having troubles with this step?

Here you can download project with all above steps completed.

Updated