背景
大约是 2023 年 2 月底,Epic 开放了开发者自由发布游戏的权限,和 Steam 一样,缴纳 100 美元即可发布,且对开发引擎没有限制,使用 Unity 开发的游戏也可以发布(用 UE 开发会有减免)。看到消息后,我尝试将之前的旧项目发布上去,没想到流程如此繁琐,历经 N 个下班后的夜晚终于将流程走通。
如果你也在尝试接入,这篇文章或可做个参考,有任何不清楚的地方,可直接在 indienova 私信我。
1.如何开始
在 Epic 商店底部点击“分发”按钮,即可进入 Epic 开发者相关操作页面。
此时通常会有一系列注册流程,不再详述。在这之后,需要创建组织并创建产品,大致如下:
税务审查
如果是新账户,系统会提示填写税务审查资料,网上有些朋友因地址填写问题审核失败,我也因地址填写问题被打回一次,后来按照英文格式详细填写一遍就通过审核了。
2.发布管理
接下来就可以进行商店页的编辑操作。Epic 后台与 Steam 不同,它分为 3 个部分,所有的编辑操作都在 Dev 部分进行,随后推送到 Stage 即可提交审核,审核完毕可随时推送至 Live。
页面编辑部分没有太多难点,此处略过。
日期设置
如果要开卖而不是积累愿望单,需检查“商品”-“发行日期”,改为“特定”,否则你的游戏只能看到页面,无法进入售卖审核环节(坑啊)。
3.程序包提交
3.1 BuildPatch Tool
Epic 游戏包的提交通过 BuildPatch Tool 工具进行,可在后台面板此处进行下载。
使用该工具与 Steam 程序包提交方式类似,但需要手动编写批处理文件并指定各种 ID。例如,我们可以在这个目录进行一些文件创建:
打开压缩包附带的 PDF 文档,里面有如何提交这些参数的说明:
bat 文件可编写如下:
BuildPatchTool.exe -OrganizationId="" -ProductId="" -ArtifactId="" -ClientId="" -ClientSecret="" -mode=UploadBinary -BuildRoot="./Release_Epic" -BuildVersion="1001" -AppLaunch="yourApp.exe" -AppArgs="" -FileAttributeList="" -FileIgnoreList=""
最后,将程序包和 bat 文件都编辑好,大致如下:
下面讲讲这些 ID 如何获取。
3.2 BuildPatch Tool 的各类 ID
ProductID 可在产品设置(SDK 下载与凭证)中找到:
OrganizationID 在“组织”->“设置”这里:
ArtifactID 在“构建与二进制文件”处:
最后的 ClientID 和密钥在 BPT(不在 SDK 下载与凭证那一栏,很坑)那里:
3.3 提交应用
运行 bat 没有问题后,就会提交到后台,然后需要在后台“构建与二进制文件”页面,创建对应构建,点击“分配平台”,设置新的版本 ID 即可生效:
3.4 商品设置
还需要在“商品详情”->“构建设置”处配置构建对应的文件夹。
最后这部分可能还有一些疏漏,因为撰文时已经是提交完成的状态,没法复现。请大家自行查询文档完成剩余步骤。
4.杂项
4.1 账户服务
继续流程。还需要开通账户服务,点击右侧“创建应用程序”即可。
创建后,“许可”和“已关联客户端”部分很简单,但“品牌设置”这一项比较麻烦,需要创建官方网站。但看网上一些朋友的分享,好像不设置也不影响游戏上架。
不过这一步我是完成了操作的,创建工作室官网并绑定品牌设置的流程如下:
1.在域名的 txt 地址解析处粘贴上 Epic 的一串验证码,验证域名。域名用非.com 后缀也可,所以购买那种 1 块钱的域名就可以解决。
2.不管后台是什么样的,主机需要支持 https(对,仔细看会发现要 https 开头的网站),并且需要一个主页网页和一个 PrivacyPolic 隐私政策网页。隐私政策随便找一个别的产品的作为模板,改下就行。
我配置好后的页面长这样:
4.2 产品设置-客户端策略
接下来设置客户端策略,以便为玩家开通成就权限等。
Metrics 必须勾选,否则无法统计后台数据;unlockAchievementForLocalUser 必须勾选,否则影响到成就接入(添加新的策略时选择特殊策略模板即可勾选)。
5.成就接入
5.1 创建成就
如果你的游戏在 Steam 平台(或别的平台)有成就,就必须在 Epic 上也接入成就,否则无法通过审核。接下来讲讲操作。
在“游戏服务”->“成就”处可以查看成就,点击“创建成就”,这时会提示“添加统计信息”,无需勾选。
下一步,成就 ID 就是你在 Unity 里需要填入解锁的 ID,和 Steam 一样:
5.2 分配成就奖励
和 Steam 不同的是,Epic 创建的成就要分配满 1000 经验值的奖励。
5.3 成就测试
那么,测试游戏的时候怎么知道自己已经获得了成就呢?在 Epic 库中,可以将预览模式设置为 Dev 以进行查看。
5.4 推送
最后留意 2 件事情:成就在 Live 面板是否存在,是否点击“推送到台面”。我记得“推送到台面”按钮要经过审核才会出现。
6.Unity SDK 接入
6.1 安装 Unity 版本 SDK
终于来到 Unity SDK 接入的部分,官网封装得很烂,我使用的是网络上开源的 Unity 封装版本:
https://github.com/PlayEveryWare/eos_plugin_for_unity_upm
注意:一定要用导入 Git 包的功能导入到 Unity Package Manager,不行自己挂一个梯子。我就是用第一次直接下载的包,导致文件不一致报错了。
6.2 配置
在入口场景新建一个 GameObject 挂载 EOSManager 和登录:
登录脚本 EpicLogin 的出处:https://blog.csdn.net/final5788/article/details/128202742
脚本内容如下:
public class EpicLogin : MonoBehaviour { private ProductUserId _productUserId; private void Start() { DontDestroyOnLoad(gameObject); Login(); } public void Login() { var token = string.Empty; string[] commandArgs = Environment.GetCommandLineArgs(); foreach (var commandArg in commandArgs) { if (commandArg.Contains("AUTH_PASSWORD")) { var args = commandArg.Split('='); if (args.Length >= 2) { token = args[1]; } } } EOSManager.Instance.StartLoginWithLoginTypeAndToken(LoginCredentialType.AccountPortal, null, token, callbackInfo => { if (callbackInfo.ResultCode != Epic.OnlineServices.Result.Success) { LoginWithPersistentMode(); } else { StartLoginWithLoginTypeAndTokenCallback(callbackInfo); } }); } public void LoginWithPersistentMode() { EOSManager.Instance.StartPersistentLogin((Epic.OnlineServices.Auth.LoginCallbackInfo callbackInfo) => { if (callbackInfo.ResultCode != Epic.OnlineServices.Result.Success) { LoginWithLoginTypeAndToken(); } else { StartLoginWithLoginTypeAndTokenCallback(callbackInfo); } }); } private void LoginWithLoginTypeAndToken() { EOSManager.Instance.StartLoginWithLoginTypeAndToken( Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal, ExternalCredentialType.Epic, null, null, loginResult => { EOSManager.Instance.StartConnectLoginWithEpicAccount(loginResult.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo connectLoginCallbackInfo) => { if (connectLoginCallbackInfo.ResultCode == Result.Success) { _productUserId = connectLoginCallbackInfo.LocalUserId; } else if (connectLoginCallbackInfo.ResultCode == Result.InvalidUser) { // ask user if they want to connect; sample assumes they do EOSManager.Instance.CreateConnectUserWithContinuanceToken(connectLoginCallbackInfo.ContinuanceToken, (Epic.OnlineServices.Connect.CreateUserCallbackInfo createUserCallbackInfo) => { EOSManager.Instance.StartConnectLoginWithEpicAccount(loginResult.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo retryConnectLoginCallbackInfo) => { if (retryConnectLoginCallbackInfo.ResultCode == Result.Success) { _productUserId = retryConnectLoginCallbackInfo.LocalUserId; } }); }); } else { } }); }); } private void StartConnectLoginWithLoginCallbackInfo(LoginCallbackInfo loginCallbackInfo) { EOSManager.Instance.StartConnectLoginWithEpicAccount(loginCallbackInfo.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo connectLoginCallbackInfo) => { if (connectLoginCallbackInfo.ResultCode == Result.Success) { _productUserId = connectLoginCallbackInfo.LocalUserId; } else if (connectLoginCallbackInfo.ResultCode == Result.InvalidUser) { EOSManager.Instance.CreateConnectUserWithContinuanceToken(connectLoginCallbackInfo.ContinuanceToken, (Epic.OnlineServices.Connect.CreateUserCallbackInfo createUserCallbackInfo) => { EOSManager.Instance.StartConnectLoginWithEpicAccount(loginCallbackInfo.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo retryConnectLoginCallbackInfo) => { if (retryConnectLoginCallbackInfo.ResultCode == Result.Success) { _productUserId = retryConnectLoginCallbackInfo.LocalUserId; } }); }); } }); } public void StartLoginWithLoginTypeAndTokenCallback(LoginCallbackInfo loginCallbackInfo) { if (loginCallbackInfo.ResultCode == Epic.OnlineServices.Result.AuthMFARequired) { // collect MFA // do something to give the MFA to the SDK print("MFA Authentication not supported in sample. [" + loginCallbackInfo.ResultCode + "]"); } else if (loginCallbackInfo.ResultCode == Result.AuthPinGrantCode) { ///TODO(mendsley): Handle pin-grant in a more reasonable way } else if (loginCallbackInfo.ResultCode == Epic.OnlineServices.Result.Success) { StartConnectLoginWithLoginCallbackInfo(loginCallbackInfo); } else if (loginCallbackInfo.ResultCode == Epic.OnlineServices.Result.InvalidUser) { EOSManager.Instance.AuthLinkExternalAccountWithContinuanceToken(loginCallbackInfo.ContinuanceToken, #if UNITY_SWITCH LinkAccountFlags.NintendoNsaId, #else LinkAccountFlags.NoFlags, #endif (Epic.OnlineServices.Auth.LinkAccountCallbackInfo linkAccountCallbackInfo) => { if (linkAccountCallbackInfo.ResultCode == Result.Success) { StartConnectLoginWithLoginCallbackInfo(loginCallbackInfo); } else { print("Error Doing AuthLink with continuance token in. [" + linkAccountCallbackInfo.ResultCode + "]"); } }); } else { print("Error logging in. [" + loginCallbackInfo.ResultCode + "]"); } // Re-enable the login button and associated UI on any error if (loginCallbackInfo.ResultCode != Epic.OnlineServices.Result.Success) { //ConfigureUIForLogin(); } } }
在 Unity 顶部菜单的 Tools->EosPlugin->Dev Portal Configuration 处配置各类 ID,蓝色框出的区域可以不用配置。所有的配置内容都在 SDK 下载与凭证那一栏,ClietID 也用这一栏的。
出包之后检查有没有 EOSBootstrapper 文件,如果没有就说明 SDK 没装正确:
6.3 成就,Unity 部分
导入 SDK 插件的第一个案例,案例中封装好了成就管理器,会稍微方便些。
成就代码:
public static class EpicAchievementMediator { public static void CompleteAchievement(string achievementID) { UnlockAchievementsOptions options = new UnlockAchievementsOptions(); options.AchievementIds = new Epic.OnlineServices.Utf8String[1]; options.AchievementIds[0] = new Epic.OnlineServices.Utf8String(achievementID); EOSManager.Instance.GetEOSAchievementInterface().UnlockAchievements(ref options, null, null); EOSManager.Instance.GetOrCreateManager<EOSAchievementManager>().RefreshData(); try { EOSManager.Instance.GetOrCreateManager<EOSAchievementManager>().UnlockAchievementManually(achievementID, (ref OnUnlockAchievementsCompleteCallbackInfo info) => { if (info.ResultCode == Result.Success) { Debug.LogError("UnlockAchievement Succeesful"); EOSManager.Instance.GetOrCreateManager<EOSAchievementManager>().RefreshData(); } Debug.LogError("info.ResultCode: " + info.ResultCode); }); } catch (System.Exception e) { Debug.LogError("Errr! " + e); } } }
6.4 打包测试
最后打包、上传、后台更新最新包体 ID,进入 Epic 启动器测试。如果游戏运行时有 Epic Overlay UI 覆盖在游戏之上,并且获得成就也会有 Epic UI 的成就特效,说明基本上成功了,可以丢给 Epic 审核。
到这里,整个发布流程告一段落,与大家共勉~
暂无关于此文章的评论。