Creating an Ionic Vue + Phaser App

Cecelia Martinez - Sep 8 '23 - - Dev Community

I love playing video games, especially console and mobile games. I learned about Phaser (an open source framework for building games with JavaScript) last year and have wanted to try out game development using the skills I already have as a web developer.

Turns out, it's easier than I expected! Thanks to Phaser, along with Ionic, Capacitor, and Vue, I was able to get a mobile game up and running on an iOS device working only a few hours here and there over two weeks.

Ionitron game screenshot

Blog Series Overview

Are you interested in a video walkthrough version of this blog post? Let me know in the comments! If there's enough interest I can put one together.

This blog series will walk through how to use Phaser, Ionic Vue, and Capacitor to build a mobile game of your own and run it on an actual mobile device.

For this series, we'll make a simplified version of the Ionitron game linked above to illustrate the concepts you'll need to learn to make your own game.

The source code for this tutorial is here, and I'll reference specific commits throughout so you can see the specific changes for each section.

What We're Building

This is a Vue mobile app built with Ionic Framework components that houses a Phaser game. We'll also use Capacitor to compile the app to native IOS and Android so it can run on mobile devices.

The app has the following features:

  • A Play tab that launches the Phaser game for play
  • An About tab that describes the game
  • A Scores tab that shows your score history for the game

Let's get started!

Table of Contents

Creating a new Ionic Vue app

First we'll create a new Ionic Vue app using the Tabs starter template.

I'll use the Ionic CLI for this, but you can also use the Ionic VS Code Extension if you prefer.

Install the Ionic CLI:



npm install -g @ionic/cli


Enter fullscreen mode Exit fullscreen mode

Then run the following to create a new app:



ionic start phaserGame tabs --type vue


Enter fullscreen mode Exit fullscreen mode

Then, cd into the directory and run ionic serve to start the dev server and open the web version of our app in a browser. Switch your browser to mobile view using DevTools as you're working to see how the app will look on mobile.

Screenshot of tabs starter app

We now have an Ionic Vue app with three tabs on the bottom for navigation.

Change the tab names to "Play", "About", and "Scores", and update the router and file names to correspond with the new names. Feel free to update the icons as well.

You'll need to change the following files:

  • src/router/index.ts
  • All the files in src/views

Here is the git commit with the changes for this section.

Adding Phaser to the app

Phaser Installation

Phaser has an NPM package we can install into our project.

Run npm install phaser. This will add the dependency to your package.json.



"dependencies": {
...
"phaser": "^3.60.0",
}


Enter fullscreen mode Exit fullscreen mode

Here is the git commit with the changes for this section.

Creating a Phaser game component

Let's create a component that will manage launching our Phaser game.

Create a PhaserContainer.vue file inside src/components.

This component will contain the following:

  • A button to start the game
  • A div that will be the parent for the Phaser game
  • A click handler for the button that hides the button and runs a Phaser launch command

Add the following template to your PhaserContainer component:



// src/components/PhaserContainer.vue

<template>
    <div id="game">
      <ion-button v-if="showButton" @click="handleClickStart">Start
      </ion-button>
    </div>
</template>


Enter fullscreen mode Exit fullscreen mode

The id of "game" on the div will be passed to Phaser so it knows what HTML element is the parent for our game.

Now add the following script to your component:



// src/components/PhaserContainer.vue

<script setup lang="ts">
import { ref } from 'vue'
import { IonButton } from '@ionic/vue';
import { Game} from 'phaser';

// binds to the v-if on our button to toggle visibility
const showButton = ref(true)

// Creates the new Phaser Game instance
function launch() {
// We'll fill this in later
}

function handleClickStart() {
  // hides launch button
  showButton.value = false;

  // Runs the launch function
  launch();
}
</script>


Enter fullscreen mode Exit fullscreen mode

We'll also add some styling to make our button in the middle of the screen.



// src/components/PhaserContainer.vue

<style scoped>
#game {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0;
}
</style>


Enter fullscreen mode Exit fullscreen mode

Now our PhaserContainer component is ready! We just need to add it to our PlayPage view.



// src/views/PlayPage.vue

<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Play</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content :fullscreen="true">
      <ion-header collapse="condense">
        <ion-toolbar>
          <ion-title size="large">Play</ion-title>
        </ion-toolbar>
      </ion-header>
      <PhaserContainer/>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/vue';
import PhaserContainer from '@/components/PhaserContainer.vue';
</script>


