restructure
This commit is contained in:
8
Unity/Assets/Convai/Scripts/Runtime/UI/CanvasItems.meta
Normal file
8
Unity/Assets/Convai/Scripts/Runtime/UI/CanvasItems.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be34952afeab7ff428b9448053f70d21
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,68 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Convai.Scripts.Runtime.Features;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the crosshair behavior for the Convai application.
|
||||
/// It can detect which Convai game object the player's crosshair is currently looking at.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
[AddComponentMenu("Convai/Crosshair Handler")]
|
||||
public class ConvaiCrosshairHandler : MonoBehaviour
|
||||
{
|
||||
private Camera _camera;
|
||||
private Dictionary<GameObject, string> _interactableReferences;
|
||||
private ConvaiInteractablesData _interactablesData;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_camera = Camera.main;
|
||||
|
||||
_interactablesData = FindObjectOfType<ConvaiInteractablesData>();
|
||||
if (_interactablesData == null) return;
|
||||
// Build the interactable references dictionary
|
||||
_interactableReferences = new Dictionary<GameObject, string>();
|
||||
foreach (ConvaiInteractablesData.Object eachObject in _interactablesData.Objects)
|
||||
_interactableReferences[eachObject.gameObject] = eachObject.Name;
|
||||
foreach (ConvaiInteractablesData.Character eachCharacter in _interactablesData.Characters)
|
||||
_interactableReferences[eachCharacter.gameObject] = eachCharacter.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the reference object that the player's crosshair is currently looking at.
|
||||
/// </summary>
|
||||
/// <returns>A reference string of the interactable object or character, "None" if no valid hit.</returns>
|
||||
public string FindPlayerReferenceObject()
|
||||
{
|
||||
if (_interactablesData == null || _camera == null) return "None";
|
||||
|
||||
Vector3 centerOfScreen = new(0.5f, 0.5f, 0);
|
||||
if (Physics.Raycast(_camera.ViewportPointToRay(centerOfScreen), out RaycastHit hit))
|
||||
{
|
||||
_interactablesData.DynamicMoveTargetIndicator.position = hit.point;
|
||||
string reference = FindInteractableReference(hit.transform.gameObject);
|
||||
ConvaiLogger.DebugLog($"Player is looking at: {reference}", ConvaiLogger.LogCategory.Actions);
|
||||
return reference;
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the reference object that the player's crosshair is currently looking at.
|
||||
/// </summary>
|
||||
/// <param name="hitGameObject"> The game object that the player's crosshair is currently looking at.</param>
|
||||
/// <returns> A reference string(name) of the interactable object or character, "None" if no valid hit.</returns>
|
||||
private string FindInteractableReference(GameObject hitGameObject)
|
||||
{
|
||||
foreach (KeyValuePair<GameObject, string> kvp in _interactableReferences.Where(kvp => hitGameObject.GetComponentInParent<Transform>() == kvp.Key.transform))
|
||||
return kvp.Value;
|
||||
|
||||
return "None";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9075946a1a51c9f4abe905231892b484
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
public class ConvaiLogoHandler : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private GameObject convaiLogo;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
ChangeLogo(false);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ChatUIBase.UIStatusChange += ChangeLogo;
|
||||
UIAppearanceSettings.UIStatusChange += ChangeLogo;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ChatUIBase.UIStatusChange -= ChangeLogo;
|
||||
UIAppearanceSettings.UIStatusChange -= ChangeLogo;
|
||||
}
|
||||
|
||||
private void ChangeLogo(bool isHidden)
|
||||
{
|
||||
convaiLogo.SetActive(!isHidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2924d3c1ffa3eb947a1b7f15033a49ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Unity/Assets/Convai/Scripts/Runtime/UI/Chat.meta
Normal file
8
Unity/Assets/Convai/Scripts/Runtime/UI/Chat.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b29b467ab45cbb449852364ddf243fe
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
269
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/ChatBoxUI.cs
Normal file
269
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/ChatBoxUI.cs
Normal file
@ -0,0 +1,269 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using ColorUtility = UnityEngine.ColorUtility;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the chat UI for displaying messages from characters and the player.
|
||||
/// </summary>
|
||||
public class ChatBoxUI : ChatUIBase
|
||||
{
|
||||
private const int MAX_MESSAGES = 25;
|
||||
[SerializeField] private GameObject _playerMessageObject, _characterMessageObject;
|
||||
|
||||
private readonly List<Message> _messageList = new();
|
||||
|
||||
private GameObject _chatPanel;
|
||||
private ScrollRect _chatScrollRect;
|
||||
private Speaker _currentSpeaker;
|
||||
private bool _isFirstMessage = true;
|
||||
private bool _isNewMessage;
|
||||
|
||||
/// <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;
|
||||
_chatScrollRect = UIInstance.transform.GetChild(0).GetChild(0).GetComponent<ScrollRect>();
|
||||
_isFirstMessage = true;
|
||||
_currentSpeaker = Speaker.Player;
|
||||
}
|
||||
|
||||
/// <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.SenderTextObject.gameObject);
|
||||
Destroy(message.MessageTextObject.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, false);
|
||||
}
|
||||
|
||||
|
||||
/// <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, true);
|
||||
}
|
||||
|
||||
|
||||
/// <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.MessageTextObject.text = text;
|
||||
lastMessage.SenderTextObject.text = FormatSpeakerName(playerName, playerTextColor);
|
||||
|
||||
// RTL Update done due to text arriving after create message
|
||||
// Once for every message
|
||||
if (text != "" && _isNewMessage)
|
||||
{
|
||||
lastMessage.RTLUpdate();
|
||||
_isNewMessage = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <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.MessageTextObject.text += " " + text;
|
||||
|
||||
if (text != "" && _isNewMessage)
|
||||
{
|
||||
lastMessage.RTLUpdate();
|
||||
_isNewMessage = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
private void ScrollToBottom()
|
||||
{
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(_chatPanel.GetComponent<RectTransform>());
|
||||
Canvas.ForceUpdateCanvases();
|
||||
_chatScrollRect.verticalNormalizedPosition = 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the speaker name
|
||||
/// </summary>
|
||||
/// <param name="speakerName">Name of the speaker.</param>
|
||||
/// <param name="speakerColor">Color of the speaker's text.</param>
|
||||
/// <returns>Formatted speaker's name with color tag.</returns>
|
||||
private static string FormatSpeakerName(string speakerName, Color speakerColor)
|
||||
{
|
||||
string speakerColorHtml = ColorUtility.ToHtmlStringRGB(speakerColor);
|
||||
return $"<color=#{speakerColorHtml}>{speakerName}</color>";
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the oldest message in the chat UI.
|
||||
/// </summary>
|
||||
private void DestroyOldestMessage()
|
||||
{
|
||||
Destroy(_messageList[0].SenderTextObject.gameObject);
|
||||
Destroy(_messageList[0].MessageTextObject.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>
|
||||
/// <param name="isSpeakerPlayer"> Flag to check if the speaker is a player.</param>
|
||||
private void CreateNewMessage(string text, string speakerName, Color speakerColor, bool isSpeakerPlayer)
|
||||
{
|
||||
_isNewMessage = true;
|
||||
|
||||
GameObject messageInstance = isSpeakerPlayer ? Instantiate(_playerMessageObject, _chatPanel.transform) : Instantiate(_characterMessageObject, _chatPanel.transform);
|
||||
messageInstance.SetActive(true);
|
||||
|
||||
Transform container = messageInstance.transform.Find("Container");
|
||||
Transform senderBox = container.Find("SenderBox");
|
||||
|
||||
Message newMessage = new()
|
||||
{
|
||||
SenderTextObject = senderBox.Find("Sender").GetComponent<TMP_Text>(),
|
||||
MessageTextObject = container.Find("Message").GetComponent<TMP_Text>()
|
||||
};
|
||||
|
||||
newMessage.SenderTextObject.text = FormatSpeakerName(speakerName, speakerColor);
|
||||
newMessage.MessageTextObject.text = text;
|
||||
|
||||
_messageList.Add(newMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of the possible speakers.
|
||||
/// </summary>
|
||||
private enum Speaker
|
||||
{
|
||||
Player,
|
||||
Character
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f71cd28901fa48b285d005b201cff508
|
||||
timeCreated: 1699396061
|
||||
154
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/ChatUIBase.cs
Normal file
154
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/ChatUIBase.cs
Normal file
@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Convai.Scripts.Runtime.Addons;
|
||||
using Convai.Scripts.Runtime.Core;
|
||||
using Convai.Scripts.Runtime.Extensions;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <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();
|
||||
private float _markerInitialAlpha;
|
||||
|
||||
private Image _recordingMarkerImage;
|
||||
[NonSerialized] protected GameObject UIInstance;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the recording marker and subscribes to the OnPlayerSpeakingChanged event.
|
||||
/// </summary>
|
||||
protected virtual void Start()
|
||||
{
|
||||
SetupMarkerImage();
|
||||
SetRecordingMarkerActive(true);
|
||||
ConvaiGRPCAPI.Instance.OnPlayerSpeakingChanged += OnPlayerSpeakingChanged;
|
||||
}
|
||||
|
||||
/// <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 if transcript UI status is active.
|
||||
/// </summary>
|
||||
public virtual void ActivateUI()
|
||||
{
|
||||
if (UISaveLoadSystem.Instance.TranscriptUIActiveStatus)
|
||||
{
|
||||
SetUIActive(true);
|
||||
UIStatusChange?.Invoke(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates the UI instance.
|
||||
/// </summary>
|
||||
public virtual void DeactivateUI()
|
||||
{
|
||||
SetUIActive(false);
|
||||
UIStatusChange.Invoke(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>();
|
||||
}
|
||||
|
||||
public static event Action<bool> UIStatusChange;
|
||||
|
||||
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>
|
||||
/// 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
|
||||
ConvaiLogger.Error("Image component not found on recording marker.", ConvaiLogger.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
|
||||
ConvaiLogger.Error("Recording marker GameObject is not assigned.", ConvaiLogger.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
|
||||
ConvaiLogger.Error("UI instance GameObject is not assigned.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cea0ba3421149a5ae768e07f2214a68
|
||||
timeCreated: 1700807213
|
||||
@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Convai.Scripts.Runtime.Addons;
|
||||
using Convai.Scripts.Runtime.Attributes;
|
||||
using Convai.Scripts.Runtime.Core;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using Convai.Scripts.Runtime.PlayerStats;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
[AddComponentMenu("Convai/Chat UI Handler")]
|
||||
[DisallowMultipleComponent]
|
||||
[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();
|
||||
|
||||
[ColorUsage(true)] [Tooltip("Color of the player's text. Alpha value will be ignored.")]
|
||||
public Color playerTextColor = Color.white;
|
||||
|
||||
private ConvaiPlayerDataSO _convaiPlayerData;
|
||||
|
||||
private IChatUI _currentUIImplementation;
|
||||
|
||||
private bool _hasPlayerData;
|
||||
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
|
||||
ConvaiLogger.DebugLog("<color=red> There's More Than One ConvaiChatUIHandler </color> " + transform + " - " + Instance, ConvaiLogger.LogCategory.UI);
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the singleton instance
|
||||
Instance = this;
|
||||
|
||||
ValidateUIPrefabs();
|
||||
InitializeUIStrategies();
|
||||
|
||||
_hasPlayerData = ConvaiPlayerDataSO.GetPlayerData(out _convaiPlayerData);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
ConvaiLogger.Warn($"The provided prefab for {uiType} does not have a component that implements IChatUI.", ConvaiLogger.LogCategory.UI);
|
||||
return;
|
||||
}
|
||||
|
||||
uiComponent.Initialize(uiPrefab);
|
||||
GetUIAppearances[uiType] = uiComponent;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ConvaiLogger.Exception($"An error occurred while initializing the UI: {ex.Message}", ConvaiLogger.LogCategory.UI);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
ConvaiLogger.Warn($"No character found named {charName}", ConvaiLogger.LogCategory.Character);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ConvaiNPCManager.Instance.CheckForNPCToNPCConversation(character.characterGameObject))
|
||||
_currentUIImplementation?.SendCharacterText(charName, text, character.CharacterTextColor);
|
||||
else
|
||||
ConvaiLogger.DebugLog($"Character {charName} is in conversation with another NPC.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends player text to the current UI.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to send.</param>
|
||||
public void SendPlayerText(string text)
|
||||
{
|
||||
_currentUIImplementation?.SendPlayerText(_hasPlayerData ? _convaiPlayerData.PlayerName : _convaiPlayerData.DefaultPlayerName, 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)
|
||||
{
|
||||
ConvaiLogger.Exception($"An error occurred while validating UI prefabs: {ex.Message}", ConvaiLogger.LogCategory.UI);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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))
|
||||
{
|
||||
ConvaiLogger.Warn($"The UI type {newUIType} is not available.", ConvaiLogger.LogCategory.UI);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Convai.Scripts.Runtime.Addons
|
||||
{
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 119d1116f25796d4da5239037da0977f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using Convai.Scripts.Runtime.Core;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using Service;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
public class ConvaiFeedbackHandler : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Button _thumbsUPButton;
|
||||
[SerializeField] private Button _thumbsDownButton;
|
||||
|
||||
[SerializeField] private GameObject _thumbsUPFill;
|
||||
[SerializeField] private GameObject _thumbsDownFill;
|
||||
[SerializeField] private TextMeshProUGUI _feedbackText;
|
||||
|
||||
private string _feedbackTextString;
|
||||
|
||||
private string _interactionID;
|
||||
|
||||
/// <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))
|
||||
{
|
||||
ConvaiLogger.Error("InteractionId is null or empty", ConvaiLogger.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
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/IChatUI.cs
Normal file
14
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/IChatUI.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d6a823a222a48cc891ff35e847d3a87
|
||||
timeCreated: 1699396034
|
||||
27
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/Message.cs
Normal file
27
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/Message.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Convai.Scripts.Runtime.Utils;
|
||||
using TMPro;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to keep track of individual chat messages.
|
||||
/// </summary>
|
||||
public class Message
|
||||
{
|
||||
public TMP_Text SenderTextObject { get; set; }
|
||||
public TMP_Text MessageTextObject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Does an RTL check for the message and changes the order of sender-text in the UI if both are in an
|
||||
/// RTL Language
|
||||
/// </summary>
|
||||
public void RTLUpdate()
|
||||
{
|
||||
// Enable the RTL on the Sender Component
|
||||
if (ConvaiLanguageCheck.IsRTL(SenderTextObject.text)) MessageTextObject.isRightToLeftText = true;
|
||||
|
||||
// Enable the RTL on the Text Component
|
||||
if (ConvaiLanguageCheck.IsRTL(MessageTextObject.text)) MessageTextObject.isRightToLeftText = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e83a67a9db6402382ef267ca82285ae
|
||||
timeCreated: 1718117860
|
||||
@ -0,0 +1,86 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <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 Message _answer;
|
||||
private GameObject _feedbackButtons;
|
||||
private Message _question;
|
||||
|
||||
/// <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);
|
||||
_question = new Message
|
||||
{
|
||||
SenderTextObject = UIInstance.transform.Find("Background").Find("Question").Find("Sender").GetComponent<TMP_Text>(),
|
||||
MessageTextObject = UIInstance.transform.Find("Background").Find("Question").Find("Text").GetComponent<TMP_Text>()
|
||||
};
|
||||
_answer = new Message
|
||||
{
|
||||
SenderTextObject = UIInstance.transform.Find("Background").Find("Answer").Find("Sender").GetComponent<TMP_Text>(),
|
||||
MessageTextObject = UIInstance.transform.Find("Background").Find("Answer").Find("AnswerText").Find("Text").GetComponent<TMP_Text>()
|
||||
};
|
||||
UIInstance.SetActive(false);
|
||||
_feedbackButtons = _answer.MessageTextObject.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 (string.IsNullOrEmpty(text)) return;
|
||||
|
||||
if (_answer != null)
|
||||
{
|
||||
_feedbackButtons.SetActive(false);
|
||||
_answer.SenderTextObject.text = FormatSpeakerName(charName, characterTextColor);
|
||||
_answer.MessageTextObject.text = text;
|
||||
_answer.RTLUpdate();
|
||||
_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 (_question != null)
|
||||
{
|
||||
_question.SenderTextObject.text = FormatSpeakerName(playerName, playerTextColor);
|
||||
_question.MessageTextObject.text = text;
|
||||
_answer.RTLUpdate();
|
||||
_feedbackButtons.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the speaker's name with the color tag
|
||||
/// </summary>
|
||||
/// <param name="speakerName">The name of the speaker.</param>
|
||||
/// <param name="speakerColor">The color associated with the speaker.</param>
|
||||
/// <returns>Formatted speaker name.</returns>
|
||||
private string FormatSpeakerName(string speakerName, Color speakerColor)
|
||||
{
|
||||
string colorHex = ColorUtility.ToHtmlStringRGBA(speakerColor);
|
||||
return $"<color=#{colorHex}>{speakerName}</color>: ";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 851f7ed955104489b2e32a7c6da4debc
|
||||
timeCreated: 1699985777
|
||||
100
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/SubtitleChatUI.cs
Normal file
100
Unity/Assets/Convai/Scripts/Runtime/UI/Chat/SubtitleChatUI.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <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 GameObject _feedbackButtons;
|
||||
private Message _subtitle;
|
||||
|
||||
/// <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);
|
||||
_subtitle = new Message
|
||||
{
|
||||
SenderTextObject = UIInstance.transform.Find("Background").Find("ChatBox").Find("Subtitle").Find("Sender").GetComponent<TMP_Text>(),
|
||||
MessageTextObject = UIInstance.transform.Find("Background").Find("ChatBox").Find("Subtitle").Find("Text").GetComponent<TMP_Text>()
|
||||
};
|
||||
|
||||
// Start with the UI inactive
|
||||
UIInstance.SetActive(false);
|
||||
|
||||
_feedbackButtons = _subtitle.MessageTextObject.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)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return;
|
||||
|
||||
// Check if the subtitle text component is available before updating.
|
||||
if (_subtitle != null)
|
||||
{
|
||||
_subtitle.SenderTextObject.text = FormatSpeakerName(speakerName, color);
|
||||
_subtitle.MessageTextObject.text = text;
|
||||
_subtitle.RTLUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiLogger.Warn("Subtitle text component is not available.", ConvaiLogger.LogCategory.UI);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats the speaker's name with the color tag.
|
||||
/// </summary>
|
||||
/// <param name="speakerName">The name of the speaker.</param>
|
||||
/// <param name="color">The color associated with the speaker.</param>
|
||||
/// <returns>The formatted speaker name</returns>
|
||||
private string FormatSpeakerName(string speakerName, 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>: ";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57c8aa8be91242bc9cd11702857a3cde
|
||||
timeCreated: 1699396102
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc1284d8ba9278f4ebb97d7ba44c3a73
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used to control the fade in and fade out animations of a CanvasGroup.
|
||||
/// </summary>
|
||||
public class FadeCanvas : MonoBehaviour
|
||||
{
|
||||
// Current alpha value of the CanvasGroup
|
||||
private float _currentAlpha;
|
||||
|
||||
// Event called when the Active Fade is completed.
|
||||
public Action OnCurrentFadeCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Starts the fade in animation for the given CanvasGroup.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to fade in.</param>
|
||||
/// <param name="duration">The duration of the fade in animation.</param>
|
||||
public void StartFadeIn(CanvasGroup canvasGroup, float duration)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(FadeIn(canvasGroup, duration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the fade out animation for the given CanvasGroup.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to fade out.</param>
|
||||
/// <param name="duration">The duration of the fade out animation.</param>
|
||||
public void StartFadeOut(CanvasGroup canvasGroup, float duration)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(FadeOut(canvasGroup, duration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a sequence of fade in and fade out animations with a gap in between for the given CanvasGroup.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to animate.</param>
|
||||
/// <param name="fadeInDuration">The duration of the fade in animation.</param>
|
||||
/// <param name="fadeOutDuration">The duration of the fade out animation.</param>
|
||||
/// <param name="gapDuration">The duration of the gap between the fade in and fade out animations.</param>
|
||||
public void StartFadeInFadeOutWithGap(CanvasGroup canvasGroup, float fadeInDuration, float fadeOutDuration,
|
||||
float gapDuration)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(FadeInFadeOutWithGap(canvasGroup, fadeInDuration, fadeOutDuration, gapDuration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a sequence of fade out and fade in animations with a gap in between for the given CanvasGroup.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to animate.</param>
|
||||
/// <param name="fadeInDuration">The duration of the fade in animation.</param>
|
||||
/// <param name="fadeOutDuration">The duration of the fade out animation.</param>
|
||||
/// <param name="gapDuration">The duration of the gap between the fade out and fade in animations.</param>
|
||||
public void StartFadeOutFadeInWithGap(CanvasGroup canvasGroup, float fadeInDuration, float fadeOutDuration,
|
||||
float gapDuration)
|
||||
{
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(FadeOutFadeInWithGap(canvasGroup, fadeInDuration, fadeOutDuration, gapDuration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the alpha value of the given CanvasGroup.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to set the alpha value for.</param>
|
||||
/// <param name="value">The alpha value to set.</param>
|
||||
private void SetAlpha(CanvasGroup canvasGroup, float value)
|
||||
{
|
||||
_currentAlpha = value;
|
||||
canvasGroup.alpha = _currentAlpha;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine for the fade in animation.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to fade in.</param>
|
||||
/// <param name="duration">The duration of the fade in animation.</param>
|
||||
private IEnumerator FadeIn(CanvasGroup canvasGroup, float duration)
|
||||
{
|
||||
float elapsedTime = 0.0f;
|
||||
// Gradually increase alpha from 0 to 1
|
||||
while (_currentAlpha <= 1.0f)
|
||||
{
|
||||
SetAlpha(canvasGroup, elapsedTime / duration);
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
OnCurrentFadeCompleted?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine for the fade out animation.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to fade out.</param>
|
||||
/// <param name="duration">The duration of the fade out animation.</param>
|
||||
private IEnumerator FadeOut(CanvasGroup canvasGroup, float duration)
|
||||
{
|
||||
float elapsedTime = 0.0f;
|
||||
|
||||
// Gradually decrease alpha from 1 to 0
|
||||
while (_currentAlpha >= 0.0f)
|
||||
{
|
||||
SetAlpha(canvasGroup, 1 - elapsedTime / duration);
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
|
||||
OnCurrentFadeCompleted?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine for a sequence of fade in and fade out animations with a gap in between.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to animate.</param>
|
||||
/// <param name="fadeInDuration">The duration of the fade in animation.</param>
|
||||
/// <param name="fadeOutDuration">The duration of the fade out animation.</param>
|
||||
/// <param name="gapDuration">The duration of the gap between the fade in and fade out animations.</param>
|
||||
private IEnumerator FadeInFadeOutWithGap(CanvasGroup canvasGroup, float fadeInDuration, float fadeOutDuration,
|
||||
float gapDuration)
|
||||
{
|
||||
float elapsedTime = 0.0f;
|
||||
|
||||
// Gradually increase alpha from 0 to 1
|
||||
while (_currentAlpha <= 1.0f)
|
||||
{
|
||||
SetAlpha(canvasGroup, elapsedTime / fadeInDuration);
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Pause for a specified gap duration
|
||||
yield return new WaitForSeconds(gapDuration);
|
||||
|
||||
elapsedTime = 0.0f;
|
||||
|
||||
// Gradually decrease alpha from 1 to 0
|
||||
while (_currentAlpha >= 0.0f)
|
||||
{
|
||||
SetAlpha(canvasGroup, 1 - elapsedTime / fadeOutDuration);
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
OnCurrentFadeCompleted?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine for a sequence of fade out and fade in animations with a gap in between.
|
||||
/// </summary>
|
||||
/// <param name="canvasGroup">The CanvasGroup to animate.</param>
|
||||
/// <param name="fadeInDuration">The duration of the fade in animation.</param>
|
||||
/// <param name="fadeOutDuration">The duration of the fade out animation.</param>
|
||||
/// <param name="gapDuration">The duration of the gap between the fade out and fade in animations.</param>
|
||||
private IEnumerator FadeOutFadeInWithGap(CanvasGroup canvasGroup, float fadeInDuration, float fadeOutDuration,
|
||||
float gapDuration)
|
||||
{
|
||||
float elapsedTime = 0.0f;
|
||||
|
||||
// Gradually decrease alpha from 1 to 0
|
||||
while (_currentAlpha >= 0.0f)
|
||||
{
|
||||
SetAlpha(canvasGroup, 1 - elapsedTime / fadeOutDuration);
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Pause for a specified gap duration
|
||||
yield return new WaitForSeconds(gapDuration);
|
||||
|
||||
elapsedTime = 0.0f;
|
||||
|
||||
// Gradually increase alpha from 0 to 1
|
||||
while (_currentAlpha <= 1.0f)
|
||||
{
|
||||
SetAlpha(canvasGroup, elapsedTime / fadeInDuration);
|
||||
elapsedTime += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
OnCurrentFadeCompleted?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e8b13f4d5b1d4742a6a02493b450f0c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
public class MicrophoneManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Private Instance of the Singleton
|
||||
/// </summary>
|
||||
private static MicrophoneManager _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps track on the selected microphone device index
|
||||
/// </summary>
|
||||
private int _selectedMicrophoneIndex;
|
||||
|
||||
private MicrophoneManager()
|
||||
{
|
||||
_selectedMicrophoneIndex = UISaveLoadSystem.Instance.SelectedMicrophoneDeviceNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the MicrophoneTestController.
|
||||
/// </summary>
|
||||
public static MicrophoneManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null) _instance = new MicrophoneManager();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public Getter for Selected Microphone Name
|
||||
/// </summary>
|
||||
public string SelectedMicrophoneName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_selectedMicrophoneIndex < 0 || _selectedMicrophoneIndex >= Microphone.devices.Length) return string.Empty;
|
||||
return Microphone.devices[_selectedMicrophoneIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event indicating that the selected Microphone has changed.
|
||||
/// </summary>
|
||||
public event Action<string> OnMicrophoneDeviceChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the selected microphone device is changed.
|
||||
/// </summary>
|
||||
public void SetSelectedMicrophoneIndex(int selectedMicrophoneDeviceValue)
|
||||
{
|
||||
_selectedMicrophoneIndex = selectedMicrophoneDeviceValue;
|
||||
OnMicrophoneDeviceChanged?.Invoke(SelectedMicrophoneName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether any microphone is present in the system or not
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool HasAnyMicrophoneDevices()
|
||||
{
|
||||
return Microphone.devices.Length != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 139f8e0a80b8c38428e24202d3bb9ae4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,137 @@
|
||||
using System.Collections;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
[RequireComponent(typeof(MicrophoneTestController))]
|
||||
public class MicrophoneTestAudioWaveform : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The UI element to display the audio waveform.")] [SerializeField]
|
||||
private RectTransform waveVisualizerUI;
|
||||
|
||||
/// <summary>
|
||||
/// Array to hold audio sample data.
|
||||
/// </summary>
|
||||
private readonly float[] _clipSampleData = new float[1024];
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier for adjusting the amplitude of the waveform.
|
||||
/// </summary>
|
||||
private readonly float _waveMultiplier = 75f;
|
||||
|
||||
/// <summary>
|
||||
/// AudioSource to fetch and play audio from.
|
||||
/// </summary>
|
||||
private AudioSource _audioSource;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the MicrophoneManager component.
|
||||
/// </summary>
|
||||
private MicrophoneTestController _microphoneTestController;
|
||||
|
||||
/// <summary>
|
||||
/// The initial size of the wave visualizer UI.
|
||||
/// </summary>
|
||||
private Vector2 _startSizeDelta;
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine reference for stopping the waveform display.
|
||||
/// </summary>
|
||||
private Coroutine _waveformCoroutine;
|
||||
|
||||
/// <summary>
|
||||
/// Get the components on Awake.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
_microphoneTestController = GetComponent<MicrophoneTestController>();
|
||||
_audioSource = GetComponent<AudioSource>();
|
||||
_startSizeDelta = waveVisualizerUI.sizeDelta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribing to events when enabled.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
_microphoneTestController.OnRecordStarted += MicrophoneTestControllerOnRecordStarted;
|
||||
_microphoneTestController.OnRecordCompleted += MicrophoneTestControllerOnRecordCompleted;
|
||||
_microphoneTestController.OnAudioClipCompleted += MicrophoneTestControllerOnAudioClipCompleted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribing from events when disabled.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
_microphoneTestController.OnRecordStarted -= MicrophoneTestControllerOnRecordStarted;
|
||||
_microphoneTestController.OnRecordCompleted -= MicrophoneTestControllerOnRecordCompleted;
|
||||
_microphoneTestController.OnAudioClipCompleted -= MicrophoneTestControllerOnAudioClipCompleted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when recording starts.
|
||||
/// </summary>
|
||||
private void MicrophoneTestControllerOnRecordStarted()
|
||||
{
|
||||
ConvaiLogger.DebugLog("<color=green> Record Started! </color>", ConvaiLogger.LogCategory.UI);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when recording is completed.
|
||||
/// </summary>
|
||||
private void MicrophoneTestControllerOnRecordCompleted()
|
||||
{
|
||||
ConvaiLogger.DebugLog("<color=green> Record Completed! </color>", ConvaiLogger.LogCategory.UI);
|
||||
_waveformCoroutine = StartCoroutine(DisplayWaveformUntilAudioComplete());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the audio clip playback is completed.
|
||||
/// </summary>
|
||||
private void MicrophoneTestControllerOnAudioClipCompleted()
|
||||
{
|
||||
_audioSource.clip = null;
|
||||
StopCoroutine(_waveformCoroutine);
|
||||
waveVisualizerUI.sizeDelta = _startSizeDelta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetch and display audio waves from the audio source.
|
||||
/// </summary>
|
||||
private void ShowAudioSourceAudioWaves()
|
||||
{
|
||||
_audioSource.GetSpectrumData(_clipSampleData, 0, FFTWindow.Rectangular);
|
||||
float currentAverageVolume = SampleAverageCalculator() * _waveMultiplier;
|
||||
Vector2 size = waveVisualizerUI.sizeDelta;
|
||||
size.x = currentAverageVolume;
|
||||
size.x = Mathf.Clamp(size.x, 1, 145);
|
||||
waveVisualizerUI.sizeDelta = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to display the waveform while the audio is playing.
|
||||
/// </summary>
|
||||
private IEnumerator DisplayWaveformUntilAudioComplete()
|
||||
{
|
||||
while (_audioSource.isPlaying)
|
||||
{
|
||||
ShowAudioSourceAudioWaves();
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the average volume from the sampled audio data.
|
||||
/// </summary>
|
||||
private float SampleAverageCalculator()
|
||||
{
|
||||
float sum = 0f;
|
||||
foreach (float t in _clipSampleData)
|
||||
sum += t;
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e673a03d46af90446baa34d979119284
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Convai.Scripts.Runtime.Addons;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used to control the microphone test.
|
||||
/// It requires a UIMicrophoneSettings, AudioSource, and MicrophoneInputChecker components to work.
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-100)]
|
||||
[RequireComponent(typeof(UIMicrophoneSettings), typeof(AudioSource), typeof(MicrophoneInputChecker))]
|
||||
public class MicrophoneTestController : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Constants for frequency and maximum recording time.
|
||||
/// </summary>
|
||||
private const int FREQUENCY = 44100;
|
||||
|
||||
private const int MAX_RECORD_TIME = 10;
|
||||
|
||||
/// <summary>
|
||||
/// The AudioSource component attached to this GameObject.
|
||||
/// </summary>
|
||||
private AudioSource _audioSource;
|
||||
|
||||
/// <summary>
|
||||
/// The Audio Playing Status.
|
||||
/// </summary>
|
||||
private bool _isAudioPlaying;
|
||||
|
||||
/// <summary>
|
||||
/// The Recording Status.
|
||||
/// </summary>
|
||||
private bool _isRecording;
|
||||
|
||||
/// <summary>
|
||||
/// The MicrophoneInputChecker component attached to this GameObject.
|
||||
/// </summary>
|
||||
private MicrophoneInputChecker _microphoneInputChecker;
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine reference for stopping the recording.
|
||||
/// </summary>
|
||||
private Coroutine _recordTimeCounterCoroutine;
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected microphone device.
|
||||
/// </summary>
|
||||
private string _selectedDevice;
|
||||
|
||||
/// <summary>
|
||||
/// The UIMicrophoneSettings component attached to this GameObject.
|
||||
/// </summary>
|
||||
private UIMicrophoneSettings _uiMicrophoneSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the MicrophoneTestController.
|
||||
/// </summary>
|
||||
public static MicrophoneTestController Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the components on Awake.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
// Ensure there's only one instance of MicrophoneTestController
|
||||
if (Instance != null)
|
||||
{
|
||||
ConvaiLogger.DebugLog("<color=red> There's More Than One MicrophoneTestController </color> " + transform + " - " + Instance, ConvaiLogger.LogCategory.UI);
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
|
||||
// Get the components
|
||||
_uiMicrophoneSettings = GetComponent<UIMicrophoneSettings>();
|
||||
_audioSource = GetComponent<AudioSource>();
|
||||
_microphoneInputChecker = GetComponent<MicrophoneInputChecker>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add click event listeners for record and stop buttons on Start.
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
_uiMicrophoneSettings.GetRecordControllerButton().onClick.AddListener(RecordController);
|
||||
_selectedDevice = MicrophoneManager.Instance.SelectedMicrophoneName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Events for different states of recording.
|
||||
/// </summary>
|
||||
public event Action OnRecordStarted;
|
||||
|
||||
public event Action OnRecordCompleted;
|
||||
public event Action OnAudioClipCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the selected microphone device is working.
|
||||
/// </summary>
|
||||
public void CheckMicrophoneDeviceWorkingStatus(AudioClip audioClip)
|
||||
{
|
||||
_microphoneInputChecker.IsMicrophoneWorking(audioClip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the Record/Stop button click.
|
||||
/// </summary>
|
||||
private void RecordController()
|
||||
{
|
||||
if (_isRecording)
|
||||
StopMicrophoneTestRecording();
|
||||
else if (!_isAudioPlaying) StartMicrophoneTestRecording();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start recording from the selected microphone device.
|
||||
/// </summary>
|
||||
private void StartMicrophoneTestRecording()
|
||||
{
|
||||
if (_uiMicrophoneSettings.GetMicrophoneSelectDropdown().options.Count > 0)
|
||||
{
|
||||
_selectedDevice = MicrophoneManager.Instance.SelectedMicrophoneName;
|
||||
AudioClip recordedClip = Microphone.Start(_selectedDevice, false, MAX_RECORD_TIME, FREQUENCY);
|
||||
_audioSource.clip = recordedClip;
|
||||
CheckMicrophoneDeviceWorkingStatus(recordedClip);
|
||||
|
||||
OnRecordStarted?.Invoke();
|
||||
_isRecording = true;
|
||||
|
||||
_recordTimeCounterCoroutine = StartCoroutine(RecordTimeCounter());
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiLogger.Error("No microphone devices found.", ConvaiLogger.LogCategory.UI);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop recording from the selected microphone device.
|
||||
/// </summary>
|
||||
private void StopMicrophoneTestRecording()
|
||||
{
|
||||
if (Microphone.IsRecording(_selectedDevice))
|
||||
{
|
||||
StopCoroutine(_recordTimeCounterCoroutine);
|
||||
int position = Microphone.GetPosition(_selectedDevice);
|
||||
|
||||
Microphone.End(_selectedDevice);
|
||||
|
||||
TrimAudio(position);
|
||||
_audioSource.Play();
|
||||
_isAudioPlaying = true;
|
||||
|
||||
OnRecordCompleted?.Invoke();
|
||||
_isRecording = false;
|
||||
|
||||
StartCoroutine(AudioClipTimeCounter(_audioSource.clip.length));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to automatically stop recording after MAX_RECORD_TIME.
|
||||
/// </summary>
|
||||
private IEnumerator RecordTimeCounter()
|
||||
{
|
||||
yield return new WaitForSeconds(MAX_RECORD_TIME);
|
||||
StopMicrophoneTestRecording();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to invoke OnAudioClipCompleted after the audio clip duration.
|
||||
/// </summary>
|
||||
private IEnumerator AudioClipTimeCounter(float length)
|
||||
{
|
||||
yield return new WaitForSeconds(length);
|
||||
|
||||
OnAudioClipCompleted?.Invoke();
|
||||
_isAudioPlaying = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trim the audio based on the last recorded position.
|
||||
/// </summary>
|
||||
private void TrimAudio(int micRecordLastPosition)
|
||||
{
|
||||
if (_audioSource.clip == null)
|
||||
{
|
||||
ConvaiLogger.Error("AudioSource clip is null.", ConvaiLogger.LogCategory.UI);
|
||||
return;
|
||||
}
|
||||
|
||||
if (micRecordLastPosition <= 0)
|
||||
{
|
||||
ConvaiLogger.Error("Microphone position is zero or negative. Cannot trim audio.", ConvaiLogger.LogCategory.UI);
|
||||
return;
|
||||
}
|
||||
|
||||
AudioClip tempAudioClip = _audioSource.clip;
|
||||
int channels = tempAudioClip.channels;
|
||||
int position = micRecordLastPosition;
|
||||
float[] samplesArray = new float[position * channels];
|
||||
tempAudioClip.GetData(samplesArray, 0);
|
||||
AudioClip newClip = AudioClip.Create("RecordedSound", position * channels, channels, FREQUENCY, false);
|
||||
newClip.SetData(samplesArray, 0);
|
||||
_audioSource.clip = newClip;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 518be0891425e7543bee2e69fb5ffc86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,34 @@
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
// Handles the activation status of the transcript UI based on Settings Panel Toggle.
|
||||
public class TranscriptUIActiveStatusHandler : ActiveStatusHandler
|
||||
{
|
||||
protected override void UISaveLoadSystem_OnLoad()
|
||||
{
|
||||
// Retrieve the saved notification system activation status.
|
||||
bool newValue = UISaveLoadSystem.Instance.TranscriptUIActiveStatus;
|
||||
|
||||
// Update the UI and internal status based on the loaded value.
|
||||
OnStatusChange(newValue);
|
||||
_activeStatusToggle.isOn = newValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the activation status of the notification system.
|
||||
/// </summary>
|
||||
/// <param name="value"> The new activation status. </param>
|
||||
public override void OnStatusChange(bool value)
|
||||
{
|
||||
// Save the current transcript UI activation status.
|
||||
UISaveLoadSystem.Instance.TranscriptUIActiveStatus = _activeStatusToggle.isOn;
|
||||
|
||||
IChatUI currentImplementation = ConvaiChatUIHandler.Instance.GetCurrentUI();
|
||||
|
||||
// Activate or Deactivate Toggle Values based on the toggle value
|
||||
if (value)
|
||||
currentImplementation.ActivateUI();
|
||||
else
|
||||
currentImplementation.DeactivateUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02b1fa8555b04926b453b3ef98d8026f
|
||||
timeCreated: 1718274575
|
||||
@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Convai.Scripts.Runtime.Core;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used to control the appearance settings of the UI.
|
||||
/// It requires a UIMicrophoneSettings, AudioSource, and MicrophoneInputChecker components to work.
|
||||
/// </summary>
|
||||
public class UIAppearanceSettings : MonoBehaviour
|
||||
{
|
||||
public static Action<bool> UIStatusChange;
|
||||
|
||||
[Header("Settings Panel Animation Values")] [SerializeField] [Tooltip("Duration of the appearance preview animation.")]
|
||||
private float _appearancePreviewDuration = 1f;
|
||||
|
||||
[Header("References")] [Tooltip("Dropdown for selecting the appearance.")] [SerializeField]
|
||||
private TMP_Dropdown _appearanceDropdown;
|
||||
|
||||
/// <summary>
|
||||
/// FadeCanvas for handling appearance transitions.
|
||||
/// </summary>
|
||||
[SerializeField] private FadeCanvas _fadeCanvas;
|
||||
|
||||
/// <summary>
|
||||
/// Duration for fade in animation.
|
||||
/// </summary>
|
||||
private readonly float _fadeInDuration = 0.3f;
|
||||
|
||||
/// <summary>
|
||||
/// Duration for fade out animation.
|
||||
/// </summary>
|
||||
private readonly float _fadeOutDuration = 0.45f;
|
||||
|
||||
/// <summary>
|
||||
/// Currently active appearance.
|
||||
/// </summary>
|
||||
private IChatUI _currentActiveAppearance;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the UISettingsPanel for coordinating with appearance changes.
|
||||
/// </summary>
|
||||
private UISettingsPanel _uiSettingsPanel;
|
||||
|
||||
/// <summary>
|
||||
/// Action notifying the change of appearance.
|
||||
/// </summary>
|
||||
public Action OnAppearanceChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initialization when the script is loaded.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
// Clear existing options in the dropdown.
|
||||
_appearanceDropdown.ClearOptions();
|
||||
|
||||
// Get reference to the UISettingsPanel.
|
||||
_uiSettingsPanel = GetComponent<UISettingsPanel>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
InitializeAppearanceDropdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to events when this component is enabled.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
ConvaiNPCManager.Instance.OnActiveNPCChanged += ConvaiNPCManager_OnActiveNPCChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from events when this component is disabled.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
ConvaiNPCManager.Instance.OnActiveNPCChanged -= ConvaiNPCManager_OnActiveNPCChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the appearance dropdown with available options.
|
||||
/// </summary>
|
||||
private void InitializeAppearanceDropdown()
|
||||
{
|
||||
List<string> appearanceNames = new();
|
||||
foreach (KeyValuePair<ConvaiChatUIHandler.UIType, IChatUI> appearance in ConvaiChatUIHandler.Instance
|
||||
.GetUIAppearances) appearanceNames.Add(appearance.Key.ToString());
|
||||
|
||||
_appearanceDropdown.AddOptions(appearanceNames);
|
||||
int value = (int)UISaveLoadSystem.Instance.UIType;
|
||||
_appearanceDropdown.value = value;
|
||||
// Subscribe to the appearance change event.
|
||||
_appearanceDropdown.onValueChanged.AddListener(ChangeAppearance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for when the active NPC changes.
|
||||
/// </summary>
|
||||
private void ConvaiNPCManager_OnActiveNPCChanged(ConvaiNPC newActiveNPC)
|
||||
{
|
||||
if (newActiveNPC != null)
|
||||
{
|
||||
if (!UISaveLoadSystem.Instance.TranscriptUIActiveStatus) return;
|
||||
_fadeCanvas.StartFadeIn(_currentActiveAppearance.GetCanvasGroup(), _fadeInDuration);
|
||||
UIStatusChange?.Invoke(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_fadeCanvas.OnCurrentFadeCompleted += FadeOutChat;
|
||||
_fadeCanvas.StartFadeOut(_currentActiveAppearance.GetCanvasGroup(), _fadeOutDuration);
|
||||
}
|
||||
}
|
||||
|
||||
private void FadeOutChat()
|
||||
{
|
||||
UIStatusChange?.Invoke(false);
|
||||
_fadeCanvas.OnCurrentFadeCompleted -= FadeOutChat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when saved data is loaded.
|
||||
/// </summary>
|
||||
private void UISaveLoadSystem_OnLoad()
|
||||
{
|
||||
// Set the current active appearance based on loaded data.
|
||||
_currentActiveAppearance = ConvaiChatUIHandler.Instance.GetCurrentUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when data is saved.
|
||||
/// </summary>
|
||||
private void UISaveLoadSystem_OnSave()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for appearance change.
|
||||
/// </summary>
|
||||
private void ChangeAppearance(int selectedOptionNumber)
|
||||
{
|
||||
// Initiate Settings Panel transition.
|
||||
_uiSettingsPanel.FadeOutFadeinWithGap(_fadeInDuration, _fadeOutDuration, _appearancePreviewDuration);
|
||||
|
||||
// Deactivate the current appearance.
|
||||
_currentActiveAppearance.DeactivateUI();
|
||||
|
||||
ConvaiChatUIHandler.UIType newUIType = (ConvaiChatUIHandler.UIType)_appearanceDropdown.value;
|
||||
ConvaiChatUIHandler.Instance.SetUIType(newUIType);
|
||||
_currentActiveAppearance = ConvaiChatUIHandler.Instance.GetChatUIByUIType(newUIType);
|
||||
|
||||
// Update the current active appearance reference.
|
||||
_currentActiveAppearance.ActivateUI();
|
||||
|
||||
// Initiate appearance transition.
|
||||
FadeInFadeOutWithGap();
|
||||
|
||||
OnAppearanceChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fade out the currently active appearance.
|
||||
/// </summary>
|
||||
public void FadeOutCurrentAppearance()
|
||||
{
|
||||
_fadeCanvas.StartFadeOut(_currentActiveAppearance.GetCanvasGroup(), _fadeOutDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fade in the currently active appearance.
|
||||
/// </summary>
|
||||
public void FadeInCurrentAppearance()
|
||||
{
|
||||
_fadeCanvas.StartFadeIn(_currentActiveAppearance.GetCanvasGroup(), _fadeInDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a fade-in, fade-out transition with a gap in between.
|
||||
/// </summary>
|
||||
private void FadeInFadeOutWithGap()
|
||||
{
|
||||
_fadeCanvas.StartFadeInFadeOutWithGap(_currentActiveAppearance.GetCanvasGroup(), _fadeInDuration,
|
||||
_fadeOutDuration, _appearancePreviewDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the currently active appearance.
|
||||
/// </summary>
|
||||
public IChatUI GetCurrentActiveAppearance()
|
||||
{
|
||||
return _currentActiveAppearance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e22ed8432ed28324891d428b6a8fa2dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,73 @@
|
||||
using System.Collections;
|
||||
using Convai.Scripts.Runtime.PlayerStats;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used to manage the display name settings in the UI.
|
||||
/// </summary>
|
||||
public class UIDisplayNameSettings : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference to the TextMeshPro input field for entering/displaying the display name.
|
||||
/// </summary>
|
||||
[SerializeField] private TMP_InputField playerNameInputField;
|
||||
|
||||
private ConvaiPlayerDataSO _convaiPlayerData;
|
||||
private bool _hasPlayerNameBeenSaved;
|
||||
private string _originalPlayerName;
|
||||
private UISettingsPanel _uiSettingsPanel;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_originalPlayerName = string.Empty;
|
||||
_uiSettingsPanel = GetComponentInParent<UISettingsPanel>();
|
||||
|
||||
_uiSettingsPanel.SaveChangesButton.onClick.AddListener(() =>
|
||||
{
|
||||
_hasPlayerNameBeenSaved = true;
|
||||
_originalPlayerName = string.Empty;
|
||||
});
|
||||
|
||||
_uiSettingsPanel.SettingsPanelExitButton.onClick.AddListener(() =>
|
||||
{
|
||||
if (_hasPlayerNameBeenSaved || string.IsNullOrEmpty(_originalPlayerName)) return;
|
||||
playerNameInputField.text = _originalPlayerName;
|
||||
_originalPlayerName = string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
if (ConvaiPlayerDataSO.GetPlayerData(out _convaiPlayerData))
|
||||
playerNameInputField.text = string.IsNullOrEmpty(_convaiPlayerData.PlayerName) ? _convaiPlayerData.DefaultPlayerName : _convaiPlayerData.PlayerName;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
UISaveLoadSystem.Instance.OnSave += UISaveLoadSystem_OnSave;
|
||||
playerNameInputField.onSelect.AddListener(PlayerNameInputField_OnSelect);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
UISaveLoadSystem.Instance.OnSave -= UISaveLoadSystem_OnSave;
|
||||
playerNameInputField.onSelect.RemoveListener(PlayerNameInputField_OnSelect);
|
||||
}
|
||||
|
||||
private void PlayerNameInputField_OnSelect(string value)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_originalPlayerName)) return;
|
||||
_originalPlayerName = value;
|
||||
}
|
||||
|
||||
private void UISaveLoadSystem_OnSave()
|
||||
{
|
||||
if (_convaiPlayerData == null || !_hasPlayerNameBeenSaved || string.IsNullOrEmpty(playerNameInputField.text)) return;
|
||||
_convaiPlayerData.PlayerName = playerNameInputField.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6995c132b7884398811c9d7df7806a59
|
||||
timeCreated: 1697567638
|
||||
@ -0,0 +1,254 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Convai.Scripts.Runtime.Addons;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
#if UNITY_ANDROID
|
||||
using UnityEngine.Android;
|
||||
#endif
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is used to manage the microphone settings in the UI.
|
||||
/// </summary>
|
||||
public class UIMicrophoneSettings : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Dropdown to select the microphone device to use.")] [SerializeField]
|
||||
private TMP_Dropdown _microphoneSelectDropdown;
|
||||
|
||||
[Tooltip("Button to control the recording.")] [SerializeField]
|
||||
private Button _recordControllerButton;
|
||||
|
||||
[Tooltip("Text to display the status of the recording system.")] [SerializeField]
|
||||
private TextMeshProUGUI _recordSystemStatusText;
|
||||
|
||||
/// <summary>
|
||||
/// Image component of the recording control button.
|
||||
/// </summary>
|
||||
private Image _buttonImage;
|
||||
|
||||
/// <summary>
|
||||
/// Text component of the recording control button.
|
||||
/// </summary>
|
||||
private TextMeshProUGUI _buttonText;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the MicrophoneManager to subscribe to its events.
|
||||
/// </summary>
|
||||
private MicrophoneTestController _microphoneTestController;
|
||||
|
||||
/// <summary>
|
||||
/// Index of the selected microphone device.
|
||||
/// </summary>
|
||||
private int _selectedMicrophoneDeviceNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize references.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
_microphoneTestController = GetComponent<MicrophoneTestController>();
|
||||
_buttonImage = _recordControllerButton.GetComponent<Image>();
|
||||
_buttonText = _recordControllerButton.GetComponentInChildren<TextMeshProUGUI>();
|
||||
|
||||
RequestMicrophonePermissions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to events when this component is enabled.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
_microphoneTestController.OnRecordStarted += MicrophoneTestControllerOnRecordStarted;
|
||||
_microphoneTestController.OnRecordCompleted += MicrophoneTestControllerOnRecordCompleted;
|
||||
_microphoneTestController.OnAudioClipCompleted += MicrophoneTestControllerOnAudioClipCompleted;
|
||||
|
||||
UISaveLoadSystem.Instance.OnLoad += UISaveLoadSystem_OnLoad;
|
||||
UISaveLoadSystem.Instance.OnSave += UISaveLoadSystem_OnSave;
|
||||
|
||||
#if !UNITY_ANDROID && !UNITY_IOS
|
||||
InitializeMicrophoneDevices();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from events when this component is disabled.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
_microphoneTestController.OnRecordStarted -= MicrophoneTestControllerOnRecordStarted;
|
||||
_microphoneTestController.OnRecordCompleted -= MicrophoneTestControllerOnRecordCompleted;
|
||||
_microphoneTestController.OnAudioClipCompleted -= MicrophoneTestControllerOnAudioClipCompleted;
|
||||
UISaveLoadSystem.Instance.OnLoad -= UISaveLoadSystem_OnLoad;
|
||||
UISaveLoadSystem.Instance.OnSave -= UISaveLoadSystem_OnSave;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request Microphone permissions on Android or iOS.
|
||||
/// </summary>
|
||||
private void RequestMicrophonePermissions()
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (Permission.HasUserAuthorizedPermission(Permission.Microphone))
|
||||
{
|
||||
// Initialize Microphone devices if permission is already granted.
|
||||
InitializeMicrophoneDevices();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Request Microphone permission with callback
|
||||
PermissionCallbacks callbacks = new();
|
||||
callbacks.PermissionGranted += PermissionCallbacks_PermissionGranted;
|
||||
callbacks.PermissionDenied += s => ShowNoMicrophoneDetectedNotification();
|
||||
callbacks.PermissionDeniedAndDontAskAgain += s => ShowNoMicrophoneDetectedNotification();
|
||||
Permission.RequestUserPermission(Permission.Microphone, callbacks);
|
||||
}
|
||||
|
||||
#elif UNITY_IOS
|
||||
// Check Microphone permission on iOS and start coroutine to request permission if not granted.
|
||||
StartCoroutine(iOSTryToAccessMicrophone());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show notification when no microphone is detected.
|
||||
/// </summary>
|
||||
private void ShowNoMicrophoneDetectedNotification()
|
||||
{
|
||||
NotificationSystemHandler.Instance.NotificationRequest(NotificationType.NoMicrophoneDetected);
|
||||
_recordSystemStatusText.text = "No Microphone Detected...";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the dropdown with available microphone devices after obtaining permission.
|
||||
/// </summary>
|
||||
private void InitializeMicrophoneDevices()
|
||||
{
|
||||
_microphoneSelectDropdown.ClearOptions();
|
||||
_microphoneSelectDropdown.AddOptions(new List<string>(Microphone.devices));
|
||||
_microphoneSelectDropdown.onValueChanged.AddListener(ChangeSelectedDevice);
|
||||
_recordSystemStatusText.text = "Waiting For Record...";
|
||||
|
||||
// Checking if system has at-least one microphone to record the audio
|
||||
if (!MicrophoneManager.Instance.HasAnyMicrophoneDevices()) ShowNoMicrophoneDetectedNotification();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to check and request Microphone permission on iOS.
|
||||
/// </summary>
|
||||
private IEnumerator iOSTryToAccessMicrophone()
|
||||
{
|
||||
if (Application.HasUserAuthorization(UserAuthorization.Microphone))
|
||||
{
|
||||
InitializeMicrophoneDevices();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return Application.RequestUserAuthorization(UserAuthorization.Microphone);
|
||||
|
||||
if (Application.HasUserAuthorization(UserAuthorization.Microphone))
|
||||
InitializeMicrophoneDevices();
|
||||
else
|
||||
ShowNoMicrophoneDetectedNotification();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when Microphone permission is granted on Android.
|
||||
/// </summary>
|
||||
/// <param name="obj">Permission string</param>
|
||||
private void PermissionCallbacks_PermissionGranted(string obj)
|
||||
{
|
||||
InitializeMicrophoneDevices();
|
||||
ConvaiLogger.Info("Microphone Permission Granted.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when the selected microphone device is changed.
|
||||
/// </summary>
|
||||
private void ChangeSelectedDevice(int selectedDeviceNumber)
|
||||
{
|
||||
_selectedMicrophoneDeviceNumber = selectedDeviceNumber;
|
||||
MicrophoneManager.Instance.SetSelectedMicrophoneIndex(selectedDeviceNumber);
|
||||
UISaveLoadSystem.Instance.SelectedMicrophoneDeviceNumber = _selectedMicrophoneDeviceNumber;
|
||||
ConvaiLogger.Info("Microphone Device Updated.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when saved data is loaded.
|
||||
/// </summary>
|
||||
private void UISaveLoadSystem_OnLoad()
|
||||
{
|
||||
_microphoneSelectDropdown.value = UISaveLoadSystem.Instance.SelectedMicrophoneDeviceNumber;
|
||||
ConvaiLogger.Info("Loaded Microphone Device. ", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when data is saved.
|
||||
/// </summary>
|
||||
private void UISaveLoadSystem_OnSave()
|
||||
{
|
||||
UISaveLoadSystem.Instance.SelectedMicrophoneDeviceNumber = _microphoneSelectDropdown.value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when a recording is started.
|
||||
/// </summary>
|
||||
private void MicrophoneTestControllerOnRecordStarted()
|
||||
{
|
||||
_recordSystemStatusText.text = "Recording...";
|
||||
_buttonImage.color = Color.red;
|
||||
_buttonText.text = "Stop";
|
||||
_buttonText.color = new Color(1, 1, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when a recording is completed.
|
||||
/// </summary>
|
||||
private void MicrophoneTestControllerOnRecordCompleted()
|
||||
{
|
||||
_recordSystemStatusText.text = "Playing...";
|
||||
_buttonImage.color = Color.green;
|
||||
_buttonText.text = "Rec";
|
||||
_buttonText.color = new Color(0.14f, 0.14f, 0.14f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler when the audio clip playback is completed.
|
||||
/// </summary>
|
||||
private void MicrophoneTestControllerOnAudioClipCompleted()
|
||||
{
|
||||
_recordSystemStatusText.text = "Waiting For Record...";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the microphone selection dropdown object.
|
||||
/// </summary>
|
||||
public TMP_Dropdown GetMicrophoneSelectDropdown()
|
||||
{
|
||||
return _microphoneSelectDropdown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the record control button object.
|
||||
/// </summary>
|
||||
public Button GetRecordControllerButton()
|
||||
{
|
||||
return _recordControllerButton;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the selected microphone device name.
|
||||
/// </summary>
|
||||
public string GetSelectedMicrophoneDeviceName()
|
||||
{
|
||||
if (_selectedMicrophoneDeviceNumber < 0 || _selectedMicrophoneDeviceNumber >= _microphoneSelectDropdown.options.Count)
|
||||
return string.Empty;
|
||||
|
||||
return _microphoneSelectDropdown.options[_selectedMicrophoneDeviceNumber].text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f5afe5603f66984b88bd5f54bbd762e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// The UISaveLoadSystem class manages the storage and retrieval of persistent data using PlayerPrefs.
|
||||
/// It also ensures that there is only one instance of the class.
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-110)]
|
||||
public class UISaveLoadSystem : MonoBehaviour
|
||||
{
|
||||
private const string SELECTED_MICROPHONE_DEVICE_NUMBER = "SelectedMicrophoneDeviceNumber";
|
||||
private const string UI_TYPE = "UIType";
|
||||
private const string NOTIFICATION_SYSTEM_ACTIVE_STATUS = "NotificationSystemActiveStatus";
|
||||
private const string TRANSCRIPT_UI_ACTIVE_STATUS = "TranscriptUIActiveStatus";
|
||||
|
||||
/// <summary>
|
||||
/// Stores the UI type.
|
||||
/// </summary>
|
||||
[HideInInspector] public ConvaiChatUIHandler.UIType UIType;
|
||||
|
||||
/// <summary>
|
||||
/// Events triggered during load and save operations
|
||||
/// </summary>
|
||||
public Action OnLoad; // Event triggered after loading values from PlayerPrefs.
|
||||
|
||||
public Action OnSave; // Event triggered before saving values to PlayerPrefs.
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the UISaveLoadSystem
|
||||
/// </summary>
|
||||
public static UISaveLoadSystem Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores the selected microphone device number.
|
||||
/// </summary>
|
||||
public int SelectedMicrophoneDeviceNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores the status of the notification system.
|
||||
/// </summary>
|
||||
public bool NotificationSystemActiveStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores the status of the transcript UI.
|
||||
/// </summary>
|
||||
public bool TranscriptUIActiveStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ensure there is only one instance of UISaveLoadSystem
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
// Log a warning and destroy the duplicate instance
|
||||
ConvaiLogger.Warn("There's More Than One UISaveLoadSystem", ConvaiLogger.LogCategory.UI);
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the singleton instance
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start is called before the first frame update
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
// Load values from PlayerPrefs at the start of the application
|
||||
LoadValues();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the script instance is being destroyed
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Save values to PlayerPrefs when the script is destroyed (e.g., when the scene changes)
|
||||
SaveValues();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load values from PlayerPrefs
|
||||
/// </summary>
|
||||
private void LoadValues()
|
||||
{
|
||||
// Retrieve selected microphone device number from PlayerPrefs, use 0 as default if not found
|
||||
SelectedMicrophoneDeviceNumber = PlayerPrefs.GetInt(SELECTED_MICROPHONE_DEVICE_NUMBER, 0);
|
||||
|
||||
// Retrieve UI type from PlayerPrefs, use 0 as default if not found
|
||||
UIType = (ConvaiChatUIHandler.UIType)PlayerPrefs.GetInt(UI_TYPE, 0);
|
||||
|
||||
// Retrieve notification system status from PlayerPrefs, default to true if not found
|
||||
NotificationSystemActiveStatus = PlayerPrefs.GetInt(NOTIFICATION_SYSTEM_ACTIVE_STATUS, 1) == 1;
|
||||
|
||||
// Retrieve transcript UI status from PlayerPrefs, default to true if not found
|
||||
TranscriptUIActiveStatus = PlayerPrefs.GetInt(TRANSCRIPT_UI_ACTIVE_STATUS, 1) == 1;
|
||||
|
||||
// Invoke the OnLoad event to notify listeners that loading has completed
|
||||
OnLoad?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save values to PlayerPrefs
|
||||
/// </summary>
|
||||
public void SaveValues()
|
||||
{
|
||||
// Invoke the OnSave event to notify listeners that saving is about to occur
|
||||
OnSave?.Invoke();
|
||||
|
||||
// Save selected microphone device number to PlayerPrefs
|
||||
PlayerPrefs.SetInt(SELECTED_MICROPHONE_DEVICE_NUMBER, SelectedMicrophoneDeviceNumber);
|
||||
|
||||
// Save UI type to PlayerPrefs as an integer representation
|
||||
PlayerPrefs.SetInt(UI_TYPE, (int)UIType);
|
||||
|
||||
// Save notification system status to PlayerPrefs as 1 or 0 (true or false)
|
||||
PlayerPrefs.SetInt(NOTIFICATION_SYSTEM_ACTIVE_STATUS, NotificationSystemActiveStatus ? 1 : 0);
|
||||
|
||||
// Save transcript UI status to PlayerPrefs as 1 or 0 (true or false)
|
||||
PlayerPrefs.SetInt(TRANSCRIPT_UI_ACTIVE_STATUS, TranscriptUIActiveStatus ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6b5b7a24f2267c4893e5ca390ff8a7f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,161 @@
|
||||
using Convai.Scripts.Runtime.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// The UISettingsPanel class manages the settings panel UI, including animations and user interactions.
|
||||
/// </summary>
|
||||
public class UISettingsPanel : MonoBehaviour
|
||||
{
|
||||
[field: Header("References")]
|
||||
[field: SerializeField]
|
||||
public Button SaveChangesButton { get; private set; }
|
||||
|
||||
[field: SerializeField] public Button SettingsPanelExitButton { get; private set; }
|
||||
[SerializeField] private CanvasGroup _panelCanvasGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Animation durations for fade in, fade out, and gap between animations
|
||||
/// </summary>
|
||||
[Header("Settings Panel Animation Values")] [SerializeField] [Tooltip("Duration for fade in animation")]
|
||||
private float _fadeInDuration = 0.35f;
|
||||
|
||||
[SerializeField] [Tooltip("Duration for fade out animation")]
|
||||
private float _fadeOutDuration = 0.35f;
|
||||
|
||||
private FadeCanvas _fadeCanvas;
|
||||
|
||||
/// <summary>
|
||||
/// References to other scripts and components
|
||||
/// </summary>
|
||||
private UIAppearanceSettings _uiAppearanceSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize references to other scripts and components and populate the dropdown with available microphone devices.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
// Initialize references to other scripts and components
|
||||
_fadeCanvas = GetComponent<FadeCanvas>();
|
||||
_uiAppearanceSettings = GetComponent<UIAppearanceSettings>();
|
||||
|
||||
// Attach event listeners to UI buttons
|
||||
SaveChangesButton.onClick.AddListener(SaveChanges);
|
||||
SettingsPanelExitButton.onClick.AddListener(delegate { ToggleSettingsPanel(false); });
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ConvaiInputManager.Instance.toggleSettings += ToggleSettings;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ConvaiInputManager.Instance.toggleSettings -= ToggleSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update is called once per frame
|
||||
/// </summary>
|
||||
private void ToggleSettings()
|
||||
{
|
||||
if (_panelCanvasGroup.alpha == 0)
|
||||
{
|
||||
ToggleSettingsPanel(true);
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ToggleSettingsPanel(false);
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
Cursor.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle the settings panel on or off
|
||||
/// </summary>
|
||||
public void ToggleSettingsPanel(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
// Fade in the settings panel
|
||||
ActivatePanel();
|
||||
FadeInSettingsPanel();
|
||||
|
||||
// Check if the alpha of the appearance canvas is at its maximum
|
||||
const float MAX_ALPHA = 1;
|
||||
if (_uiAppearanceSettings.GetCurrentActiveAppearance().GetCanvasGroup().alpha >= MAX_ALPHA)
|
||||
// If true, fade out the current appearance canvas
|
||||
_uiAppearanceSettings.FadeOutCurrentAppearance();
|
||||
|
||||
// Set the cursor lock state to none
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fade out the settings panel
|
||||
_fadeCanvas.OnCurrentFadeCompleted += DeactivatePanel;
|
||||
FadeOutSettingsPanel();
|
||||
|
||||
// Check if there is an active Convai NPC
|
||||
if (ConvaiNPCManager.Instance.GetActiveConvaiNPC() != null)
|
||||
// If true, fade in the current appearance canvas
|
||||
_uiAppearanceSettings.FadeInCurrentAppearance();
|
||||
|
||||
// Set the cursor lock state to locked
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
|
||||
// Save values when the settings panel is closed
|
||||
UISaveLoadSystem.Instance.SaveValues();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save changes and close the settings panel
|
||||
/// </summary>
|
||||
private void SaveChanges()
|
||||
{
|
||||
UISaveLoadSystem.Instance.SaveValues();
|
||||
ToggleSettingsPanel(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger fade in animation for the settings panel
|
||||
/// </summary>
|
||||
private void FadeInSettingsPanel()
|
||||
{
|
||||
_fadeCanvas.StartFadeIn(_panelCanvasGroup, _fadeInDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger fade out animation for the settings panel
|
||||
/// </summary>
|
||||
private void FadeOutSettingsPanel()
|
||||
{
|
||||
_fadeCanvas.StartFadeOut(_panelCanvasGroup, _fadeOutDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger fade out and fade in animation with a gap in between
|
||||
/// </summary>
|
||||
public void FadeOutFadeinWithGap(float fadeInDuration, float fadeOutDuration, float previewDuration)
|
||||
{
|
||||
_fadeCanvas.StartFadeOutFadeInWithGap(_panelCanvasGroup, fadeInDuration, fadeOutDuration, previewDuration);
|
||||
}
|
||||
|
||||
private void DeactivatePanel()
|
||||
{
|
||||
_panelCanvasGroup.gameObject.SetActive(false);
|
||||
_fadeCanvas.OnCurrentFadeCompleted -= DeactivatePanel;
|
||||
}
|
||||
|
||||
private void ActivatePanel()
|
||||
{
|
||||
_panelCanvasGroup.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2c69b63f88551e49990c3b9f70f833f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
public class UISettingsPanelOpenButton : MonoBehaviour
|
||||
{
|
||||
private Button _openButton;
|
||||
private UISettingsPanel _uiSettingsPanel;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_uiSettingsPanel = FindObjectOfType<UISettingsPanel>();
|
||||
_openButton = GetComponent<Button>();
|
||||
|
||||
_openButton.onClick.AddListener(OpenSettingsPanel);
|
||||
}
|
||||
|
||||
private void OpenSettingsPanel()
|
||||
{
|
||||
_uiSettingsPanel.ToggleSettingsPanel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdbf93e7ba3ee774b9258ae70cdb88ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Unity/Assets/Convai/Scripts/Runtime/UI/TalkButton.meta
Normal file
8
Unity/Assets/Convai/Scripts/Runtime/UI/TalkButton.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd97eaeeeb2d6be46bd6e4efcf008252
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,138 @@
|
||||
using Convai.Scripts.Runtime.Core;
|
||||
using Convai.Scripts.Runtime.LoggerSystem;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
public class ConvaiTalkButtonHandler : Button
|
||||
{
|
||||
private const float NORMAL_ALPHA = 1f; // The alpha when button is not pressed
|
||||
private const float PRESSED_ALPHA = 0.5f; // The alpha when button is pressed
|
||||
private ConvaiNPC _currentActiveNPC;
|
||||
private bool _subscribed;
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
if (ConvaiNPCManager.Instance != null)
|
||||
{
|
||||
ConvaiNPCManager.Instance.OnActiveNPCChanged += OnActiveNPCChangedHandler;
|
||||
ConvaiLogger.Info("Listening to OnActiveNPCChanged event.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiLogger.Warn("Instance of ConvaiNPCManager is not yet initialized.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
// Check if NPC Manager instance is available before subscribing
|
||||
ConvaiNPCManager npcManager = ConvaiNPCManager.Instance;
|
||||
if (npcManager != null)
|
||||
{
|
||||
npcManager.OnActiveNPCChanged += OnActiveNPCChangedHandler;
|
||||
_currentActiveNPC = npcManager.GetActiveConvaiNPC();
|
||||
if (!_subscribed)
|
||||
{
|
||||
_subscribed = true;
|
||||
ConvaiLogger.Info("Subscribed to OnActiveNPCChanged event.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiLogger.Warn("NPC Manager instance is not available during enabling.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
// Always make sure to unsubscribe from events when the object is disabled
|
||||
ConvaiNPCManager npcManager = ConvaiNPCManager.Instance;
|
||||
if (npcManager != null)
|
||||
{
|
||||
npcManager.OnActiveNPCChanged -= OnActiveNPCChangedHandler;
|
||||
if (_subscribed)
|
||||
{
|
||||
_subscribed = false;
|
||||
ConvaiLogger.Info("Unsubscribed from OnActiveNPCChanged event.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
if (ConvaiNPCManager.Instance != null)
|
||||
{
|
||||
ConvaiNPCManager.Instance.OnActiveNPCChanged -= OnActiveNPCChangedHandler;
|
||||
ConvaiLogger.Info("Stopped listening to OnActiveNPCChanged event.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnActiveNPCChangedHandler(ConvaiNPC newActiveNPC)
|
||||
{
|
||||
_currentActiveNPC = newActiveNPC;
|
||||
if (_currentActiveNPC != null)
|
||||
ConvaiLogger.Info($"Active NPC has changed to: {_currentActiveNPC.name}", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
|
||||
public override void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
base.OnPointerDown(eventData);
|
||||
|
||||
ColorBlock colorBlock = colors;
|
||||
colorBlock.normalColor = new Color(colorBlock.normalColor.r, colorBlock.normalColor.g,
|
||||
colorBlock.normalColor.b,
|
||||
PRESSED_ALPHA);
|
||||
colors = colorBlock;
|
||||
|
||||
if (_currentActiveNPC != null)
|
||||
{
|
||||
// _grpcAPI.InterruptCharacterSpeech();
|
||||
_currentActiveNPC.playerInteractionManager.UpdateActionConfig();
|
||||
_currentActiveNPC.StartListening();
|
||||
IncreaseScale();
|
||||
ConvaiLogger.DebugLog($"{gameObject.name} Was Clicked.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiLogger.Warn("No active NPC found when button was pressed.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
base.OnPointerUp(eventData);
|
||||
|
||||
ColorBlock colorBlock = colors;
|
||||
colorBlock.normalColor =
|
||||
new Color(colorBlock.normalColor.r, colorBlock.normalColor.g, colorBlock.normalColor.b, NORMAL_ALPHA);
|
||||
colors = colorBlock;
|
||||
|
||||
if (_currentActiveNPC != null)
|
||||
{
|
||||
_currentActiveNPC.StopListening();
|
||||
DecreaseScale();
|
||||
ConvaiLogger.DebugLog($"{gameObject.name} Was Released.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiLogger.Warn("No active NPC found when button was released.", ConvaiLogger.LogCategory.Character);
|
||||
}
|
||||
}
|
||||
|
||||
private void IncreaseScale()
|
||||
{
|
||||
Vector3 targetScale = new(1.25f, 1.25f, 1.25f);
|
||||
transform.localScale = Vector3.Lerp(transform.localScale, targetScale, 1f);
|
||||
}
|
||||
|
||||
private void DecreaseScale()
|
||||
{
|
||||
Vector3 targetScale = new(1, 1, 1);
|
||||
transform.localScale = Vector3.Lerp(transform.localScale, targetScale, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21b93a6353a9d2240a66a6e8d47585d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Unity/Assets/Convai/Scripts/Runtime/UI/Utils.meta
Normal file
8
Unity/Assets/Convai/Scripts/Runtime/UI/Utils.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45066e1caa2676a4cb8a9fc6b4c846d9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,65 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class to handle different toggle status, including loading and saving
|
||||
/// </summary>
|
||||
public class ActiveStatusHandler : MonoBehaviour
|
||||
{
|
||||
[SerializeField] protected Toggle _activeStatusToggle;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Subscribe to the toggle's value change event.
|
||||
_activeStatusToggle.onValueChanged.AddListener(OnStatusChange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to events when this component is enabled.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from events when this component is disabled.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for when saved data is loaded.
|
||||
/// </summary>
|
||||
protected virtual void UISaveLoadSystem_OnLoad()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for when data is saved.
|
||||
/// </summary>
|
||||
protected virtual void UISaveLoadSystem_OnSave()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the activation status
|
||||
/// </summary>
|
||||
/// <param name="value"> The new activation status. </param>
|
||||
public virtual void OnStatusChange(bool value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef81b07ac96d481f954ce8ce8431b7fb
|
||||
timeCreated: 1718273073
|
||||
25
Unity/Assets/Convai/Scripts/Runtime/UI/Utils/UIUtilities.cs
Normal file
25
Unity/Assets/Convai/Scripts/Runtime/UI/Utils/UIUtilities.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using TMPro;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Convai.Scripts.Runtime.UI
|
||||
{
|
||||
public abstract class UIUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks and return if any input field is focused or not
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsAnyInputFieldFocused()
|
||||
{
|
||||
foreach (Selectable selectable in Selectable.allSelectablesArray)
|
||||
switch (selectable)
|
||||
{
|
||||
case InputField { isFocused: true }:
|
||||
case TMP_InputField { isFocused: true }:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33916f7f7f464544ba8f96a3a5c5e6f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user