SpeechSynthesizer inside for loop not iterating in unity - c#

I am modifying the code from https://github.com/Azure-Samples/cognitive-services-speech-sdk/blob/master/quickstart/csharp/unity/text-to-speech/Assets/Scripts/HelloWorld.cs to make it count from 1 to 10. While the iteration is displayed fine in the console but the system only says "ten".
I am trying to make it say "one", "two", "three" .... "ten" using for loop.
What am I missing?
using UnityEngine;
using UnityEngine.UI;
using Microsoft.CognitiveServices.Speech;
using System.Threading.Tasks;
public class HelloWorld : MonoBehaviour
{
// Hook up the three properties below with a Text, InputField and Button object in your UI.
public Text outputText;
public InputField inputField;
public Button speakButton;
public AudioSource audioSource;
private object threadLocker = new object();
private bool waitingForSpeak;
private string message;
private SpeechConfig speechConfig;
private SpeechSynthesizer synthesizer;
public void ButtonClick()
{
// Starts speech synthesis, and returns after a single utterance is synthesized.
for (int j = 1; j <= 10; j++)
{
Debug.Log("Iteration: " + j);
using (var result = synthesizer.SpeakTextAsync(j.ToString()).Result)
{
// Checks result.
if (result.Reason == ResultReason.SynthesizingAudioCompleted)
{
var sampleCount = result.AudioData.Length / 2;
var audioData = new float[sampleCount];
for (var i = 0; i < sampleCount; ++i)
{
audioData[i] = (short)(result.AudioData[i * 2 + 1] << 8 | result.AudioData[i * 2]) / 32768.0F;
}
// The output audio format is 16K 16bit mono
var audioClip = AudioClip.Create("SynthesizedAudio", sampleCount, 1, 16000, false);
audioClip.SetData(audioData, 0);
audioSource.clip = audioClip;
audioSource.Play();
newMessage = "Speech synthesis succeeded!";
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
newMessage = $"CANCELED:\nReason=[{cancellation.Reason}]\nErrorDetails=[{cancellation.ErrorDetails}]\nDid you update the subscription info?";
}
}
}
}
void Start()
{
if (outputText == null)
{
UnityEngine.Debug.LogError("outputText property is null! Assign a UI Text element to it.");
}
else if (inputField == null)
{
message = "inputField property is null! Assign a UI InputField element to it.";
UnityEngine.Debug.LogError(message);
}
else if (speakButton == null)
{
message = "speakButton property is null! Assign a UI Button to it.";
UnityEngine.Debug.LogError(message);
}
else
{
inputField.text = "Enter text you wish spoken here.";
message = "Click button to synthesize speech";
speakButton.onClick.AddListener(ButtonClick);
speechConfig = SpeechConfig.FromSubscription("YourSubscriptionKey", "northeurope");
// The default format is Riff16Khz16BitMonoPcm.
// We are playing the audio in memory as audio clip, which doesn't require riff header.
// So we need to set the format to Raw16Khz16BitMonoPcm.
speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm);
// Creates a speech synthesizer.
// Make sure to dispose the synthesizer after use!
synthesizer = new SpeechSynthesizer(speechConfig, null);
}
}
void Update()
{
lock (threadLocker)
{
if (speakButton != null)
{
speakButton.interactable = !waitingForSpeak;
}
if (outputText != null)
{
outputText.text = message;
}
}
}
void OnDestroy()
{
synthesizer.Dispose();
}
}

I ended up doing the following and it's working as I wanted. Basically made the button click function to async and awaited for the synthesizer.
public async void ButtonClick()
{
Debug.Log("Button Pressed!");
for (int j = 1; j <= 10; j++)
{
string newMessage = string.Empty;
var config = SpeechConfig.FromSubscription("KEY", "northeurope");
using var synthesizer = new SpeechSynthesizer(config);
await synthesizer.SpeakTextAsync(j.ToString());
}
}

Related

