Func Stateless C# 开发指南
Func Stateless C# 开发指南
通过集成 Func Stateless SDK, 用户可以通过在 Unity Editor 中编写 C# 代码, 并让其托管、运行在 Func Stateless 上。
1. 安装配置 UOS Launcher
参考 Launcher 教程,安装 Launcher 后,关联 UOS APP, 开启 Func Stateless 服务并安装 Func Stateless SDK。
注意: 请务必确认在进行后续教程之前,已经成功完成了 Launcher 教程的安装步骤,否则可能导致后续接入无法顺利进行。
2. 编写 C# 函数代码
安装完成 Func Stateless SDK 后, 通过点击菜单栏 「UOS -> Func Stateless -> Open Panel」 打开 Func Stateless 开发工具进行创建 C# 函数代码。
注意,需要在 UOS Launcher 中绑定已启用 Func Stateless 服务( Runtime 需为 Dotnet)的 UOS App。
第一次打开工具后,需要点击 「+新建云函数」 来创建您的第一个云函数,其中会包含示例代码,您可以基于业务需求在此代码基础上进行修改。(SDK 会默认创建 Assets/Scripts/CloudService 目录作为云函数代码的目录)
您可以手动修改函数代码的存储目录,但需要注意所有的函数代码需要存放在同一目录下。
图中 NewService 为一个云函数类(CloudService),对应 Stateless 一个版本中的一条记录。每个云函数类中可以创建多个云函数(CloudFunc)(如图中 Echo 和 GetUserInfo 所示) 提供调用。
当您需要新建其他函数时,可以点击工具右上角 「+新建」 按钮进行创建。当您需要快速跳转到函数的代码时,可以点击工具上的函数名称,代码编辑器会自动跳转到代码所在的行号。
试用用户无法在 Stateless 上部署超过5个函数类(CloudService),且一小时后会自动回收。请联系我们成为正式用户,解除限制。

当函数编写完成,等待 UnityEditor 编译完成后,您可以通过本地直接运行进行代码验证。
3. 远程调用
当您想要调试远程模式或进行打包时,可以切换到远程模式。点击 「上传云函数」 后,等待云函数的生成。
若您使用了工具生成的默认代码,可以编写下述 Unity 脚本并挂载到 GameObject 上进行测试。
private async void Start()
{
var ns = new NewService();
var echoResult = await ns.Echo("hello world");
Debug.Log($"echoResult: {echoResult}");
var userInfo = await ns.GetUserInfo("userId");
Debug.Log($"userInfo: {userInfo.Name} - {userInfo.Age}");
}任何代码的修改请切换成本地模式后进行。修改完成后请重新上传。请不要修改远程模式下的任何代码和代码文件的只读权限。

