When I was growing up, many video games were single-player. This meant I would play through a story line once as the sole human player. Some games like Super Mario World were two-player, but only one player would take their turn at a time. At best, other players would need to be present in the room to play together on a single screen.
Battle.net was my first true multiplayer experience. I played on my screen against strangers on the internet who had their own screen. What a game-changer!
I had been wanting to create a multiplayer app on PowerApps for a while; I was certain it would open many doors for partner activities in my classroom. Yet I could not quite put my finger on how to limit a session to two users. It was related to another problem I had pondered: how could I ensure each user only had one instance of a given PowerApp?
So when Daniel Christian shared a project featuring a tic-tac-toe game, I had an idea. If I built a multiplayer system for a simple game like tic-tac-toe, I could mimic the same system for more complicated apps down the line. It was another case of problem-based learning–it was time for a PowerApps Remix!
Follow along or remix it again: https://1drv.ms/u/s!AvtvG5BA8E3vj6Ew-7t7-AYcMIdsYg
The Big Idea
I was not very creative with my multiplayer solution. I copied the host/join system from games I had played in the past.
- One player creates a session. They are the host. They could password-protect a session so that only a desired guest could join.
- Another player selects that session to join as a guest. They input a password if a session is protected.
- Information about each game session is stored in a table GameServer. The column ‘status’ begins as false, but once someone joins, it is rewritten as true. So games will only appear to players if their status is still false.
- A toggle in the app detects once a session has two players. Then it will then trigger the start of the game. The host and guest create their own skeleton of a tic-tac-toe board as a collection, “GameBoardtemp.”
- Both players retrieve 9 records from the connected table GameBoard. This data is continuously returned to the app so that both players have the most accurate data for their session of tic-tac-toe.
- Each player takes a turn. They modify the GameBoard datasource when they place their X or O. Accordingly, the session information in GameServer is updated to reflect whose turn it is.
- A toggle detects the conditions for winning or, more commonly, a draw. It will update the leaderboard in the event of a winner, otherwise, it will show a dialog for a draw.
- Both players may remain in the same game session for more rounds. They can click ‘next round’ to reset the board and continue playing. They do not need to host or join a new session.
Primer
If you are interested in how the gallery for my tic-tac-toe game board was made, that will be a topic for another blog post in this series. I’ll also be diving more deeply in setting up the GameServer and GameBoard tables.
For this blog post, I will focus on big idea 1 and 2: hosting and joining.
Please note that my style of coding in PowerApps is that I use variables that activate toggles which then trigger actions.
At first it seems unnecessary to go this far. However, the benefit is that this allows the developer to store common actions into toggles. Those actions could be activated from anywhere by using the global variable. It’s easier to find and edit formulas when they’re all in one place.
In this case, when the player clicks a button, its OnSelect property causes my variable ‘hostgame’ to become true. A toggle, ToggleGameHost, has its Default property bound to the variable hostgame, so it performs the actions in its OnCheck property when hostgame rings true.
Hosting a session
Step 1: One player creates a session. They are the host. They could password-protect a session so that only a desired guest could join.
Below I’ve broken up the OnCheck property for my toggle, but you can view it altogether within the zip file.
1A. Determine the name of the player and which mark they selected: X or O. Here I placed both bits of information into one tidy variable, playerid:
Set(playerid, {name: InputPlayer2.Text, mark: GalleryMarkerSelect.Selected.mark } );
1B. Generate a random session id number. Assign it as the activeGameID, which will be used for identification later:
Set(gamehost, RoundUp(Rand()*9,0) & RoundUp(Rand()*9,0) & RoundUp(Rand()*9,0) & "-" & RoundUp(Rand()*9,0) & RoundUp(Rand()*9,0) & RoundUp(Rand()*9,0) ); Set(activeGameID,gamehost);
1C. Remove all other sessions and game data from the same player to avoid confusion:
If(!IsEmpty(Filter(GameServer,host=playerid.name)), ClearCollect(clearcache,Filter(GameServer,host=playerid.name)); ForAll(RenameColumns(clearcache,"gameid","gid"), Remove(GameBoard,Filter(GameBoard,gameid=gid)) ); Remove(GameServer,Filter(GameServer,host=playerid.name)) );
1D. Write the new session to the GameServer, complete with the player name, marker, privacy settings, and more:
Patch(GameServer,Defaults(GameServer), {appid: "tictactoe", gameid: gamehost, host: playerid.name, guest: "", status: false, active: true, private: CheckboxPrivate.Value, time: Now(), comment: playerid.mark, turn: "" } );
1E. Pop up a dialog box that shows the queue with the session ID for sharing. Return the hostgame variable to false so it can be used again. Trigger a fade to black animation:
Set(showQueue,true); Set(hostgame,false); Set(resetfade,!resetfade); Set(resetfade,!resetfade)
Joining a session
Step 2: Another player selects that session to join as a guest. They input a password if a session is protected.
Available sessions are displayed in a gallery for players looking to join a game. Inside is an Arrow icon which contains the following code:
Select(Parent); Set(selectedGame,ThisItem); If((ThisItem.private=false) || ((ThisItem.private=true) && InputKey.Text=ThisItem.gameid), Set(joingame,true), Set(showPasskey,true) )
This means, “Identify which game was selected as the variable selectedGame. If the game is not private, or if it is and the player has inputted the correct password, activate the joingame variable. Otherwise, if the game is private, show an input field so the player could type in a session ID.”
The following code belongs to ToggleGameJoin. It gets triggered by the joingame variable.
2A: Hide the input field for typing in a session ID.
Set(showPasskey,false);
2B: Identify the activeGameID for the guest:
Set(activeGameID,selectedGame.gameid);
2C: Update the session on the GameServer so that the status is true. This way nobody else can join. Provide the name of the guest.
Patch(GameServer,First(Filter(GameServer,gameid=activeGameID)), {status: true, guest: InputPlayerName.Text } );
2D: The variable refreshserver will cause a toggle to trigger a refresh of the server and gameboard. The variable resetstart will cause another toggle to recreate a tic-tac-toe game board. The variable joingame becomes false so it can be used again.
Set(refreshserver,true); Set(resetstart,true); Set(joingame,false)
Series
Next in this series, I will be sharing more about setting up the tables for GameServer and GameBoard. Besides multiplayer, it has great utility for scenarios where a match needs to be made.