你的下一项功能
玩家收件箱
个人通知与项目公告,提供受保护奖励,并通过统一轮询读取社交消息。
Unity

可见的结果
游戏显示个人消息与公告,标记已读,并确保每项受保护奖励最多领取一次。
此步骤尚无视频。指南和MCP请求已可使用。
玩家收件箱
准备同一个项目
在仪表盘中选择与游戏配置相同的项目。
玩家收件箱 →首次请求不需要额外定义。没有对应游戏数据时,空结果也有效。
先注册测试玩家,再在本项目发布纯文本广播。使用同一玩家读取收件箱。
公告面向当前活跃且在发布时或之前已注册的项目玩家。先前不活跃的玩家重新激活后也可能符合资格。发布后注册的玩家不在范围内。
实现此功能
你可以预览指南。运行需要有效的项目密钥。 准备项目
已安装 SDK、导入项目配置,且玩家已登录。
此 SDK 示例使用 develop。请检查已安装的 SDK 是否具备所需 API。
先通过快速入门连接目标玩家,再通过明确的按钮或场景操作调用RunReviewedAction()。仅打开文件不会发送请求。
C#
1using PM.horizOn.Cloud.Objects.Network.Responses;
2using UnityEngine;
3using PM.horizOn.Cloud.Core;
4using PM.horizOn.Cloud.Manager;
5
6// Existing SDK/project configuration and authenticated player session required.
7// Call RunReviewedAction explicitly after reviewing this example and its data.
8// 使用当前玩家的已认证会话。领取结果不确定时,使用同一条目 ID 重试。用服务器状态替换数值缓存,不要重复累加奖励。
9public sealed class InboxDemo : MonoBehaviour
10{
11 public async void RunReviewedAction()
12 {
13 var sessionUser = UserManager.Instance.CurrentUser;
14 if (sessionUser == null || !sessionUser.IsValid()) return;
15 var userId = UserManager.Instance.CurrentUser.UserId;
16 var manager = InboxManager.Instance;
17 var loaded = await manager.GetInbox(userId, since: null);
18 if (!loaded.IsSuccess) { Debug.LogWarning(loaded.ErrorCode); return; }
19 foreach (var entry in loaded.Data.entries) Debug.Log(entry.textKey);
20 // The manager retains full cached entries when the wire response is unchanged.
21 // Poll again only after loaded.Data.pollAfterSeconds, and stop with the game lifecycle.
22
23 bool claimInFlight = false;
24 async System.Threading.Tasks.Task ClaimSelectedReward(string entryId)
25 {
26 if (claimInFlight) return;
27 const string projectId = "YOUR_PROJECT_ID";
28 string pendingKey = "horizOn_inbox_claim_" + projectId + "_" + userId;
29 string pending = PlayerPrefs.GetString(pendingKey, "");
30 if (pending.Length > 0 && pending != entryId) return; // Resolve the original claim first.
31 if (pending.Length == 0)
32 {
33 var current = manager.CurrentInbox; // Authorized for the current project/session.
34 if (current == null || current.userId != userId) return;
35 var selected = System.Array.Find(current.entries, entry => entry.id == entryId);
36 if (selected == null || selected.claimed || selected.attachment?.values == null ||
37 selected.attachment.values.Length == 0) return;
38 }
39 // Previously saved IDs remain replayable even after their visible state changes.
40 PlayerPrefs.SetString(pendingKey, entryId);
41 PlayerPrefs.Save();
42 if (PlayerPrefs.GetString(pendingKey) != entryId) return;
43 claimInFlight = true;
44 try
45 {
46 var claimed = await manager.Claim(userId, entryId);
47 if (!claimed.IsSuccess) { Debug.LogWarning(claimed.ErrorCode); return; }
48 // Display claimed.Data.inbox and replace values with claimed.Data.playerValues.
49 // The SDK refreshes its cache. Never add reward deltas yourself.
50 if (PlayerPrefs.GetString(pendingKey, "") == entryId)
51 {
52 PlayerPrefs.DeleteKey(pendingKey);
53 PlayerPrefs.Save();
54 }
55 }
56 finally { claimInFlight = false; }
57 }
58 // Invoke ClaimSelectedReward only from an explicit player action.
59 // Retry the saved entryId after a lost response; no replacement grant or new action ID.
60 // Other deliberate actions: await manager.MarkRead(userId, entryId);
61 // await manager.Remove(userId, entryId); // Protects unclaimed rewards.
62 }
63}
测试你的新功能。
游戏显示个人消息与公告,标记已读,并确保每项受保护奖励最多领取一次。
在仪表盘中选择与游戏配置相同的项目。


