228 lines
No EOL
6.4 KiB
C#
228 lines
No EOL
6.4 KiB
C#
#if UNITY_5_3_OR_NEWER
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using GumYum.NPC;
|
|
using GumYum.NPC.Unity;
|
|
|
|
namespace GumYum.NPC.Unity.Examples {
|
|
/// <summary>
|
|
/// Simple example showing NPC spawning and dialogue
|
|
/// Attach this to a GameObject in your scene
|
|
/// </summary>
|
|
public class QuickstartChat : MonoBehaviour {
|
|
[Header("API Configuration")]
|
|
[SerializeField]
|
|
private string apiKey = "your-api-key";
|
|
[SerializeField]
|
|
private string apiSecret = "your-api-secret";
|
|
[SerializeField]
|
|
private string universeId = "blade-runner";
|
|
[SerializeField]
|
|
private int worldSeed = 42;
|
|
|
|
[Header("UI References")]
|
|
[SerializeField]
|
|
private Text npcNameText;
|
|
[SerializeField]
|
|
private Text npcInfoText;
|
|
[SerializeField]
|
|
private InputField userInput;
|
|
[SerializeField]
|
|
private Button sendButton;
|
|
[SerializeField]
|
|
private Text chatOutput;
|
|
[SerializeField]
|
|
private ScrollRect scrollRect;
|
|
|
|
[Header("Chat Settings")]
|
|
[SerializeField]
|
|
private float temperature = 0.8f;
|
|
[SerializeField]
|
|
private int maxTokens = 2000;
|
|
[SerializeField]
|
|
private bool useStreamingChat = false;
|
|
|
|
private GumYumUnityClient client;
|
|
private NPC currentNPC;
|
|
private bool isProcessing = false;
|
|
|
|
void Start() {
|
|
// Initialize the client
|
|
client = GumYumUnityClient.Instance;
|
|
client.SetCredentials(apiKey, apiSecret);
|
|
|
|
// Setup UI
|
|
sendButton.onClick.AddListener(OnSendButtonClicked);
|
|
userInput.onEndEdit.AddListener(OnInputEndEdit);
|
|
|
|
// Subscribe to events
|
|
client.OnCharacterSpawned += OnCharacterSpawned;
|
|
client.OnDialogueReceived += OnDialogueReceived;
|
|
client.OnDialogueChunkReceived += OnDialogueChunkReceived;
|
|
client.OnRequestFailed += OnRequestFailed;
|
|
|
|
// Start by spawning an NPC
|
|
StartCoroutine(SpawnRandomNPC());
|
|
}
|
|
|
|
private IEnumerator SpawnRandomNPC() {
|
|
UpdateChatOutput("Spawning a random NPC...");
|
|
|
|
yield return client.SpawnNPCCoroutine(universeId, worldSeed, null, npc => {
|
|
if (npc != null) {
|
|
currentNPC = npc;
|
|
UpdateNPCInfo();
|
|
UpdateChatOutput($"\n{npc.Name} has entered the chat!\n");
|
|
UpdateChatOutput(
|
|
$"[{npc.GetMood()} mood, stress level: {npc.GetStressLevel()}]\n\n");
|
|
} else {
|
|
UpdateChatOutput(
|
|
"\nFailed to spawn NPC. Check your API credentials.\n");
|
|
}
|
|
});
|
|
}
|
|
|
|
private void OnCharacterSpawned(NPC npc) {
|
|
Debug.Log($"Character spawned: {npc.GetInfo()}");
|
|
}
|
|
|
|
private void OnDialogueReceived(string response,
|
|
Dictionary<string, object> context,
|
|
Dictionary<string, object> moodTransition) {
|
|
if (!useStreamingChat) {
|
|
UpdateChatOutput($"\n{currentNPC.Name}: {response}\n");
|
|
|
|
if (currentNPC.HasMoodChanged()) {
|
|
UpdateChatOutput(
|
|
$"[Mood changed from {currentNPC.GetPreviousMood()} to {currentNPC.GetMood()}]\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnDialogueChunkReceived(string chunk) {
|
|
if (useStreamingChat) {
|
|
UpdateChatOutput(chunk);
|
|
}
|
|
}
|
|
|
|
private void OnRequestFailed(string endpoint, string error) {
|
|
Debug.LogError($"Request failed - {endpoint}: {error}");
|
|
UpdateChatOutput($"\n[Error: {error}]\n");
|
|
isProcessing = false;
|
|
}
|
|
|
|
private void OnSendButtonClicked() {
|
|
if (!isProcessing && !string.IsNullOrEmpty(userInput.text) &&
|
|
currentNPC != null) {
|
|
SendMessage(userInput.text);
|
|
}
|
|
}
|
|
|
|
private void OnInputEndEdit(string value) {
|
|
if (Input.GetKeyDown(KeyCode.Return) ||
|
|
Input.GetKeyDown(KeyCode.KeypadEnter)) {
|
|
OnSendButtonClicked();
|
|
}
|
|
}
|
|
|
|
private void SendMessage(string message) {
|
|
isProcessing = true;
|
|
|
|
// Display user message
|
|
UpdateChatOutput($"\nYou: {message}\n");
|
|
|
|
// Clear input
|
|
userInput.text = "";
|
|
userInput.ActivateInputField();
|
|
|
|
// Send to NPC
|
|
if (useStreamingChat) {
|
|
StartCoroutine(StreamChatWithNPC(message));
|
|
} else {
|
|
StartCoroutine(ChatWithNPC(message));
|
|
}
|
|
}
|
|
|
|
private IEnumerator ChatWithNPC(string message) {
|
|
yield return client.SimpleChatCoroutine(currentNPC, message, response => {
|
|
isProcessing = false;
|
|
if (response == null) {
|
|
UpdateChatOutput("\n[Failed to get response]\n");
|
|
}
|
|
UpdateNPCInfo();
|
|
}, temperature, maxTokens);
|
|
}
|
|
|
|
private IEnumerator StreamChatWithNPC(string message) {
|
|
UpdateChatOutput($"\n{currentNPC.Name}: ");
|
|
|
|
var messages =
|
|
new List<ChatMessage> { new ChatMessage { Role = "user",
|
|
Content = message } };
|
|
|
|
bool streamComplete = false;
|
|
string fullResponse = "";
|
|
|
|
// Use streaming
|
|
var task = currentNPC.Chat.CompletionsAsync(messages, temperature,
|
|
maxTokens, true);
|
|
|
|
// Set up streaming event handlers
|
|
void OnChunk(object sender, string chunk) {
|
|
fullResponse += chunk;
|
|
UpdateChatOutput(chunk);
|
|
}
|
|
|
|
void OnStreamEnd(object sender, DialogueStreamEndedEventArgs e) {
|
|
streamComplete = true;
|
|
if (currentNPC.HasMoodChanged()) {
|
|
UpdateChatOutput(
|
|
$"\n[Mood changed from {currentNPC.GetPreviousMood()} to {currentNPC.GetMood()}]\n");
|
|
}
|
|
}
|
|
|
|
currentNPC.DialogueChunkReceived += OnChunk;
|
|
currentNPC.DialogueStreamEnded += OnStreamEnd;
|
|
|
|
// Wait for stream to complete
|
|
yield return new WaitUntil(() => streamComplete || task.IsCompleted);
|
|
|
|
// Clean up event handlers
|
|
currentNPC.DialogueChunkReceived -= OnChunk;
|
|
currentNPC.DialogueStreamEnded -= OnStreamEnd;
|
|
|
|
UpdateChatOutput("\n");
|
|
UpdateNPCInfo();
|
|
isProcessing = false;
|
|
}
|
|
|
|
private void UpdateNPCInfo() {
|
|
if (currentNPC != null) {
|
|
npcNameText.text = currentNPC.Name;
|
|
npcInfoText.text =
|
|
$"{currentNPC.Profession} | Type {currentNPC.PersonalityType} | {currentNPC.GetLocation()}\n" +
|
|
$"Mood: {currentNPC.GetMood()} | Stress: {currentNPC.GetStressLevel()}";
|
|
}
|
|
}
|
|
|
|
private void UpdateChatOutput(string text) {
|
|
chatOutput.text += text;
|
|
|
|
// Auto-scroll to bottom
|
|
Canvas.ForceUpdateCanvases();
|
|
scrollRect.verticalNormalizedPosition = 0f;
|
|
}
|
|
|
|
private void OnDestroy() {
|
|
if (client != null) {
|
|
client.OnCharacterSpawned -= OnCharacterSpawned;
|
|
client.OnDialogueReceived -= OnDialogueReceived;
|
|
client.OnDialogueChunkReceived -= OnDialogueChunkReceived;
|
|
client.OnRequestFailed -= OnRequestFailed;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif |