Documentation

Support

Services

Services

In-Game Mailbox

Implement an in-game mailbox system for sending messages and rewards to players, with delivery and expiration management.
Read time 12 minutesLast updated 17 hours ago

In-game mailboxes allow game developers to communicate with their players. You can use them to tell players about in-game events, gift them useful resources, or help keep them coming back to your game.

Prerequisites

To use this sample use case, you must download and install the UGS Use Cases project in your Unity project.

Overview

When a player loads the scene for the first time, they see an inbox with a list of messages waiting for them to read. On subsequent loads, the inbox is either in the state they left it, or an updated state due to messages expiring between sessions. Players can interact with the messages, claim their attachments, delete messages, or reset the inbox to a brand new state. To see this use case in action:
  1. In the Unity Editor Project window, select Assets > Use Case Samples > In-Game Mailbox, and then double-click
    InGameMailboxSample.unity
    to open the sample scene.
  2. Enter Play Mode to interact with the use case.

Initialization

When the scene loads, the
InGameMailboxSceneManager
script performs the following initialization steps:
  1. Initializes Unity Gaming Services.
  2. Signs in the player anonymously using the Authentication service. If you’ve previously initialized any of the other sample scenes, Authentication will use your cached Player ID instead of creating a new one.
  3. Refreshes the Economy configuration data. If new Economy items were created since the last time the player opened the app, this will initialize those items in the player's configuration.
  4. Retrieves and updates currency balances from the Economy service for that authenticated user.
  5. Uses the sprite addresses stored in the Economy item configuration's custom data to load all possible currency and inventory item sprites from Addressables.
  6. Retrieves the updated message info for the player's inbox:
    1. Downloads the list of all possible messages from Remote Config.
    2. Retrieves the player's current inbox data from Cloud Save.
    3. Checks whether any of the messages already saved in the player's inbox are expired, and deletes them if so.
    4. Checks for any new messages that were downloaded from Remote Config and not yet added to the player's inbox.
    5. Saves the updated inbox state for the player in Cloud Save.
  7. Displays the updated list of inbox messages in the scene.

Functionality

The left panel in the scene displays the list of messages in the player's inbox. This list updates over time as messages expire, or based on player interaction. Below the list, a counter displays how many messages are in the inbox and the max number of messages that can be in the inbox at any given time. When a player loads the scene for the first time, the inbox is full. When all messages have been deleted from the inbox (either through player interaction or message expiration), a pop-up appears prompting the player to reset the inbox.

Open a message

When you select a message from the list, the following occurs:
  1. The full details of the message appears on the right side of the scene.
  2. The message is marked as read, and that status is saved to Cloud Save.
If the message has an attachment, there is also an indication of which Economy items are attached, along with a button to claim them.

Claim an attachment

When you press the Claim button, the client code calls the
Messages_ClaimAttachment.js
Cloud Code script with the selected message's ID included as a parameter. The following occurs on the backend:
  1. The client requests the Cloud Save inbox data, and locates the message with the supplied ID.
  2. The script checks whether the message has an unclaimed attachment.
  3. Assuming the message does have an unclaimed attachment, the script makes an Economy
    makeVirtualPurchase
    call using the Virtual Purchase ID from the
    message.messageInfo.attachment
    field.
  4. If the purchase processes successfully, the
    message.metadata.hasUnclaimedAttachment
    field is set to
    false
    and saved in Cloud Save, so that the player cannot claim the attachment again.

Claim all attachments

When you press the Claim All button, the client code makes a call to the
Messages_ClaimAllAttachments.js
Cloud Code script. The following occurs on the backend:
  1. The client requests inbox data from Cloud Save.
  2. Cloud Save returns a list of inbox messages filtered to only show messages with
    message.metadata.hasUnclaimedAttachment
    set to
    true
    .
  3. For each message in this filtered list:
    1. The script makes an Economy
      makeVirtualPurchase
      call using the Virtual Purchase ID from the
      message.messageInfo.attachment
      field.
    2. If the purchase processes successfully,
      message.metadata.hasUnclaimedAttachment
      is set to
      false
      , and
      message.metadata.isRead
      is set to
      true
      .
  4. Once all message attachments are claimed, the updated message list is saved in Cloud Save.

Delete a message

