Unity3D UI button need multiple click to play or pause audio - unity3d

Why does my play or pause button need multiple clicks to play or pause the audio? If the target is detected it will display a UI button and if clicked it will play a sound and when clicked again it will pause but I need multiple click to trigger the code.
any solutions? this is my code.
void playSound(string ss)
{
clipTarget = (AudioClip)Resources.Load(ss);
soundTarget.clip = clipTarget;
soundTarget.loop = false;
soundTarget.playOnAwake = false;
soundTarget.ignoreListenerPause = true;
}
if (name == "quezon")
{
if (Translate.GetComponent<Text>().text == "ENGLISH")
{
Narrator.gameObject.SetActive(true);
myGirl.gameObject.SetActive(false);
myBoy.enabled = false;
ButtonAction.GetComponent<Button().onClick.AddListener(delegate {
if (soundTarget.isPlaying)
{
soundTarget.Pause();
btn.image.overrideSprite = Play;
myBoy.enabled = false;
}
else if(!soundTarget.isPlaying)
{
soundTarget.Play();
playSound("sounds/English");
btn.image.overrideSprite = Pause;
myBoy.enabled = true;
}
});
TextDescription.GetComponent<Text>().text = "Manuel L. Quezon was born on August 19, 1878, and died on August 1, 1944. "
+ "He was a Filipino statesman, soldier, and politician who served as president of the Commonwealth of the "
+ "Philippines from 1935 to 1944.";
TextTargetName.GetComponent<Text>().text = name;
}
}

You have to define the button onClick behaviour only once. Like this:
void Awake ()
{
ButtonAction.GetComponent<Button>().onClick.AddListener (() => ToggleSound ());
}
private void ToggleSound ()
{
if (soundTarget.isPlaying)
{
soundTarget.Pause();
btn.image.overrideSprite = Play;
myBoy.enabled = false;
}
else
{
soundTarget.Play();
playSound("sounds/English");
btn.image.overrideSprite = Pause;
myBoy.enabled = true;
}
}
What I think is happening is since you are adding listeners everytime that routine happens it triggers first once, then twice, then thrice and so on.
Thats is the syntax I commonly use for adding behaviour to UI buttons, is easy, you can add parameters and looks clean. I recommend it.
Also if you dont have parameters you can do this and its easier.
ButtonAction.GetComponent<Button().onClick.AddListener (ToggleSound);

Related

Unity UI Button keeps double clicking, how can I work around this?