Is it a bad idea to make a loop inside OnGUI in EditorWindow script?

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
public class GetChildsEditorWindow : EditorWindow
{
public Transform transform;
int levelsMin = 0, levelsMax = 50;
static int currentLevel;
List<Transform> allChildren = new List<Transform>();
static int oldLevel = 0;
// Add menu named "My Window" to the Window menu
[MenuItem("Get Childs/Get")]
static void Init()
{
oldLevel = currentLevel;
// Get existing open window or if none, make a new one:
GetChildsEditorWindow window = (GetChildsEditorWindow)EditorWindow.GetWindow(typeof(GetChildsEditorWindow), false, "Get Childs");
window.Show();
}
private void OnGUI()
{
GUILayout.Space(20);
transform = EditorGUILayout.ObjectField("Transform to get childs", transform, typeof(Transform), true) as Transform;
EditorGUI.BeginDisabledGroup(transform == null);
currentLevel = (int)EditorGUILayout.Slider("Slider", currentLevel, levelsMin, levelsMax);
EditorGUI.EndDisabledGroup();
if (allChildren != null && allChildren.Count > 0)
{
for (int i = 0; i < allChildren.Count; i++)
{
allChildren[i] = EditorGUILayout.ObjectField("Transform " + i.ToString(), allChildren[i], typeof(Transform), true) as Transform;
}
}
if (oldLevel != currentLevel)
{
if (transform != null)
{
allChildren = new List<Transform>();
IterateOverChild(transform, currentLevel, levelsMax);
oldLevel = currentLevel;
}
}
}
public void IterateOverChild(Transform original, int currentLevel, int maxLevel)
{
if (currentLevel > maxLevel) return;
for (var i = 0; i < original.childCount; i++)
{
Debug.Log($"{original.GetChild(i)}"); //Do with child what you need
allChildren.Add(original.GetChild(i));
IterateOverChild(original.GetChild(i), currentLevel + 1, maxLevel);
}
}
}
The script is working as i wanted , the question is if calling the loop inside the OnGUI is a bad practice performance ? because it's working fine i just wonder if it's wrong to have a loop inside the OnGUI.

Why the fading in/out of the saving text is fading only one time when saving?