When you press the Delete button for a message:
  1. The message is removed from the local list of inbox messages.
  2. If the inbox was previously full, such that deleting the message created space for a new message, the client rechecks the list of all possible messages downloaded from Remote Config against the message ID of the last message saved in Cloud Save to see if there are any new messages to add to the inbox. If there are, it adds as many of those messages to the inbox as will fit.
  3. The updated list of inbox messages is then saved in Cloud Save.
  4. Finally, the view refreshes to show the updated list. If the deleted message was previously selected, this also updates the UI to not display the deleted message's detail view.

Delete all read and claimed attachments

When you press the Delete Read button:
  1. The script loops through the list of inbox messages, and removes each message that has both
    message.metadata.isRead
    set to
    true
    and
    message.metadata.hasUnclaimedAttachment
    set to
    false
    . This implementation does not delete messages with unclaimed attachments, to prevent players from accidentally deleting messages with available attachments.
  2. If the inbox was previously full, such that deleting the message created space for a new message, the client rechecks the list of all possible messages downloaded from Remote Config against the message ID of the last message saved in Cloud Save to see if there are any new messages to add to the inbox. If there are, it adds as many of those messages to the inbox as will fit.
  3. The updated list of inbox messages is then saved in Cloud Save.
  4. Finally, the view refreshes to show the updated list. If the deleted message was previously selected, this also updates the UI to not display the deleted message's detail view.

Reset the inbox for a specific audience

At the bottom of the scene, you can reset the inbox while impersonating a particular audience:
  • Default
  • All Spenders
  • Unengaged Players
  • French Speakers
  • New Players
Each of the non-default audiences adds a message to the message list that is specific to that particular audience. These messages are determined by Game Overrides. When you reset the inbox for the given audience, the following occurs:
  1. The scene resets, clearing the selected message field and deleting the Cloud Save inbox data.
  2. The client queries Remote Config with the specified audience, to retrieve the list of potential messages that includes any audience specific ones that were previously omitted.
  3. The maximum number of messages are added to the inbox from the Remote Config data, and saved in Cloud Save.
  4. The view refreshes to show the new list of messages.

Open the player inventory

When you press the inventory bag icon, the following occurs:
  1. The client calls
    EconomyService.Instance.PlayerInventory.GetInventoryAsync()
    to refresh the player's list of owned Economy inventory items.
  2. A pop-up window displays the resulting list of inventory items.

Setup

Requirements

To replicate this use case, you need the following Unity packages in your project:

Package

Role

AddressablesAllows developers to retrieve an asset by using its address. In this sample, the service looks up Economy item sprites based on the information stored in the Economy item's custom data.
AuthenticationAutomatically signs in the user anonymously to keep track of their data server-side.
Cloud CodeStores important validation logic server-side. In this use case it is used to validate that message attachments haven't already been claimed, and to process the virtual purchase that stores the attachment rewards.
Cloud SaveStores the player's inbox state, including the message info downloaded from Remote Config, and the player-specific message metadata like whether the message has been read yet.
DeploymentProvides a cohesive interface to deploy assets for Cloud Services.
EconomyMaintains the player's wallet and inventory, and the virtual purchase info associated with a given message's attachment.
Game OverridesDefines the audience grouping and message data for messages that you want to only send to a certain audience.
Remote ConfigProvides key-value pairs where the value that is mapped to a given key can change on the server side, either manually or based on specific Game Overrides. In this sample, we store the message info in Remote Config. Messages that should only be sent to a particular audience are stored as blank messages and are completed by Game Overrides.
Note: Although it is listed as a package and requires separate dashboard configuration, Game Overrides doesn't actually have an SDK to install from Package Manager. It is a server-side offering that affects values returned from other services. To use these services in your game, activate each service for your Organization and project in the Unity Dashboard.

Unity Cloud services configuration

To replicate this sample scene's setup in your own Unity project, configure the following items:
  • Cloud Code scripts
  • Economy items
  • Remote Config values
  • Remote Config Game Overrides
To configure these items you can use the Deployment package, or manually enter them using the Unity Dashboard. The recommended best practice is to use the Deployment package as it greatly accelerates this process.

Using the Deployment package

To deploy configurations using the Deployment package:
  1. Open the Deployment window.
  2. Check in
    Common
    and
    In-Game Mailbox
    .
  3. Click
    Deploy Selection
    .
This deploys the following items:
  • Cloud Code scripts
  • Economy items
  • Remote Config values