I have two buttons set up in an interactive fiction game, each press calls a new line of text. The problem is, every time I press on the button, I get two logged debug messages informing me of the click and my game moves two sections of text.
I've tried many different things to try to work around this including trying to alter the submit input in project settings and many different code forms. Any help would be greatly appreciated.
Here's my current code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class AdventureGame : MonoBehaviour
{
[SerializeField]
private Text _textComponent;
[SerializeField]
private State _startingState;
private State state;
[SerializeField]
private Button _input0Button;
[SerializeField]
private Button _input1Button;
[SerializeField]
private Text _choices1;
[SerializeField]
private Text _choices2;
private bool _buttonOnePressed;
private bool _buttonTwoPressed;
void Start()
{
state = _startingState;
_textComponent.text = state.GetStateStory();
_input0Button.onClick.AddListener(Input0Button);
_input1Button.onClick.AddListener(Input1Button);
_buttonOnePressed = false;
_buttonTwoPressed = false;
}
void Update()
{
ManageState();
}
private void ManageState()
{
if (state._choice == true)
{
_choices1.text = state.GetChoiceOne();
_choices2.text = state.GetChoiceTwo();
_textComponent.text = state.GetStateStory();
_input0Button.gameObject.SetActive(true);
_input1Button.gameObject.SetActive(true);
if(_buttonOnePressed == true)
{
StartCoroutine(WaitForItOne());
}
else if(_buttonTwoPressed == true)
{
StartCoroutine(WaitForItTwo());
}
}
else if (state._choice == false)
{
_choices1.text = state.GetChoiceOne();
_choices2.text = state.GetChoiceTwo();
_textComponent.text = state.GetStateStory();
_input0Button.gameObject.SetActive(true);
_input1Button.gameObject.SetActive(false);
if(_buttonOnePressed == true)
{
StartCoroutine(WaitForItOne());
}
}
}
private void ManageChoiceOne()
{
_buttonOnePressed = false;
State[] _newState = state.GetNextStatesArray();
state = _newState[0];
}
private void ManageChoiceTwo()
{
_buttonTwoPressed = false;
State[] _newState = state.GetNextStatesArray();
state = _newState[1];
}
public void Input0Button()
{
Debug.Log("Input 0 pressed");
_buttonOnePressed = true;
}
public void Input1Button()
{
Debug.Log("Input 1 pressed");
_buttonTwoPressed = true;
}
IEnumerator WaitForItOne()
{
yield return new WaitForSeconds(3.0f);
ManageChoiceOne();
}
IEnumerator WaitForItTwo()
{
yield return new WaitForSeconds(3.0f);
ManageChoiceTwo();
}
}
First of all you keep starting new Coroutines each frame as long as e.g. _buttonOnePressed == true .. you wait 3 seconds before you finally unset this flag!
Then for the double call make sure the callbacks are not configured also in the Inspector! It seems like you have them once in the Inspector and additionally add them in your Start method so they are called twice!
Note that you won't see the callbacks added on runtime in the Inspector!
Why are you even using Update here at all? It is quite redundant to poll the state and the bool values and constantly check and handle their states in each frame. I would rather simply start the routine in the button method itself and make the whole code event driven instead!
(optionally) To give the user better feedback I would additionally in the meantime during the 3 seconds make the buttons non-interactable .. keep them active but not clickable:
// Remove state;
// Remove _buttonOnePressed
// Remove _buttonTwoPressed
private void Start()
{
// Either remove this two lines or the callbacks set in the Inspector
_input0Button.onClick.AddListener(Input0Button);
_input1Button.onClick.AddListener(Input1Button);
ManageState(_startingState);
}
// Remove Update
// This will be called only when actually needed
// since the state is passed in as parameter you don't need the private field
private void ManageState(State state)
{
// These happen in both cases anyway
_choices1.text = state.GetChoiceOne();
_choices2.text = state.GetChoiceTwo();
_textComponent.text = state.GetStateStory();
_input0Button.gameObject.SetActive(true);
// Here directly use the state flag
// since the button is disabled when needed
// there is no need for having different "states"
// since anyway only the according button(s) is(are) available
_input1Button.gameObject.SetActive(state._choice);
}
// (optional) Flag for avoiding concurrent routines
// Should be impossible since buttons get disabled but you never know
private bool alreadyHandlingButton;
private IEnumerator ManageChoice(bool isButtonOne)
{
// (optional) Skip if another routine running
if(alreadyHandlingButton) yield break;
// (optional) Block other routines just in cade
alreadyHandlingButton = true;
// Disable interactions
_input0Button.interactable = false;
_input1Button.interactable = false;
// This is the same for both buttons
yield return new WaitForSeconds(3f);
State[] _newState = state.GetNextStatesArray();
var state = _newState[isButtonOne ? 0 : 1];
// Only call ManageState when the state is actually changing
ManageState(state);
// (optional) Allow a new routine
alreadyHandlingButton = false;
// Enable interactions
_input0Button.interactable = true;
_input1Button.interactable = true;
}
public void Input0Button()
{
// (optional) Ignore if other button is already handled
if(alreadyHandlingButton) return;
Debug.Log("Input 0 pressed");
StartCoroutine(ManageChoice(true));
}
public void Input1Button()
{
// (optional) Ignore if other button is already handled
if(alreadyHandlingButton) return;
Debug.Log("Input 1 pressed");
StartCoroutine(ManageChoice(false));
}

KeyPressed Called Multiple times on Update

