Cancelling audio clip before starting a new one? - c#

I almost have a working radio, where the player can walk up to it, press a button and cycle through songs. Everything works except for that with each button press the new song will play but the original will continue to play underneath it. For each new song that is played a new object is created but they are all called 'One Shot Audio,' so I don't know how to destroy them. If I can fix this bug then this should be a useful radio script for anyone who wants to use it.
Update, here is my modified radio code, now working, with the help of the answer below:
void OnTriggerEnter2D(Collider2D target) {
if (target.gameObject.tag == "radio") {
radioEnter = true;
}
}
void OnTriggerExit2D(Collider2D target) {
if (target.gameObject.tag == "radio") {
radioEnter = false;
}
}
public void radioUse(){
if ((Input.GetKeyDown (KeyCode.M)) && song3on == true && radioEnter == true) {
TurnOn ();
song1on = true;
song2on = false;
song3on = false;
}
else if ((Input.GetKeyDown (KeyCode.M)) && song1on == true && radioEnter == true) {
TurnOff ();
song1on = false;
song2on = true;
song3on = false;
TurnOn();
}
else if ((Input.GetKeyDown (KeyCode.M)) && song2on == true && radioEnter == true) {
TurnOff ();
song1on = false;
song2on = false;
song3on = true;
TurnOn ();
}
}
public void Update() {
raidoUse();
}

