Running a Phaser Game on Mobile Devices
Tags: phaser, ionic, mobile, gamedev
Published: 2024-05-05 02:21:18

We have finally arrived at the final part of the tutorial! We’ve come a long way — from scaffolding a Vue Ionic app, building an entire game with Phaser, and connecting the app and game together for a seamless experience. 

Now it’s time to take this app from web to mobile. Fortunately, Capacitor, which is built into Ionic, makes this easy. Let’s get started!

> **Note**: Your development machine must be set up for iOS and Android development if you want to run the game on a simulator or real device! You can read the [Capacitor documentation here](https://capacitorjs.com/docs/getting-started/environment-setup) for information on environment setup.

## Table of Contents
- [Adding iOS and Android Device Support](#adding-ios-and-android-device-support)
- [Running on a Simulator](#running-on-a-simulator)
- [Running on a Real Device](#running-on-a-real-device)
- [Persistent High Score Data](#persistent-high-score-data)
- [Next Steps](#next-steps)

## Adding iOS and Android Device Support

Using the Ionic CLI that we installed in [Part 1](), we can run the following commands to add Android and iOS to our project.

```bash
ionic capacitor add android
ionic capacitor add ios
```

However, my favorite way to add iOS and Android device support to an Ionic/Capacitor project is using the [Ionic VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ionic.ionic). This extension has a number of different features and functionality for working with an Ionic/Capacitor app, regardless of whether you’re using Angular, React, Vue, or vanilla JavaScript.

![Screenshot of Ionic extension page in VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kn2kyjonj4kb06ol8hsx.png)

Once you install the extension, you’ll see in the sidebar that it recommends adding Android and iOS to your project.

![Screenshot of Ionic Extension sidebar in VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iktyc7y2tiam21oa2ppb.png)

You can select these options in the sidebar, and the extension will run the CLI commands to add both Android and iOS to the existing project. You’ll now see an `android` and `ios` folder in the root directory.

![Screenshot of new android and ios directories](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gozz16pghku4ulqd6y53.png)

## Running on a Simulator

Now, in order to actually build the game for iOS or Android, there are some steps we need to follow.

1. Create a web build of our app
2. Sync the web build to the native projects with Capacitor
3. Create and run the native build for each platform (Android and iOS)

Any time we make a change to our project, we need to re-run these steps so our native app always stays in sync.

The [Capacitor documentation](https://capacitorjs.com/docs/basics/workflow) runs through the CLI commands and details for these steps, but I prefer to use the VS Code extension.

You’ll see now in the extension sidebar that new commands have been added.

![Screenshot of extension sidebar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gp5bjuocib2ysyer1awe.png)

To run on an iOS simulator, we’ll use the following commands in order:

1. Project > Build
2. Project > Sync
3. Run > iOS

When you select Run > iOS, a menu will appear asking to select a device. These correspond to the simulators available on your computer, provided by XCode.

Once you select a device, Capacitor will create a native build of the app that can run on an iOS simulator. Then, it will deploy that build and launch the simulator. This can take a few minutes, but you’ll see updates in the bottom bar of VS Code.

Once the build is ready, the simulator will launch with the app!

![Screenshot of app running on iOS simulator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/znt7baqpdrrsczrsd9wl.png)

You’ll notice the styling is a bit different — this is thanks to Ionic’s adaptive styling, which automatically updates the components for whichever device is used to run the app. Now, our app has a native look-and-feel you’d expect for an iOS app, as opposed to the web browser version.

You can click “Start” and play the game, and see your scores saved. This is a real iOS build of our game, running on an iOS simulator. Success!

You can close the simulator once you’re done to stop the process.

## Running on a Real Device

To walk through running the game on a real device, we’ll switch to Android. In the VS Code extension, select “Project > Open in Android Studio”. This will launch the project in Android Studio.

You’ll notice the option to run on an emulator if you have one installed.

![Screenshot of Device manager in Android Studio with Pixel emulator options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elrqku6vr3iaobkd8dtx.png)

Let’s connect our real Android device to run the app and play our game on device as it was intended.

First, you’ll need to ensure the device you have is enabled for development. Go to Settings on your Android Device, then “About phone”, and scroll down to the “Build number”. Tap on the Build number 7 times (you’ll see a pop-up message notifying you that you are enabling developer mode).

Once enabled, you’ll see “Developer options” under Settings > System. 


![Screenshot of Android device settings showing Developer options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8rumpdxxs8kapozjkw4t.png)

You have two options to add your device:

- USB debugging
- Wireless debugging


![Screenshot of Android device with debugging options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7lawyh2jvx1f7mbvocl1.png)

Toggle whichever method you prefer (I selected USB debugging) and accept. Once connected, you’ll see your physical device available in the Device Manager in Android Studio.


![Screenshot of Android Studio with physical device shown in device manager](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ldyz4q3furtzfnwx6ife.png)

Select the device and click the green arrow “Play” icon button to run your app.

Now you can play your game using touch controls on a real device!

[Here is the git commit](https://github.com/ceceliacreates/phaser-tutorial/commit/62e1e2b2a589d505df3556adb584e6288b8a5a37) with changes for this section.

## Persistent High Score Data

We have one more thing to do.

Right now, our data storage of the high scores is not persistent. Once our browser or app closes, we lose our history of high scores. For this tutorial, let’s leverage local storage in the browser or on the device.

Capacitor provides plugins for us to make this easier. We’ll use:

- [Capacitor`isNativePlatform()` method](https://capacitorjs.com/docs/core-apis/web#isnativeplatform) to determine which storage method to use.
- [Capacitor Preferences API](https://capacitorjs.com/docs/apis/preferences)  for light storage of key/value pairs.

**Note:** The Preferences API is only meant for light storage, so we’ll only keep 10 scores at a time. To save more data, use an [actual storage solution or database](https://capacitorjs.com/docs/guides/storage).

First, we’ll install the Preferences API by running the following command in the terminal.

```bash
npm install @capacitor/preferences
```

**Note:** You will need to be on Capacitor 6 to install the latest version of the Preferences API.

Next, let’s update the `addGameScore` method inside our `App.vue`.

```js
const addGameScore = (score: number) => {
  if (gameScores.value.length >= 10) {
    gameScores.value.shift()
  }
  
  gameScores.value.push(score)

  const scoreString = JSON.stringify(gameScores.value)

  saveGameScores(scoreString)
}
```

With this change, we are:

- Ensuring we only save 10 scores at a time
- Creating a JSON string of our scores array
- Passing the score string to a new function, `saveGameScores()`

Now let’s define `saveGameScores()` in the same component.

```js
const saveGameScores = async (scoreString: string) => {
  if (Capacitor.isNativePlatform()) {
    await Preferences.set({
      key: 'gameScores',
      value: scoreString
    })
  } else {
    localStorage.setItem('gameScores', scoreString)
  }
}
```

This function takes the JSON string of our scores array and saves it to either device storage OR browser local storage, depending on the return value of `Capacitor.isNativePlatform()`. 

Finally, we need to check if there are any scores stored in storage when we launch the app so we can load the scores. We’ll do this in a new `loadGameScores()` function, again in the same `App.vue` component.

```js
const loadGameScores = async () => {

  let savedScoreString = ''

   if (Capacitor.isNativePlatform()) {
    const result = await Preferences.get({ key: 'gameScores'})

    savedScoreString = result.value || '';
   } else {
    savedScoreString = localStorage.getItem('gameScores') || '';
   }

   if (savedScoreString) {
    gameScores.value = JSON.parse(savedScoreString)
   }
};

...

loadGameScores();
```

This function again checks which platform the game is on, then loads scores from either device storage or browser local storage. You’ll need to make sure you run `loadGameScores()` in the `App.vue` component so it runs every time the app launches.

We can confirm this is working in the browser by checking local storage, where we can see the scores array is being stored as a string. Now if we close the browser and reopen, our score history data is persistent!


![Screenshot of local storage with scores string](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ied5vr9zxkr5gl8uages.png)

We can also test this on our Android mobile device. Rebuild, sync, and run the app. Play a few games, then close out the app completely by swiping up on the app from the App Overview carousel view.

Reopen the app, and you’ll see the saved scores load!

Again, this is not a production solution, but it shows how you can leverage Capacitor to interact with device functionality, no matter what platform your app is running on.

## Next Steps

Congratulations, we made it! You have built a game using open source web technologies that can run on real native mobile devices. I encourage you to continue building:

- Add more game functionality or new levels
- Connect to a persistent database for score storage
- Explore new Phaser features like the Phaser CLI and Sandbox
- Make new games!

By the way, I’ll be streaming more web and game development on Twitch weekly, so [follow me on Twitch](https://www.twitch.tv/ceceliacreates/) to get notified when I’m live. See you there!