The Deployment package doesn't support the following items:
  • Remote Config Game Overrides
To configure them, refer to Using the Unity Dashboard.

Using the Unity Dashboard

You can use the Unity Dashboard to manually configure your services by project and environment. Refer to the following sections to configure this sample.

Cloud Code

Publish the following scripts in the Unity Dashboard:

Script

Parameters

Description

Location in project

Messages_ClaimAttachment
messageId

The ID of the message that owns the attachment the player wants to claim.
Fetches the appropriate attachment for the given message, validates that the attachment hasn't already been claimed, calls Economy's process purchase method for that Virtual Purchase, and marks the attachment as claimed.
Assets/Use Case Samples/In-Game Mailbox/Cloud Code/Messages_ClaimAttachment.js
Messages_ClaimAllAttachments
noneGets the list of messages in a player's inbox, finds all messages that have an unclaimed attachment, and calls Economy's process purchase method for each Virtual Purchase, marking each message as read and attachment as claimed.
Assets/Use Case Samples/In-Game Mailbox/Cloud Code/Messages_ClaimAllAttachments.js

Economy

Configure the following resources in the Unity Dashboard:

Resource type

Resource item

ID

Custom Data

Description

CurrencyGemGEM
{"spriteAddress": "Sprites/Currency/Gem"}
A premium currency gifted by some messages.
CurrencyCoinCOIN
{"spriteAddress": "Sprites/Currency/Coin"}
A soft currency gifted by some messages.
Inventory ItemSwordSWORD
{"spriteAddress": "Sprites/Inventory/Sword"}
An inventory item gifted by some messages.
Inventory ItemShieldSHIELD
{"spriteAddress": "Sprites/Inventory/Shield"}
An inventory item gifted by some messages.
In addition, configure the following virtual purchases:

Item name

ID

This purchase rewards

This purchase costs

Message 003 Gift for New Players
MESSAGE_003_GIFT_NEW_PLAYERS
Sword (1), Shield (1), Coin (100)Nothing*
Message 004 Gift for Unengaged Players
MESSAGE_004_GIFT_UNENGAGED
Gem (50)Nothing*
Message 005 Gift
MESSAGE_005_GIFT
Coin (50)Nothing*
Message 008 Gift
MESSAGE_008_GIFT
Coin (100), Gem (50)Nothing*
Message 010 Gift
MESSAGE_010_GIFT
Gem (50)Nothing*
Message 011 Gift
MESSAGE_011_GIFT
Sword (1)Nothing*

Remote Config

Set up the following config values in the Unity Dashboard:

Key

Type

Description

Value

MESSAGES_ALL
JSONThe JSON list of all possible message IDs that exist in the Unity Dashboard, listed in the order they should be downloaded in.
{"messageList": ["MESSAGE_001","MESSAGE_002","MESSAGE_003","MESSAGE_004","MESSAGE_005","MESSAGE_006","MESSAGE_007","MESSAGE_008","MESSAGE_009","MESSAGE_010","MESSAGE_011"]}
MESSAGE_001
JSONOne of the messages that a player could receive.
{"title": "","content": "","attachment": "","expiration": "0.00:03:00.00"}
MESSAGE_002
JSONOne of the messages that a player could receive.
{"title": "","content": "","attachment": "","expiration": "0.00:03:00.00"}
MESSAGE_003
JSONOne of the messages that a player could receive.
{"title": "","content": "","attachment": "","expiration": "0.00:03:00.00"}
MESSAGE_004
JSONOne of the messages that a player could receive.
{"title": "","content": "","attachment": "","expiration": "0.00:03:00.00"}
MESSAGE_005
JSONOne of the messages that a player could receive.
{"title": "Got new Use Case sample ideas?","content": "We'd love to hear your suggestionsabout what kind of new samples you would likeus to deliver. Let us know at headstart@unity3d.com","attachment": "MESSAGE_005_GIFT","expiration": "0.00:10:00.00"}
MESSAGE_006
JSONOne of the messages that a player could receive.
{"title": "New update coming soon","content": "Our next update will bereleased soon and will require a clientupdate from the App Store or Google PlayStore. No, that's a joke. Our samplesare only available on Github for now ;)","attachment": "","expiration": "0.00:03:00.00"}
MESSAGE_007
JSONOne of the messages that a player could receive.
{"title": "There's cake at the end","content": "The cake is a lie.","attachment": "","expiration": "0.00:03:00.00"}
MESSAGE_008
JSONOne of the messages that a player could receive.
{"title": "The new update is here!!","content": "We hope you will enjoy the newcontent the team has prepared for you. Onthe menu: this brand new in-game messagingsample and an updated version of the IdleClicker game sample that now showcases mergingand evolving inventory items.","attachment": "MESSAGE_008_GIFT","expiration": "0.00:10:00.00"}
MESSAGE_009
JSONOne of the messages that a player could receive.
{"title": "Where is my mind?","content": "This is a very uninspired anduninspiring message. Our apologies.","attachment": "","expiration": "0.00:03:00.00"}
MESSAGE_010
JSONOne of the messages that a player could receive.
{"title": "Server Downtime","content": "Our servers were offline for2 hours yesterday for unexpected reasons.Please accept our apologies and this giftas a compensation. Of course, this is onlyfor illustration purposes; UGS Use Casesamples are never offline!","attachment": "MESSAGE_010_GIFT","expiration": "0.00:10:00.00"}
MESSAGE_011
JSONOne of the messages that a player could receive.
{"title": "It's dangerous to go alone","content": "Take this!","attachment": "MESSAGE_011_GIFT","expiration": "0.00:10:00.00"}

Game Overrides

Configure the following Overrides in the Unity Dashboard:
DetailsName the Override “Messages All Spenders Overrides”.
TargetingSelect JEXL with the following JEXL code*:
user.audience == "AllSpenders"
ContentSelect Choose content type > Config Overrides, then enter override values for the following key: Key:
MESSAGE_001
Value:
{"title": "Thank you for supporting us!","content": "This message specifically targetsplayers that spend, thanks to Game Overridesthat enable other Unity services to targetpredefined or custom audiences.","attachment": "","expiration": "0.00:03:00.00"}
SchedulingSet the following start and end dates:
  • Set Start Date to Update content immediately.
  • Set End Date to Run indefinitely.
StatusAfter finishing creating the Game Override, click Enable.
DetailsName the Override “Messages French Speaker Overrides”.
TargetingSelect JEXL with the following JEXL code*:
user.audience == "FrenchSpeakers"
ContentSelect Choose content type > Config Overrides, then enter override values for the following key: Key:
MESSAGE_002
Value:
{"title": "Oh oui, le message!","content": "Et oui, ce message est enfrançais car il cible les joueurs dontla langue du jeu est paramétrée enfrançais. Ceci est rendu possible grâceà Game Overrides qui permet à certainsservices de Unity de cibler des audiencespré-définies ou personnalisées.","attachment": "","expiration": "0.00:03:00.00"}
SchedulingSet the following start and end dates:
  • Set Start Date to Update content immediately.
  • Set End Date to Run indefinitely.
StatusAfter finishing creating the Game Override, click Enable.
DetailsName the Override “Messages New Players Overrides”.
TargetingSelect JEXL with the following JEXL code*:
user.audience == "NewPlayers"
ContentSelect Choose content type > Config Overrides, then enter override values for the following key: Key:
MESSAGE_003
Value:
{"title": "Welcome to the game!","content": "This message specifically targetsnew players, thanks to Game Overrides thatenable other Unity services to target pre-definedor custom audiences.","attachment": "MESSAGE_003_GIFT_NEW_PLAYERS","expiration": "0.00:10:00.00"}
SchedulingSet the following start and end dates:
  • Set Start Date to Update content immediately.
  • Set End Date to Run indefinitely.
StatusAfter finishing creating the Game Override, click Enable.
DetailsName the Override “Messages Unengaged Players Overrides”.
TargetingSelect JEXL with the following JEXL code*:
user.audience == "UnengagedPlayers"
ContentSelect Choose content type > Config Overrides, then enter override values for the following key: Key:
MESSAGE_004
Value:
{"title": "Welcome back to the game!","content": "This message specifically targetsunengaged players, thanks to Game Overridesthat enable other Unity services to targetpre-defined or custom audiences.","attachment": "MESSAGE_004_GIFT_UNENGAGED","expiration": 0.00:10:00.00"}
SchedulingSet the following start and end dates:
  • Set Start Date to Update content immediately.
  • Set End Date to Run indefinitely.
StatusAfter finishing creating the Game Override, click Enable.