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 for information on environment setup.
Table of Contents
- Adding iOS and Android Device Support
- Running on a Simulator
- Running on a Real Device
- Persistent High Score Data
- 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.
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. 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.
Once you install the extension, you’ll see in the sidebar that it recommends adding Android and iOS to your project.
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.
Running on a Simulator
Now, in order to actually build the game for iOS or Android, there are some steps we need to follow.
- Create a web build of our app
- Sync the web build to the native projects with Capacitor
- 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 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.
To run on an iOS simulator, we’ll use the following commands in order:
- Project > Build
- Project > Sync
- 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!
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.
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.
You have two options to add your device:
- USB debugging
- Wireless debugging
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.
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 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 to determine which storage method to use. - Capacitor Preferences API 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.
First, we’ll install the Preferences API by running the following command in the terminal.
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
.
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.
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.
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!
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 to get notified when I’m live. See you there!
Top comments (1)
Hi! I'm working on a game myself using React and will be adding PhaserJS for some flare later on.
I haven't yet decided if I'll just be doing a browser game or if I'll be publishing to app stores, but it's awesome to know I can come back to this series of posts later one.
Thanks for sharing!