Enter fullscreen mode Exit fullscreen mode

Here is the git commit with the changes for this section.

Creating Phaser launch function

Our PhaserContainer component is working, but nothing happens when we click "Start" because we still need to write our launch() function. Let's do that next!

Our launch() function returns a new Phaser.Game object with the configuration for our game, as well as the scene(s) for the game.

To keep our component clean and enforce separation of concerns, let's move our launch() function to a separate file.

Create a new game directory within src with a new game.js file.

Note: While Phaser does have some TypeScript support, I ran into several issues when trying to use Phaser with TS. For simplicity, we'll use JS for our Phaser game code.

Within this file, add the following code:



// src/game/game.js

import { Game, AUTO, Scale} from "phaser";

export function launch() {
    return new Game({
      type: AUTO,
      scale: {
        mode: Scale.RESIZE,
        width: window.innerWidth * window.devicePixelRatio,
        autoCenter: Scale.CENTER_BOTH,
        height: window.innerHeight * window.devicePixelRatio,
      },
      parent: "game",
      backgroundColor: "#201726",
      physics: {
        default: "arcade",
      },
      scene: MainScene,
    });
  }


Enter fullscreen mode Exit fullscreen mode

There are several things happening here, so let's break them down.

  • We are returning a Phaser Game object that contains a configuration object.
  • The type of AUTO means that Phaser will detect if it should use WebGL or Canvas to render the game
  • The scale property handles our sizing and display. The Scale.RESIZE mode means the game will resize to fit the parent window. If you want your game to always have the same aspect ratio, use Scale.FIT instead.
  • Scale.CENTER_BOTH means the game is centered both vertically and horizontally within the parent
  • Using window.innerWidth * window.devicePixelRatio and the corresponding height values will make the game dynamic to the window, and ensure quality rendering on high-DPI (retina) displays
  • The parent value is the id of our parent div HTML element
  • We're setting a backgroundColor for the game, this is optional
  • We're setting a Physics engine type of arcade. This is suitable for simple games and is more performant for mobile.

The last property is scene, and this defines the scenes in our game.

Scenes are a core concept in Phaser and other game dev frameworks. You can think of Scenes like Views in a web app, but with some additional flexibility. Your player will navigate between Scenes, and sometimes multiple Scenes can run simultaneously.

We'll explore more about Scenes later in this series. For now, we'll start with a single Main Scene to start the game.

Here is the git commit with the changes for this section.

Creating our first Phaser Scene

We've passed a value of MainScene to our scene property for our game, so let's create that next.

In the same game.js file, update your Phaser import statement to include Scene.



// src/game/game.js

import { Game, AUTO, Scale, Scene} from "phaser";


Enter fullscreen mode Exit fullscreen mode

Next, add the following code, again in the same file.



// src/game/game.js

export class MainScene extends Scene {
    constructor () {
      super({ key: 'MainScene' })
    }

    create () {
        this.add.text(100, 100, "Hello Phaser!", {
            font: "24px Courier",
            fill: "#ffffff",
        });
    }

  }


Enter fullscreen mode Exit fullscreen mode

Scenes in Phaser are classes that extend the Phaser Scene class. The key 'MainScene' is how we'll reference this scene in our game.

The create() block is one of the Phaser lifecycle methods. We'll learn more about other lifecycle methods in later posts in this series.

For now, know that we are using the create() block to add some initial text to our game. The first two parameters 100, 100 refers to the x and y coordinates of the text.

Here is the git commit with the changes for this section.

Putting it all together

The last thing we need to do is update our PhaserContainer component to use our new launch function and Phaser scene.

Update the script tag to import the launch() function, and remove the the function declaration within the component.



// src/components/PhaserContainer.vue

<script setup lang="ts">
import { ref } from 'vue'
import { IonButton } from '@ionic/vue';
import { launch } from '@/game/game.js';

// binds to the v-if on our button to toggle visibility
const showButton = ref(true)

function handleClickStart() {
  // hides launch button
  showButton.value = false;

  // Runs the launch function
  launch();
}
</script>


Enter fullscreen mode Exit fullscreen mode

Now we when we click 'Start', our Phaser game launches!

Screenshot of app in browser with

Here is the git commit with the changes for this section.

What's Next

In the next post in this series, we'll make our game more interesting and learn more about Phaser concepts like Scenes, Game Objects, Physics, Assets, and more.

Stay tuned!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .