游戏内邮箱

游戏内邮箱允许游戏开发者与玩家沟通。您可以使用它们来告知玩家游戏内活动、赠送有用的资源或帮助他们继续玩您的游戏。

先决条件

要使用此示例使用案例,您必须下载并安装 UGS 使用案例项目到您的 Unity 项目中。

概述

当玩家第一次加载场景时,他们会看到一个收件箱,其中列出了等待他们阅读的消息。在后续加载中,收件箱要么处于他们离开时的状态,要么由于会话之间消息过期而处于更新状态。

玩家可以与消息进行交互,领取附件、删除消息或将收件箱重置为全新的状态。

要查看此使用案例的实际操作

  1. 在 Unity 编辑器项目窗口中,选择**Assets** > **Use Case Samples** > **In-Game Mailbox**,然后双击InGameMailboxSample.unity打开示例场景。
  2. 进入播放模式与使用案例进行交互。

初始化

当场景加载时,InGameMailboxSceneManager脚本执行以下初始化步骤

  1. 初始化 Unity 游戏服务。

  2. 使用身份验证服务匿名登录玩家。如果您之前已初始化任何其他示例场景,身份验证将使用您的缓存玩家 ID,而不是创建新的玩家 ID。

  3. 刷新经济配置数据。如果自玩家上次打开应用程序以来创建了新的经济物品,这将初始化玩家配置中的这些物品。

  4. 从经济服务检索并更新该已验证用户的货币余额。

  5. 使用存储在经济物品配置的自定义数据中的精灵地址,从 Addressables 加载所有可能的货币和库存物品精灵。

  6. 检索玩家收件箱的更新消息信息

    1. 从远程配置下载所有可能消息的列表。
    2. 从云保存检索玩家当前的收件箱数据。
    3. 检查玩家收件箱中已保存的任何消息是否已过期,如果是,则删除这些消息。
    4. 检查从远程配置下载但尚未添加到玩家收件箱的任何新消息。
    5. 将更新的收件箱状态保存到玩家的云保存中。
  7. 在场景中显示更新的收件箱消息列表。

功能

场景中的左侧面板显示玩家收件箱中的消息列表。此列表会随着时间的推移而更新,因为消息会过期,或根据玩家的交互而更新。

在列表下方,一个计数器显示收件箱中有多少条消息以及在任何给定时间收件箱中可以容纳的最大消息数。当玩家第一次加载场景时,收件箱已满。

当所有消息都从收件箱中删除时(通过玩家交互或消息过期),会出现一个弹出窗口,提示玩家重置收件箱

Note: This pop-up is a usability feature of the sample, and would not be an expected interaction in a real-world implementation.

打开消息

当您从列表中选择一条消息时,将发生以下情况

  1. 消息的完整详细信息将显示在场景的右侧。
  2. 消息将标记为已读,并将该状态保存到云保存中。

如果消息有附件,则还会显示哪些经济物品附加以及一个领取它们的按钮。

领取附件

当您按下**领取**按钮时,客户端代码将调用Messages_ClaimAttachment.js云代码脚本,并将所选消息的 ID 作为参数包含在内。以下情况发生在后端

  1. 客户端请求云保存收件箱数据,并定位具有所提供 ID 的消息。
  2. 该脚本检查消息是否具有未领取的附件。
  3. 假设消息确实具有未领取的附件,该脚本将使用message.messageInfo.attachment字段中的虚拟购买 ID 进行经济makeVirtualPurchase调用。
  4. 如果购买过程成功,message.metadata.hasUnclaimedAttachment字段将设置为false并保存在云保存中,因此玩家无法再次领取附件。

领取所有附件

当您按下**领取全部**按钮时,客户端代码将调用Messages_ClaimAllAttachments.js云代码脚本。以下情况发生在后端

  1. 客户端从云保存请求收件箱数据。

  2. 云保存返回收件箱消息列表,该列表已过滤,仅显示message.metadata.hasUnclaimedAttachment设置为true的消息。

  3. 对于此过滤列表中的每条消息

    1. 该脚本使用message.messageInfo.attachment字段中的虚拟购买 ID 进行经济makeVirtualPurchase调用。
    2. 如果购买过程成功,message.metadata.hasUnclaimedAttachment将设置为false,并且message.metadata.isRead将设置为true
  4. 一旦所有消息附件都已领取,更新的消息列表将保存在云保存中。

Note: Saving changes in Cloud Save after each attachment is claimed would make the process more fault tolerant. However, it would also require more server calls and therefore be less efficient than the selected approach. It is up to the developer which advantage to prioritize.

删除消息

当您按下消息的**删除**按钮时

  1. 该消息将从本地收件箱消息列表中删除。
  2. 如果收件箱以前已满,因此删除消息为新消息腾出了空间,客户端将重新检查从远程配置下载的所有可能消息与云保存中保存的最后一条消息的 ID,以查看是否有要添加到收件箱的新消息。如果有,它将尽可能多地将这些消息添加到收件箱中。
  3. 然后将更新的收件箱消息列表保存在云保存中。
  4. 最后,视图刷新以显示更新的列表。如果删除的消息以前被选中,这也将更新 UI 以不显示删除的消息的详细信息视图。

删除所有已读并已领取的附件

当您按下**删除已读**按钮时

  1. 该脚本将循环遍历收件箱消息列表,并删除每条message.metadata.isRead设置为truemessage.metadata.hasUnclaimedAttachment设置为false的消息。此实现不会删除带有未领取附件的消息,以防止玩家意外删除带有可用附件的消息。
  2. 如果收件箱以前已满,因此删除消息为新消息腾出了空间,客户端将重新检查从远程配置下载的所有可能消息与云保存中保存的最后一条消息的 ID,以查看是否有要添加到收件箱的新消息。如果有,它将尽可能多地将这些消息添加到收件箱中。
  3. 然后将更新的收件箱消息列表保存在云保存中。
  4. 最后,视图刷新以显示更新的列表。如果删除的消息以前被选中,这也将更新 UI 以不显示删除的消息的详细信息视图。

为特定受众重置收件箱

在场景底部,您可以在模拟特定受众的同时重置收件箱

  • 默认
  • 所有支出者
  • 未参与的玩家
  • 法语使用者
  • 新玩家

每个非默认受众都会向消息列表添加一条特定于该受众的消息。这些消息由游戏覆盖确定。

当您为给定受众重置收件箱时,将发生以下情况

  1. 场景将重置,清除所选消息字段并删除云保存收件箱数据。
  2. 客户端使用指定的受众查询远程配置,以检索包含先前省略的任何受众特定消息的潜在消息列表。
  3. 从远程配置数据中将最大数量的消息添加到收件箱,并保存在云保存中。
  4. 视图刷新以显示新消息列表。

打开玩家库存

当您按下库存包图标时,将发生以下情况

  1. 客户端调用EconomyService.Instance.PlayerInventory.GetInventoryAsync()以刷新玩家拥有的经济库存物品列表。
  2. 一个弹出窗口将显示生成的库存物品列表。

设置

要求

要复制此使用案例,您需要在项目中使用以下Unity 包

角色
Addressables允许开发者使用资产地址检索资产。在此示例中,该服务根据存储在经济物品的自定义数据中的信息查找经济物品精灵。
身份验证自动以匿名身份登录用户,以便在服务器端跟踪其数据。
云代码在服务器端存储重要的验证逻辑。在这个用例中,它用于验证消息附件是否已被领取,并处理存储附件奖励的虚拟购买。
云保存存储玩家的收件箱状态,包括从远程配置下载的消息信息,以及玩家特定的消息元数据,例如消息是否已读。
部署提供一个连贯的界面来部署云服务的资产。
经济维护玩家的钱包和库存,以及与特定消息附件相关的虚拟购买信息。
游戏覆盖为只想发送给特定受众的消息定义受众分组和消息数据。
远程配置提供键值对,其中映射到给定键的值可以在服务器端更改,无论是手动更改还是基于特定游戏覆盖更改。在这个示例中,我们在远程配置中存储消息信息。应该只发送给特定受众的消息存储为空白消息,并由游戏覆盖完成。

注意:虽然它被列为一个包,并且需要单独的仪表盘配置,但游戏覆盖实际上没有来自包管理器的 SDK 可供安装。它是一个服务器端产品,会影响从其他服务返回的值。

要在您的游戏中使用这些服务,请在 Unity Cloud 仪表盘 中为您的组织和项目激活每个服务。

Unity Cloud 服务配置

要在您自己的 Unity 项目中复制此示例场景的设置,请配置以下项目

  • 云代码脚本
  • 经济项目
  • 远程配置值
  • 远程配置游戏覆盖

要配置这些项目,您可以 使用部署包,或 使用 Unity Cloud 仪表盘手动输入它们。推荐的最佳实践是使用部署包,因为它可以大大加快此过程。

使用部署包

要使用部署包部署配置,请执行以下操作

  1. 打开 部署窗口
  2. 选中 CommonIn-Game Mailbox
  3. 单击 Deploy Selection

这将部署以下项目

  • 云代码脚本
  • 经济项目
  • 远程配置值

部署包不支持以下项目

  • 远程配置游戏覆盖

要配置它们,请参考 使用 Unity Cloud 仪表盘

使用 Unity Cloud 仪表盘

