Show Sidebar

Memory Game Demo

The Memory Demo is a fully working example of a simple two-player game that can be played asynchronously. It has matchmaking, shows "saved games", who's turn it is in those games and supports push notifications for inactive opponents.

Memory Main Menu
Memory Main Menu

The client is available in the Unity SDK. On the server side, the reference implementation for asynchronous games is used. This can be activated in the Dashboard and you can get the source on GitHub.

Preparing The Client

The client is pretty much ready to run. You only need to load MemoryDemoScene and insert your own AppId in the Editor.

Get your application's AppId from the Realtime Dashboard after free signup.

Copy your AppId from the dashboard and open the MemoryDemoScene. Find the "Scripts" GameObject and the MemoryGui component to set the "App Id".

MemoryGui Component
MemoryGui Component

Preparing The Server

From the Realtime Dashboard, go to the management page of your application by clicking "Manage".

Manage Application
"MemoryDemo" example application as seen from the Realtime Dashboard

Then at the bottom you will find the Webhooks section where you should create a new webhooks setup using the "Create a new Webhook" button.

Webhooks section
Webhooks section without any configured webhooks

From the dropdown list, choose "WebHooks 1.2 Demo". You can also choose "WebHooks 1.2" if your own web service is ready. For the sake of this demo, only the relevant parameters are shown in the screenshot below.

Please note that the BaseUrl shown in the screenshot is the default and should be changed with your own custom URL.

For more information about Webhooks please visit this page.

Webhooks example setup
Example of webhooks setup for MemoryDemo

A Look At The Code

To get the most out of the Memory Demo, you should definitely take a look at the code. The Unity Inspector settings define the looks and some options but the logic is done in code.

Important classes in this demo, that you might want to peek at are MemoryGui, MemoryBoard, MemoryGameClient and NamePickerGui.

Authentication

For turnbased games the user plays an important role: You need to keep a list of games per user and that is best accessed by some userID.

To keep things simple, this demo does not require a password and just accepts any entered name as userID.

Photon's Custom Authentication feature can be used to actually authenticate a user's account. Read more about it in the custom authentication doc.

For your reference, a second scene "CustomAuthDemo" is in the package. It shows you how to implement authentication on the client side.

Matchmaking

This demo uses a very simple but matchmaking workflow: It tries to join a random room and creates a new one if that fails.

Find additional info in the reference doc for matchmaking and lobby.

Matchmaking only works as long as a room has at least one active player joined. As a single player does not do much on his own, this usually means a room is in matchmaking only for a short time.

The gui elements call NewGameMsg() to do matchmaking, which in turn calls OpJoinRandomRoom(). The response to this call, as any other operation's response, will call OnOperationResponse. If needed, the MemoryGameClient calls the actual room-creating code with MemoryGameClient.CreateTurnbasedRoom().

Saving Your State

Any game that is not abandoned explicitly can be continued later on. This includes cases where the client gets closed mid-game or loses connection.

Photon Realtime automatically saves buffered events and all room- and player-properties.

When a player re-joins a room, it will get the properties of the players - active and inactive - and all buffered events. With this data you are able to reproduce the state and continue playing.

For a memory game, we need to keep track of the tiles on the board.

Memory Board
Memory Board

Cached Events

Aside from sending events only to the active players in a Room, you can deliberately cache them for joining and "returning" players. The room will keep cached events in the order of their arrival and sends them to players who join, before those get any "live" events.

In Photon Realtime, cached events will also be saved and sent any time a player continues the game later on.

The demo does not use cached events but below you can see how the OpRaiseEvent call could look like:

As usual, saved events in a turnbased game are also passed to your code by calling OnEvent().

If you don't need a long history of turns (for replay) you should speed up loading the game by clearing the cache of unused events. This is done (a bit confusingly) by OpRaiseEvent again but with the CachingOption set to EventCaching.RemoveFromRoomCache.

Properties

The memory game saves the complete game state in a set of room properties. Properties are great for values which don't change frequently and only need to store the current value.

Best of all: You can cherry-pick some room properties and make them available in the "saved games" list.

In Photon Realtime, all properties are saved when all players become inactive. This is why you configure "IsPersistent" to "true" in the dashboard.

The demo saves each tile in its own property with the tile id as string key. This way, each tile could be updated individually. However, the demo always saves the whole game state at the end of each turn.

A key element in Memory is that you can see the other's flipped tiles. At least, if they don't match.

To implement this, the players do not hand over the turn when done. Instead, both flipped tiles are simply saved and the logic keeps you from selecting others. The opponent has to show the tiles and "take over" the turn when ready.

The scores and a turn counter are also part of the saved properties.

Check out SaveBoardToProperties and LoadBoardFromProperties in MemoryGameClient.cs.

Saved Games

Photon Realtime does not only save the state of each room but can also help you keep a game list for each user. If your users log in with a fixed account (i.e. same UserId), the list is available even if a user switches to another device.

Get Saved Games

Clients access the saved games list by a WebRPC call. WebRPCs are simply operations that are executed by your own web service.

WebRPCs are easy to call from the client side:

Photon will use the BaseUrl configured for Webhooks and add "GetGameList" to the end. Of course, the server configured as "Base Path" needs to provide the games list per player under this path.

The Dictionary jsonParameters is not really needed in this case but shows how to pass parameters to the web server if needed. Photon will automatically provide info about the user (like the UserID) to the web service.

The returned list of saved games contains the names of rooms the player can re-join. Per room, the server also sends the actorNumber the user had in that room. Last but not least: If you provided a list of room properties that should be available in the lobby, those are also sent by GetGameList in our default implementation.

The MemoryGameClient turns this into a List<SaveGameInfo> SavedGames, which is used to show the rooms list. The demo sends just enough data per game to show if it's your turn and who you are playing in per game. The actual room-name is not important in this context.

Note: Try to minimize the data you send to keep things lean and cheap for players.

For more information about GetGameList WebRPC, a more in depth explanation is available here.

Opening Saved Games

Players can return to saved games by "re-joining" them. They need to know the name of the room to open and the player number they had in the room.

This is done in MemoryGui.LoadGameMsg().

The roomName defines which room we plan to open in 'OpJoinRoom'. With the actorNumber, you take over a specific player's spot in the player list.

Push Notifications

We found the excellent PushWoosh service for notifications. As example, our SDK includes their client library for Android.

With Photon, we are focussing on the multiplayer / networking part of gaming. We decided against re-inventing the wheel and instead make use of other services where needed. Push Notifications are such a topic.

The general idea is that Remote API (to send the actual push) gets implemented int he Web Service. The clients only need some way to trigger the push when needed and to tell the server who to push. Clients set 2 "Tags" currently, which allow us to target each user individually: UID2 and AppId. Together, these identify a user in one app.

To trigger the push, we decided to use SetProperties with a "web forward" on demand (when a turn will end). This forwards the new room properties to the Web Service, which looks up the "turn". The "turn" contains the ID of the player who should get the push. The Web Service finds the related userID and sends a directed push, using Tag "UID2" and the AppId.

An alternative to using Tags would be to store a "PushWoosh Device ID" per user in the WebService. Or you could send that deviceId to each room (as player property).

Or you do it in yet another way.

Things To Extend

The demo still has some rough edges. Amongst others you might want to try sanding these ...

  • The game does not detect when Webhooks are not configured. It could show a note.
  • There is no visual feedback if you got disconnected.
  • Show if the other player is active or not as you never know if it makes sense to wait.
  • Show how many players are online.
  • ...

Start playing!

 To Document Top