The post is a bit long but the scripts are connected to each other.
When the game start first time it's saving after 3 seconds and fading for 3 seconds.
but in the game later when it's saving again it's showing the save text fading only once.
In this script I'm making the save and also set the time to start the saving and the time it will fade in/out :
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SaveLoad : MonoBehaviour
{
public FadeInOutSaveGameText fadeInOutSaveGame;
public float timeToStartSaving;
public float savingFadeInOutTime;
private List<GameObject> objectsToSave = new List<GameObject>();
private void Awake()
{
SaveSystem.Init();
var objectsWithGenerateGuid = GameObject.FindObjectsOfType<GenerateGuid>().ToList();
if (objectsWithGenerateGuid.Count > 0)
{
for (int i = 0; i < objectsWithGenerateGuid.Count; i++)
{
objectsToSave.Add(objectsWithGenerateGuid[i].gameObject);
}
}
Debug.Log("Start");
for (int i = 0; i < objectsToSave.Count; i++)
{
Debug.Log($"{i}");
Debug.Log($"{objectsToSave[i].name}");
}
Debug.Log("End Init");
}
public void Save()
{
SaveGame saveGame = new SaveGame();
saveGame.saveObjects = new List<SaveObject>();
for (int i = 0; i < objectsToSave.Count; i++)
{
SaveObject saveObject = new SaveObject();
saveObject.transformSaver = new TransformSaver();
Debug.Log($"{i}");
Debug.Log($"{objectsToSave[i].name}");
saveObject.gameObjectUniqueID = objectsToSave[i].GetComponent<GenerateGuid>().uniqueGuidID;
var x = objectsToSave[i].GetComponents<Component>();
var stateQueryComponent = x.Where(component => component is IStateQuery).ToList();
List<KeyToValue> componentsState = new List<KeyToValue>();
foreach (var z in stateQueryComponent)
{
var w = z as IStateQuery;
componentsState.Add(new KeyToValue(w.UniqueId.ToString(), w.GetState()));
}
saveObject.transformSaver.position = objectsToSave[i].transform.position;
saveObject.transformSaver.rotation = objectsToSave[i].transform.rotation;
saveObject.transformSaver.scaling = objectsToSave[i].transform.localScale;
saveObject.componentsState = componentsState;
saveGame.saveObjects.Add(saveObject);
}
string json = JsonUtility.ToJson(saveGame);
SaveSystem.Save(json);
}
public void Load()
{
Dictionary<string, GameObject> uniqueIdToObject = objectsToSave
.ToDictionary(o => o.GetComponent<GenerateGuid>().uniqueGuidID, o => o);
var saveString = SaveSystem.Load();
if (saveString != null)
{
SaveGame saveGame = JsonUtility.FromJson<SaveGame>(saveString);
foreach (var saveObject in saveGame.saveObjects)
{
List<KeyToValue> loadedComponents = saveObject.componentsState;
var objectToSetState = uniqueIdToObject[saveObject.gameObjectUniqueID];
objectToSetState.transform.position = saveObject.transformSaver.position;
objectToSetState.transform.rotation = saveObject.transformSaver.rotation;
objectToSetState.transform.localScale = saveObject.transformSaver.scaling;
var y = objectToSetState.GetComponents<Component>();
var z = y.Where(component => component is IStateQuery).ToList();
Dictionary<string, IStateQuery> zz = z.ToDictionary(sq => (sq as IStateQuery).UniqueId.ToString(), sq => sq as IStateQuery);
foreach (KeyToValue keyvalue in loadedComponents)
{
zz[keyvalue.Key].SetState(keyvalue.Value);
}
}
}
}
public IEnumerator SaveWithTime()
{
yield return new WaitForSeconds(timeToStartSaving);
Save();
StartCoroutine(fadeInOutSaveGame.OverAllTime(savingFadeInOutTime));
}
}
At the bottom I created the to save with time :
public IEnumerator SaveWithTime()
{
yield return new WaitForSeconds(timeToStartSaving);
Save();
StartCoroutine(fadeInOutSaveGame.OverAllTime(savingFadeInOutTime));
}
The method OverAllTime make the fading depending on the time I set in this case 3 seconds :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FadeInOutSaveGameText : MonoBehaviour
{
public Canvas canvas;
public float fadingSpeed;
private bool stopFading = false;
private const float THRESHOLD = 0.01F;
// Start is called before the first frame update
void Start()
{
}
IEnumerator CanvasAlphaChangeOverTime(Canvas canvas, float duration)
{
float alphaColor = canvas.GetComponent<CanvasGroup>().alpha;
while (true)
{
alphaColor = (Mathf.Sin(Time.time * duration) + 1.0f) / 2.0f;
canvas.GetComponent<CanvasGroup>().alpha = alphaColor;
// only break, if current alpha value is close to 0 or 1
if (stopFading && Mathf.Abs(alphaColor) <= THRESHOLD)//if (stopFading && (Mathf.Abs(alphaColor) <= THRESHOLD || Mathf.Abs(alphaColor - 1) <= THRESHOLD))
{
break;
}
yield return null;
}
}
public IEnumerator OverAllTime(float time)
{
StartCoroutine(CanvasAlphaChangeOverTime(canvas, fadingSpeed));
yield return new WaitForSeconds(time);
stopFading = true;
}
}
And in this script at the bottom I'm starting the save :
private void Update()
{
if(dimLights.lightsDimmed == true && lightsDim == false && unlockCarte.HasOpened() == true
&& MenuController.LoadSceneForSavedGame == false)
{
m_state.naviLightsIntensity = dimLights.lightsToDim[0].intensity;
lightsDim = true;
var brainBlendTime = Camera.main.GetComponent<CinemachineBrain>();
saveLoad.timeToStartSaving = brainBlendTime.m_DefaultBlend.m_Time;
StartCoroutine(saveLoad.SaveWithTime());
}
}
I'm setting the timeToStartSaving to the time it takes to blend from one cinemachine camera to another camera in this case 5 seconds.
I used break points and it's getting to the SaveWithTime method when the value of timeToStartSaving is 5 and the value of savingFadeInOutTime stay 3 as before. Only the timeToStartSaving variable vlaue was 3 and changed to 5.
but ion the game the fading happens only once one time instead 3 times.
in the start of the game first time saving it does fading 3 times for 3 seconds. but on the second saving for some reason it's fading only one time even if the value of savingFadeInOutTime is 3.
You have to reset stopFading, otherwise your loop will always break after the first time reaching the zero threshold.
public IEnumerator OverAllTime(float time)
{
stopFading = false; // reset this, otherwise it stays true, after the first run
StartCoroutine(CanvasAlphaChangeOverTime(canvas, fadingSpeed));
yield return new WaitForSeconds(time);
stopFading = true;
}