您可以使用 Unity Cloud 仪表盘 按项目和环境手动配置您的服务。请参考以下部分配置此示例。

云代码

在 Unity Cloud 仪表盘中发布以下脚本

脚本参数描述项目中的位置
Messages_ClaimAttachmentmessageId
拥有玩家想要领取的附件的消息的 ID。
获取给定消息的适当附件,验证附件是否已被领取,调用 Economy 的处理购买方法以进行该虚拟购买,并将附件标记为已领取。Assets/Use Case Samples/In-Game Mailbox/Cloud Code/Messages_ClaimAttachment.js
Messages_ClaimAllAttachments获取玩家收件箱中的消息列表,找到所有具有未领取附件的消息,并为每个虚拟购买调用 Economy 的处理购买方法,并将每条消息标记为已读,并将附件标记为已领取。Assets/Use Case Samples/In-Game Mailbox/Cloud Code/Messages_ClaimAllAttachments.js

Note: The Cloud Code scripts included in the Cloud Code folder are local copies because you cannot view the sample project's dashboard. Changes to these scripts do not affect the behavior of this sample because they are not automatically uploaded to the Cloud Code service.

经济

在 Unity Cloud 仪表盘中配置以下资源

资源类型资源项目ID自定义数据描述
货币宝石GEM
{ 
  "spriteAddress": "Sprites/Currency/Gem" 
}
一些消息赠送的优质货币。
货币金币COIN
{ 
  "spriteAddress": "Sprites/Currency/Coin" 
}
一些消息赠送的软货币。
库存物品SWORD
{ 
  "spriteAddress": "Sprites/Inventory/Sword" 
}
一些消息赠送的库存物品。
库存物品盾牌SHIELD
{ 
  "spriteAddress": "Sprites/Inventory/Shield" 
}
一些消息赠送的库存物品。

此外,配置以下虚拟购买

项目名称ID此购买奖励此购买费用
消息 003 新玩家礼包MESSAGE_003_GIFT_NEW_PLAYERS剑 (1),盾牌 (1),金币 (100)无 *
消息 004 非活跃玩家礼包MESSAGE_004_GIFT_UNENGAGED宝石 (50)无 *
消息 005 礼包MESSAGE_005_GIFT金币 (50)无 *
消息 008 礼包MESSAGE_008_GIFT金币 (100),宝石 (50)无 *
消息 010 礼包MESSAGE_010_GIFT宝石 (50)无 *
消息 011 礼包MESSAGE_011_GIFT剑 (1)无 *

* There is no cost associated with these Virtual Purchases because they are all gifts to the player reading the message.

远程配置

在 Unity Cloud 仪表盘中设置以下配置值

