How to make a game like Pong with Felgo - Menus

Tutorial Chapters

  1. Overview
  2. Creation of a Felgo game
  3. GameWindow, Scenes and Physical Worlds
  4. The Level and the Ball
  5. Level Boundaries
  6. Paddles
  7. HUD
  8. Menus
  9. AI
  10. Special Effects
  11. Music and Sound
  12. Further Perspectives

No game is complete without a custom font. It is very easy to add a custom font to the game. First of all, search a nice font in True Type Font (.ttf) format and create a fonts folder in your qml directory. Put your font into this folder. Use a FontLoader item at the end of the FelgoScene.qml to load your custom font which is stored in the fonts directory.

 ...
   // Custom font loading of ttf fonts
   FontLoader {
     id: fontHUD
     source: "fonts/Anton.ttf"
   }
 ..

When using a custom menu, the most important component is an own button which can be styled easily. Create a MenuButton.qml in your qml dir and use a SimpleButton with custom font for the text to style your MenuButton.

 import QtQuick 2.0
 import Felgo 3.0

 SimpleButton {
   // use the custom font
   font.family: fontHUD.name

   color: "black"
   textColor: "white"
   height: 60
   width: 200
 }

Now you can create a menu with your new Button. Basically you can distinguish between in-game menus and a main menu. The main menu is called directly on start-up which displays settings and other options to start the game. While the in-game menu is called when the player presses the menu button. You can end the game and return to the main menu or resume the game in the in-game menu. First of all, you need a main menu where you can start the game. Create a MainMenu.qml and add a Column item with some buttons.

 import QtQuick 2.0
 import Felgo 3.0

 Column {
   anchors.centerIn: parent
   MenuButton {
     text: "Start Game"
     onClicked: {
       scene.state = "game"
     }
   }
 }

Now you have to add the main menu to your scene.

 ...
    MainMenu {
      id: mainMenu
      // in the main menu state, the in-game menu should be invisible!
      visible: true
      anchors.centerIn: parent
    }

    FontLoader {
      id: fontHUD
      source: "fonts/Anton.ttf"
    }
 ...

Now there is a menu which can not be used yet and you can't play, which is not very satisfying.

As you can see, there is this piece of code

 scene.state = "game"

in the main menu button, which is used to change states. Every item can have own states and you can use now different states in your VPongScene to define what happens in which state. This seems to be the perfect technique to disable and enable the menu. The main states of the game will be main menu, game, and paused game. Each state changes the visibility of defined components.

 ...
   MainMenu {
     id: mainMenu
     // in the main menu state, the in-game menu should be invisible!
     visible: true
     anchors.centerIn: parent
   }

   // The HUD, main menu and the in-game menu are only visible in special states
   states: [
     State {
       name: "mainmenu"
       PropertyChanges { target: mainMenu; visible: true}
       PropertyChanges { target: hud; visible: false}
     },
     State {
       name: "game"
       PropertyChanges { target: mainMenu; visible: false}
       PropertyChanges { target: hud; visible: true}
     }
   ]

   FontLoader {
     id: fontHUD
     source: "fonts/Anton.ttf"
   }
 ...

Your game has a start button and starts the game when it is clicked, but you might also want to pause or stop the game. An in-game menu would be a great idea. Create an In-gameMenu.qml in your main qml dir and add a few menu buttons.

 import QtQuick 2.0
 import Felgo 3.0

 Column {
     spacing: 2
     anchors.centerIn: parent
     MenuButton {
         text: "Main Menu"
         onClicked: scene.state = "mainmenu"
     }
     MenuButton {
         text: "Resume"
         onClicked: scene.state = "game"
     }
 }

Of course, you have to add an instance of the In-gameMenu to the Scene below the MainMenu. The initial visibility should be set to false, because it should not be visible on start in the main menu state. At this point you should also change the visibility of the HUD element to false because it is not visible too in the main menu state.

 ...

   HUD {
     id: hud
     height: 60
     // in the main menu state, the HUD should be invisible!
     visible: false
     anchors.centerIn: parent
   }

   MainMenu {
     id: mainMenu
     // in the main menu state, the in-game menu should be invisible!
     visible: true
     anchors.centerIn: parent
   }

   In-gameMenu {
     id: in-gameMenu
     // in the main menu state, the in-game menu should be invisible!
     visible: false
     anchors.centerIn: parent
   }

 ...