Why the when adding new item to the list it's replacing it with the item at index 0 instead adding new item at the end?

In this case the list is at size 17 (index 0 to 16)
And each time when adding new item it's replacing the new item with the one at index 0 instead adding new item to and make the list size bigger.
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(PickupObjects))]
public class PickupObjectsEditor : Editor
{
private static List<GameObject> pickeditems = new List<GameObject>();
private static bool picked = false;
private SerializedProperty _serializedpickeditems;
private Vector2 scrollPos;
[MenuItem("GameObject/Generate as Pickup Item", false, 30)]
public static void GeneratePickupItems()
{
if (Selection.gameObjects.Length > 0)
{
pickeditems.Clear();
for (int i = 0; i < Selection.gameObjects.Length; i++)
{
if (Selection.gameObjects[i].GetComponent<Whilefun.FPEKit.FPEInteractablePickupScript>() == null)
{
Selection.gameObjects[i].AddComponent<BoxCollider>();
Selection.gameObjects[i].AddComponent<Whilefun.FPEKit.FPEInteractablePickupScript>();
}
Selection.gameObjects[i].layer = 9;
Selection.gameObjects[i].tag = "Pickup Item";
pickeditems.Add(Selection.gameObjects[i]);
}
picked = true;
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
if (picked)
{
for (var i = 0; i < pickeditems.Count; i++)
{
// NOTE: Never mix serializedProperties and direct access/modifications on the target!
// This messes up the marking dirty and saving these changes!
// Rather always go through the SerializedProperties so the editor handles everything automatically
_serializedpickeditems.arraySize++;
_serializedpickeditems.GetArrayElementAtIndex(i).objectReferenceValue = pickeditems[i];
}
picked = false;
pickeditems.Clear();
}
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(250));
var color = GUI.color;
for (var i = 0; i < _serializedpickeditems.arraySize; i++)
{
var item = _serializedpickeditems.GetArrayElementAtIndex(i);
// little bonus from me: Color the field if the value is null ;)
if (!item.objectReferenceValue) GUI.color = Color.red;
{
if (item.objectReferenceValue != null)
{
EditorGUILayout.PropertyField(item, new GUIContent("Picked Item " + i + " ")); //+ (item.objectReferenceValue ? item.objectReferenceValue.name : "null")));
}
}
GUI.color = color;
// The only case you would need to go deeper here and use
// your new SerializedObject would be if you actually make changes
// to these objects/components like e.g. directly allow to edit their name
}
serializedObject.ApplyModifiedProperties();
EditorGUILayout.EndScrollView();
}
private void OnEnable()
{
_serializedpickeditems = serializedObject.FindProperty("pickUpObjects");
}
}
And the mono script
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class PickupObjects : MonoBehaviour
{
public List<GameObject> pickUpObjects = new List<GameObject>();
}
Sorry this was a mistake of mine in your last question!
It should actually be
_serializedpickeditems.arraySize++;
_serializedpickeditems.GetArrayElementAtIndex(_serializedpickeditems.arraySize - 1).objectReferenceValue = pickeditems[i];
in order to add objects in the end of the list!

Unity Scoreboard: NullReferenceException: Object reference not set to an instance of an object