I'm trying to add an attack to my character, everything works fine except my button its called multiple times by click (I'm not holding the key down, and its called in average 4 times).
Thats my Update method:
void Update() {
attackArea.enabled = false;
InputCharacter();
MoveAttackArea();
SetAnimation();
ApplyColorFilters();
}
and my InputCharacter method:
void InputCharacter() {
direction = Vector2.zero;
if (Input.GetKey(KeyCode.B)) {
lastAttackTime = currentTime;
Attack();
} else if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W)) {
Move(Vector2.up);
} else if (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S)) {
Move(Vector2.down);
} else if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A)) {
Move(Vector2.left);
} else if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D)) {
Move(Vector2.right);
}
}
also my Attack method:
private void Attack() {
Debug.Log("attacking");
animator.SetTrigger("attack");
attackArea.enabled = true;
}
I don't know if the this part its related but:
The log show multiple times and my animations are playing twice (when I play an object destruction animation it runs twice, I don't know if its related)
Replace
Input.GetKey
with
Input.GetKeyDown

Unity - Google cloud speech-to-text voice recognition, Unity freezes after successful result

A friend of mine and I are working on a VR project in Unity at the moment and we are trying to implement voice recognition as a feature. We are using Unity version 2018.3.3f1. The idea is that a user can say a word and the voice recognition will see if they pronounced it correctly. We have chosen to use the Google cloud speech-to-text service for this as it supports the target language (Norwegian). In addition, the application is also multiplayer and so we are trying to use the streaming version of Google cloud speech. Here is a link to their documentation: https://cloud.google.com/speech-to-text/docs/streaming-recognize
What we have done is to have a plugin that essentially runs the speech recognition for us. It is a modification of the example code given in the link above:
public Task<bool> StartSpeechRecognition()
{
return StreamingMicRecognizeAsync(20, "fantastisk");
}
static async Task<bool> StreamingMicRecognizeAsync(int inputTime, string inputWord)
{
bool speechSuccess = false;
Stopwatch timer = new Stopwatch();
Task delay = Task.Delay(TimeSpan.FromSeconds(1));
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
//Console.WriteLine("No microphone!");
return false;
}
var speech = SpeechClient.Create();
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 16000,
LanguageCode = "nb",
},
InterimResults = true,
}
});
// Compare speech with the input word, finish if they are the same and speechSuccess becomes true.
Task compareSpeech = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(
default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
if (alternative.Transcript.Replace(" ", String.Empty).Equals(inputWord, StringComparison.InvariantCultureIgnoreCase))
{
speechSuccess = true;
return;
}
}
}
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(16000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
waveIn.StartRecording();
timer.Start();
//Console.WriteLine("Speak now.");
//Delay continues as long as a match has not been found between speech and inputword or time that has passed since recording is lower than inputTime.
while (!speechSuccess && timer.Elapsed.TotalSeconds <= inputTime)
{
await delay;
}
// Stop recording and shut down.
waveIn.StopRecording();
timer.Stop();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await compareSpeech;
//Console.WriteLine("Finished.");
return speechSuccess;
}
We made a small project in Unity to test if this was working with a cube GameObject that had this script:
private CancellationTokenSource tokenSource;
VR_VoiceRecognition.VoiceRecognition voice = new VR_VoiceRecognition.VoiceRecognition();
IDisposable speech;
// Use this for initialization
void Start() {
speech = Observable.FromCoroutine(WaitForSpeech).Subscribe();
}
// Update is called once per frame
void Update() {
}
IEnumerator WaitForSpeech()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
Debug.Log("Starting up");
Task<bool> t = Task.Run(() => voice.StartSpeechRecognition());
while (!(t.IsCompleted || t.IsCanceled))
{
yield return null;
}
if (t.Status != TaskStatus.RanToCompletion)
{
yield break;
}
else
{
bool result = t.Result;
UnityEngine.Debug.Log(t.Result);
yield return result;
}
}
void OnApplicationQuit()
{
print("Closing application.");
speech.Dispose();
}
We are also using a plugin that was recommended to us by Unity support that they thought might have a workaround called UniRx (https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276).
At the moment it is working fine when you play it in the editor for the first time. When the voice recognition returns false then everything is fine (two cases when this happens is if it cannot find a microphone or if the user does not say the specific word). However, if it is a success then it still returns true, but if you exit play mode in the editor and try to play again then Unity will freeze. Unity support suspects that it might have something to do with the Google .dll files or Google API. We are not quite sure what to do from here and we hope that someone could point us to the right direction.

checking paragraph property loses the selection