The in-game menu is available, but how can you call it? Remember the HUD.qml, you have created the middle element "M" (for Menu) which uses a MouseArea. Adding

 scene.state = "pausedGame"

to the MouseArea will enable the in-game menu.

 ...
   MouseArea {
     anchors.fill: parent
     onClicked: {
       // this activates the pausedGame state, which will show the In-gameMenu item
       scene.state = "pausedGame"
     }
   }
 ...

The states in FelgoScene.qml need an update. A new state called pausedGame will be added. Furthermore, you have to add a propertyChanged for the in-gameMenu in the game and mainMenu states.

 ...
   // The HUD, main menu and the in-game menu are only visible in special states
   states: [
     State {
       name: "mainmenu"
       PropertyChanges { target: in-gameMenu; visible: false}
       PropertyChanges { target: mainMenu; visible: true}
       PropertyChanges { target: hud; visible: false}
     },
     State {
       name: "pausedGame"
       PropertyChanges { target: in-gameMenu; visible: true}
       PropertyChanges { target: mainMenu; visible: false}
       PropertyChanges { target: hud; visible: false}
     },
     State {
       name: "game"
       PropertyChanges { target: in-gameMenu; visible: false}
       PropertyChanges { target: mainMenu; visible: false}
       PropertyChanges { target: hud; visible: true}
     }
   ]
 ...

You may have recognized that the game still runs when you go to the paused state. The physicsWorld needs to be paused in the paused state. This can be achieved with a StateChangeScript in each state. It sets the physicsWorld properly and (re)starts the game accordingly.

 ...
   // The HUD, main menu and the in-game menu are only visible in special states
   states: [
     State {
       name: "mainmenu"
       PropertyChanges { target: in-gameMenu; visible: false}
       PropertyChanges { target: mainMenu; visible: true}
       PropertyChanges { target: hud; visible: false}
       StateChangeScript {
         script: {
           level.end()
           // Unpause the physicsWorld if which happens when the user goes to main menu from the pause menu
           if(!physicsWorld.running) {
             physicsWorld.running = true
           }
         }
       }
     },
     State {
       name: "pausedGame"
       PropertyChanges { target: in-gameMenu; visible: true}
       PropertyChanges { target: mainMenu; visible: false}
       PropertyChanges { target: hud; visible: false}
       StateChangeScript {
         script: {
           // Pause the physicsWorld
           physicsWorld.running = false
         }
       }
     },
     State {
       name: "game"
       PropertyChanges { target: in-gameMenu; visible: false}
       PropertyChanges { target: mainMenu; visible: false}
       PropertyChanges { target: hud; visible: true}
       StateChangeScript {
         script: {
           // If the user presses resume, the physicsWorld needs to be unpaused
           // If the user enters the game from main menu, the game is restarted.
           if(physicsWorld.running) {
             level.reStart()
           } else {
             physicsWorld.running = true
           }
         }
       }
     }
   ]
 ...

After using states, the onComplete method in the Ball.qml can be removed. Now the ball will be spawned when going to game state.

 ...
   // Starts the ball temporarily until a button is created
   Component.onCompleted: {
     reStart(scene.width/2, scene.height/2)
   }
 ...

The game should run successfully now if you start the game; it should pause in the pause menu and it should stop in the main menu.

Voted #1 for:

  • Easiest to learn
  • Most time saving
  • Best support

Develop Cross-Platform Apps and Games 50% Faster!

  • Voted the best supported, most time-saving and easiest to learn cross-platform development tool
  • Based on the Qt framework, with native performance and appearance on all platforms including iOS and Android
  • Offers a variety of plugins to monetize, analyze and engage users
FREE!
create apps
create games
cross platform
native performance
3rd party services
game network
multiplayer
level editor
easiest to learn
biggest time saving
best support