Since PlayClipAtPoint does not return an AudioSource to manage you should not use it for these kind of sounds. I recommend setting up your radio with one AudioSource and multiple AudioClips in a array. Just drag the clips you want to the inspector and use the public methods to control the radio. This way you can reuse it with different songs.
The following code is not tested.
Public class Radio : MonoBehaviour
{
AudioSource output;
public AudioClip[] songs;
int songIndex = 0;
void Start(){
output = gameObject.AddComponent<AudioSource>();
}
public void ToggleSong(){
songIndex++;
output.clip = songs[songIndex % songs.Length];
output.Play();
}
public void TurnOn(){
ToggleSong();
}
public void TurnOff(){
output.Stop();
}

Related

Need a boolean check every frame(update call).But the function called from that boolean check should be called only once

So i am working on a project using a webcam.If i go out-of-screen(view of the camera) a timer starts which track the time i am out of view and if it goes above a certain max value my game gets paused.
Similarly for the In-screen view(inside camera view), timer starts and after max value it resumes the game.
void Update()
{
if (InFrontOfScreen()) //This is a condition
{
isInFront = true;
}
else
{
isInFront = false;
}
InView();
Pausing();
}
private void Pausing()
{
if (isPaused)
Time.timeScale = 0f;
else
Time.timeScale = 1f;
}
private void InView()
{
if (isInFront)
{
inViewTime += Time.unscaledDeltaTime;
if (inViewTime >= maxInViewTime)
{
isPaused = false;
}
outOfViewTime = 0f;
}
else
{
outOfViewTime += Time.unscaledDeltaTime;
if (outOfViewTime >= maxOutOfViewTime)
{
isPaused = true;
}
inViewTime = 0f;
}
}
Now in another script I have to call functions that depend on whether the game is paused or not.
void Update()
{
SwitchCameras();
}
private void SwitchCameras()
{
if(PauseController.isPaused)
{
gameCam.enabled = false;
mainCam.cullingMask = pausedLayerMask;
}
else
{
gameCam.enabled = true;
mainCam.cullingMask = originalLayerMask;
}
}
The problem is that this gameCam.enabled and mainCam should be called only once when the bool isPaused changes.It is being called every frame. I get the answer right but I want it to be called only once and not every frame.Is there a way to acheive this?
Though isPaused can change back and forth the functions should be called once and then again when isPaused changes .
Thank you
From what I can see you only need to cache the value and when a new update is called just check if the value changed
// Somewhere have a field that saves previous state
private bool _wasPaused;
void Update()
{
SwitchCameras();
}
private void SwitchCameras()
{
if (_wasPaused != PauseController.isPaused)
{
if (PauseController.isPaused)
{
gameCam.enabled = false;
mainCam.cullingMask = pausedLayerMask;
}
else
{
gameCam.enabled = true;
mainCam.cullingMask = originalLayerMask;
}
}
_wasPaused = PauseController.isPaused;
}
We can use a private variable and a property. This way we can check to see when a property changes value, and just process an action when the value changes.
Very quickly, this should be a way to achieve what you’re trying to do:
private bool _isInFront;
public bool isInFront
{
get => _isInFront;
set
{
if ( _isInFront == value )
return;
_isInFront = value;
InView();
}
}
private void Update ()
{
isInFront = InFrontOfScreen(); //This is a condition
Pausing();
}
The problem is that this gameCam.enabled and mainCam should be called only once when the bool isPaused changes
Well in that case (and in general) you should not poll check flags in Update at all but rather make your code reactive event driven and only react a single time the moment the value actually is changed.
So in your first script I would add a
public event Action<bool> pauseStateChanged;
whenever you set the state of isPaused also invoke the event accordingly and do the change check already here in the responsible class instead of all listeners having to track it themselves
...
// Check makes sure the event is really only called when the state is changed
if(!isPaused)
{
isPaused = true;
pauseStateChanged?.Invoke(true);
}
and then in the other script(s) attach a listener once which will only be invoked when the event is invoked -> when the state actually changed
private void Start()
{
// start listening to the event
PauseController.pauseStateChanged += SwitchCameras;
// initially call the callback once with the current state
SwitchCameras(PauseController.isPaused);
}
private void OnDestroy()
{
// remove the listener once not needed anymore to avoid exceptions
PauseController.pauseStateChanged -= SwitchCameras;
}
private void SwitchCameras(bool isPaused)
{
gameCam.enabled = !isPaused;
// Using a ternary here makes this way easier to read imho
mainCam.cullingMask = isPaused ? pausedLayerMask : originalLayerMask;
}
And following this very same principle you could also make the rest of your first script reactive the same way instead of calling things every frame in Update I would probably use a Coroutine and do
private Coroutine routine;
void Update()
{
var currentIsInFront = InFrontOfScreen();
// Did state change?
if(isInFront != currentIsInFront)
{
if(routine != null)
{
StopCoroutine(routine);
}
isInFront = currentIsInFront;
routine = StartCoroutine(PauseRoutine);
}
}
private IEnumerator PauseRoutine()
{
var newPausedState = !isInFront;
if(isPaused != newPausedState)
{
var delay = isInFront ? maxInViewTime : maxOutOfViewTime;
yield return new WaitForSecondsRealtime(delay);
isPaused = newPausedState;
pauseStateChanged?.Invoke(newPausedState);
Time.timeScale = newPausedState ? 0f : 1f;
}
}

How do I deactivate all the virtual buttons in Vuforia after pressing just one?

Good afternoon!
I'm trying to make a simple AR quiz game that requires virtual buttons to answer. The game goes on if the correct button is pressed.
The question is: how do I deactivate all the virtual buttons when a virtual button is pressed?
I've looked at nearly every topic regarding this, but can't figure it out how to make this work.
At this point I can press all the buttons but only once.
Thanks in advance (and sorry for my bad English)!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;
public class PushToDebug : MonoBehaviour, IVirtualButtonEventHandler
{
public string vbName;
public AudioSource audi;
void Start()
{
//Register with the virtual buttons TrackableBehaviour
VirtualButtonBehaviour[] vrb = GetComponentsInChildren<VirtualButtonBehaviour>();
audi = GetComponent<AudioSource>();
for (int i = 0; i < vrb.Length; i++)
vrb[i].RegisterEventHandler(this);
}
public void OnButtonPressed(VirtualButtonBehaviour vb)
{
vbName = vb.VirtualButtonName;
if (vbName == "VB1")
{
Debug.Log("Button 1 is pressed!");
audi.Play();
vb.GetComponent<VirtualButtonBehaviour>().enabled = false;
}
else if (vbName == "VB2")
{
Debug.Log("Button 2 is pressed!");
audi.Play();
vb.GetComponent<VirtualButtonBehaviour>().enabled = false;
}
else
{
Debug.Log("Button 3 is pressed!");
audi.Play();
vb.GetComponent<VirtualButtonBehaviour>().enabled = false;
}
}
public void OnButtonReleased(VirtualButtonBehaviour vb)
{
if (vbName == "VB1")
{
Debug.Log("Button 1 is released!");
}
else if (vbName == "VB2")
{
Debug.Log("Button 2 is released!");
}
else
{
Debug.Log("Button 3 is released!");
}
}
}
Thank you for your reply!
For some weird reason it doesn't just work.
I added Debug.Log("DeactivateButtons is called!"); and this method wasn't called at all.
Looks like you are disabling only one button when pressed whereas others remain active. You can add a function to check if any of the buttons are pressed so that you can deactivate them.
Or since you are finding the name of the button when pressed, then you can probably create 3 variables such as:
var VB1 = GameObject.Find("VB1");
var VB2 = GameObject.Find("VB2");
var VB3 = GameObject.Find("VB3");
if(VB1 != null && VB2 != null && VB3 != null){
VB1.GetComponent<VirtualButtonBehavior>().enabled = false;
VB2.GetComponent<VirtualButtonBehavior>().enabled = false;
VB3.GetComponent<VirtualButtonBehavior>().enabled = false;
}
Now to integrate the above to your code, perhaps under OnButtonPressed() function; you can add:
public void OnButtonPressed(VirtualButtonBehaviour vb)
{
vbName = vb.VirtualButtonName;
if (vbName == "VB1")
{
Debug.Log("Button 1 is pressed!");
audi.Play();
vb.GetComponent<VirtualButtonBehaviour>().enabled = false;
DeactivateButtons();
}
else if (vbName == "VB2")
{
Debug.Log("Button 2 is pressed!");
audi.Play();
vb.GetComponent<VirtualButtonBehaviour>().enabled = false;
DeactivateButtons();
}
else
{
Debug.Log("Button 3 is pressed!");
audi.Play();
vb.GetComponent<VirtualButtonBehaviour>().enabled = false;
DeactivateButtons();
}
}
public void DeactivateButtons()
{
var VB1 = GameObject.Find("VB1");
var VB2 = GameObject.Find("VB2");
var VB3 = GameObject.Find("VB3");
if(VB1 != null && VB2 != null && VB3 != null){
VB1.GetComponent<VirtualButtonBehavior>().enabled = false;
VB2.GetComponent<VirtualButtonBehavior>().enabled = false;
VB3.GetComponent<VirtualButtonBehavior>().enabled = false;
}
}

Is there a way to hold to long jump in Unity with a touchInput button?

I'm making a game and I want jumping to feel like jumping in Super Mario Bros. I'm able to get the result I want with a Keyboard or a Controller because they have KeyDown, Key (While pressed), and KeyUp. But touchButtons only have a single boolean. (Pressed or Not Pressed) Is there a way I can work around this?
I tried using Input.GetTouch and using the begin and end phase, this gave the correct result but I'm not sure how to implement it into a GUI button.
The code I'm using has a GUI button with a script that when the button is pressed, joybutton.Pressed = true
void PlayerJump()
{
bool canJump = charController.isGrounded;
//Button Pressed start jumpDuration
if (joybutton.Pressed && canJump)
{
isJumping = true;
jumpDuration = jumpTime;
}
if (isJumping == true)
{
if (jumpDuration > 0)
{
vertical_Velocity = jump_Force;
jumpDuration -= Time.deltaTime;
}
//timer runs out
else
{
isJumping = false;
}
}
//cancel jump if mid-air
if (!joybutton.Pressed)
{
isJumping = false;
}
}
I have no way of stopping the player from jumping as soon as they land with the GUI touchButton. I get desired results with keyboard and gamepad buttons.
Add a variable to remember the state of the button last frame. That way, you can enter the jump start block only if it's the first frame of the button being hit:
private bool wasJumpPressedLastFrame = false;
void PlayerJump()
{
bool canJump = charController.isGrounded;
//Button Pressed start jumpDuration
if (joybutton.Pressed && canJump && !wasJumpPressedLastFrame )
{
isJumping = true;
jumpDuration = jumpTime;
}
if (isJumping == true)
{
if (jumpDuration > 0)
{
vertical_Velocity = jump_Force;
jumpDuration -= Time.deltaTime;
}
//timer runs out
else
{
isJumping = false;
}
}
//cancel jump if mid-air
if (!joybutton.Pressed)
{
isJumping = false;
}
wasJumpPressedLastFrame = joyButton.Pressed;
}

Converting mouse position functionality to work with touchscreen

I'm developing in Unity, C#, and I have a bit of code that checks for player activity based on mouse position and it's working well but I'm needing to also check player activity on a touchscreen (not a mobile phone touchscreen, but a touchscreen attached to a pc). How should I modify the code that I have below to also work with touch?
private void Start()
{
InvokeRepeating("LastMousePosition", 0, _checkMousePositionTimingInterval);
}
private void Update()
{
_currentMousePosition = Input.mousePosition;
}
void LastMousePosition()
{
_prevMousePosition = Input.mousePosition;
}
void CheckPlayerIdle()
{
if (_currentMousePosition != _prevMousePosition)
UserActive = true;
else if (_currentMousePosition == _prevMousePosition)
UserActive = false;
}
Well for touch you start your inactivity timer if you don't get any touch inputs and wait for x duration.
for eg:
private void Update()
{
if(Input.touchCount == 0)
{
if(!checkingForInactivity)
{
checkingForInactivity = true;
myRoutine = StartCoroutine(CheckForInactivity());
}
}
else
{
if(checkingForInactivity) StopCoroutine(myRoutine);
}
}
Ienumrable CheckForInactivity()
{
yield new waitForSecond(3.0f);
//user is inactive
}
}