等待云函数上传完成后,可以通过 「云函数代码模式」 的下拉框选择模式。切换到远程模式后,点击 「最近一次上传」 的时间标签进入 WEB 控制台检查云函数在 Stateless 上的部署情况后即可开始进行远程模式的访问。
4. 自定义数据类型
如果您需要定义一些在客户端和云函数端共用的数据类型(例如,云函数的输入参数或返回类型),您可以参考以下中任意一种方式实现。在切换远程模式后,会保留采用下列方式编写的数据类型。
定义的数据类型需要和云函数在同一个命名空间中。
存放在云函数代码文件中
// CloudService/TestService.cs
using ...
namespace CloudService
{
[CloudService]
public class NewService
{
...
[CloudFunc]
public async Task<UserInfo> GetUserInfo(string userId)
{
...
}
...
}
public class UserInfo
{
public string Name;
public int Age;
}
}存放在「Model」目录中
// CloudService/Model/UserInfo.cs
namespace CloudService
{
public class UserInfo
{
public string Name;
public int Age;
}
}5. 函数上下文
FuncContext 是一个用于管理调用函数上下文的工具类,主要用于设置和获取函数调用环境中的认证信息、Token信息以及自定义的上下文键值对。该类提供了以下的方法来简化与上下文相关的操作。
...
using Unity.UOS.Func.Stateless.Core.Context;
...
// 设置 app id/secret (默认使用 launcher 中的值)
// 在需要调用其他 uos app 提供的服务时,调用传入对应的 app id/secret
FuncContext.SetAppIdSecret(string appId, string appSecret);
// 设置 Token (passport jwt)
FuncContext.SetToken(string token);
// 设置 UserId
FuncContext.SetUserId(string userId);
// 设置 PersonaId
FuncContext.SetPersonaId(string personaId);
// 设置自定义上下文信息
FuncContext.Set(string key, string value);
// 获取上下文信息
FuncContext.GetAppId();
FuncContext.GetAppSecret();
FuncContext.GetToken();
FuncContext.GetUserId();
FuncContext.GetPersonaId();
FuncContext.Get(string key);
// 清空上下文信息
FuncContext.Clear();
// 获取请求的 Headers 和 QueryStrings
FuncContext.GetHeaders();
FuncContext.GetQueryStrings();
// 校验 JWT
// 校验 Token 字段, 并将解析后的 UserId 和 PersonaId 存入上下文
FuncContext.ValidateToken();您可以参考以下的云函数和客户端代码来使用 FuncContext。
// 云函数
...
[CloudFunc]
public async Task<string> PrintCtxToken()
{
var appId = FuncContext.GetAppId();
var appSecret = FuncContext.GetAppSecret();
var token = FuncContext.GetToken();
var myKey = FuncContext.Get("myKey");
var headers = FuncContext.GetHeaders();
var queryStrings = FuncContext.GetQueryStrings();
return token;
}
...
// 客户端
...
FuncContext.SetToken("xxx.yyy.zzz");
FuncContext.Set("myKey", "myValue");
...
var _ctxService = new
var resToken = await _ctxService.PrintCtxToken();
...6. 使用第三方库
注意,本节导入的第三方库仅在本地模式下的云函数中使用,客户端中其他代码请勿使用。
在 Unity Editor 菜单栏中 「UOS -> Func Stateless -> Import NuGetForUnity」 导入 UOS 版本的 NuGetForUnity 工具。
导入完成后点击菜单栏中 「NuGet -> Preferences」 打开配置界面。修改 Packages Install Path 为云函数所在目录下的 Packages 目录; Packages Config Path 为云函数所在目录。
修改完成配置后,点击 「NuGet -> Manager NuGet Packges」 打开界面。在输入框中输入需要安装的库的名称(例如,UOS.FuncStateless.MongoDB)点击 「Search」 搜索。点击 「Install」 下载安装所需要的库。
如果当您导入第三方库后出现版本冲突问题,例如 Assembly references: 4.1.1.0 Found in project: 4.1.3.0.。您可以在 「Edit -> Project Settings -> Player -> 下滑找到 Other Settings 中的 Configuration -> 取消勾选 Assembly Version Validation」。
当您完成本地调试并切换到远程模式后,您可以重新勾选此选项。
您可以参考 CRUD 连接指南了解如何通过 C# Stateless 连接到 CRUD 数据库。
云函数运行环境中已默认集成「Newtonsoft.JSON」,您可以无需安装直接使用此库中的功能。
由于 Unity 和云函数的运行时环境不同,某些第三方库可能会出现兼容性问题。目前,以下第三方库经过测试并可正常使用:
- MongoDB.Driver
- StackExchange.Redis
- MySQLConnector
- UOS.FuncStateless.MongoDB
- UOS.FuncStateless.Redis
- UOS.FuncStateless.Mysql
- UOS.FuncStateless.PostgreSQL
云函数环境中默认使用了以下的第三方库,您可以无需导入直接使用:
- Newtonsoft.Json
- Google.Protobuf
7. 使用 UOS SDK
Passport
您可以在云函数中直接使用 Passport Feature SDK 中所提供的功能。
参考 「Passport 登录」 在客户端正确接入 Passport Feature SDK,确保在调用相关方法之前用户已经登录完成。
在接入 SDK 前,请确保在代码中引入了 Passport 的命名空间:
using Unity.Passport.Runtime;以排行榜为示例,您可以编写以下云函数,并在云函数内直接使用 Passport Feature SDK 所提供的功能。云函数提供了 ExecuteException 来封装调用相关方法中产生的错误。
// PassportService.cs
using System.Threading.Tasks;
using Unity.Passport.Runtime;
using Unity.UOS.Func.Stateless.Core.Attributes;
using UnityEngine;
namespace CloudService
{
[CloudService]
public class PassportService
{
[CloudFunc]
public async Task<string> GetLeaderboardName(int idx)
{
var leaderboardList = await PassportFeatureSDK.Leaderboard.GetLeaderboards();
if (idx < 0 || idx >= leaderboardList.Total) return "not found";
Debug.Log($"Total: {leaderboardList.Total}, Start: {leaderboardList.Start}, Count: {leaderboardList.Count}");
return leaderboardList.Leaderboards[idx].SlugName;
}
}
}
// TestLeaderboard.cs
var ps = new PassportService();
try
{
Debug.Log(await ps.GetLeaderboardName(1));
}
catch (ExecuteException e)
{
Debug.Log(e);
}Save
您可以在云函数中直接使用 CloudSave SDK 中所提供的功能。
参考「初始化」在客户端正确接入 CloudSave SDK,确保在调用相关方法之前完成SDK鉴权。
以通过字节数组创建一个文件存档为示例,您可以编写以下云函数,并且在云函数内直接使用 CloudSave SDK 所提供的功能。
// CloudSaveService.cs
using System.Threading.Tasks;
using Unity.UOS.CloudSave;
using Unity.UOS.CloudSave.Model.Files;
using Unity.UOS.Func.Stateless.Core.Attributes;
using UnityEngine;
namespace CloudService
{
[CloudService]
public class CloudSaveService
{
[CloudFunc]
public async Task<string> CreateByBytes(byte[] data, string name)
{
CreateOptions options = new CreateOptions()
{
Namespace = "stateless",
Description = "test by stateless",
};
var saveId = await CloudSaveSDK.Instance.Files.CreateAsync(name, data, options);
Debug.Log(saveId);
return saveId;
}
}
}
// TestSave.cs
var cs = new CloudSaveService();
var data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a };
try
{
var saveId = await cs.CreateByBytes(data, "my-save");
Debug.Log(saveId);
}
catch (ExecuteException e)
{
Debug.Log(e);
}8. 函数配置
配置文件
如果您需要将一些自定义的配置文件(例如,json, xml, yaml等)一起打包上传到云函数可以按照本流程实现。
首先需要在云函数代码目录中创建 「Config」 文件夹,若文件夹已存在可以跳过这一步。
将您所需要上传的配置文件移动到此目录中,采用如下的示例代码去获取文件的路径,随后您可以编写代码读取配置文件内容。
...
using Unity.UOS.Func.Stateless.Helper;
using Newtonsoft.Json;
...
// 使用 Config.GetPath() 获取文件夹路径,保证本地调试和远程模式下都可以正常使用
var configFilePath = Path.Combine(Config.GetPath(), "test.json");
var deserialize = JsonConvert.DeserializeObject(await File.ReadAllTextAsync(configFilePath));在完成了函数的开发与部署之后,可以在 Func Stateless 的云函数页面对每个云函数的属性进行单独配置来灵活地应对各种业务需求和调用场景。
执行超时时间
为了避免陷入无限的调用等待时间,云函数执行超过执行超时时间后会被终止(视作调用失败)。执行超时时间默认为3秒。
环境变量
在函数上传部署后,可以通过增加、编辑环境变量来修改云函数在后续执行过程中获取到的环境变量,从而来应对业务场景的变动。在代码中获取环境变量的代码示例:
var workDir = Environment.GetEnvironmentVariable("WORK_DIR");定时触发器
试用用户暂时无法配置定时触发器, 请 联系我们 成为正式用户。
在函数上传部署后,可以通过添加标签标注 [CloudFunc(CronJob = true)] 指定定时任务所需要执行的函数,标注为定时任务的函数不能有输入输出参数,每个云函数类只能标记一个定时任务。在部署完成云函数后,可以通过网页控制台点击配置函数设置触发器。在代码中使用定时触发器的函数实例代码如下:
[CloudService]
public class NewService
{
[CloudFunc(CronJob = true)]
public async Task CronJob()
{
Debug.Log("CronJob");
}
}

