Lingara Lingara Documentation Guides API Bibliothèques Applications Créer Intégration Application web
Langue: Français

Dialogue de PNJ

Cette page est traduite de l'anglais. En cas de différence, la page anglaise fait foi. Lire la page en anglais

Un personnage de votre jeu peut tenir une conversation avec le joueur dans la langue qu’il apprend, adaptée à son niveau. Chaque réplique du joueur constitue un tour : le kit joueur l’envoie, et la réponse du personnage revient en flux, morceau par morceau.

Ce que contient un tour

Chaque tour envoie tout ce dont le personnage a besoin, car Lingara ne conserve aucune conversation entre les tours :

  • npc : qui parle. Son name compte de 1 à 32 caractères, et son persona facultatif est une ligne de description de 1 à 120 caractères, par exemple « un vendeur de raviolis ».
  • scene : facultatif, le lieu de la conversation, de 1 à 160 caractères.
  • source_lang et target_lang : la langue du joueur et celle qu’il apprend. Elles doivent être différentes, et le personnage ne parle que target_lang.
  • level : le niveau du joueur, de 1 à 9. Le guide de Lingara « Ce que signifie un niveau » décrit l’échelle.
  • line : ce que le joueur vient de dire, de 1 à 500 caractères.
  • history : la conversation jusqu’ici, de la plus ancienne à la plus récente, au plus 12 lignes. Chaque ligne a un speaker, soit player soit npc, et son text, de 1 à 500 caractères.

Chaque texte tient sur une seule ligne. C’est votre jeu qui conserve la conversation : le kit joueur garde la fenêtre de chaque dialogue que vous ouvrez, y ajoute chaque tour terminé, tronque chaque réponse à ses 500 premiers caractères et abandonne les lignes les plus anciennes au-delà de 12.

Lingara traite le persona et la scène comme du texte non fiable provenant de votre jeu. Ils façonnent la manière dont le personnage parle, jamais ce qu’il est autorisé à faire.

Comment arrive une réponse

La réponse arrive en flux sous forme de morceaux de texte, que votre jeu ajoute à mesure qu’ils arrivent. Un avis peut s’intercaler entre eux, par exemple pour indiquer que la réponse a été écourtée. Le tour se termine par done lorsque la réponse est complète, ou par error lorsqu’elle a échoué. Un tour ne peut pas être repris : si la connexion tombe, le joueur répète sa réplique.

Dites une seule réplique à la fois à chaque personnage. Une réplique dite pendant que le personnage répond encore est refusée plutôt que mise en file d’attente, car chaque tour est facturé.

Qui paie

Seuls les joueurs d’un client au compteur peuvent tenir une conversation, et chaque tour est facturé à ce client. Le jeton d’un joueur doit inclure embed:play.

Langage de l'exemple de code

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());
  }
}

Voir aussi