Godot 이 페이지는 영어에서 번역되었습니다. 두 내용이 다를 경우 영어 페이지가 정확합니다. 영어 페이지 읽기
Godot는 두 가지 언어를 제공하며, Lingara에는 각각을 위한 키트가 있습니다. GDScript 게임은 Lingara 애드온을 사용합니다. Godot C# 게임은 Unity 게임이 쓰는 것과 같은 패키지인 Lingara.Embed를 사용합니다. 한 프로젝트에서는 둘 중 하나만 쓰고, 둘 다 쓰지 마세요. 아래 탭에서 각각을 보여 줍니다.
설치
GDScript. Godot Asset Library에서 Lingara 애드온을 설치한 다음, Project Settings의 Plugins에서 활성화합니다. 활성화하면 Lingara라는 이름의 오토로드가 추가되며, 모든 씬에서 /root/Lingara로 접근할 수 있습니다.
C#. 프로젝트에 Lingara.Embed를 패키지 참조로 추가합니다. 아래 샘플 노드인 LingaraEmbedNode는 패키지에 포함되어 있지 않습니다. 그 소스를 프로젝트에 추가하고 씬에 배치하세요.
서버에서 토큰 받기
어느 키트도 시크릿을 보관하지 않습니다. 코어가 토큰이 필요하면 여러분 자체의 서버에 요청하고, 서버는 서버 키트 로 토큰을 발급해 토큰과 그 만료 시각으로 응답합니다.
애드온에서는 Project Settings에 서버의 토큰 라우트를 설정하면 Lingara가 자체 스레드에서 그 라우트를 호출합니다. 라우트에 플레이어의 세션이 필요하다면, 프로젝트 설정은 내보내기에 포함되어 배포되므로 대신 런타임에 설정하세요. 또는 token_needed에 연결해 각 요청에 직접 응답합니다. C#에서는 클라이언트가 토큰을 요청할 때마다 샘플 노드가 토큰 라우트로 POST합니다. 어느 쪽이든 실패는 token_unavailable 또는 TokenUnavailable로 보고하세요. 그러면 대기 중인 호출이 즉시 실패합니다.
매 프레임 이벤트 처리하기
코어는 네트워크 I/O를 자체 스레드에서 실행합니다. 애드온의 오토로드는 프레임마다 한 번 이를 비우고 이벤트마다 시그널을 하나씩 내보내므로, 게임에 필요한 시그널만 연결하면 되고 직접 무언가를 비울 필요가 없습니다. C# 노드는 _process에서 프레임당 최대 256개의 이벤트를 비우고, 각각을 Godot 시그널로 다시 내보냅니다.
같은 시그널로 스트리밍되는 NPC 응답(NPC 대화 참조)과 연습 업데이트(연습 결과 참조)도 전달됩니다.
< PackageReference Include = "Lingara.Embed" Version = "0.1.0-alpha.6" /> 코드 예제 언어 Godot C# GDScript Godot C#
// Your own server mints the token: never put a client secret in a game. The
// continuation may run off the main thread, which the client allows.
private async Task FetchToken ()
{
try
{
using var response = await Http. PostAsync (TokenUrl, new StringContent ( "{}" ));
response. EnsureSuccessStatusCode ();
using var body = JsonDocument. Parse ( await response.Content. ReadAsStringAsync ());
Client ? . ProvideToken (body.RootElement. GetProperty ( "token" ). GetString (), body.RootElement. GetProperty ( "expires_at" ). GetString ());
}
catch ( Exception e ) when (e is HttpRequestException || e is JsonException || e is KeyNotFoundException )
{
Client ? . TokenUnavailable (e.Message); // fail the waiting calls now
}
} GDScript
# The common case: set Project Settings > lingara/token_url to your server's
# token route, which answers exactly {"token", "expires_at"}. Lingara POSTs to
# it on its own thread whenever it needs a token. If the route needs the
# player's session, set it at run time instead (never in ProjectSettings,
# which ships inside the export):
func use_my_session (session_cookie: String ) -> void :
lingara. set_token_endpoint ( "https://game.example.com/lingara-token" , { "cookie" : session_cookie})
# Or answer each token_needed yourself, with whatever login your game has.
func _on_token_needed (_request: int , _reason: String ) -> void :
var http := HTTPRequest . new ()
add_child (http)
http. request ( "https://game.example.com/lingara-token" , [], HTTPClient . METHOD_POST , " {} " )
var reply: Array = await http.request_completed
http. queue_free ()
var body: Variant = JSON . parse_string ((reply[ 3 ] as PackedByteArray ). get_string_from_utf8 ())
if reply[ 1 ] == 200 and body is Dictionary :
lingara. provide_token (body[ "token" ], body[ "expires_at" ])
else :
lingara. token_unavailable ( "the token route answered %d " % reply[ 1 ]) 코드 예제 언어 Godot C# GDScript Godot C#
public override void _Process ( double delta )
{
if (Client == null ) return ;
events. Clear ();
Client. Drain (events); // at most 256 a frame; the rest wait for the next
foreach ( var e in events)
{
switch (e)
{
case EmbedEvent . TokenNeeded _ : _ = FetchToken (); break ;
case EmbedEvent . NpcTurn { Update: NpcTurnUpdate . Fragment f } t :
EmitSignal (SignalName.NpcTurnFragment, t.Dialogue.Value, t.Request.Value, f.Text); break ;
case EmbedEvent . NpcTurn { Update: NpcTurnUpdate . Finished f } t :
EmitSignal (SignalName.NpcTurnFinished, t.Dialogue.Value, t.Request.Value, f.Text); break ;
case EmbedEvent . Practice p : EmitSignal (SignalName.PracticeChanged, p.Session.Value, p.Snapshot.Phase. ToString ()); break ;
case EmbedEvent . Failed f : EmitSignal (SignalName.Failed, f.Error.KindName, f.Error.Message, f.Error.Request ? .Value ?? 0 ); break ;
case EmbedEvent . Log log : GD. Print ( $"[lingara] { log . Level } { log . Target } : { log . Message } " ); break ;
}
}
} GDScript
# The Lingara autoload the plugin adds, reached by path and typed, so every
# call and signal below is checked against LingaraEmbed.
@onready var lingara: LingaraEmbed = get_node ( "/root/Lingara" )
# Enable the plugin (Project > Project Settings > Plugins): it adds the
# Lingara autoload, which drains the core once per frame and emits one signal
# per event. Connect the signals your game needs; you never call drain.
func _ready () -> void :
lingara.token_needed. connect (_on_token_needed)
lingara.event_received. connect (_on_event)
lingara.error_raised. connect ( func (error: Dictionary ) -> void : push_warning (error.message))
lingara.practice_changed. connect (_on_practice_changed)
lingara.practice_finished. connect (_on_practice_finished)
mei.replied. connect (_on_mei_replied)
func _on_event (_id: String , event_type: String , _created_at: String , data: Dictionary ) -> void :
if event_type == "lesson_plan.ready" :
print ( "a plan is ready: " , data. get ( "plan_id" ))