Lingara Lingara 文档 学习指南 API 库 应用 构建 嵌入 网页版
语言: 中文简体

Unreal

本页译自英文。如两者有出入,以英文页面为准。 阅读英文页面

适用于 Unreal Engine 5 的 Lingara 插件是一个游戏实例子系统,构建在玩家核心的 C 接口之上。它运行在玩家工具包支持的五个平台上:Windows、macOS、Linux、Android 和 iOS。目前尚不支持游戏主机。

安装插件

每次发布都会为每个受支持的引擎版本附带一个插件 zip 包,命名为 LingaraEmbed-<semver>-UE5.<m>.zip。把与你的引擎对应的那个解压到项目的 Plugins/ 文件夹中,然后在编辑器中启用该插件。无需安装其他任何东西。

从你的服务器获取令牌

插件从不持有密钥。当它需要令牌时,会广播 OnTokenNeeded。响应方式是向你自己服务器的令牌路由发送 POST 请求,服务器用服务器工具包签发令牌并返回令牌及其过期时间,然后把两者交给 ProvideToken。如果你的服务器无法响应,请调用 TokenUnavailable,让等待令牌的调用立即失败,而不是等到超时。

绑定你使用的事件

核心在自己的线程上执行网络 I/O。子系统在自己的 tick 中清空队列,每帧最多 MaxEventsPerTick 个事件(一项项目设置),并在委托上逐一广播。在游戏开始时绑定你的游戏需要的委托,然后启动子系统:

  • OnTokenNeeded:需要令牌时;
  • OnNpcTurn:流式回复的每一个片段(见 NPC 对话);
  • OnPractice:每一次练习更新(见练习结果);
  • OnError:调用失败时。

Unreal C++

// Your own server mints the token: never put a client secret in a game.
void ALingaraEmbedSnippetActor::HandleTokenNeeded(const FLingaraTokenNeeded& Event)
{
	const TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
	Request->SetVerb(TEXT("POST"));
	Request->SetURL(TokenUrl);
	Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
	Request->SetContentAsString(TEXT("{}"));
	Request->OnProcessRequestComplete().BindUObject(this, &ALingaraEmbedSnippetActor::HandleTokenResponse);
	Request->ProcessRequest();
}

void ALingaraEmbedSnippetActor::HandleTokenResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnected)
{
	ELingaraCallResult Result;
	FLingaraEmbedError Error;
	TSharedPtr<FJsonObject> Body;
	FString Token;
	FString ExpiresAt;
	const bool bOk = bConnected && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())
		&& FJsonSerializer::Deserialize(TJsonReaderFactory<TCHAR>::Create(Response->GetContentAsString()), Body)
		&& Body->TryGetStringField(TEXT("token"), Token) && Body->TryGetStringField(TEXT("expires_at"), ExpiresAt);
	if (bOk)
	{
		Lingara()->ProvideToken(Token, ExpiresAt, Result, Error);
	}
	else
	{
		// Fail the waiting calls now rather than at the core's timeout.
		Lingara()->TokenUnavailable(TEXT("the token endpoint failed"), Result, Error);
	}
}

Unreal C++

// The subsystem drains the core's queue from its own tick (at most
// MaxEventsPerTick a frame, from Project Settings): bind the events you use.
void ALingaraEmbedSnippetActor::BeginPlay()
{
	Super::BeginPlay();
	ULingaraEmbedSubsystem* Subsystem = Lingara();
	Subsystem->OnTokenNeeded.AddDynamic(this, &ALingaraEmbedSnippetActor::HandleTokenNeeded);
	Subsystem->OnNpcTurn.AddDynamic(this, &ALingaraEmbedSnippetActor::HandleNpcTurn);
	Subsystem->OnPractice.AddDynamic(this, &ALingaraEmbedSnippetActor::HandlePractice);
	Subsystem->OnError.AddDynamic(this, &ALingaraEmbedSnippetActor::HandleError);

	FLingaraEmbedConfig Config;
	Config.MaxEventsPerTick = 256;
	ELingaraCallResult Result;
	FLingaraEmbedError Error;
	Subsystem->Start(Config, Result, Error);
}

void ALingaraEmbedSnippetActor::HandleError(const FLingaraEmbedError& Error)
{
	UE_LOG(LogTemp, Warning, TEXT("Lingara %s: %s"), *Error.KindName, *Error.Message);
}