Initialer Upload neues Unity-Projekt
This commit is contained in:
248
Assets/Convai/Scripts/Runtime/UI/Chat/ChatBoxUI.cs
Normal file
248
Assets/Convai/Scripts/Runtime/UI/Chat/ChatBoxUI.cs
Normal file
@ -0,0 +1,248 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the chat UI for displaying messages from characters and the player.
|
||||
/// </summary>
|
||||
public class ChatBoxUI : ChatUIBase
|
||||
{
|
||||
private const int MAX_MESSAGES = 25;
|
||||
|
||||
private readonly List<Message> _messageList = new();
|
||||
private GameObject _chatPanel, _textObject;
|
||||
private ScrollRect _chatScrollRect;
|
||||
private Speaker _currentSpeaker;
|
||||
private bool _isFirstMessage = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the chat UI with the specified prefab.
|
||||
/// </summary>
|
||||
/// <param name="uiPrefab">The UI prefab to instantiate.</param>
|
||||
public override void Initialize(GameObject uiPrefab)
|
||||
{
|
||||
UIInstance = Instantiate(uiPrefab);
|
||||
_chatPanel = UIInstance.transform.GetChild(0).GetChild(0).GetChild(0).GetChild(0).gameObject;
|
||||
_textObject = _chatPanel.transform.GetChild(0).gameObject;
|
||||
_chatScrollRect = UIInstance.transform.GetChild(0).GetChild(0).GetComponent<ScrollRect>();
|
||||
UIInstance.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message as a character.
|
||||
/// </summary>
|
||||
/// <param name="charName">The name of the character.</param>
|
||||
/// <param name="text">The message text.</param>
|
||||
/// <param name="characterTextColor">The color of the character's text.</param>
|
||||
public override void SendCharacterText(string charName, string text, Color characterTextColor)
|
||||
{
|
||||
BroadcastCharacterDialogue(charName, text, characterTextColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message as the player.
|
||||
/// </summary>
|
||||
/// <param name="playerName">The name of the player.</param>
|
||||
/// <param name="text">The message text.</param>
|
||||
/// <param name="playerTextColor">The color of the player's text.</param>
|
||||
public override void SendPlayerText(string playerName, string text, Color playerTextColor)
|
||||
{
|
||||
BroadcastPlayerDialogue(playerName, text, playerTextColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all messages from the UI.
|
||||
/// </summary>
|
||||
public void ClearUI()
|
||||
{
|
||||
foreach (Message message in _messageList) Destroy(message.TextObject.gameObject);
|
||||
_messageList.Clear();
|
||||
}
|
||||
|
||||
// Helper methods and private functions are below. These are not part of the public API
|
||||
// and are used internally by the ChatBoxUI class to manage chat messages.
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a dialogue message from a character.
|
||||
/// </summary>
|
||||
/// <param name="characterName">Name of the character.</param>
|
||||
/// <param name="text">Text of the dialogue message.</param>
|
||||
/// <param name="characterTextColor">Color of the character's text.</param>
|
||||
private void BroadcastCharacterDialogue(string characterName, string text, Color characterTextColor)
|
||||
{
|
||||
string trimmedText = text.Trim();
|
||||
|
||||
if (_currentSpeaker != Speaker.Character || _isFirstMessage)
|
||||
{
|
||||
_isFirstMessage = false;
|
||||
_currentSpeaker = Speaker.Character;
|
||||
HandleNewCharacterMessage(characterName, trimmedText, characterTextColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendToExistingMessage(trimmedText);
|
||||
}
|
||||
|
||||
_currentSpeaker = Speaker.Character;
|
||||
ScrollToBottom();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a new dialogue message from a character.
|
||||
/// </summary>
|
||||
/// <param name="characterName">Name of the character.</param>
|
||||
/// <param name="text">Text of the dialogue message.</param>
|
||||
/// <param name="characterTextColor">Color of the character's text.</param>
|
||||
private void HandleNewCharacterMessage(string characterName, string text, Color characterTextColor)
|
||||
{
|
||||
if (_messageList.Count >= MAX_MESSAGES) DestroyOldestMessage();
|
||||
|
||||
CreateNewMessage(text, characterName, characterTextColor);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Broadcasts a dialogue message from a player.
|
||||
/// </summary>
|
||||
/// <param name="playerName">Name of the player.</param>
|
||||
/// <param name="text">Text of the dialogue message.</param>
|
||||
/// <param name="playerTextColor">Color of the player's text.</param>
|
||||
private void BroadcastPlayerDialogue(string playerName, string text, Color playerTextColor)
|
||||
{
|
||||
string trimmedText = text.Trim();
|
||||
|
||||
if (_currentSpeaker != Speaker.Player || !_messageList.Any())
|
||||
{
|
||||
_currentSpeaker = Speaker.Player;
|
||||
HandleNewPlayerMessage(playerName, trimmedText, playerTextColor);
|
||||
}
|
||||
else
|
||||
ReplaceExistingPlayerMessage(playerName, trimmedText, playerTextColor);
|
||||
|
||||
_currentSpeaker = Speaker.Player;
|
||||
ScrollToBottom();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles a new dialogue message from a player.
|
||||
/// </summary>
|
||||
/// <param name="playerName">Name of the player.</param>
|
||||
/// <param name="text">Text of the dialogue message.</param>
|
||||
/// <param name="playerTextColor">Color of the player's text.</param>
|
||||
private void HandleNewPlayerMessage(string playerName, string text, Color playerTextColor)
|
||||
{
|
||||
if (_messageList.Count >= MAX_MESSAGES) DestroyOldestMessage();
|
||||
|
||||
CreateNewMessage(text, playerName, playerTextColor);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Replaces an existing player message with a new one.
|
||||
/// </summary>
|
||||
/// <param name="playerName">Name of the player.</param>
|
||||
/// <param name="text">New text of the dialogue message.</param>
|
||||
/// <param name="playerTextColor">Color of the player's text.</param>
|
||||
private void ReplaceExistingPlayerMessage(string playerName, string text, Color playerTextColor)
|
||||
{
|
||||
Message lastMessage = _messageList[^1];
|
||||
lastMessage.Text = text;
|
||||
lastMessage.TextObject.text = FormatDialogueText(playerName, text, playerTextColor);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Appends a text to the existing message.
|
||||
/// </summary>
|
||||
/// <param name="text">Text which needs to append to the existing message.</param>
|
||||
private void AppendToExistingMessage(string text)
|
||||
{
|
||||
if (_messageList.Count > 0)
|
||||
{
|
||||
Message lastMessage = _messageList[^1];
|
||||
lastMessage.Text += " " + text;
|
||||
lastMessage.TextObject.text += " " + text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
private void ScrollToBottom()
|
||||
{
|
||||
Canvas.ForceUpdateCanvases();
|
||||
_chatScrollRect.verticalNormalizedPosition = 0f;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Formats the dialogue text.
|
||||
/// </summary>
|
||||
/// <param name="speakerName">Name of the speaker.</param>
|
||||
/// <param name="text">Text of the dialogue message.</param>
|
||||
/// <param name="speakerColor">Color of the speaker's text.</param>
|
||||
/// <returns>Formatted dialogue text with color tag and speaker's name.</returns>
|
||||
private static string FormatDialogueText(string speakerName, string text, Color speakerColor)
|
||||
{
|
||||
string speakerColorHtml = ColorUtility.ToHtmlStringRGB(speakerColor);
|
||||
return $"<b><color=#{speakerColorHtml}>{speakerName}</color></b>: {text}";
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the oldest message in the chat UI.
|
||||
/// </summary>
|
||||
private void DestroyOldestMessage()
|
||||
{
|
||||
Destroy(_messageList[0].TextObject.gameObject);
|
||||
_messageList.RemoveAt(0);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new dialogue message.
|
||||
/// </summary>
|
||||
/// <param name="text">Text of the dialogue message.</param>
|
||||
/// <param name="speakerName">Name of the speaker.</param>
|
||||
/// <param name="speakerColor">Color of the speaker's text.</param>
|
||||
private void CreateNewMessage(string text, string speakerName, Color speakerColor)
|
||||
{
|
||||
Message newMessage = new()
|
||||
{
|
||||
Text = text,
|
||||
TextObject = Instantiate(_textObject, _chatPanel.transform).GetComponent<TMP_Text>()
|
||||
};
|
||||
|
||||
// If the speaker is the player, disable the feedback icon
|
||||
GameObject feedbackButtons = newMessage.TextObject.transform.GetChild(0).gameObject;
|
||||
feedbackButtons.SetActive(_currentSpeaker == Speaker.Character);
|
||||
|
||||
newMessage.TextObject.text = FormatDialogueText(speakerName, text, speakerColor);
|
||||
_messageList.Add(newMessage);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of the possible speakers.
|
||||
/// </summary>
|
||||
private enum Speaker
|
||||
{
|
||||
Player,
|
||||
Character
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class to keep track of individual chat messages.
|
||||
/// </summary>
|
||||
private class Message
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public TMP_Text TextObject { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Convai/Scripts/Runtime/UI/Chat/ChatBoxUI.cs.meta
Normal file
3
Assets/Convai/Scripts/Runtime/UI/Chat/ChatBoxUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f71cd28901fa48b285d005b201cff508
|
||||
timeCreated: 1699396061
|
||||
149
Assets/Convai/Scripts/Runtime/UI/Chat/ChatUIBase.cs
Normal file
149
Assets/Convai/Scripts/Runtime/UI/Chat/ChatUIBase.cs
Normal file
@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for chat UI components, providing common functionality and abstract methods to be implemented by derived
|
||||
/// classes.
|
||||
/// </summary>
|
||||
public abstract class ChatUIBase : MonoBehaviour, IChatUI
|
||||
{
|
||||
[SerializeField] protected GameObject recordingMarker;
|
||||
private readonly List<Character> _characters = new();
|
||||
[NonSerialized] protected GameObject UIInstance;
|
||||
|
||||
private Image _recordingMarkerImage;
|
||||
private float _markerInitialAlpha;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the recording marker and subscribes to the OnPlayerSpeakingChanged event.
|
||||
/// </summary>
|
||||
protected virtual void Start()
|
||||
{
|
||||
SetupMarkerImage();
|
||||
SetRecordingMarkerActive(true);
|
||||
ConvaiGRPCAPI.Instance.OnPlayerSpeakingChanged += OnPlayerSpeakingChanged;
|
||||
}
|
||||
|
||||
private void SetupMarkerImage()
|
||||
{
|
||||
if (recordingMarker == null)
|
||||
{
|
||||
throw new NullReferenceException("Recording Marker Image cannot be null, please assign an image for it");
|
||||
}
|
||||
_recordingMarkerImage = recordingMarker.GetComponent<Image>();
|
||||
if (_recordingMarkerImage == null)
|
||||
{
|
||||
throw new NullReferenceException("Recording Marker does not have an Image Component attached, system cannot work without it");
|
||||
}
|
||||
_markerInitialAlpha = _recordingMarkerImage.color.a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the UI with the provided prefab.
|
||||
/// </summary>
|
||||
/// <param name="uiPrefab">The UI prefab to instantiate.</param>
|
||||
public abstract void Initialize(GameObject uiPrefab);
|
||||
|
||||
/// <summary>
|
||||
/// Activates the UI instance.
|
||||
/// </summary>
|
||||
public virtual void ActivateUI()
|
||||
{
|
||||
SetUIActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates the UI instance.
|
||||
/// </summary>
|
||||
public virtual void DeactivateUI()
|
||||
{
|
||||
SetUIActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends character text to the UI.
|
||||
/// </summary>
|
||||
/// <param name="charName">The name of the character.</param>
|
||||
/// <param name="text">The text to send.</param>
|
||||
/// <param name="characterTextColor">The color of the character's text.</param>
|
||||
public abstract void SendCharacterText(string charName, string text, Color characterTextColor);
|
||||
|
||||
/// <summary>
|
||||
/// Sends player text to the UI.
|
||||
/// </summary>
|
||||
/// <param name="playerName">The name of the player.</param>
|
||||
/// <param name="text">The text to send.</param>
|
||||
/// <param name="playerTextColor">The color of the player's text.</param>
|
||||
public abstract void SendPlayerText(string playerName, string text, Color playerTextColor);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the CanvasGroup component from the UI instance.
|
||||
/// </summary>
|
||||
/// <returns>The CanvasGroup component.</returns>
|
||||
public CanvasGroup GetCanvasGroup()
|
||||
{
|
||||
return UIInstance.GetComponent<CanvasGroup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a character to the list of known characters if it does not already exist.
|
||||
/// </summary>
|
||||
/// <param name="character">The character to add.</param>
|
||||
public void AddCharacter(Character character)
|
||||
{
|
||||
if (!HasCharacter(character.characterName))
|
||||
_characters.Add(character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a character with the given name exists in the list of known characters.
|
||||
/// </summary>
|
||||
/// <param name="characterName">The name of the character to check.</param>
|
||||
/// <returns>True if the character exists, false otherwise.</returns>
|
||||
public bool HasCharacter(string characterName)
|
||||
{
|
||||
return _characters.Any(character => character.characterName == characterName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the player speaking state change by updating the recording marker's visibility.
|
||||
/// </summary>
|
||||
/// <param name="isSpeaking">Whether the player is currently speaking.</param>
|
||||
private void OnPlayerSpeakingChanged(bool isSpeaking)
|
||||
{
|
||||
if (_recordingMarkerImage != null)
|
||||
_recordingMarkerImage = _recordingMarkerImage.WithColorValue(a: isSpeaking ? 1.0f : _markerInitialAlpha);
|
||||
else
|
||||
Logger.Error("Image component not found on recording marker.", Logger.LogCategory.Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the active state of the recording marker.
|
||||
/// </summary>
|
||||
/// <param name="active">The active state to set.</param>
|
||||
private void SetRecordingMarkerActive(bool active)
|
||||
{
|
||||
if (recordingMarker != null)
|
||||
recordingMarker.SetActive(active);
|
||||
else
|
||||
Logger.Error("Recording marker GameObject is not assigned.", Logger.LogCategory.Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the active state of the UI instance.
|
||||
/// </summary>
|
||||
/// <param name="active">The active state to set.</param>
|
||||
private void SetUIActive(bool active)
|
||||
{
|
||||
if (UIInstance != null)
|
||||
UIInstance.SetActive(active);
|
||||
else
|
||||
Logger.Error("UI instance GameObject is not assigned.", Logger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Convai/Scripts/Runtime/UI/Chat/ChatUIBase.cs.meta
Normal file
3
Assets/Convai/Scripts/Runtime/UI/Chat/ChatUIBase.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cea0ba3421149a5ae768e07f2214a68
|
||||
timeCreated: 1700807213
|
||||
314
Assets/Convai/Scripts/Runtime/UI/Chat/ConvaiChatUIHandler.cs
Normal file
314
Assets/Convai/Scripts/Runtime/UI/Chat/ConvaiChatUIHandler.cs
Normal file
@ -0,0 +1,314 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Utils
|
||||
{
|
||||
[Serializable]
|
||||
public class Character
|
||||
{
|
||||
[Header("Character settings")]
|
||||
[Tooltip("Convai NPC Game Object")]
|
||||
public ConvaiNPC characterGameObject;
|
||||
|
||||
[ReadOnly]
|
||||
[Tooltip("Display name of the NPC")]
|
||||
public string characterName = "Character";
|
||||
|
||||
[ColorUsage(true)]
|
||||
[Tooltip("Color of the NPC text. Alpha value will be ignored.")]
|
||||
[SerializeField]
|
||||
private Color characterTextColor = Color.red;
|
||||
|
||||
public Color CharacterTextColor
|
||||
{
|
||||
get => characterTextColor;
|
||||
set => characterTextColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
[AddComponentMenu("Convai/Chat UI Handler")]
|
||||
[DisallowMultipleComponent]
|
||||
[HelpURL(
|
||||
"https://docs.convai.com/api-docs/plugins-and-integrations/unity-plugin/scripts-overview/convaichatuihandler.cs")]
|
||||
[DefaultExecutionOrder(-100)]
|
||||
public class ConvaiChatUIHandler : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum to represent different UI types.
|
||||
/// </summary>
|
||||
public enum UIType
|
||||
{
|
||||
ChatBox,
|
||||
QuestionAnswer,
|
||||
Subtitle
|
||||
}
|
||||
|
||||
[Header("UI Prefabs")]
|
||||
[Tooltip("Prefab for the chat box UI.")]
|
||||
public GameObject chatBoxPrefab;
|
||||
|
||||
[Tooltip("Prefab for the subtitle UI.")]
|
||||
public GameObject subtitlePrefab;
|
||||
|
||||
[Tooltip("Prefab for the question-answer UI.")]
|
||||
public GameObject questionAnswerPrefab;
|
||||
|
||||
[Header("Character List")]
|
||||
[Tooltip("List of characters.")]
|
||||
public List<Character> characters = new();
|
||||
|
||||
[Header("Player settings")]
|
||||
[Tooltip("Display name of the player.")]
|
||||
public string playerName = "Player";
|
||||
|
||||
[ColorUsage(true)]
|
||||
[Tooltip("Color of the player's text. Alpha value will be ignored.")]
|
||||
public Color playerTextColor = Color.white;
|
||||
|
||||
private IChatUI _currentUIImplementation;
|
||||
public static ConvaiChatUIHandler Instance { get; private set; }
|
||||
|
||||
public Dictionary<UIType, IChatUI> GetUIAppearances { get; } = new();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
// Log a warning and destroy the duplicate instance
|
||||
Debug.Log("<color=red> There's More Than One ConvaiChatUIHandler </color> " + transform + " - " +
|
||||
Instance);
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the singleton instance
|
||||
Instance = this;
|
||||
|
||||
ValidateUIPrefabs();
|
||||
InitializeUIStrategies();
|
||||
}
|
||||
|
||||
// Subscribe to events when this component is enabled.
|
||||
private void OnEnable()
|
||||
{
|
||||
// Subscribe to the event when saved data is loaded.
|
||||
UISaveLoadSystem.Instance.OnLoad += UISaveLoadSystem_OnLoad;
|
||||
|
||||
// Subscribe to the event when data is saved.
|
||||
UISaveLoadSystem.Instance.OnSave += UISaveLoadSystem_OnSave;
|
||||
}
|
||||
|
||||
// Unsubscribe from events when this component is disabled.
|
||||
private void OnDisable()
|
||||
{
|
||||
// Unsubscribe from the event when saved data is loaded.
|
||||
UISaveLoadSystem.Instance.OnLoad -= UISaveLoadSystem_OnLoad;
|
||||
|
||||
// Unsubscribe from the event when data is saved.
|
||||
UISaveLoadSystem.Instance.OnSave -= UISaveLoadSystem_OnSave;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
SaveUIType();
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
UpdateCharacterList();
|
||||
RemoveDuplicateCharacters();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the character list by synchronizing names between Convai Transcript UI Character list and NPC
|
||||
/// characterName, removing null characters, and adding missing characters.
|
||||
/// </summary>
|
||||
public void UpdateCharacterList()
|
||||
{
|
||||
// Synchronize names between Convai Transcript UI Character list and NPC characterName
|
||||
for (int i = 0; i < characters.Count; i++)
|
||||
{
|
||||
Character character = characters[i];
|
||||
// If the character's game object is missing, remove it from the list
|
||||
if (character.characterGameObject == null)
|
||||
characters.Remove(character);
|
||||
else
|
||||
// Update the character's name using the game object's characterName
|
||||
character.characterName = character.characterGameObject.characterName;
|
||||
}
|
||||
|
||||
// Remove null characters
|
||||
characters = characters.Where(c => c.characterGameObject != null).ToList();
|
||||
|
||||
// Add missing characters
|
||||
foreach (ConvaiNPC convaiNpc in FindObjectsOfType<ConvaiNPC>())
|
||||
{
|
||||
if (characters.Any(c => c.characterGameObject == convaiNpc))
|
||||
continue;
|
||||
|
||||
characters.Add(new Character
|
||||
{
|
||||
characterGameObject = convaiNpc,
|
||||
characterName = convaiNpc.characterName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes duplicate characters from the character list based on their GameObject.
|
||||
/// </summary>
|
||||
private void RemoveDuplicateCharacters()
|
||||
{
|
||||
characters = characters
|
||||
.GroupBy(c => c.characterGameObject)
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when saved data is loaded.
|
||||
/// </summary>
|
||||
private void UISaveLoadSystem_OnLoad()
|
||||
{
|
||||
_currentUIImplementation = GetChatUIByUIType(UISaveLoadSystem.Instance.UIType);
|
||||
SetUIType(UISaveLoadSystem.Instance.UIType);
|
||||
_currentUIImplementation.ActivateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when data is saved.
|
||||
/// </summary>
|
||||
private void UISaveLoadSystem_OnSave()
|
||||
{
|
||||
SaveUIType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the UI with the given prefab and UI type.
|
||||
/// </summary>
|
||||
private void InitializeUIStrategies()
|
||||
{
|
||||
InitializeUI(chatBoxPrefab, UIType.ChatBox);
|
||||
InitializeUI(questionAnswerPrefab, UIType.QuestionAnswer);
|
||||
InitializeUI(subtitlePrefab, UIType.Subtitle);
|
||||
}
|
||||
|
||||
private void InitializeUI(GameObject uiPrefab, UIType uiType)
|
||||
{
|
||||
try
|
||||
{
|
||||
IChatUI uiComponent = uiPrefab.GetComponent<IChatUI>();
|
||||
if (uiComponent == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"The provided prefab for {uiType} does not have a component that implements IChatUI.");
|
||||
return;
|
||||
}
|
||||
|
||||
uiComponent.Initialize(uiPrefab);
|
||||
GetUIAppearances[uiType] = uiComponent;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"An error occurred while initializing the UI: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends character text to the current UI.
|
||||
/// </summary>
|
||||
/// <param name="charName">The character's name.</param>
|
||||
/// <param name="text">The text to send.</param>
|
||||
public void SendCharacterText(string charName, string text)
|
||||
{
|
||||
Character character = characters.Find(c => c.characterName == charName);
|
||||
if (character == null)
|
||||
{
|
||||
Debug.LogError($"No character found named {charName}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ConvaiNPCManager.Instance.CheckForNPCToNPCConversation(character.characterGameObject))
|
||||
_currentUIImplementation?.SendCharacterText(charName, text, character.CharacterTextColor);
|
||||
else
|
||||
Debug.LogWarning($"{charName} is in a NPC2NPC conversation.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends player text to the current UI.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to send.</param>
|
||||
public void SendPlayerText(string text)
|
||||
{
|
||||
_currentUIImplementation?.SendPlayerText(playerName, text, playerTextColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that all UI prefabs are assigned.
|
||||
/// </summary>
|
||||
private void ValidateUIPrefabs()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (chatBoxPrefab == null || subtitlePrefab == null || questionAnswerPrefab == null)
|
||||
throw new InvalidOperationException("All UI prefabs must be assigned in the inspector.");
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Debug.LogError($"An error occurred while validating UI prefabs: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current UI type and fades between UIs.
|
||||
/// </summary>
|
||||
/// <param name="newUIType">The new UI type to set.</param>
|
||||
public void SetUIType(UIType newUIType)
|
||||
{
|
||||
if (!GetUIAppearances.ContainsKey(newUIType))
|
||||
{
|
||||
Debug.LogError($"The UI type {newUIType} does not exist in the GetUIAppearances dictionary.");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentUIImplementation = GetUIAppearances[newUIType];
|
||||
}
|
||||
|
||||
private void SaveUIType()
|
||||
{
|
||||
foreach (KeyValuePair<UIType, IChatUI> strategy in GetUIAppearances.Where(strategy =>
|
||||
strategy.Value == _currentUIImplementation))
|
||||
{
|
||||
UISaveLoadSystem.Instance.UIType = strategy.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public IChatUI GetChatUIByUIType(UIType uiType)
|
||||
{
|
||||
return GetUIAppearances[uiType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current UI implementation.
|
||||
/// </summary>
|
||||
/// <returns>The current IChatUI implementation.</returns>
|
||||
public IChatUI GetCurrentUI()
|
||||
{
|
||||
return _currentUIImplementation;
|
||||
}
|
||||
|
||||
public bool HasCharacter(string convaiNPCCharacterName)
|
||||
{
|
||||
return characters.Any(character => character.characterName == convaiNPCCharacterName);
|
||||
}
|
||||
|
||||
public void AddCharacter(Character newCharacter)
|
||||
{
|
||||
characters.Add(newCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 119d1116f25796d4da5239037da0977f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
129
Assets/Convai/Scripts/Runtime/UI/Chat/ConvaiFeedbackHandler.cs
Normal file
129
Assets/Convai/Scripts/Runtime/UI/Chat/ConvaiFeedbackHandler.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using Convai.Scripts.Utils;
|
||||
using Service;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Logger = Convai.Scripts.Utils.Logger;
|
||||
|
||||
public class ConvaiFeedbackHandler : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Button _thumbsUPButton;
|
||||
[SerializeField] private Button _thumbsDownButton;
|
||||
|
||||
[SerializeField] private GameObject _thumbsUPFill;
|
||||
[SerializeField] private GameObject _thumbsDownFill;
|
||||
|
||||
private string _interactionID;
|
||||
private TextMeshProUGUI _feedbackText;
|
||||
|
||||
private string _feedbackTextString;
|
||||
|
||||
/// <summary>
|
||||
/// Awake is called when the script instance is being loaded.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
_feedbackText = GetComponentInParent<TextMeshProUGUI>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object becomes enabled and active.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
ConvaiGRPCAPI.Instance.OnResultReceived += ConvaiGRPCAPI_OnResultReceived;
|
||||
_thumbsUPButton.onClick.AddListener((() => OnFeedbackButtonClicked(_thumbsUPButton)));
|
||||
_thumbsDownButton.onClick.AddListener((() => OnFeedbackButtonClicked(_thumbsDownButton)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object is disabled.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
ConvaiGRPCAPI.Instance.OnResultReceived -= ConvaiGRPCAPI_OnResultReceived;
|
||||
_thumbsUPButton.onClick.RemoveAllListeners();
|
||||
_thumbsDownButton.onClick.RemoveAllListeners();
|
||||
|
||||
_thumbsUPFill.SetActive(false);
|
||||
_thumbsDownFill.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the event when a result is received from ConvaiGRPCAPI.
|
||||
/// </summary>
|
||||
/// <param name="result">The result received.</param>
|
||||
private void ConvaiGRPCAPI_OnResultReceived(GetResponseResponse result)
|
||||
{
|
||||
// Check if InteractionId is not null or empty.
|
||||
if (result.InteractionId.Length > 0)
|
||||
{
|
||||
_interactionID = result.InteractionId;
|
||||
ConvaiGRPCAPI.Instance.OnResultReceived -= ConvaiGRPCAPI_OnResultReceived;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the event when the feedback button is clicked.
|
||||
/// </summary>
|
||||
private void OnFeedbackButtonClicked(Button button)
|
||||
{
|
||||
if (button == _thumbsUPButton)
|
||||
{
|
||||
SendFeedback(true);
|
||||
}
|
||||
else if (button == _thumbsDownButton)
|
||||
{
|
||||
SendFeedback(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends feedback to ConvaiGRPCAPI asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="thumbsUP">Indicates whether the feedback is a thumbs up or thumbs down.</param>
|
||||
private async void SendFeedback(bool thumbsUP)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_interactionID))
|
||||
{
|
||||
Logger.Error("InteractionId is null or empty", Logger.LogCategory.Character);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the fill visuals for thumbs up and thumbs down buttons.
|
||||
HandleThumbsFill(thumbsUP);
|
||||
|
||||
// Extract feedback text after the colon character.
|
||||
string feedbackText = RemoveBeforeColon(_feedbackText.text);
|
||||
|
||||
// Send feedback to ConvaiGRPCAPI.
|
||||
await ConvaiGRPCAPI.Instance.SendFeedback(thumbsUP, _interactionID, feedbackText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the text before the colon character in the given string.
|
||||
/// </summary>
|
||||
/// <param name="text">The input text.</param>
|
||||
/// <returns>The modified text after removing the portion before the colon.</returns>
|
||||
private string RemoveBeforeColon(string text)
|
||||
{
|
||||
int colonIndex = text.IndexOf(':', StringComparison.Ordinal);
|
||||
if (colonIndex != -1)
|
||||
{
|
||||
return text.Substring(colonIndex + 2);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the fill state of the Thumbs Up and Thumbs Down buttons.
|
||||
/// </summary>
|
||||
/// <param name="thumbsUP">Indicates whether the feedback is a thumbs up or thumbs down.</param>
|
||||
private void HandleThumbsFill(bool thumbsUP)
|
||||
{
|
||||
_thumbsUPFill.SetActive(thumbsUP);
|
||||
_thumbsDownFill.SetActive(!thumbsUP);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8219b448c27c0849acf7d7425a79e54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Convai/Scripts/Runtime/UI/Chat/IChatUI.cs
Normal file
14
Assets/Convai/Scripts/Runtime/UI/Chat/IChatUI.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Utils
|
||||
{
|
||||
public interface IChatUI
|
||||
{
|
||||
void Initialize(GameObject uiPrefab);
|
||||
void ActivateUI();
|
||||
void DeactivateUI();
|
||||
void SendCharacterText(string charName, string text, Color characterTextColor);
|
||||
void SendPlayerText(string playerName, string text, Color playerTextColor);
|
||||
CanvasGroup GetCanvasGroup();
|
||||
}
|
||||
}
|
||||
3
Assets/Convai/Scripts/Runtime/UI/Chat/IChatUI.cs.meta
Normal file
3
Assets/Convai/Scripts/Runtime/UI/Chat/IChatUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d6a823a222a48cc891ff35e847d3a87
|
||||
timeCreated: 1699396034
|
||||
74
Assets/Convai/Scripts/Runtime/UI/Chat/QuestionAnswerUI.cs
Normal file
74
Assets/Convai/Scripts/Runtime/UI/Chat/QuestionAnswerUI.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// The QuestionAnswerUI class is responsible for managing the UI elements
|
||||
/// that display questions and answers in a conversational interface.
|
||||
/// </summary>
|
||||
public class QuestionAnswerUI : ChatUIBase
|
||||
{
|
||||
private TextMeshProUGUI _answerText;
|
||||
private TextMeshProUGUI _questionText;
|
||||
private GameObject _feedbackButtons;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the UI with the provided prefab.
|
||||
/// </summary>
|
||||
/// <param name="uiPrefab">The UI prefab to instantiate.</param>
|
||||
public override void Initialize(GameObject uiPrefab)
|
||||
{
|
||||
UIInstance = Instantiate(uiPrefab);
|
||||
_questionText = UIInstance.transform.Find("Background").Find("QuestionText")
|
||||
.GetComponent<TextMeshProUGUI>();
|
||||
_answerText = UIInstance.transform.Find("Background").Find("AnswerBox").Find("AnswerText").GetComponent<TextMeshProUGUI>();
|
||||
UIInstance.SetActive(false);
|
||||
_feedbackButtons = _answerText.transform.GetChild(0).gameObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the character's text to the UI, formatted with the character's color.
|
||||
/// </summary>
|
||||
/// <param name="charName">The name of the character speaking.</param>
|
||||
/// <param name="text">The text spoken by the character.</param>
|
||||
/// <param name="characterTextColor">The color associated with the character.</param>
|
||||
public override void SendCharacterText(string charName, string text, Color characterTextColor)
|
||||
{
|
||||
if (_answerText != null)
|
||||
{
|
||||
_feedbackButtons.SetActive(false);
|
||||
_answerText.text = FormatDialogueText(charName, text, characterTextColor);
|
||||
_feedbackButtons.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the player's text to the UI, formatted with the player's color.
|
||||
/// </summary>
|
||||
/// <param name="playerName">The name of the player speaking.</param>
|
||||
/// <param name="text">The text spoken by the player.</param>
|
||||
/// <param name="playerTextColor">The color associated with the player.</param>
|
||||
public override void SendPlayerText(string playerName, string text, Color playerTextColor)
|
||||
{
|
||||
if (_questionText != null)
|
||||
{
|
||||
_questionText.text = FormatDialogueText(playerName, text, playerTextColor);
|
||||
_feedbackButtons.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the dialogue text with the speaker's name and color.
|
||||
/// </summary>
|
||||
/// <param name="speakerName">The name of the speaker.</param>
|
||||
/// <param name="text">The text spoken by the speaker.</param>
|
||||
/// <param name="speakerColor">The color associated with the speaker.</param>
|
||||
/// <returns>Formatted dialogue text.</returns>
|
||||
private string FormatDialogueText(string speakerName, string text, Color speakerColor)
|
||||
{
|
||||
string colorHex = ColorUtility.ToHtmlStringRGBA(speakerColor);
|
||||
return $"<color=#{colorHex}>{speakerName}</color>: {text}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 851f7ed955104489b2e32a7c6da4debc
|
||||
timeCreated: 1699985777
|
||||
88
Assets/Convai/Scripts/Runtime/UI/Chat/SubtitleChatUI.cs
Normal file
88
Assets/Convai/Scripts/Runtime/UI/Chat/SubtitleChatUI.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// SubtitleChatUI is responsible for displaying subtitles on the screen.
|
||||
/// It inherits from ChatUIBase and overrides methods to provide specific functionality for subtitle UI.
|
||||
/// </summary>
|
||||
public class SubtitleChatUI : ChatUIBase
|
||||
{
|
||||
private TextMeshProUGUI _subtitleText;
|
||||
private GameObject _feedbackButtons;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the subtitle UI with the provided prefab.
|
||||
/// </summary>
|
||||
/// <param name="uiPrefab">The UI prefab to instantiate.</param>
|
||||
public override void Initialize(GameObject uiPrefab)
|
||||
{
|
||||
// Instantiate the UI prefab and get the subtitle text component
|
||||
UIInstance = Instantiate(uiPrefab);
|
||||
_subtitleText = UIInstance.GetComponentInChildren<TextMeshProUGUI>();
|
||||
|
||||
// Start with the UI inactive
|
||||
UIInstance.SetActive(false);
|
||||
|
||||
_feedbackButtons = _subtitleText.transform.GetChild(0).gameObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the character's text to the subtitle UI.
|
||||
/// </summary>
|
||||
/// <param name="charName">The name of the character speaking.</param>
|
||||
/// <param name="text">The text spoken by the character.</param>
|
||||
/// <param name="characterTextColor">The color associated with the character.</param>
|
||||
public override void SendCharacterText(string charName, string text, Color characterTextColor)
|
||||
{
|
||||
// Update the subtitle text with formatted character dialogue.
|
||||
_feedbackButtons.SetActive(false);
|
||||
UpdateSubtitleText(charName, text, characterTextColor);
|
||||
_feedbackButtons.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the player's text to the subtitle UI.
|
||||
/// </summary>
|
||||
/// <param name="playerName">The name of the player speaking.</param>
|
||||
/// <param name="text">The text spoken by the player.</param>
|
||||
/// <param name="playerTextColor">The color associated with the player.</param>
|
||||
public override void SendPlayerText(string playerName, string text, Color playerTextColor)
|
||||
{
|
||||
// Update the subtitle text with formatted player dialogue.
|
||||
UpdateSubtitleText(playerName, text, playerTextColor);
|
||||
_feedbackButtons.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the subtitle text with the provided speaker's name, text, and color.
|
||||
/// </summary>
|
||||
/// <param name="speakerName">The name of the speaker.</param>
|
||||
/// <param name="text">The text spoken by the speaker.</param>
|
||||
/// <param name="color">The color associated with the speaker.</param>
|
||||
private void UpdateSubtitleText(string speakerName, string text, Color color)
|
||||
{
|
||||
// Check if the subtitle text component is available before updating.
|
||||
if (_subtitleText != null)
|
||||
_subtitleText.text = FormatText(speakerName, text, color);
|
||||
else
|
||||
Debug.LogError("Subtitle text component not found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the text with the speaker's name and color.
|
||||
/// </summary>
|
||||
/// <param name="speakerName">The name of the speaker.</param>
|
||||
/// <param name="text">The text spoken by the speaker.</param>
|
||||
/// <param name="color">The color associated with the speaker.</param>
|
||||
/// <returns>The formatted text string.</returns>
|
||||
private string FormatText(string speakerName, string text, Color color)
|
||||
{
|
||||
// Convert the color to a hex string for HTML color formatting.
|
||||
string colorHex = ColorUtility.ToHtmlStringRGB(color);
|
||||
// Return the formatted text with the speaker's name and color.
|
||||
return $"<color=#{colorHex}>{speakerName}</color>: {text}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57c8aa8be91242bc9cd11702857a3cde
|
||||
timeCreated: 1699396102
|
||||
Reference in New Issue
Block a user