So I'm trying so create a scoreboard and I always get the NullReferenceException. It's a simple game where you save your time as your score. The one with the shortest time wins and gets a place in the scoreboard. I'm quite a beginner with unity and coding in general. I use 2 arrays to save scores and names.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class ScoreBoard2 : MonoBehaviour
{
private float[] scoreboard = new float[3]; //saves highscored in correct
order
private string[] scorenames = new string[3]; //saves names in correct order
public Text score1Text;
public Text score2Text;
public Text score3Text;
public Text name1, name2, name3;
// Start is called before the first frame update
void Start()
{
score1Text = GetComponent<Text>();
score2Text = GetComponent<Text>();
score3Text = GetComponent<Text>();
name1 = GetComponent<Text>();
name2 = GetComponent<Text>();
name3 = GetComponent<Text>();
if (scorenames[0] == null && scorenames[1] == null && scorenames[2] == null)
{
scorenames[0] = "X";
scorenames[1] = "X";
scorenames[2] = "X";
}
for (int i = 0; i < scoreboard.Length; i++)
{
if (PlayerPrefs.GetFloat("score") >= scoreboard[i]) //after GameOver the score will always saved in PlayerPrefs.SetFloat("score")
{
for (int j = scoreboard.Length - 1; j > i; j--)
{
scoreboard[j] = scoreboard[j - 1];
scorenames[j] = scorenames[j - 1];
}
scoreboard[i] = PlayerPrefs.GetFloat("score");
scorenames[i] = PlayerPrefs.GetString("Username");
break;
}
}
PlayerPrefs.SetFloat("1st", scoreboard[0]);
PlayerPrefs.SetFloat("2nd", scoreboard[1]);
PlayerPrefs.SetFloat("3rd", scoreboard[2]);
PlayerPrefs.SetString("name1", scorenames[0]);
PlayerPrefs.SetString("name2", scorenames[1]);
PlayerPrefs.SetString("name3", scorenames[2]);
}
// Update is called once per frame
void Update()
{
score1Text.text = PlayerPrefs.GetFloat("1st").ToString();
score2Text.text = PlayerPrefs.GetFloat("2nd").ToString();
score3Text.text = PlayerPrefs.GetFloat("3rd").ToString();
name1.text = PlayerPrefs.GetString("name1");
name2.text = PlayerPrefs.GetString("name2");
name3.text = PlayerPrefs.GetString("name3");
}
public void LoadMenue()
{
SceneManager.LoadScene("Menu");
}
}