类型描述
MESSAGES_ALLJSONUnity Cloud 仪表盘中所有可能的消息 ID 的 JSON 列表,按它们应该下载的顺序排列。
{
  "messageList": [
    "MESSAGE_001", 
    "MESSAGE_002", 
    "MESSAGE_003", 
    "MESSAGE_004", 
    "MESSAGE_005", 
    "MESSAGE_006", 
    "MESSAGE_007", 
    "MESSAGE_008", 
    "MESSAGE_009", 
    "MESSAGE_010", 
    "MESSAGE_011"
  ]
}
MESSAGE_001JSON玩家可能收到的消息之一。
{
  "title": "", 
  "content": "", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
MESSAGE_002JSON玩家可能收到的消息之一。
{
  "title": "", 
  "content": "", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
MESSAGE_003JSON玩家可能收到的消息之一。
{
  "title": "", 
  "content": "", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
MESSAGE_004JSON玩家可能收到的消息之一。
{
  "title": "", 
  "content": "", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
MESSAGE_005JSON玩家可能收到的消息之一。
{
  "title": "Got new Use Case sample ideas?", 
  "content": "We'd love to hear your suggestions 
  about what kind of new samples you would like 
  us to deliver. Let us know at [email protected]", 
  "attachment": "MESSAGE_005_GIFT", 
  "expiration": "0.00:10:00.00"
}
MESSAGE_006JSON玩家可能收到的消息之一。
{
  "title": "New update coming soon", 
  "content": "Our next update will be 
  released soon and will require a client 
  update from the App Store or Google Play 
  Store. No, that's a joke. Our samples 
  are only available on Github for now ;)", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
MESSAGE_007JSON玩家可能收到的消息之一。
{
  "title": "There's cake at the end", 
  "content": "The cake is a lie.", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
MESSAGE_008JSON玩家可能收到的消息之一。
{
  "title": "The new update is here!!", 
  "content": "We hope you will enjoy the new 
  content the team has prepared for you. On 
  the menu: this brand new in-game messaging 
  sample and an updated version of the Idle 
  Clicker game sample that now showcases merging 
  and evolving inventory items.", 
  "attachment": "MESSAGE_008_GIFT", 
  "expiration": "0.00:10:00.00"
}
MESSAGE_009JSON玩家可能收到的消息之一。
{
  "title": "Where is my mind?", 
  "content": "This is a very uninspired and 
  uninspiring message. Our apologies.", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
MESSAGE_010JSON玩家可能收到的消息之一。
{
  "title": "Server Downtime", 
  "content": "Our servers were offline for 
  2 hours yesterday for unexpected reasons. 
  Please accept our apologies and this gift 
  as a compensation. Of course, this is only 
  for illustration purposes; UGS Use Case 
  samples are never offline!", 
  "attachment": "MESSAGE_010_GIFT", 
  "expiration": "0.00:10:00.00"
}
MESSAGE_011JSON玩家可能收到的消息之一。
{
   "title": "It's dangerous to go alone", 
   "content": "Take this!", 
   "attachment": "MESSAGE_011_GIFT", 
   "expiration": "0.00:10:00.00"
 }

游戏覆盖

在 Unity Cloud 仪表盘中配置以下覆盖

详细信息将覆盖命名为“所有支出者消息覆盖”。
目标

选择使用以下 JEXL 代码的 JEXL *

user.audience == "AllSpenders"
内容选择 选择内容类型 > 配置覆盖,然后为以下键输入覆盖值

MESSAGE_001

{
  "title": "Thank you for supporting us!", 
  "content": "This message specifically targets 
  players that spend, thanks to Game Overrides 
  that enable other Unity services to target 
  predefined or custom audiences.", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}

调度设置以下开始和结束日期
  • 开始日期 设置为 立即更新内容
  • 结束日期 设置为 无限期运行
状态

完成创建游戏覆盖后,单击 启用

详细信息将覆盖命名为“法语使用者消息覆盖”。
目标

选择使用以下 JEXL 代码的 JEXL *

user.audience == "FrenchSpeakers"
内容选择 选择内容类型 > 配置覆盖,然后为以下键输入覆盖值

MESSAGE_002

{
  "title": "Oh oui, le message!", 
  "content": "Et oui, ce message est en 
  français car il cible les joueurs dont 
  la langue du jeu est paramétrée en 
  français. Ceci est rendu possible grâce 
  à Game Overrides qui permet à certains 
  services de Unity de cibler des audiences 
  pré-définies ou personnalisées.", 
  "attachment": "", 
  "expiration": "0.00:03:00.00"
}
调度设置以下开始和结束日期
  • 开始日期 设置为 立即更新内容
  • 结束日期 设置为 无限期运行
状态

完成创建游戏覆盖后,单击 启用

详细信息将覆盖命名为“新玩家消息覆盖”。
目标

选择使用以下 JEXL 代码的 JEXL *

user.audience == "NewPlayers"
内容选择 选择内容类型 > 配置覆盖,然后为以下键输入覆盖值

MESSAGE_003

{
  "title": "Welcome to the game!", 
  "content": "This message specifically targets 
  new players, thanks to Game Overrides that 
  enable other Unity services to target pre-defined 
  or custom audiences.", 
  "attachment": "MESSAGE_003_GIFT_NEW_PLAYERS", 
  "expiration": "0.00:10:00.00"
}

调度设置以下开始和结束日期
  • 开始日期 设置为 立即更新内容
  • 结束日期 设置为 无限期运行
状态

完成创建游戏覆盖后,单击 启用

详细信息将覆盖命名为“非活跃玩家消息覆盖”。
目标

选择使用以下 JEXL 代码的 JEXL *

user.audience == "UnengagedPlayers"
内容选择 选择内容类型 > 配置覆盖,然后为以下键输入覆盖值

MESSAGE_004

{
  "title": "Welcome back to the game!", 
  "content": "This message specifically targets 
  unengaged players, thanks to Game Overrides 
  that enable other Unity services to target 
  pre-defined or custom audiences.", 
  "attachment": "MESSAGE_004_GIFT_UNENGAGED", 
  "expiration": 0.00:10:00.00"
}

调度设置以下开始和结束日期
  • 开始日期 设置为 立即更新内容
  • 结束日期 设置为 无限期运行
状态

完成创建游戏覆盖后,单击 启用

* This sample determines which Game Override data to return based on a JEXL match with the audience value specified in the client. This allows the sample to simulate a player being in different audiences on-demand. In a real app, the Game Overrides would likely be set up to use built-in or custom-defined Analytics audiences for targeting. For example, during the Game Override targeting step, you could choose Stateful (Audiences) and check the appropriate Analytics audience from the list or click Build a new Audience.