9. 附录
示例代码
// 文件名: NewService.cs
using System.Threading.Tasks;
using Unity.UOS.Func.Stateless.Core.Attributes;
using UnityEngine;
namespace CloudService
{
[CloudService]
public class NewService
{
public NewService()
{
// 初始化
}
[CloudFunc]
public async Task<string> Echo(string msg)
{
Debug.Log($"call echo with {msg}");
return msg;
}
[CloudFunc]
public async Task<UserInfo> GetUserInfo(string userId)
{
Debug.Log($"get information about user {userId}");
return new UserInfo
{
Name = "Jack",
Age = 18
};
}
[CloudFunc]
public async Task<UserInfo> UpdateUserInfo(string userId, UserInfo u)
{
Debug.Log($"update information about user {u.Name} age {u.Age}");
return u;
}
}
public class UserInfo
{
public string Name;
public int Age;
}
}在线调试
以「示例代码」为例,当部署完成后,可以在云函数界面中查看到新创建出来的云函数。此时,可以点击右侧菜单,通过「调试函数」进入在线调试函数的窗口。
在调试的「PARAMS」选项中,通过 "cloudfunc" 来指定当前要在线调试的方法。我们以 "UpdateUserInfo" 方法为例:
然后,可以在「BODY」选项中,可以以 JSON 的方式来指定在调试该方法时需要传入的参数。在 "UpdateUserInfo" 的例子中,我们可以通过 "userId" 字段来传入一段字符串参数和 "u" 字段来传入一个结构体:
在调试 .net 环境下的云函数时,默认的请求方法为 "POST",在完成请求构建后,点击「发送」按钮,即可发起调试请求:
调试完成后,还可以通过「查看日志」进入日志页面来查看当前调试请求的详细日志信息

代码编写的注意事项
云函数类所在脚本文件的名称必须与类名相同。
所有的类必须放置于命名空间内,且所用到的代码文件必须放到同一目录中。
使用 [CloudService] 标记有远程调用函数的类,使用 [CloudFunc] 标记需要远程调用的函数。
请在云函数类构造函数中初始化云函数,不要创建并调用其他带有参数的构造函数。
切换到远程模式后云函数类只会保留带有 [CloudFunc] 的方法,其他字段将会被隐藏。
使用 [CloudFunc] 标记的函数必须符合 public async Task<返回数据类型> 函数名称(输出参数) { 函数体 } 这样的格式。
编写代码中只能使用 UnityEngine 命名空间下的 Debug.Log,Debug.LogWarning,Debug.LogError 函数,不能使用其他函数。