How can I return from a method also List of transforms or gameobjects without making instances fro new gameobjects?

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine.SceneManagement;
public static class TransformSaver
{
[System.Serializable]
public class TransformInfo
{
public string sceneName;
public string name;
public Transform parent;
public Vector3 pos;
public Quaternion rot;
public Vector3 scale;
}
//Save Transform
public static void SaveTransform(Transform[] tranformToSave)
{
TransformInfo[] trnfrm = new TransformInfo[tranformToSave.Length];
for (int i = 0; i < trnfrm.Length; i++)
{
trnfrm[i] = new TransformInfo();
trnfrm[i].sceneName = tranformToSave[i].gameObject.scene.name;
trnfrm[i].name = tranformToSave[i].name;
trnfrm[i].parent = tranformToSave[i].parent;
trnfrm[i].pos = tranformToSave[i].localPosition;
trnfrm[i].rot = tranformToSave[i].localRotation;
trnfrm[i].scale = tranformToSave[i].localScale;
}
string jsonTransform = JsonHelper.ToJson(trnfrm, true);
File.WriteAllText(#"d:\json\json.txt", jsonTransform);
}
//Load Transform
public static Transform[] LoadTransform()
{
string jsonTransform = File.ReadAllText(#"d:\json\json.txt");
if (jsonTransform == null)
{
return null;
}
TransformInfo[] savedTransforms = JsonHelper.FromJson<TransformInfo>(jsonTransform);
GameObject[] gameObjects = new GameObject[savedTransforms.Length];
Transform[] loadedTransforms = new Transform[savedTransforms.Length];
for (int i = 0; i < gameObjects.Length; i++)
{
SceneManager.SetActiveScene(SceneManager.GetSceneByName(savedTransforms[i].sceneName));
gameObjects[i] = new GameObject();
loadedTransforms[i] = gameObjects[i].transform;
loadedTransforms[i].name = savedTransforms[i].name;
loadedTransforms[i].parent = savedTransforms[i].parent;
loadedTransforms[i].localPosition = savedTransforms[i].pos;
loadedTransforms[i].localRotation = savedTransforms[i].rot;
loadedTransforms[i].localScale = savedTransforms[i].scale;
}
return loadedTransforms;
}
}
The problem is that the method LoadTransform will return array of Transforms but also will create new GameObjects in the Editor in the Hierarchy.
What I want to do is to add something like a boolean flag or maybe other way so I can select if toe return the array of transforms with or without creating new gameobjects so I tried something like that:
//Load Transform
public static Transform[] LoadTransform(bool returnInfo)
{
string jsonTransform = File.ReadAllText(#"d:\json\json.txt");
if (jsonTransform == null)
{
return null;
}
TransformInfo[] savedTransforms = JsonHelper.FromJson<TransformInfo>(jsonTransform);
GameObject[] gameObjects = new GameObject[savedTransforms.Length];
Transform[] loadedTransforms = new Transform[savedTransforms.Length];
for (int i = 0; i < gameObjects.Length; i++)
{
SceneManager.SetActiveScene(SceneManager.GetSceneByName(savedTransforms[i].sceneName));
if (returnInfo == true)
{
gameObjects[i] = new GameObject();
loadedTransforms[i] = gameObjects[i].transform;
}
loadedTransforms[i].name = savedTransforms[i].name;
loadedTransforms[i].parent = savedTransforms[i].parent;
loadedTransforms[i].localPosition = savedTransforms[i].pos;
loadedTransforms[i].localRotation = savedTransforms[i].rot;
loadedTransforms[i].localScale = savedTransforms[i].scale;
}
return loadedTransforms;
}
I added a bool flag returnInfo but now loadedTransforms is null.
But the main goal is to decide if to return array of transforms with or without making instances for new gameobjects.
The problem is when I'm using Editorwindow type script and inside OnGUI to save and load the objects:
List<GameObject> selections = Selection.objects.OfType<GameObject>().ToList();
if (selections.Count > 0)
{
GUI.enabled = true;
}
else
{
GUI.enabled = false;
}
if (GUILayout.Button("Save selected objects data"))
{
if (selections.Count > 0)
{
tempTransformSelection = selections;
for (var i = selections.Count - 1; i >= 0; --i)
{
var selected = selections[i];
transformSelection.Add(selected.transform);
}
TransformSaver.SaveTransform(transformSelection.ToArray());
transformSelection = new List<Transform>();
}
}
var file = #"d:\json\json.txt";
FileInfo fi = new FileInfo(file);
// By default asume you don't want to show the button
// it will than only be enabled if the later conditions match
bool showButton = false;
// you can check this already here not in the loop
// if no file -> nothing to do
if (!File.Exists(#"d:\json\json.txt") || fi.Length <= 0) return;
// This is only reached if the file exists and is not empty
// check for null transforms
for (int i = 0; i < tempTransformSelection.Count(); i++)
{
// if not null do nothing
if (tempTransformSelection[i] != null)
continue;
// otherwise enable the button and leave the loop
showButton = true;
break;
}
// if not true then button won't be shown
if (showButton == true)
{
GUI.enabled = true;
}
else
{
GUI.enabled = false;
}
if (GUILayout.Button("Instantiate back deleted selected bojects"))
{
TransformSaver.LoadTransform(true);
showButton = false;
}
}
private void OnInspectorUpdate()
{
Repaint();
}
The main goal is to select objects in the hierachy in the editor. Then to click on "Save selected objects data" to save the selected object to a json file.
Then I want to make that only if one of the saved objects to the file is null in the hierarchy fro example if I deleted one or more of the objects in the saved file ! only then enable true the button "Instantiate back deleted selected bojects"
And then when clicking on the button "Instantiate back deleted selected bojects" set the button back to enabled false !
But the important thing is to check for null against the saved list of objects in the json file ! But once again I'm stuck with the tempTransformSelection and with how to enable false/true the button.

Categories