Lingara Lingara Documentación Guías API Bibliotecas Apps Crear Integración Aplicación web
Idioma: Español

Diálogo con PNJ

Esta página está traducida del inglés. Si ambas difieren, la página en inglés es la correcta. Leer la página en inglés

Un personaje de tu juego puede mantener una conversación con el jugador en el idioma que está aprendiendo, ajustada a su nivel. Cada frase que dice el jugador es un turno: el kit de jugador la envía y la respuesta del personaje vuelve en streaming, por fragmentos.

Qué lleva un turno

Cada turno envía todo lo que el personaje necesita, porque Lingara no guarda ninguna conversación entre turnos:

  • npc: quién habla. Su name tiene de 1 a 32 caracteres, y su persona opcional es una línea de descripción de 1 a 120 caracteres, como «un vendedor de empanadillas».
  • scene: opcional, dónde ocurre la conversación, de 1 a 160 caracteres.
  • source_lang y target_lang: el idioma propio del jugador y el que está aprendiendo. Deben ser distintos, y el personaje solo habla target_lang.
  • level: el nivel del jugador, de 1 a 9. La guía de Lingara «Qué significa un nivel» describe la escala.
  • line: lo que el jugador acaba de decir, de 1 a 500 caracteres.
  • history: la conversación hasta ahora, de la más antigua a la más reciente, como máximo 12 líneas. Cada línea tiene un speaker, player o npc, y su text, de 1 a 500 caracteres.

Cada texto es una sola línea. Tu juego mantiene la conversación: el kit de jugador guarda la ventana de cada diálogo que abres, le añade cada turno terminado, recorta cada respuesta a sus primeros 500 caracteres y descarta las líneas más antiguas a partir de 12.

Lingara trata la persona y la escena como texto no fiable procedente de tu juego. Determinan cómo habla el personaje, nunca lo que tiene permitido hacer.

Cómo llega una respuesta

La respuesta llega en streaming como fragmentos de texto, que tu juego va añadiendo a medida que llegan. Entre ellos puede llegar un aviso, por ejemplo uno que indique que la respuesta se ha cortado. El turno termina con done cuando la respuesta está completa, o con error cuando ha fallado. Un turno no se puede reanudar: si se cae la conexión, el jugador vuelve a decir la frase.

Di una frase cada vez a cada personaje. Una frase dicha mientras el personaje aún está respondiendo se rechaza en lugar de ponerse en cola, porque cada turno se factura.

Quién paga

Solo los jugadores de un cliente con facturación por consumo pueden mantener una conversación, y cada turno se factura a ese cliente. El token de un jugador necesita embed:play.

Lenguaje del ejemplo de código

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

Véase también