Making enemy sensors not detect you immediately

I'm working on making a drone-type enemy that has a sensor that sticks out in front of it. I've been stuck on it for a while now, the problem is that I want the drone to not attack immediately after it detects the player, and for it to stay in suspicious mode a little while after the player leaves the drone's field of view. I'm relatively new to coding so my code is a bit of a mess.
void Update () {
if (dTect == true && dTime > -1.6) {
dTime -= Time.deltaTime;
if (dTime < -1.5f) {
aTak = true;
animator.SetTrigger ("Host");
animator.ResetTrigger ("Susp");
} else {
dTect = false;
//animator.ResetTrigger ("Susp");
}
}
if (dTect == false && dTime > 2.1) {
dTime += Time.deltaTime;
}
if (dTime > 2) {
aTak = false;
animator.SetTrigger ("Susp");
animator.ResetTrigger ("Host");
}} void OnTriggerEnter2D(Collider2D other){
if (other.gameObject.CompareTag("Player")){
dTime = 0;
dTect = true;
animator.SetTrigger("Susp");
}
}
void OnTriggerExit2D(Collider2D other){
if (other.gameObject.CompareTag ("Player")) {
dTime = 0;
dTect = false;
}
}
Any help or advice at all would be greatly appreciated.
void Update()
{
if (dTect == true)
{
dTime += Time.deltaTime;
if (dTime >= 1.5f)
{
aTak = true;
animator.SetTrigger("Host");
animator.ResetTrigger("Susp");
dTect = false;
}
}
if (aTak == true)
{
Attack();
}
}
public void Attack()
{
//fill in whatever you want it to do when attacking
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
dTime = 0;
dTect = true;
sPect = false;
animator.SetTrigger("Susp");
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
dTime = 0;
sPect = true;
dTect = false;
aTak = false;
}
}
I believe the problem was with the timers not adding up correctly and not setting the bools to the right state. Also I would highly recommend looking into using an enum for states instead of booleans, they're a lot easier to use cause you don't have to turn off every other state to change it. Good luck with your game and let me know if this wasn't the solution or if you have any other questions :)

Categories