成就接入指南
成就接入指南
1. 基本概念
游戏中的成就系统,用于创建、管理和更新玩家在游戏中可能获得的成就。
成就系统主要分为两个部分:
- 成就配置:成就自身的属性。包含:名称、描述、分类、组别、起始值、阈值、奖励、是否为隐藏成就等。
- 玩家成就信息:某玩家的某个成就的相关信息。包含:达成值,是否已达成,达成时间,是否已领取奖励等。
2. 功能配置与管理
创建成就配置
在 UOS Passport 服务 「成就 -> 成就配置」 处点击 「立即创建」,创建一个名为 “人头收集者” 的成就配置:
配置说明:
- 唯一标识(SlugName):成就配置的唯一标识,同一个UOS APP下可以唯一定位一个成就配置,仅支持字母、数字、-、_的组合。此项必填,且创建后不可更改。
- 名称:成就的显示名称,此项必填。
- 描述:有关成就内容的描述。
- 分类:成就配置所属的类别,如皮肤收藏进度类别、对局成就类别等,可以为空。
- 组别:可用于配置分步成就组,如:同一组别下的成就可以具有相同的达成内容(如击杀数),但是具有不同的起始值和阈值(如累计击杀10人和累计击杀20人可配置成同一组别下的两个成就配置)。
- 自定义属性:自定义成就属性。
- 成就起始值:成就开始计算的起始值。如:累计击杀十次的成就,配置起始值应为0,阈值为10。玩家的默认初始达成值为成就配置的起始值。
- 成就阈值:如果配置成就阈值大于起始值,那么玩家此成就的达成值大于等于阈值,则表示此成就已达成。如果配置成就阈值小于起始值,那么玩家此成就的达成值小于等于阈值,则表示此成就已达成。
- 成就达成奖励:玩家达成成就后可以获得的资源及其数量。资源为已配置在 「经济系统 -> 资源」 中的资源。玩家兑换成就奖励后,玩家背包内会增加相应数量的资源。可在 「经济系统 -> 背包」 中查看玩家背包内的资源情况。


至此,我们创建了一个名称为“人头收集者”的成就,达成它需要成就达成值大于等于10。达成后可以兑换成就达成奖励,获取物品钻石*1。确认成就配置内容后点击 「发布」 将成就发布,完成一个成就的所有配置工作。
此时,可在 「成就 -> 成就配置」 页面查看和管理成就配置。成就配置需要在发布之后才能被玩家获取和使用。发布后的成就配置只能编辑名称、描述和自定义属性。发布后的成就配置不可删除。
分步成就
成就分为普通成就和分步成就,对于分类和组别都相同的一系列成就称为分步成就,分类成就配置下分类和组别都不可为空:
配置说明:
- 分类:成就配置所属的类别,如皮肤收藏进度类别、对局成就类别等。
- 组别:可用于配置分步成就组,如:同一组别下的成就可以具有相同的达成内容(如击杀数),但是具有不同的起始值和阈值(如累计击杀10人和累计击杀20人可配置成同一组别下的两个成就配置)。
- 是否为系列成就的隐藏计数成就:当使用「更新玩家组别成就」更新时,如果成就值更新后超出系列成就范围,会将数据保留在系列的隐藏计数成就中。此成就相关数据不会在SDK端返回,仅在服务端「获取玩家成就列表」API和成就配置页面中的「成就管理」中可以查询。