in my vsto addin i have some simple code on a timer
private void MainTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!dialogopen & Application.Documents.Count > 0)
{
var doc = Application.ActiveDocument;
Word.InlineShapes shps;
Word.Paragraphs pars;
try
{
pars = doc.Paragraphs;
}
catch (Exception)
{
return;
}
var pars2 = pars.Cast<Word.Paragraph>().ToList();
foreach (var obj in pars2)
{
if (obj.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText )//PROBLEM HERE
{
};
}
}
}
as soon as it reaches the line that checks the outlinelevel, even if i dont do a thing, the selection in the activedocument gets lost
of course the user cant use a plugin which keeps on canceling his selection...
googling didnt give me a thing
thanks
EDIT1
I tried making a static function for checking the styles, but it did not help. Here's the code
static public class Helpers
{
static public Word.Paragraph checkPars(List<Word.Paragraph> pars)
{
return pars.FirstOrDefault();//(x) => x.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText);
}
}
As you can see, I had to remove the actual check, since it was causing the cursor to blink and lose selection
please advise
We use the Application.ScreenUpdating = true; and this keep the selection for all properties in Paragraph except for the Range property.
Then, we tried to access the range via Reflection and this was the solution.
Range rng = (Range)typeof(Paragraph).GetProperty("Range").GetValue(comObj);
We tried to eliminate querying ActiveDocument thinking that that might have had side-effects that were causing the problem but that was not the case.
Then, we confirmed that the selection was not "lost" and screen-updating is the only problem, so we tried restoring the UI with Application.ScreenRefresh and while it did work, it causes the screen to flicker every time the timer fires and this is not good enough.
Finally, knowing that it's only a UI problem, I tried simply switching off Application.ScreenUpdating...
in ThisAddin
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Timer timer = new Timer(2000);
timer.Elapsed += (s, t) =>
{
var scrnUpdating = Application.ScreenUpdating;
Application.ScreenUpdating = false;
MainTimer.onElapsed(Application, t);
if (scrnUpdating)
Application.ScreenUpdating = true;
};
timer.Start();
}
In another class library (note that it's static, I still think this is the best way)...
public static class MainTimer
{
public static void onElapsed (Word.Application state, System.Timers.ElapsedEventArgs e)
{
if (state.Documents.Count > 0)
{
var doc = state.ActiveDocument;
Word.InlineShapes shps;
Word.Paragraphs pars;
try
{
pars = doc.Paragraphs;
}
catch (Exception)
{
return;
}
var pars2 = pars.Cast<Word.Paragraph>()
.Where(p => p.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText)
.Select(p => p) // do stuff with the selected parragraphs...
.ToList();
}
}
}
And this works for me.
The selection remains and is displayed in the UI without flicker.
I also eliminated some premature enumeration from your code: you don't meed the .ToList() before the foreach.

Application.Quit() won't work - Unity3D unityscript

I've little trouble with my simple game.
I'm using this code for menu control:
#pragma strict
var isIZADJI=false;
function OnMouseEnter()
{
renderer.material.color=Color.red;
}
function OnMouseExit()
{
renderer.material.color=Color.white;
}
function OnMouseUp()
{
if(isIZADJI)
{
Application.Quit();
}
else
{
Kontrola_Zivota.ZIVOTI=3;
Application.LoadLevel(1);
}
}
When I click "Play Again" it works fine but when I click "Exit" it just load first level.
Any help here?
I just thought I'd expand on the solution:
I was reading through your code and I see that what you were looking for was a way to identify which button was clicked. I would like to expand on your answer for anyone who want's to know what your solution was. The way to solve this is to create a string variable to check the name of the thing you are clicking (assuming this is GUI Button) and then to change the state of the var isIZADJI based on that eg:
// izlaz[croatian] = exit[english]
// First create a string var for the name of button/GUI/object
var nameOfButton : String;
function OnMouseUp()
{
if(!(nameOfButton == "izlaz"))
{
isIZADJI = false;
}
else
{
isIZDAJI = true;
}
// And then now you can add the rest of your code to quit or load a level
if(isIZADJI)
{
Application.Quit();
}
else
{
Kontrola_Zivota.ZIVOTI=3;
Application.LoadLevel(1);
}
}
I hope this helps anyone else with this problem.