1610 lines
54 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//============= Copyright (c) Ludic GmbH, All rights reserved. ==============
//
// Purpose: Part of the My Behaviour Tree Controller Code
//
//=============================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;
using MyBT;
using System.Linq;
using System.IO;
using System;
#if FMOD_AVAILABLE
using FMODUnity;
#endif
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
[CustomEditor(typeof(BTC))]
public class BehaviourTreeControllerInspector : Editor {
public override void OnInspectorGUI() {
BTC myTarget = (BTC)target;
if (GUILayout.Button("ClearObjects")) {
myTarget.ClearObjects();
SetDirty(myTarget);
}
if (GUILayout.Button("UpdateObjects")) {
myTarget.UpdateObjects();
SetDirty(myTarget);
}
DrawDefaultInspector();
}
public void SetDirty (BTC myTarget) {
EditorUtility.SetDirty(myTarget);
EditorSceneManager.MarkSceneDirty(myTarget.gameObject.scene);
}
}
#endif
public class BTC : MonoBehaviour {
static BTC _instance = null;
public static BTC Instance {
get {
if (_instance != null) {
return _instance;
}
if (_instance == null) {
_instance = Resources.FindObjectsOfTypeAll<BTC>().FirstOrDefault();
if (_instance == null) {
_instance = new GameObject("BTC", typeof(BTC)).GetComponent<BTC>();
}
}
return _instance;
}
}
public List<ComponentController> namedObjects = new List<ComponentController>();
private Dictionary<int, ComponentController> namedLookup = new();
private void OnEnable()
{
BuildLookup();
}
#region update list of objects in rooms
public void ClearObjects() {
//namedObjects = new List<ComponentController>();
namedObjects.Clear();
namedLookup.Clear();
}
/* public T GetNamedObject<T>(string objectName) where T : ComponentController {
for (int i = namedObjects.Count - 1; i >= 0; i--) {
T ngo = namedObjects[i] as T;
if (ngo != null) {
// if name equals
if (ngo.objectName == objectName) {
Debug.Log("BTC returns namedObject");
return ngo;
}
}
}
return null;
}*/
public void BuildLookup()
{
namedLookup.Clear();
foreach (var ctrl in namedObjects)
{
if (!string.IsNullOrEmpty(ctrl.objectName))
namedLookup[ctrl.objectName.GetHashCode()] = ctrl;
}
}
public T GetNamedObject<T>(string objectName) where T : ComponentController
{
if (namedLookup.TryGetValue(objectName.GetHashCode(), out var obj))
return obj as T;
return null;
}
public List<ComponentHandler> GetHandlers (string objectName) {
ComponentController ctrl = GetNamedObject<ComponentController>(objectName);
if (ctrl != null)
return ctrl.handlers.ToList();
return new List<ComponentHandler>();
}
public bool autoUpdateAllObjects = true;
public void UpdateObjects() {
// iterate all objects in scene
foreach (ComponentController namedObject in Resources.FindObjectsOfTypeAll<ComponentController>()) {
if (autoUpdateAllObjects) {
namedObject.UpdateObject();
}
// get object room id, create if not existing
// RoomData roomData = GetRoomData(namedObject.roomId);
// if (roomData != null) {
// if room contains object (name equals), overwrite
bool objectIdFound = false;
bool objectNameFound = false;
for (int i=0; i<namedObjects.Count; i++) {
if (namedObjects[i].GetInstanceID() == namedObject.GetInstanceID()) {
namedObjects[i] = namedObject;
objectIdFound = true;
// if (objectIdFound) {
// Debug.Log($"object {namedObject.name} already in list as {namedObject.GetInstanceID()}");
// }
}
// else {
// Debug.Log("UpdateObjects: match check failed "+roomData.namedObjects[i].objectName+" != "+namedObject.objectName );
// }
if (namedObjects[i].objectName == namedObject.objectName) {
objectNameFound = true;
}
}
// if object not found, append to list
if (objectNameFound && !objectIdFound) {
Debug.LogWarning($"Object with name {namedObject.name} already exists, adding a unque id");
namedObject.uniqueId = namedObject.GetInstanceID().ToString();
namedObject.UpdateObject();
}
if (!objectIdFound && !objectNameFound) {
namedObjects.Add(namedObject);
}
if (!objectNameFound && objectIdFound) {
Debug.LogWarning($"Found obsolete object {namedObject.name}, refresh data");
}
// }
// else {
// Debug.LogError("should not happen: roomData undefined");
// }
}
}
#endregion
#region general play, pause, unpause, stop
[Task]
public void Run(string objectName) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.Run(Task.getState));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.Run: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void Abort(string objectName) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.Abort(Task.getState));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.Abort: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void IsRunning(string objectName) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.All(handler => handler.IsRunning(Task.getState));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.IsRunning: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void Show(string objectName) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.Show(Task.getState));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.Show: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void Hide(string objectName) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.Hide(Task.getState));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.Hide: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void FadeIn(string objectName) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.FadeIn(Task.getState));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.FadeIn: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void FadeOut(string objectName) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.FadeOut(Task.getState));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.FadeOut: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void Set(string objectName, string key, string value) {
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.Set(Task.getState, key, value));
if (handlers.Count == 0) {
Debug.LogWarning($"BTC.Set: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void SetFloat(string objectName, string key, float value)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.SetFloat(Task.getState, key, value));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.SetFloat: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void Enable(string objectName)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.Enable(Task.getState));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.Enable: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void Disable(string objectName)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.Disable(Task.getState));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.Disable: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void SetPosition(string objectName, float newX, float newY, float newZ)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.SetPosition(Task.getState, newX, newY, newZ));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.SetPosition: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void RotateToAngle(string objectName, float newAngle)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.RotateToAngle(Task.getState, newAngle));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.RotateToAngle: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void DetectRotationAtLeast(string objectName, float minRotation)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.DetectRotationAtLeast(Task.getState, minRotation));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.DetectRotationAtLeast: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void DetectPositionAtLeast(string objectName, float minDistance)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.DetectPositionAtLeast(Task.getState, minDistance));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.DetectPositionAtLeast: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void AbortEventListener(string objectName)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.AbortEventListener(Task.getState));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.AbortEventListener: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void ListenToEvent(string objectName)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.ListenToEvent(Task.getState));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.ListenToEvent: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void StopSound(string objectName)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.StopSound(Task.getState));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.StopSound: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void LoadImage(string objectName, int index)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.LoadImage(Task.getState, index));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.LoadImage: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
[Task]
public void BlendImage(string objectName, int srcIndex, int dstIndex)
{
List<ComponentHandler> handlers = GetHandlers(objectName);
handlers.ForEach(handler => handler.BlendImage(Task.getState, srcIndex, dstIndex));
if (handlers.Count == 0)
{
Debug.LogWarning($"BTC.StopSound: no components under the name '{objectName}'");
Task.SetSucceeded();
}
}
#endregion
#region video & sound tasks
#if FMOD
[Task]
public void PlayVideoAndSound(string videoName, string soundName) {
NamedStudioEventEmitter sound = GetNamedObject<NamedStudioEventEmitter>(soundName);
NamedVideoPlayer player = GetNamedObject<NamedVideoPlayer>(videoName);
if ((player != null) && (sound != null)) {
int val = -1;
FMOD.RESULT res = sound.sound.EventInstance.getTimelinePosition(out val);
Task.log = "audio " + res.ToString() + " " + val + " video playing=" + player.video.isPlaying + " isPrepared=" + player.video.isPrepared;
// on start, play
if (Task.isStarting) {
Debug.Log("PlayVideoAndSound.isStarting : starting video='" + videoName + "' and sound='" + soundName + "'");
player.HideNow();
player.FadeInStart();
player.video.Play();
sound.sound.Play();
}
else {
// if stopped playing
if (!player.video.isPrepared) {
// not even prepared
return;
}
if ((!player.video.isPlaying) && (!sound.sound.IsPlaying())) {
player.ShowNow();
player.FadeOutStart();
Task.SetSucceeded();
}
}
if (Input.GetKeyDown(KeyCode.Backspace)) {
player.HideNow();
player.video.Stop();
sound.sound.Stop();
Task.SetSucceeded();
}
}
else {
Debug.LogError("PlayVideoAndSound: Play Error: " + roomId + " video='" + videoName + "' (" + player + ") sound='" + soundName + "' (" + sound + ")");
}
}
[Task]
public void AbortVideoAndSound(string videoName, string soundName) {
if (Task.isStarting) {
NamedVideoPlayer player = GetNamedObject<NamedVideoPlayer>(videoName);
if (player != null) {
player.FadeOutStart();
player.video.Stop();
}
NamedStudioEventEmitter sound = GetNamedObject<NamedStudioEventEmitter>(soundName);
if (sound != null) {
sound.sound.Stop();
}
}
Task.SetSucceeded();
}
#endif
#endregion
#region gameobject tasks
// This structure is used to store data for rotation tweening.
struct QuaternionData {
public Quaternion startQuaternion;
}
[Task]
public void TrackerRotatedAtLeast(string gameObjectName, float successRotation) {
foreach (NamedGameObject ngo in GetHandlers(gameObjectName)) {
// NamedGameObject ngo = GetNamedObject<NamedGameObject>(gameObjectName);
if (ngo != null) {
QuaternionData rd;
if (Task.isStarting) {
Debug.Log("TrackerRotatedAtLeast.isStarting : gameObject=" + ngo.name);
rd.startQuaternion = ngo.transform.rotation;
Task.data = rd;
}
rd = (QuaternionData)Task.data;
float angleOffset = Quaternion.Angle(rd.startQuaternion, ngo.transform.rotation);
Task.log = string.Format(angleOffset + " " + rd.startQuaternion + " " + ngo.transform.rotation);
if (angleOffset > successRotation) {
Task.SetSucceeded();
}
}
}
}
[Task]
public void TrackerRotatedAtMax(string gameObjectName, float successRotation) {
foreach (NamedGameObject ngo in GetHandlers(gameObjectName)) {
// NamedGameObject ngo = GetNamedObject<NamedGameObject>(gameObjectName);
if (ngo != null) {
QuaternionData rd;
if (Task.isStarting) {
Debug.Log("TrackerRotatedAtMax.isStarting : trackerId=" + ngo.name + " status " + Task.getState + " ");
rd.startQuaternion = ngo.transform.rotation;
Task.data = rd;
}
rd = (QuaternionData)Task.data;
float angleOffset = Quaternion.Angle(rd.startQuaternion, ngo.transform.rotation);
Task.log = string.Format(angleOffset + " " + rd.startQuaternion + " " + ngo.transform.rotation);
if (angleOffset < successRotation) {
Task.SetSucceeded();
}
}
}
}
[Task]
public void TrackerRotatedToAngle(string gameObjectName, float targetAngleFloat, float maxOffset) {
foreach (NamedGameObject ngo in GetHandlers(gameObjectName)) {
// NamedGameObject ngo = GetNamedObject<NamedGameObject>(gameObjectName);
if (ngo != null) {
Quaternion targetAngle = Quaternion.Euler(0, targetAngleFloat, 0);
float angleOffset = Quaternion.Angle(targetAngle, ngo.transform.rotation);
Task.log = string.Format(angleOffset + " < " + maxOffset + " ? " + ngo.transform.rotation.eulerAngles + " " + targetAngle.eulerAngles);
if (angleOffset < maxOffset) {
Task.SetSucceeded();
}
}
}
}
struct FloatData {
public float startFloat;
}
[Task]
public void TrackerMovedY(string gameObjectName, float successMovement) {
foreach (NamedGameObject ngo in GetHandlers(gameObjectName)) {
// NamedGameObject ngo = GetNamedObject<NamedGameObject>(gameObjectName);
if (ngo != null) {
//if ((trackerId >= 0) && (trackerId < GetRoomData(roomId).trackers.Count)) {
//TrackerDataApplier tda = GetRoomData(roomId).trackers[trackerId];
FloatData pd;
if (Task.isStarting) {
Debug.Log("TrackerRotated.isStarting : gameObjectName=" + ngo.go.name);
pd.startFloat = ngo.go.transform.position.y;
Task.data = pd;
}
pd = (FloatData)Task.data;
float positionOffset = Mathf.Abs(pd.startFloat - ngo.go.transform.position.y);
Task.log = string.Format(positionOffset + " " + pd.startFloat + " " + ngo.go.transform.position.y);
if (positionOffset > successMovement) {
Task.SetSucceeded();
}
}
}
}
struct DataFloat2 {
public float startTime;
public float startY;
}
[Task]
public void MoveObjectY(string gameObjectName, float startY, float finalY, float timer) {
foreach (NamedGameObject ngo in GetHandlers(gameObjectName)) {
// NamedGameObject ngo = GetNamedObject<NamedGameObject>(gameObjectName);
if (ngo != null) {
NodeState thisNodeState = Task.getState;
switch (thisNodeState) {
case NodeState.FirstRun:
Task.data = new DataFloat2() { startTime = Time.time, startY=ngo.transform.localPosition.y };
goto case NodeState.Running;
case NodeState.Running:
DataFloat2 data = (DataFloat2)Task.data;
float elapsed = Time.time - data.startTime;
float newY = finalY;
if (timer > 0) {
// newY = finalY;
//} else {
float interpolation = Mathf.Pow(elapsed / timer, 1f / 7f);
newY = Mathf.Lerp(startY, finalY, interpolation);
}
ngo.transform.localPosition = new Vector3(ngo.transform.localPosition.x, newY, ngo.transform.localPosition.z);
if (elapsed >= timer) {
Task.SetSucceeded();
return;
}
break;
case NodeState.Aborting:
ngo.transform.localPosition = new Vector3(ngo.transform.localPosition.x, finalY, ngo.transform.localPosition.z);
break;
}
}
else {
Debug.LogError($"MoveObjectY {gameObjectName} not found");
Task.SetFailed();
}
}
}
[Task]
public void SendGameObjectMessage(string gameObjectName, string methodName) {
foreach (NamedGameObject ngo in GetHandlers(gameObjectName)) {
// NamedGameObject ngo = GetNamedObject<NamedGameObject>(gameObjectName);
if (ngo != null) {
NodeState thisNodeState = Task.getState;
switch (thisNodeState) {
case NodeState.FirstRun:
ngo.SendMessage(methodName);
goto case NodeState.Running;
case NodeState.Running:
Task.SetSucceeded();
break;
case NodeState.Aborting:
break;
}
}
else {
Debug.LogError($"SendGameObjectMessage {gameObjectName} {methodName}");
Task.SetFailed();
}
}
}
[Task]
public void FindObjectsByTag(string tag, int minNumObjs)
{
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running))
{
GameObject[] objsWithTag;
objsWithTag = GameObject.FindGameObjectsWithTag(tag);
if (objsWithTag.Length >= minNumObjs)
{
Debug.Log($"FindObjectsByTag found {objsWithTag.Length} objects with tag {tag}");
Task.SetSucceeded();
return;
}
}
}
#endregion
#region camera or player tracking
[Task]
public bool CameraNearPosition(float x, float y, float z, float range) {
Vector3 position = new Vector3(x, y, z);
float positionOffset = (Camera.main.transform.position - position).magnitude;
Task.log = $"{positionOffset} {Camera.main.transform.position} {position}"; // string.Format(positionOffset + " " + Camera.main.transform.position + " " + position);
if (positionOffset < range) {
Task.SetSucceeded();
return true;
}
return false;
}
[Task]
public void CameraXZNearPosition(float x, float z, float range) {
Vector3 cameraPos = Camera.main.transform.position;
Vector3 position = new Vector3(x, cameraPos.y, z);
float positionOffset = (cameraPos - position).magnitude;
Task.log = $"{positionOffset} {cameraPos} {position}"; // string.Format(positionOffset + " " + cameraPos + " " + position);
if (positionOffset < range) {
Task.SetSucceeded();
}
}
[Task]
public void SetCameraRootPosition(float x, float y, float z) {
if (Task.isStarting) {
Camera.main.transform.root.position = new Vector3(x, y, z);
}
Task.SetSucceeded();
}
#endregion
#region MegapointCache
#if MEGAFIERS
[Task]
public void PlayMegaPointCache(string string gameObjectName, float startTime, bool playing, float playRate) {
NamedMegaPointCache nmpc = GetNamedObject<NamedMegaPointCache>(gameObjectName);
if (nmpc != null) {
if (Task.isStarting) {
nmpc.SetFrame(startTime);
nmpc.SetPlay(playing);
nmpc.SetPlayRate(playRate);
Task.SetSucceeded();
}
}
}
/// <summary>
/// attack: animation time to ease in the animation
/// delay: animation time at full speed
/// release: animation time to ease out the animation
///
/// attack * 2 + delay + release * 2 = animation play rate
/// </summary>
public float gletscherFrame = 0;
[Task]
public void PlayMegaPointCacheTimeline(string objectName, float start, float attack, float delay, float release, float duration) {
NamedMegaPointCache nmpc = GetNamedObject<NamedMegaPointCache>(objectName);
if (nmpc != null) {
float overallTime = Mathf.Abs(attack * 2 + delay + release * 2); // for example: 0.2 * 2 + 0.6 + 0.2 * 2 = 1.4, after 10 seconds of real time, the internal counter needs to have run 1.4 seconds
float timeScale = overallTime / duration; // for example: 1.4 / 10 = 0.14
float internalCounter = 0; // between 0 and overallTime
float animationTime = start; // final time written to animation
string state = "undefined";
// only for debugging
gletscherFrame = nmpc.GetFrame();
switch (Task.getState) {
case NodeState.FirstRun: // equals isStarting
Task.data = Time.time; // store starttime
nmpc.SetPlay(false);
nmpc.SetPlayRate(0);
Debug.Log("BTC.PlayMegaPointCacheTimeline: start "+ start + " attack " + attack + " delay " + delay + " release " + release + " duration " + duration + " timeScale " + timeScale + " frame " + Time.frameCount);
break;
case NodeState.Running:
float startTime = (float)Task.data; // for example 33
internalCounter = (Time.time - startTime) * timeScale; // for example: (33-35) * 0.14 = 0.28
if (internalCounter >= overallTime) {
//Debug.Log("BTC.PlayMegaPointCacheTimeline.running: finished " + nmpc.GetFrame() + " " + internalCounter);
state = "finished";
animationTime += overallTime;
Task.SetSucceeded();
//return;
}
// in release
else if (internalCounter >= Mathf.Abs(attack * 2 + delay)) { // for example: 0.28 > (0.2*2+0.6) = false
internalCounter -= Mathf.Abs(attack * 2 + delay);
float t = internalCounter / (2 * release);
animationTime += attack + delay + release * IntegralEasing.EaseOutCubic(t);
//Debug.Log("BTC.PlayMegaPointCacheTimeline: release " + animationTime + " i " + internalCounter + " t " + t);
state = "release";
}
// in delay
else if (internalCounter >= Mathf.Abs(attack * 2)) { // for example: 0.28 > (0.2*2) = false
internalCounter -= Mathf.Abs(attack * 2);
float t = internalCounter / delay;
animationTime += attack + delay * t;
//Debug.Log("BTC.PlayMegaPointCacheTimeline: delay " + animationTime + " i " + internalCounter + " t " + t);
state = "delay";
}
// in attack
else { // for example: true
internalCounter = internalCounter; // for example: 0.28 = 0.28
float t = internalCounter / (2 * attack);
animationTime += attack * IntegralEasing.EaseInCubic(t);
//Debug.Log("BTC.PlayMegaPointCacheTimeline: attack " + animationTime + " i " + internalCounter + " t " + t);
state = "attack";
}
break;
case NodeState.Aborting:
//Debug.Log("BTC.PlayMegaPointCacheTimeline.stopping: " + nmpc.GetFrame() + " " + internalCounter);
internalCounter = overallTime;
//Debug.Log("BTC.PlayMegaPointCacheTimeline: stop ");
state = "stop";
animationTime += attack + delay + release;
// prevent applying the time
return;
break;
}
//Task.log = "t " + animationTime + " i " + internalCounter + " s " + timeScale + " o " + overallTime;
float delta = (animationTime - nmpc.GetFrame()) / timeScale;
//Debug.Log("BTC.PlayMegaPointCacheTimeline: " + state + " " + animationTime + " delta " + delta + " i " + internalCounter);
nmpc.SetFrame(animationTime);
}
}
#endif
#endregion
#region Speech Intent Recognizer and Speech Synthesizer
private ViaggioAIManager _speechMng { get { return ViaggioAIManager.Instance; } }
private RequestDataModel _requestDataModel = new RequestDataModel();
private string _recognizedIntentID = "";
private string _speechErrorText = "";
private bool _onIntentRecognitionSucceededEventTriggered = false;
private bool _onUserSpeechInputStartedEventTriggered = false;
private bool _onIntentRecognitionFailedEventTriggered = false;
private bool _onConversationInitiatedEventTriggered = false;
private bool _onConversationEndedEventTriggered = false;
private bool _onSpeechOutputStartedEventTriggered = false;
private bool _onSpeechOutputEndedEventTriggered = false;
private bool _abortSpeechEventListener = false;
private bool _onErrorEventTriggered = false;
void OnDisable()
{
UnsubscribeFromEvents();
}
private void SubscribeToEvents()
{
if (_speechMng != null)
{
// Speech Intent Recognizer Events
_speechMng.SpeechRecognitionService.OnUserSpeechInputStartedEvent += UserSpeechInputStartedEventHandler;
_speechMng.IntentRecognitionService.OnIntentRecognitionSucceededEvent += IntentRecognitionSucceededEventHandler;
_speechMng.IntentRecognitionService.OnIntentRecognitionFailedEvent += IntentRecognitionFailedEventHandler;
// Speech Output Events
_speechMng.SpeechSynthesizerService.OnSpeechOutputStartedEvent += SpeechOutputStartedEventHandler;
_speechMng.SpeechSynthesizerService.OnSpeechOutputEndedEvent += SpeechOutputEndedEventHandler;
// Chatbot Conversation Events
_speechMng.ConversationService.OnConversationInitiatedEvent += ChatbotConversationStartetEventHandler;
_speechMng.ConversationService.OnConversationEndedEvent += ChatbotConversationEndedEventHandler;
_speechMng.ConversationService.OnConversationErrorEvent += SpeechErrorEventHandler;
// Error Event
_speechMng.OnViaggioAIErrorEvent += SpeechErrorEventHandler;
Debug.Log("SubscribeToEvents successfull.");
}
}
private void UnsubscribeFromEvents()
{
if (_speechMng != null)
{
_speechMng.IntentRecognitionService.OnIntentRecognitionSucceededEvent -= IntentRecognitionSucceededEventHandler;
_speechMng.SpeechRecognitionService.OnUserSpeechInputStartedEvent -= UserSpeechInputStartedEventHandler;
_speechMng.IntentRecognitionService.OnIntentRecognitionFailedEvent -= IntentRecognitionFailedEventHandler;
_speechMng.ConversationService.OnConversationInitiatedEvent -= ChatbotConversationStartetEventHandler;
_speechMng.ConversationService.OnConversationEndedEvent -= ChatbotConversationEndedEventHandler;
_speechMng.SpeechSynthesizerService.OnSpeechOutputStartedEvent -= SpeechOutputStartedEventHandler;
_speechMng.SpeechSynthesizerService.OnSpeechOutputEndedEvent -= SpeechOutputEndedEventHandler;
_speechMng.OnViaggioAIErrorEvent -= SpeechErrorEventHandler;
}
}
private void IntentRecognitionSucceededEventHandler(object sender, string intentID)
{
_onIntentRecognitionSucceededEventTriggered = true;
_recognizedIntentID = intentID;
}
private void UserSpeechInputStartedEventHandler(object sender, bool e)
{
_onUserSpeechInputStartedEventTriggered = true;
}
private void IntentRecognitionFailedEventHandler(object sender, bool e)
{
_onIntentRecognitionFailedEventTriggered = true;
}
private void ChatbotConversationStartetEventHandler(object sender, string e)
{
_onConversationInitiatedEventTriggered = true;
}
private void ChatbotConversationEndedEventHandler(object sender, string e)
{
_onConversationEndedEventTriggered = true;
}
private void SpeechOutputStartedEventHandler(object sender, bool e)
{
_onSpeechOutputStartedEventTriggered = true;
}
private void SpeechOutputEndedEventHandler(object sender, bool e)
{
_onSpeechOutputEndedEventTriggered = true;
}
private void SpeechErrorEventHandler(object sender, string errorText)
{
_onErrorEventTriggered = true;
_speechErrorText = errorText;
}
[Task]
public async void InitializeSpeechManager()
{
if (Task.getState == NodeState.FirstRun)
{
while (_speechMng == null)
{
await System.Threading.Tasks.Task.Delay(10);
}
SubscribeToEvents();
Task.SetSucceeded();
return;
}
}
[Task]
public void AddPossbileSpeechIntent(string id)
{
if (Task.getState == NodeState.FirstRun)
{
_requestDataModel.PossibleIntents.Add(id, SpeechData.intents[id]);
foreach (var r in _requestDataModel.PossibleIntents)
{
Debug.Log($"Possible Intent: {r.Key}, {r.Value}");
}
Task.SetSucceeded();
return;
}
}
[Task]
public void StartSpeechIntentRecognition()
{
if (Task.getState == NodeState.FirstRun)
{
_onUserSpeechInputStartedEventTriggered = false;
_speechMng.StartIntentRecognition(_requestDataModel.PossibleIntents);
Task.SetSucceeded();
return;
}
}
[Task]
public void UserStartedSpeechInput()
{
if (Task.getState == NodeState.FirstRun)
{
_onIntentRecognitionSucceededEventTriggered = false;
_onIntentRecognitionFailedEventTriggered = false;
}
if (_onUserSpeechInputStartedEventTriggered)
{
Task.SetSucceeded();
Debug.Log("UserStartedSpeechInput succeded.");
return;
}
if (_abortSpeechEventListener)
{
Task.SetFailed();
Debug.Log("UserStartedSpeechInput failed.");
_abortSpeechEventListener = false;
return;
}
}
[Task]
public void SpeechIntentRecognized()
{
if (_onIntentRecognitionSucceededEventTriggered)
{
Task.SetSucceeded();
Debug.Log("SpeechIntentRecognized successfull");
return;
}
if (_onIntentRecognitionFailedEventTriggered)
{
Task.SetFailed();
Debug.Log("SpeechIntentRecognized failed");
return;
}
}
[Task]
public void CompareUserSpeechInputStarted(bool value)
{
if (Task.getState == NodeState.FirstRun)
{
if (_onUserSpeechInputStartedEventTriggered == value)
{
Debug.Log($"CompareUserSpeechInputStarted with {value} = equal");
Task.SetSucceeded();
return;
}
else
{
Debug.Log($"CompareUserSpeechInputStarted with {value} = not equal");
Task.SetFailed();
return;
}
}
}
[Task]
public void CompareIntentID(string intentID)
{
if (Task.getState == NodeState.FirstRun)
{
if (_recognizedIntentID == intentID)
{
Debug.Log($"CompareIntentID {_recognizedIntentID} with {intentID} = equal");
Task.SetSucceeded();
return;
}
else
{
Debug.Log($"CompareIntentID {_recognizedIntentID} with {intentID} = not equal");
Task.SetFailed();
return;
}
}
}
[Task]
public void AbortSpeechEventListener()
{
if (Task.getState == NodeState.FirstRun)
{
_abortSpeechEventListener = true;
Task.SetSucceeded();
return;
}
}
[Task]
public void ClearPossbileSpeechIntents()
{
if (Task.getState == NodeState.FirstRun)
{
_requestDataModel.PossibleIntents.Clear();
_recognizedIntentID = "";
Task.SetSucceeded();
return;
}
}
[Task]
public void StopSpeechIntentRecognition()
{
if (Task.getState == NodeState.FirstRun)
{
_speechMng.StopIntentRecognition();
Task.SetSucceeded();
return;
}
}
[Task]
public void StartChatbotConversation()
{
if (Task.getState == NodeState.FirstRun)
{
string languageCode = "it-IT";
string instructions = "Rispondi sempre in italiano. Rispondi in lingua colloquiale al livelli di competenza A2 di quadro comune europeo di riferimento per la conoscenza delle lingue.";
string context = "Sei un essere onnisciente che risponde esclusivamente a domande sulla cultura e la storia del Cantone Ticino e dei Castelli di Bellinzona. Parla <Ciao, cosa vuoi sapere? Sono unintelligenza artificiale>. Rispondi sempre nel modo più conciso possibile. Suddividi le domande in passaggi più piccoli e sviluppale. Chiedi se hanno capito tutto. La conversazione ha inizio.";
int maxRoundTrips = 5;
float maxSilenceBeforeTimeOut = 10;
string outroText = $"Grazie mille, la conversazione è finita.";
_onConversationInitiatedEventTriggered = false;
_speechMng.StartConversation(languageCode, instructions, context, maxRoundTrips, maxSilenceBeforeTimeOut, outroText);
}
if (Task.getState == NodeState.Running)
{
if (_onConversationInitiatedEventTriggered)
{
Task.SetSucceeded();
return;
}
}
}
[Task]
public void ChatbotConversationEnded()
{
if (Task.getState == NodeState.FirstRun)
{
_onConversationEndedEventTriggered = false;
}
if (_onConversationEndedEventTriggered)
{
Task.SetSucceeded();
return;
}
}
[Task]
public void SetSpeechRecognitionLanguage(string languageCode)
{
if (Task.getState == NodeState.FirstRun)
{
_speechMng.SetSpeechRecognitionLanguage(languageCode);
SpeechData.languageCode = languageCode;
Debug.Log($"Set Speech Recognition Language to {languageCode}");
Task.SetSucceeded();
return;
}
}
[Task]
public void SetVoiceName(string voiceName)
{
if (Task.getState == NodeState.FirstRun)
{
SpeechData.voiceName = voiceName;
Debug.Log($"Set VoiceName to {voiceName}");
Task.SetSucceeded();
return;
}
}
[Task]
public void SynthesizeText(string text)
{
if (Task.getState == NodeState.FirstRun)
{
_onSpeechOutputStartedEventTriggered = false;
_speechMng.SynthesizeText(text, SpeechData.languageCode, SpeechData.voiceName);
}
if (_onSpeechOutputStartedEventTriggered)
{
Debug.Log("SynthesizeText: Speech Output started.");
Task.SetSucceeded();
return;
}
}
[Task]
public void SpeechOutputEnded()
{
if (Task.getState == NodeState.FirstRun)
{
_onSpeechOutputEndedEventTriggered = false;
}
if (_onSpeechOutputEndedEventTriggered)
{
Debug.Log("SynthesizeText: Speech Output ended.");
Task.SetSucceeded();
return;
}
}
[Task]
public void SpeechErrorOccured()
{
if (_onErrorEventTriggered)
{
WriteErrorToLog(_speechErrorText);
_onErrorEventTriggered = false;
Task.SetSucceeded();
return;
}
}
[Task]
public void CompareSpeechError(string text)
{
if (Task.getState == NodeState.FirstRun)
{
if (_speechErrorText.Contains(text))
{
Task.SetSucceeded();
return;
}
else
{
Task.SetFailed();
return;
}
}
}
private void WriteErrorToLog(string errorText)
{
try
{
string logFilePath = Path.Combine(Application.persistentDataPath, "speech_errors.log");
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
// Format log entry
string logEntry = $"[{timestamp}] ERROR: {errorText}";
File.AppendAllText(logFilePath, logEntry + Environment.NewLine);
}
catch (Exception ex)
{
Debug.LogError($"Failed to write error to log file: {ex.Message}");
}
}
#endregion
#region Visited Stories Manager
[Task]
public void StoryAVisited()
{
if (Task.getState == NodeState.FirstRun)
{
if (VisitedStories.StoryA)
{
Debug.Log("Story A was visited.");
Task.SetSucceeded();
return;
}
else
{
Debug.Log("Story A was not visited.");
Task.SetFailed();
return;
}
}
}
[Task]
public void StoryBVisited()
{
if (Task.getState == NodeState.FirstRun)
{
if (VisitedStories.StoryB)
{
Debug.Log("Story B was visited.");
Task.SetSucceeded();
return;
}
else
{
Debug.Log("Story B was not visited.");
Task.SetFailed();
return;
}
}
}
[Task]
public void SetStoryAVisited()
{
if (Task.getState == NodeState.FirstRun)
{
VisitedStories.StoryA = true;
Debug.Log($"Set Visited Story A = {VisitedStories.StoryA}");
Task.SetSucceeded();
return;
}
}
[Task]
public void SetStoryBVisited()
{
if (Task.getState == NodeState.FirstRun)
{
VisitedStories.StoryB = true;
Debug.Log($"Set Visited Story B = {VisitedStories.StoryB}");
Task.SetSucceeded();
return;
}
}
[Task]
public void NoStoriesVisited()
{
if (Task.getState == NodeState.FirstRun)
{
if (!VisitedStories.StoryA && !VisitedStories.StoryB)
{
Task.SetSucceeded();
return;
}
else
{
Task.SetFailed();
return;
}
}
}
#endregion
#region Entry Level Point
[Task]
public void CheckEntryLevel(int entryPoint)
{
if (Task.getState == NodeState.FirstRun)
{
if (EntryLevel.entries[entryPoint] == true)
{
Task.SetSucceeded();
return;
}
else
{
Task.SetFailed();
return;
}
}
}
[Task]
public void SetEntryLevel(int entryPoint)
{
if (Task.getState == NodeState.FirstRun)
{
EntryLevel.entries[entryPoint] = true;
Task.SetSucceeded();
return;
}
}
[Task]
public void ResetEntryLevel(int entryPoint)
{
if (Task.getState == NodeState.FirstRun)
{
EntryLevel.entries[entryPoint] = false;
Task.SetSucceeded();
return;
}
}
#endregion
#region keyboard input
[Task]
public void GetKeyDown (string buttonName) {
KeyCode button;
if (System.Enum.TryParse(buttonName, out button)) {
switch (Task.getState) {
case NodeState.FirstRun:
case NodeState.Running:
// Debug.Log($"GetKey: checking {button}");
if (UnityEngine.Input.GetKeyDown(button)) {
Task.SetSucceeded();
return;
}
break;
case NodeState.Aborting:
case NodeState.NotRunning:
break;
}
} else {
Debug.LogError($"GetKeyDown: unknown key {buttonName}");
Task.SetError();
}
}
[Task]
public void GetKeyUp (string buttonName) {
KeyCode button;
if (System.Enum.TryParse(buttonName, out button)) {
switch (Task.getState) {
case NodeState.FirstRun:
case NodeState.Running:
// Debug.Log($"GetKey: checking {button}");
if (UnityEngine.Input.GetKeyUp(button)) {
Task.SetSucceeded();
return;
}
break;
case NodeState.Aborting:
case NodeState.NotRunning:
break;
}
} else {
Debug.LogError($"GetKeyUp: unknown key {buttonName}");
Task.SetError();
}
}
[Task]
public void GetKey (string buttonName) {
KeyCode button;
if (System.Enum.TryParse(buttonName, out button)) {
switch (Task.getState) {
case NodeState.FirstRun:
case NodeState.Running:
// Debug.Log($"GetKey: checking {button}");
if (UnityEngine.Input.GetKey(button)) {
Task.SetSucceeded();
return;
} else {
Task.SetFailed();
return;
}
case NodeState.Aborting:
case NodeState.NotRunning:
break;
}
} else {
Debug.LogError($"GetKey: unknown key {buttonName}");
Task.SetError();
}
}
#endregion
#region SC
public Transform cameraRoot;
//public Transform[] cameraRootPositions;
[Task]
void ReparentCameraToGameObject(string gameObjectName) {
foreach (NamedGameObject ngo in GetHandlers(gameObjectName)) {
// NamedGameObject ngo = GetNamedObject<NamedGameObject>(gameObjectName);
if (ngo != null) {
if (Task.isStarting) {
cameraRoot.transform.parent = ngo.transform;
cameraRoot.localPosition = Vector3.zero;
Debug.Log($"BTC.ReparentCameraToGameObject: reparent camera to {ngo.name}");
}
Task.SetSucceeded();
return;
}
Task.SetFailed();
}
}
[Task]
public void Wait(float timer) {
switch (Task.getState) {
case NodeState.FirstRun:
Task.data = Time.time;
break;
case NodeState.Running:
float startTime = (float)Task.data;
float elapsed = Time.time - startTime;
Task.log = "" + timer;
if (elapsed > timer) {
Task.SetSucceeded();
return;
}
break;
case NodeState.Aborting:
break;
}
//if (debug)
// Debug.Log("TestScript.Timer: " + timer + " " + thisNodeState);
}
[Task]
public void RestartBT() {
switch (Task.getState) {
case NodeState.FirstRun:
case NodeState.Running:
GetComponent<TaskController>().Restart();
// dont succeeed, wait for restart
break;
case NodeState.Aborting:
break;
}
}
#endregion
#region Blackboard
Dictionary<string, bool> boolValueDictionary = new Dictionary<string, bool>();
[Task]
void ClearBool (string key) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
Debug.Log($"ClearBool {key}");
boolValueDictionary.Remove(key);
Task.SetSucceeded();
}
}
[Task]
void SetBool (string key) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
SetBool(key, true);
Task.SetSucceeded();
}
}
[Task]
void SetBool (string key, bool value) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
Debug.Log($"SetBool {key} to {value}");
boolValueDictionary[key] = value;
Task.SetSucceeded();
}
}
[Task]
void CompareBool (string key) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
CompareBool(key, true);
}
}
[Task]
void CompareBool (string key, bool value) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
if (boolValueDictionary.ContainsKey(key)) {
if (boolValueDictionary[key] == value) {
Debug.Log($"CompareBool {key} with {value} = equal");
Task.SetSucceeded();
return;
}
else {
Debug.Log($"CompareBool {key} with {value} = not equal");
Task.SetFailed();
return;
}
} else {
Task.SetFailed();
return;
}
}
}
Dictionary<string, float> floatValueDictionary = new Dictionary<string, float>();
[Task]
void ClearFloat (string key) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
floatValueDictionary.Remove(key);
Task.SetSucceeded();
}
}
[Task]
void AddFloat (string key, float value) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
if (!floatValueDictionary.ContainsKey(key)) {
floatValueDictionary[key] = 0;
}
floatValueDictionary[key] += value;
Task.SetSucceeded();
}
}
[Task]
void SetFloat (string key, int value) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
floatValueDictionary[key] = value;
Task.SetSucceeded();
}
}
[Task]
void CompareFloatBigger (string key, float compareValue) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
float value = 0;
if (floatValueDictionary.ContainsKey(key)) {
value = floatValueDictionary[key];
}
else {
Debug.LogError($"FloatBigger: no value defined for {key}");
}
if (value > compareValue) {
Task.SetSucceeded();
}
else {
Task.SetFailed();
}
}
}
Dictionary<string, int> intDictionary = new Dictionary<string, int>();
[Task]
void ClearInt (string key) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
intDictionary.Remove(key);
Task.SetSucceeded();
}
}
[Task]
void AddInt (string key, int value) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
if (!intDictionary.ContainsKey(key)) {
intDictionary[key] = 0;
}
intDictionary[key] += value;
Debug.Log($"IndAdd {key} {intDictionary[key]}");
Task.SetSucceeded();
}
}
[Task]
void SetInt (string key, int value) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
intDictionary[key] = value;
Debug.Log("IntSet "+key+" "+intDictionary[key]);
Task.SetSucceeded();
}
}
[Task]
void CompareIntBigger (string key, float compareValue) {
if ((Task.getState == NodeState.FirstRun) || (Task.getState == NodeState.Running)) {
int value = 0;
if (intDictionary.ContainsKey(key)) {
value = intDictionary[key];
}
else {
Debug.LogError("CounterBigger: no value defined for "+key);
}
Debug.Log("IntBigger "+key+" "+value+" > "+compareValue);
if (value > compareValue) {
Task.SetSucceeded();
}
else {
Task.SetFailed();
}
}
}
[Task]
void LogMessage(string logMessage) {
Debug.Log(logMessage);
Task.SetSucceeded();
}
#endregion
#region Succeed and fail
[Task]
void Fail() {
Task.SetFailed();
}
[Task]
void Succeed() {
Task.SetSucceeded();
}
#endregion
}