Lingara Lingara 說明文件 學習指南 API 程式庫 App 建立 嵌入 網頁版
語言: 粵語

NPC 對話

呢頁係由英文翻譯過嚟嘅。如果兩個版本有出入,以英文頁為準。 睇英文頁

你遊戲入面嘅角色可以用玩家學緊嘅語言同佢對話,難度啱返玩家嘅等級。玩家講嘅每一句就係一個回合:玩家套件將呢句傳出去,角色嘅回覆就一段一段咁串流返嚟。

一個回合帶住啲乜嘢

每個回合都會傳送角色需要嘅全部嘢,因為 Lingara 喺回合之間唔會保留任何對話:

  • npc:邊個講緊嘢。佢嘅 name 係 1 到 32 個字元,可選嘅 persona 係一行描述,1 到 120 個字元,例如「一個賣餃子嘅小販」。
  • scene:可選,對話發生嘅地方,1 到 160 個字元。
  • source_lang 同 target_lang:玩家自己嘅語言,同埋佢哋學緊嘅語言。兩樣一定要唔同,角色只會講 target_lang。
  • level:玩家嘅等級,由 1 到 9。Lingara 嘅指南《等級代表乜嘢》講解咗呢套等級。
  • line:玩家啱啱講嘅嘢,1 到 500 個字元。
  • history:到而家為止嘅對話,最舊嘅排先,最多 12 行。每一行都有一個 speaker(player 或者 npc)同佢嘅 text,1 到 500 個字元。

每段文字都係單獨一行。對話由你個遊戲保存:玩家套件會為你開嘅每段對話保存一個視窗,將每個完成咗嘅回合加入去,將每個回覆截到頭 500 個字元,同埋丟走超過 12 行嘅最舊內容。

Lingara 會將人設同場景當做嚟自你個遊戲嘅不可信文字。佢哋會影響角色點樣講嘢,但永遠唔會影響角色可以做啲乜。

回覆點樣送到

回覆會以文字片段嘅形式串流,你個遊戲喺片段送到嗰陣逐段加上去。片段之間可能會有通知,例如話回覆被截短咗嘅通知。回覆完整嗰陣,回合以 done 結束;失敗嗰陣就以 error 結束。回合係唔可以續傳嘅:如果連線斷咗,玩家要再講多次嗰句。

對每個角色一次淨係講一句。角色仲喺度回覆嗰陣講嘅嘢會被拒絕,而唔係排隊,因為每個回合都要收費。

邊個畀錢

淨係按用量計費用戶端嘅玩家先可以進行對話,每個回合都會向嗰個用戶端收費。玩家嘅權杖需要 embed:play。

程式碼例子嘅語言

Bevy

fn spawn_seller(mut commands: Commands) {
    let mei = NpcDialogue::new("Mei", "zh", 2)
        .and_then(|npc| npc.with_persona("a dumpling seller"))
        .and_then(|npc| npc.with_scene("the night market"))
        .expect("within Lingara's bounds");
    commands.spawn(mei);
    commands.spawn(Player);
}

/// One turn at a time: a line said while the NPC is replying is a `Busy` error.
fn talk(keys: Res<ButtonInput<KeyCode>>, npcs: Query<Entity, With<NpcDialogue>>, mut say: MessageWriter<NpcSay>) {
    if keys.just_pressed(KeyCode::Digit1) {
        for entity in &npcs {
            say.write(NpcSay { entity, line: "饺子多少钱?".into() });
        }
    }
}

/// `streaming` grows as the reply arrives; `turns` holds the finished ones.
fn show_reply(transcripts: Query<&NpcTranscript, Changed<NpcTranscript>>) {
    for transcript in &transcripts {
        if let Some(partial) = &transcript.streaming {
            info!("…{partial}");
        }
    }
}

Unity

public DialogueId OpenDialogue(string npc, string sourceLang, string targetLang, byte level) =>
    Client.DialogueOpen(new DialogueConfig { Npc = new Npc { Name = npc }, SourceLang = sourceLang, TargetLang = targetLang, Level = level });

// The reply streams back as NpcTurn events: append each Fragment's text to your label.
public RequestId Say(DialogueId dialogue, string line) => Client.DialogueSay(dialogue, line);

Godot C#

public long OpenDialogue(string npc, string sourceLang, string targetLang, byte level) =>
    Client.DialogueOpen(new DialogueConfig { Npc = new Npc { Name = npc }, SourceLang = sourceLang, TargetLang = targetLang, Level = level }).Value;

// The reply streams back as NpcTurnFragment signals, then one NpcTurnFinished.
public long Say(long dialogue, string line) => Client.DialogueSay(new DialogueId(dialogue), line).Value;

GDScript

# A LingaraNpc child per character: npc_name, target_lang and level in the
# inspector. say() returns false while a turn is in flight (turns are never
# queued, because each one is billed).
func talk_to_mei(line: String) -> void:
	if mei.say(line):
		bubble.text = "…"


func _on_mei_replied(text: String) -> void:
	bubble.text = text

Unreal C++

void ALingaraEmbedSnippetActor::Say(const FString& Line)
{
	ELingaraCallResult Result;
	FLingaraEmbedError Error;
	if (Dialogue == 0)
	{
		FLingaraDialogueConfig Config;
		Config.Npc.Name = TEXT("Mei");
		Config.Npc.Persona = TEXT("a tea seller at the night market");
		Config.SourceLang = TEXT("en");
		Config.TargetLang = TEXT("zh");
		Config.Level = 2;
		Dialogue = Lingara()->DialogueOpen(Config, Result, Error);
	}
	Reply.Reset();
	Lingara()->DialogueSay(Dialogue, Line, Result, Error);
}

// The reply streams back as npc_turn events: append each fragment.
void ALingaraEmbedSnippetActor::HandleNpcTurn(const FLingaraNpcTurn& Turn)
{
	if (Turn.Dialogue == Dialogue && Turn.Update.Type == ELingaraNpcTurnUpdateType::Fragment)
	{
		Reply += Turn.Update.Text;
	}
}

Kotlin

fun talkToMei(line: String) {
    val dialogue = npc ?: lingara.dialogueOpen(DialogueConfig(Npc("Mei", "a tea seller"), "the tea house", "en", "zh", 2, emptyList()))
    npc = dialogue
    lingara.dialogueSay(dialogue, line)
}

private fun onNpcTurn(update: NpcTurnUpdate) {
    when (update) {
        is NpcTurnUpdate.Opened -> bubble = ""
        is NpcTurnUpdate.Fragment -> bubble += update.text
        is NpcTurnUpdate.Finished -> bubble = update.text
        else -> Unit
    }
}

Java

void talkToMei(String line) {
  if (npc < 0) {
    Npc mei = new Npc("Mei", "a tea seller");
    npc =
        lingara.dialogueOpen(new DialogueConfig(mei, "the tea house", "en", "zh", 2, List.of()));
  }
  lingara.dialogueSay(npc, line);
}

void onNpcTurn(EmbedEvent.NpcTurn turn) {
  if (turn.getUpdate() instanceof NpcTurnUpdate.Fragment fragment) {
    bubble.append(fragment.getText());
  } else if (turn.getUpdate() instanceof NpcTurnUpdate.Finished finished) {
    bubble.setLength(0);
    bubble.append(finished.getText());
  }
}

另見