分布成就中,成就的起始值和阈值应当是相连的。一个完整的系列分步成就示例如下:
对于分步成就,可以使用「更新玩家分步成就」接口更新玩家的分步成就信息,只需要传入分布成就的分类、组别和更新数值,就可以更新玩家的这系列成就。如果玩家win_1达成值为9,那么更新玩家此分步成就Increase2时,玩家win_1达成值变成10并且达成win_1,玩家win_2达成值变成11,以此类推。
对于分步成就下Reset/Reduce/Accumulate更新方式,玩家已经达成的成就依旧不会更新。
管理玩家成就
在 UOS Passport 服务 「成就 -> 成就管理」 中可以查看本app下所有玩家的成就相关信息:
其中:
- 达成值:玩家此项成就的达成值。比如上面配置的kills人头收集者成就,达成值为3则可表示此玩家已完成三次击杀。当达成值大于等于阈值时,此玩家的此成就将被置为已达成,同时会记录达成时间。
- 领取奖励:如果成就配置中有相应的奖励配置,则玩家在达成成就后可以领取成就奖励,奖励不会在达成时自动领取,需要单独调用兑换奖励的接口。领取后会被标识为已领取,不可重复领取。
- 重置玩家成就:如果由于一些特殊原因,开发者想要收回某玩家的某成就,则可使用此项功能使得玩家的达成值重置为成就起始值。玩家如果已经达成并领取奖励,重置成就后奖励不会回收,但玩家再次达成此成就后无法重复领取该成就的奖励。
3. 在 Unity 中接入 SDK
在接入 SDK 前请确保完成 「功能配置」
在接入 SDK 前,请确保在代码中引入了 Passport 的命名空间:
using Unity.Passport.Runtime;接入用户登录模块
参考 「外部账号系统登录」 正确接入外部ID系统。 确保在调用 成就 相关方法之前 已完成外部ID系统的接入
- 参考 「Passport 登录」 正确接入 Passport Login,确保在调用 成就 相关方法之前 用户已经登录完成。
- 安装并初始化 Feature SDK
- 在 UOS Launcher 服务列表中,找到 Passport Feature, 点击 「Install SDK」,将服务 SDK 安装到当前项目中
- 初始化 Feature SDK
try { await PassportFeatureSDK.Initialize(); } catch (PassportException e) { Debug.Log($"failed to initialize sdk: {e.Message}"); throw; }
- 在 UOS Launcher 服务列表中,找到 Passport Feature, 点击 「Install SDK」,将服务 SDK 安装到当前项目中
1. 同步玩家成就
// 同步成就配置状态,对于已删除的成就配置,清除玩家相关成就数据
// 推荐在玩家每次登录时调用,用于将成就配置更新同步至玩家的数据中
Achievement.FetchAchievementsResponse achievementList = await PassportFeatureSDK.Achievement.SyncAchievements();
// 将玩家成就列表中的成就名称打印出来
foreach (var achievement in achievementList.Achievements)
{
Debug.Log(achievement.DisplayName);
}
public class FetchAchievementsResponse
{
// 符合搜索条件的商品目录总数
public uint Total;
// 结果起始位置
public uint Start;
// 结果获取的数量
public uint Count;
// 玩家成就列表,按正在进行、已达成的顺序排列,其中正在进行的成就按达成值降序排列、已达成的成就按达成时间降序排列
public List<AchievementInfoExpanded> Achievements;
}
public class AchievementInfoExpanded
{
// 同app下成就配置的唯一标识
public string SlugName;
// 成就配置名称
public string DisplayName;
// 描述
public string Description;
// 分类
public string Category;
// 组别
public string Series;
// 起始值
public uint StartingValue;
// 阈值,到达则表示成就已达成
public uint ThresholdValue;
// 是否为隐藏成就
public bool Hidden;
// 是否已达成
public bool Completed;
// 成就达成时间
public Timestamp CompletionTime;
// 玩家成就达成时的值,已达成的成就不会更新此项
public uint AchievedValue;
// 玩家成就记录自定义属性
public Dictionary<string, string> CustomData;
// 成就属性
public Dictionary<string, string> Properties;
// 解锁成就可获得的奖励
public List<ResourceDetail> Rewards;
// 玩家是否已经兑换成就奖励
public bool Redeemed;
}
public class ResourceDetail {
// 资源唯一标识slug
public string SlugName;
// 资源名称
public string DisplayName;
// 资源类型
public Economy.ResourceType ResourceType;
// 资源自定义属性
public Dictionary<string, string> CustomData;
// 资源数量
public uint Quantity;
// 资源类别
public string Namespace;
}2. 获取所有隐藏成就配置
Achievement.ListHiddenAchievementMetaResponse achievementMetaList = await PassportFeatureSDK.Achievement.ListHiddenAchievementMeta();
// 将获取到的隐藏成就配置名称打印出来
foreach (var achievementMetaItem in achievementMetaList.AchievementMeta)
{
Debug.Log(achievementMetaItem.DisplayName);
}
public class ListHiddenAchievementMetaResponse
{
// 符合搜索条件的商品目录总数
public uint Total;
// 结果起始位置
public uint Start;
// 结果获取的数量
public uint Count;
// 查询结果
public List<AchievementMetaExpanded> AchievementMeta;
}
public class AchievementMetaExpanded
{
// 成就配置ID
public string Id;
// 同app下成就配置的唯一标识
public string SlugName;
// 成就配置名称
public string DisplayName;
// 所属的UosAppID
public string IdDomainID;
// 描述
public string Description;
// 分类
public string Category;
// 组别
public string Series;
// 起始值
public uint StartingValue;
// 阈值,到达则表示成就已达成
public uint ThresholdValue;
// 是否已发布,sdk端能获取到的均为已发布的成就配置
public bool Released;
// 是否为隐藏成就
public bool Hidden;
// 解锁成就可获得的奖励
public List<ResourceDetail> Rewards;
// 成就配置属性
public Dictionary<string, string> Properties;
// 创建时间(UTC)
public Timestamp CreatedAt;
// 创建用户
public string CreatedBy;
// 最后修改时间(UTC)
public Timestamp ModifiedAt;
// 最后修改用户
public string ModifiedBy;
// 发布时间
public Timestamp ReleasedAt;
}
public class ResourceDetail {
// 资源唯一标识slug
public string SlugName;
// 资源名称
public string DisplayName;
// 资源类型
public Economy.ResourceType ResourceType;
// 资源自定义属性
public Dictionary<string, string> CustomData;
// 资源数量
public uint Quantity;
// 资源类别
public string Namespace;
}3. 玩家解锁隐藏成就
Achievement.AchievementMeta meta = await PassportFeatureSDK.Achievement.UnlockPersonaAchievement("hiddenAchievementSlugName");
public class AchievementMeta
{
// 成就配置ID
public string Id;
// 同app下成就配置的唯一标识
public string SlugName;
// 成就配置名称
public string DisplayName;
// 所属的UosAppID
public string IdDomainID;
// 描述
public string Description;
// 分类
public string Category;
// 组别
public string Series;
// 起始值
public uint StartingValue;
// 阈值,到达则表示成就已达成
public uint ThresholdValue;
// 是否已发布,sdk端能获取到的均为已发布的成就配置
public bool Released;
// 是否为隐藏成就
public bool Hidden;
// 解锁成就可获得的奖励
public Dictionary<string, uint> Rewards;
// 成就配置属性
public Dictionary<string, string> Properties;
// 创建时间(UTC)
public Timestamp CreatedAt;
// 创建用户
public string CreatedBy;
// 最后修改时间(UTC)
public Timestamp ModifiedAt;
// 最后修改用户
public string ModifiedBy;
// 发布时间
public Timestamp ReleasedAt;
}4. 更新玩家成就
// 更新玩家‘achievementSlugName’的成就达成值信息,将达成值增加6,同时更新该玩家该成就上的自定义属性。
var customData = new Dictionary<string, string>
{
{ "key", "value" }
};
Achievement.AchievementInfo achievement = await PassportFeatureSDK.Achievement.UpdatePersonaAchievement("achievementSlugName", Achievement.Action.Increase, 6, customData);
public enum Action {
// 增长(绝对值),如:玩家achievedValue为5,请求value为1,则更新后玩家achievedValue为6
Increase = 1,
// 减少(绝对值),如:玩家achievedValue为5,请求value为1,则更新后玩家achievedValue为4
Reduce = 2,
// 重置为成就起始值,如:玩家achievedValue为5,成就配置起始值为1,则更新后玩家achievedValue为1
Reset = 3,
// 累计值,覆盖旧值,如:玩家achievedValue为5,请求value为8,则更新后玩家achievedValue为8
Accumulate = 4,
}
public class AchievementInfo
{
// 同app下成就配置的唯一标识
public string SlugName;
// 成就配置名称
public string DisplayName;
// 描述
public string Description;
// 分类
public string Category;
// 组别
public string Series;
// 起始值
public uint StartingValue;
// 阈值,到达则表示成就已达成
public uint ThresholdValue;
// 是否为隐藏成就
public bool Hidden;
// 是否已达成
public bool Completed;
// 成就达成时间
public Timestamp CompletionTime;
// 玩家成就达成时的值,已达成的成就不会更新此项
public uint AchievedValue;
// 玩家成就记录自定义属性
public Dictionary<string, string> CustomData;
// 成就属性
public Dictionary<string, string> Properties;
// 解锁成就可获得的奖励
public Dictionary<string, uint> Rewards;
// 玩家是否已经兑换成就奖励
public bool Redeemed;
}5. 获取玩家成就
Achievement.ListPersonaAchievementsResponse personaAchievements = await PassportFeatureSDK.Achievement.SearchPersonaAchievements();
// 将获取到的玩家成就名称打印出来
foreach (var achievement in personaAchievements.Achievements)
{
Debug.Log(achievement.DisplayName);
}
public class ListPersonaAchievementsResponse
{
// 符合搜索条件的商品目录总数
public uint Total;
// 结果起始位置
public uint Start;
// 结果获取的数量
public uint Count;
// 查询结果,按正在进行、已达成的顺序排列,其中正在进行的成就按达成值降序排列、已达成的成就按达成时间降序排列
public List<AchievementInfoExpanded> Achievements;
}
public class AchievementInfoExpanded
{
// 同app下成就配置的唯一标识
public string SlugName;
// 成就配置名称
public string DisplayName;
// 描述
public string Description;
// 分类
public string Category;
// 组别
public string Series;
// 起始值
public uint StartingValue;
// 阈值,到达则表示成就已达成
public uint ThresholdValue;
// 是否为隐藏成就
public bool Hidden;
// 是否已达成
public bool Completed;
// 成就达成时间
public Timestamp CompletionTime;
// 玩家成就达成时的值,已达成的成就不会更新此项
public uint AchievedValue;
// 玩家成就记录自定义属性
public Dictionary<string, string> CustomData;
// 成就属性
public Dictionary<string, string> Properties;
// 解锁成就可获得的奖励
public List<ResourceDetail> Rewards;
// 玩家是否已经兑换成就奖励
public bool Redeemed;
}
public class ResourceDetail {
// 资源唯一标识slug
public string SlugName;
// 资源名称
public string DisplayName;
// 资源类型
public Economy.ResourceType ResourceType;
// 资源自定义属性
public Dictionary<string, string> CustomData;
// 资源数量
public uint Quantity;
// 资源类别
public string Namespace;
}6. 兑换成就奖励
try
{
// 无返回值,不报错即为成功
await PassportFeatureSDK.Achievement.RedeemAchievementRewards("achievedAchievementSlug");
}
catch (PassportException e)
{
Debug.Log(e.Code);
Debug.Log(e.ErrorMessage);
Debug.Log(e);
}7. 兑换玩家所有已完成的成就奖励
// 返回领取信息,包括领取失败信息列表redeemAllAchievementRewardFailResults及领取成功的成就信息列表redeemedAchievements,领取失败信息列表为空则表示所有都领取成功,有数据则是领取失败的成就信息及失败错误码
Achievement.RedeemAllAchievementRewardsSDKResponse redeemAllFailedResult = await PassportFeatureSDK.Achievement.RedeemAllAchievementRewards();
// 如果有失败的情况
if (redeemAllFailedResult.RedeemAllAchievementRewardFailResults.Count > 0)
{
foreach (var res in redeemAllFailedResult.RedeemAllAchievementRewardFailResults)
{
// 领取失败的成就SlugName
Debug.Log(res.Achievement.SlugName);
// 领取失败的成就名称
Debug.Log(res.Achievement.DisplayName);
// 领取失败的错误码
Debug.Log(res.ErrorCode);
// 领取失败的错误原因
Debug.Log(res.ErrorMessage);
}
}
public class RedeemAllAchievementRewardsSDKResponse
{
// 领取失败信息列表,为空则表示所有都领取成功,有数据则是领取失败的成就信息及失败错误码
public List<RedeemAllAchievementRewardFailResult> RedeemAllAchievementRewardFailResults;
// 领取成功的成就信息列表
public List<AchievementInfoExpanded> RedeemedAchievements;
}
public class RedeemAllAchievementRewardFailResults
{
// 领取失败的成就的详情
public AchievementInfoExpanded Achievement;
// 错误码
public uint ErrorCode;
// 错误信息
public string ErrorMessage;
}8. 更新玩家分步成就
// 其中成就所属类别与成就组别名称必填
// 对于玩家的此系列分步成就进行更新,假设此系列下成就A的起始值/阈值为0/10,成就B的起始值/阈值为10/20:
// 如果玩家成就A达成值为9,那么更新玩家此组别成就Increase2时,玩家成就A达成值变成10并且达成成就A,玩家成就B达成值变成11
// 对于分步成就下Reset/Reduce/Accumulate更新方式,玩家已经达成的成就依旧不会更新
Achievement.UpdatePersonaAchievementSeriesResponse res = await PassportFeatureSDK.Achievement.UpdatePersonaAchievementSeries("testCategory", "testSeries",Achievement.Action.Increase, 2);
public class UpdatePersonaAchievementSeriesResponse
{
// 玩家系列成就信息
public List<AchievementInfo> Achievements;
// 此次更新后新达成的成就slug列表
public List<string> NewlyAchieved;
}9. 获取所有成就的已达成人数
// 获取所有成就的已达成人数,统计数据有约五分钟延迟
// 可以根据slugName将结果中的统计信息匹配至玩家的成就列表进行显示
Achievement.GetAllCompletedStatisticsSDKResponse resGetAllCompletedStatistics = await PassportFeatureSDK.Achievement.GetAllCompletedStatistics();
foreach (var achievement in resGetAllCompletedStatistics.AchievementStatistics)
{
Debug.Log(achievement.SlugName);
Debug.Log(achievement.AchievedPersonaCount);
}
public class GetAllCompletedStatisticsSDKResponse
{
public List<AchievementStatistics> AchievementStatistics;
}
public class AchievementStatistics
{
// 成就配置的唯一标识
public string SlugName;
// 已达成的人数
public uint AchievedPersonaCount;
// 达成人数占总人数的百分比,保留两位小数,仅在请求参数withPercentage为true时返回
public double AchievedPercentage;
}10. 获取指定成就的已达成人数
// 获取指定成就的已达成人数,统计数据有约五分钟延迟
var achievementSlug = "testRedeem1";
GetCompletedStatisticsBySlugSDKResponse resGetCompletedStatisticsBySlug = await PassportFeatureSDK.Achievement.GetCompletedStatisticsBySlug(achievementSlug);
Debug.Log(resGetCompletedStatisticsBySlug.AchievementStatistics.SlugName);
Debug.Log(resGetCompletedStatisticsBySlug.AchievementStatistics.AchievedPersonaCount);
public class GetCompletedStatisticsBySlugSDKResponse
{
public AchievementStatistics AchievementStatistics;
}4. 附录
核心类和接口
namespace Unity.Passport.Runtime
{
public partial class PassportFeatureSDK
{
public class Achievement
{
// 同步成就配置状态,对于已删除的成就配置,清除玩家相关成就数据
// 返回玩家成就第一页列表
// 推荐在玩家每次登录时调用,用于将成就配置更新同步至玩家的数据中
public static async Task<FetchAchievementsResponse> SyncAchievements(){}
// 获取所有隐藏成就配置
public static async Task<ListHiddenAchievementMetaResponse> ListHiddenAchievementMeta(){}
// 玩家解锁隐藏成就
// slugName: 隐藏成就配置的唯一标识slug
public static async Task<AchievementMeta> UnlockPersonaAchievement(string slugName){}
// 更新玩家成就
// slugName: 成就配置的唯一标识slug
// action: 成就值修改方式,需为Increase/Reduce/Reset/Accumulate
// value: 成就值修改方式相应的值,已达成的成就不会更新成就达成值(achievedValue)
// customData: 自定义属性,大小默认限制为0.5KB
public static async Task<AchievementInfo> UpdatePersonaAchievement(string slugName, Action action, uint value, Dictionary<string, string> customData = null){}
// 获取玩家成就
// category: 分类名称,模糊查询
// completed: 是否已达成
// start: 查询起始位置,结果中按正在进行、已达成的顺序排列,其中正在进行的成就按达成值降序排列、已达成的成就按达成时间降序排列,默认为0
// count: 查询获取的数量,默认为10,最大为50
// redeemed: 成就奖励是否已被领取
public static async Task<ListPersonaAchievementsResponse> SearchPersonaAchievements(string category = null, bool? completed = null, uint start = 0, uint count = 10, bool? redeemed = null){}
// 兑换成就奖励
// slugName: 成就配置的唯一标识slug
public static async Task RedeemAchievementRewards(string slugName){}
// 兑换玩家所有已完成的成就奖励,玩家角色背包内增加相应资源
// 返回领取信息,包括领取失败信息列表redeemAllAchievementRewardFailResults及领取成功的成就信息列表redeemedAchievements,领取失败信息列表为空则表示所有都领取成功,有数据则是领取失败的成就信息及失败错误码
public static async Task<RedeemAllAchievementRewardsSDKResponse> RedeemAllAchievementRewards(){}
// 更新玩家分步成就
// category: 成就所属类别,不可为空
// series: 成就组别名称,不可为空
// action: 成就值修改方式
// value: 成就值修改方式相应的值
// customData: 自定义属性,当传入空字典时会更新为空,传入NULL时不更新,大小限制为0.5KB
public static async Task<UpdatePersonaAchievementSeriesResponse> UpdatePersonaAchievementSeries(string category, string series, Action action, uint value, Dictionary<string, string> customData = null){}
// 获取所有成就的已达成人数(统计数据有约五分钟延迟)
// withPercentage: 返回值中是否返回达成人数占总人数的百分比
// 返回所有成就的slugName及达成人数统计信息列表
public static async Task<GetAllCompletedStatisticsSDKResponse> GetAllCompletedStatistics(bool withPercentage = false){}
// 获取指定成就的已达成人数(统计数据有约五分钟延迟)
// slugName: 成就唯一标识
// withPercentage: 返回值中是否返回达成人数占总人数的百分比
// 返回成就的slugName及达成人数统计信息
public static async Task<GetCompletedStatisticsBySlugSDKResponse> GetCompletedStatisticsBySlug(string slugName, bool withPercentage = false){}
}
}
}服务端 API
客户端 API
错误码
参见 「Passport 错误码」 。