Initial commit of Unity SDK for GumYum NPC client
This commit is contained in:
commit
db46a7bf9d
29 changed files with 3073 additions and 0 deletions
215
Samples~/BasicChat/NPCFilteredSpawning.cs
Normal file
215
Samples~/BasicChat/NPCFilteredSpawning.cs
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
#if UNITY_5_3_OR_NEWER
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using GumYum.NPC;
|
||||
using GumYum.NPC.Unity;
|
||||
|
||||
namespace GumYum.NPC.Unity.Examples {
|
||||
/// <summary>
|
||||
/// Example showing filtered NPC spawning and multiple NPCs
|
||||
/// </summary>
|
||||
public class NPCFilteredSpawning : MonoBehaviour {
|
||||
[Header("API Configuration")]
|
||||
[SerializeField]
|
||||
private string apiKey = "your-api-key";
|
||||
[SerializeField]
|
||||
private string apiSecret = "your-api-secret";
|
||||
[SerializeField]
|
||||
private string universeId = "kingdom";
|
||||
[SerializeField]
|
||||
private int worldSeed = 12345;
|
||||
|
||||
[Header("Spawn Settings")]
|
||||
[SerializeField]
|
||||
private string[] professionFilter = { "warrior", "knight", "guard" };
|
||||
[SerializeField]
|
||||
private string[] locationFilter = { "barracks", "castle",
|
||||
"training_grounds" };
|
||||
[SerializeField]
|
||||
private int[] personalityTypeFilter = { 1, 8,
|
||||
6 }; // Reformer, Challenger, Loyalist
|
||||
[SerializeField]
|
||||
private string sexFilter = ""; // "male", "female", or "" for any
|
||||
|
||||
private GumYumUnityClient client;
|
||||
private List<NPC> spawnedNPCs = new List<NPC>();
|
||||
|
||||
void Start() {
|
||||
// Initialize the client
|
||||
client = GumYumUnityClient.Instance;
|
||||
client.SetCredentials(apiKey, apiSecret);
|
||||
|
||||
// Example 1: Spawn filtered NPCs
|
||||
StartCoroutine(SpawnFilteredNPCs());
|
||||
}
|
||||
|
||||
private IEnumerator SpawnFilteredNPCs() {
|
||||
Debug.Log("Spawning filtered NPCs...");
|
||||
|
||||
// Build filter object
|
||||
var filters = new Dictionary<string, object>();
|
||||
|
||||
if (professionFilter.Length > 0)
|
||||
filters["profession"] = professionFilter;
|
||||
|
||||
if (locationFilter.Length > 0)
|
||||
filters["location"] = locationFilter;
|
||||
|
||||
if (personalityTypeFilter.Length > 0)
|
||||
filters["personality_type"] = personalityTypeFilter;
|
||||
|
||||
if (!string.IsNullOrEmpty(sexFilter))
|
||||
filters["sex"] = new[] { sexFilter };
|
||||
|
||||
// Spawn 3 NPCs with filters
|
||||
for (int i = 0; i < 3; i++) {
|
||||
yield return client.SpawnFilteredNPCCoroutine(
|
||||
universeId, worldSeed + i, filters, npc => {
|
||||
if (npc != null) {
|
||||
spawnedNPCs.Add(npc);
|
||||
Debug.Log($"Spawned: {npc.GetInfo()}");
|
||||
|
||||
// Demonstrate mood and stress info
|
||||
Debug.Log(
|
||||
$" - Current mood: {npc.GetMood()}, Stress level: {npc.GetStressLevel()}");
|
||||
|
||||
// Example of saving NPC for later
|
||||
PlayerPrefs.SetString($"SavedNPC_{npc.NpcId}", npc.ToJson());
|
||||
} else {
|
||||
Debug.LogError("Failed to spawn filtered NPC");
|
||||
}
|
||||
});
|
||||
|
||||
yield return new WaitForSeconds(0.5f); // Small delay between spawns
|
||||
}
|
||||
|
||||
// Example 2: Chat with all spawned NPCs
|
||||
yield return new WaitForSeconds(1f);
|
||||
yield return ChatWithAllNPCs(
|
||||
"Hello everyone! How are you all doing today?");
|
||||
|
||||
// Example 3: Demonstrate mood transitions
|
||||
yield return new WaitForSeconds(2f);
|
||||
yield return DemonstrateMoodTransitions();
|
||||
}
|
||||
|
||||
private IEnumerator ChatWithAllNPCs(string message) {
|
||||
Debug.Log($"\nBroadcasting message to all NPCs: \"{message}\"");
|
||||
|
||||
foreach (var npc in spawnedNPCs) {
|
||||
var messages =
|
||||
new List<ChatMessage> { new ChatMessage { Role = "user",
|
||||
Content = message } };
|
||||
|
||||
yield return client.ChatCoroutine(npc, messages, response => {
|
||||
if (response?.Choices?.Count > 0) {
|
||||
var content = response.Choices[0].Message.Content;
|
||||
Debug.Log($"{npc.Name}: {content}");
|
||||
|
||||
// Check for mood changes
|
||||
if (npc.HasMoodChanged()) {
|
||||
Debug.Log(
|
||||
$" [Mood transition: {npc.GetPreviousMood()} → {npc.GetMood()}]");
|
||||
Debug.Log($" [Reason: {npc.GetLastMoodChangeReason()}]");
|
||||
Debug.Log(
|
||||
$" [Confidence: {npc.GetLastMoodChangeConfidence():F2}]");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator DemonstrateMoodTransitions() {
|
||||
Debug.Log("\nDemonstrating mood transitions with different messages...");
|
||||
|
||||
// Messages designed to potentially trigger different moods
|
||||
var moodTriggeringMessages = new[] {
|
||||
"I have terrible news. The kingdom is under attack!",
|
||||
"Great news! We've won the battle and everyone is safe!",
|
||||
"I'm not sure what to do. Everything seems so uncertain.",
|
||||
"You've done an amazing job! The king wants to reward you personally!"
|
||||
};
|
||||
|
||||
foreach (var triggerMessage in moodTriggeringMessages) {
|
||||
Debug.Log($"\nTesting message: \"{triggerMessage}\"");
|
||||
|
||||
// Send to first NPC only for this demo
|
||||
if (spawnedNPCs.Count > 0) {
|
||||
var npc = spawnedNPCs[0];
|
||||
var previousMood = npc.GetMood();
|
||||
var previousStress = npc.GetStressLevel();
|
||||
|
||||
var messages = new List<ChatMessage> {
|
||||
new ChatMessage { Role = "user", Content = triggerMessage }
|
||||
};
|
||||
|
||||
yield return client.ChatCoroutine(npc, messages, response => {
|
||||
if (response?.Choices?.Count > 0) {
|
||||
var content = response.Choices[0].Message.Content;
|
||||
Debug.Log($"{npc.Name}: {content}");
|
||||
|
||||
// Analyze mood change
|
||||
if (npc.GetMood() != previousMood) {
|
||||
Debug.Log($" ✓ Mood changed: {previousMood} → {npc.GetMood()}");
|
||||
} else {
|
||||
Debug.Log($" - Mood unchanged: {npc.GetMood()}");
|
||||
}
|
||||
|
||||
if (npc.GetStressLevel() != previousStress) {
|
||||
Debug.Log(
|
||||
$" ✓ Stress changed: {previousStress} → {npc.GetStressLevel()}");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
yield return new WaitForSeconds(2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Example: Load a previously saved NPC
|
||||
public IEnumerator LoadSavedNPC(int npcId) {
|
||||
string savedJson = PlayerPrefs.GetString($"SavedNPC_{npcId}", "");
|
||||
if (!string.IsNullOrEmpty(savedJson)) {
|
||||
var npc = NPC.FromJson(savedJson, client.Client);
|
||||
Debug.Log($"Loaded saved NPC: {npc.GetInfo()}");
|
||||
|
||||
// You can continue chatting with the loaded NPC
|
||||
yield return ChatWithNPC(npc, "Do you remember our last conversation?");
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ChatWithNPC(NPC npc, string message) {
|
||||
var messages =
|
||||
new List<ChatMessage> { new ChatMessage { Role = "user",
|
||||
Content = message } };
|
||||
|
||||
yield return client.ChatCoroutine(npc, messages, response => {
|
||||
if (response?.Choices?.Count > 0) {
|
||||
Debug.Log($"{npc.Name}: {response.Choices[0].Message.Content}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Example: Get all NPCs of a specific mood
|
||||
public List<NPC> GetNPCsByMood(string mood) {
|
||||
return spawnedNPCs.FindAll(npc => npc.GetMood() == mood);
|
||||
}
|
||||
|
||||
// Example: Get stressed NPCs (stress > 7)
|
||||
public List<NPC> GetStressedNPCs() {
|
||||
return spawnedNPCs.FindAll(npc => npc.GetStressLevel() > 7);
|
||||
}
|
||||
|
||||
private void OnDestroy() {
|
||||
// Clean up saved NPCs if needed
|
||||
foreach (var npc in spawnedNPCs) {
|
||||
PlayerPrefs.DeleteKey($"SavedNPC_{npc.NpcId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
228
Samples~/BasicChat/QuickstartChat.cs
Normal file
228
Samples~/BasicChat/QuickstartChat.cs
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#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
|
||||
Loading…
Add table
Add a link
Reference in a new issue