AudioSource attached button doesnt work in another scene - unity3d

public AudioSource menumusic;
public Button SoundButton;
public Sprite Soundoff;
public Sprite SoundOn;
bool isClicked = true;
private void Awake()
{
DontDestroyOnLoad(this.gameObject);
SoundButton.image.sprite = SoundOn;
}
// Start is called before the first frame update
void Start()
{
menumusic = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
SoundButton.onClick.AddListener(BtnOnClick);
}
void BtnOnClick()
{
changeState();
}
private void changeState()
{
isClicked = !isClicked;
if(isClicked)
{
SoundButton.image.sprite = SoundOn;
menumusic.Play();
}
else
{
SoundButton.image.sprite = Soundoff;
menumusic.Stop();
}
}
I have script like this And I am using on menu scene but when I switch to game scene with other button , this script works in there to except that he can not find soundbutton , how can I link to sound button from another scene should I create same to game scene too ? , sorry I am new on unity.

It looks like you're setting the reference to your "SoundButton" object in the inspector. When you change scenes, you'll need to update the reference on this script to refer to the new button.
There are many ways to do this. You could just tag the soundButtons with "soundButton".
Then, wherever you're loading a new scene, update the reference by searching for the tag.
SoundButton = GameObject.FindObjectWithTag("soundButton").GetComponent<Button>();
It would also be possible to have a SoundButton script which you place on every sound button. This could then reference a static SoundManager object, and tell it to toggle the sound on or off when clicked. This is probably a cleaner way to do it.
Also note: You're adding a listener to the soundButton in Update() (i.e. every frame). This is unnecessary. Add the listener one time in Start(), or when you change Scenes, and it will trigger the event whenever the button is clicked. There is no need to use Update() for this process.
If it still isn't working, you can also check the variable before using it, and get the reference again if it's null:
if(soundButton == null)
{
SoundButton = GameObject.FindObjectWithTag("soundButton").GetComponent<Button>();
}

Related

Need a way to use button click functionality vs GetKeyDown in Coroutine

This animator script works, however, in place of the Keycode input inside the WHILE LOOP, I need to use a UI button for mobile, which I haven't been able to figure out. I found an answer about putting a wrapper around it to make method available to click event, but have no idea how that's supposed to work within update function. It took me a long time to get this far, being a newbie to unity AND c#, so if you could provide a detailed answer or suggestion, I can get my life back, please help if you can.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using SWS;
public class goat_question : MonoBehaviour
{
private Animator anim;
public GameObject player;
public Text ResultText;
public Text AnswerText;
public Text AnswerText2;
public Button GoatButton;
void Start()
{
anim = GetComponent<Animator>();
Button btn = GoatButton.GetComponent<Button>();
btn.onClick.AddListener(TaskOnClick);
}
void TaskOnClick()
{
Debug.Log("You have clicked the button!");
}
// Update is called once per frame
void Update()
{
if (AnswerText.text.Equals(AnswerText2.text))
{
StartCoroutine(GoatWalkPathCoroutine());
}
IEnumerator GoatWalkPathCoroutine()
{
while (true)
{
if (Input.GetKeyDown(KeyCode.K))
{
anim.Play("goat_hi_walk");
player.GetComponent<splineMove>().enabled = true;
yield return new WaitForSeconds(27);
anim.Play("goat_hi_licking");
}
yield return null;
}
}
}
}
In a separate script for just the UI button, have a bool called isClicked or something, and when the button gets clicked set that to true. In this main script, you can reference the one you just made, and instead of the Input.GetKey, you can say, if(otherScript.isClicked).

Unity: UI.Button Adding OnClick Event From Script Fails

In my application I have a list of GameObject's, and I'm creating a button for each GameObject in the list via code. The problem is that when I add and onClick Event via code nothing shows up in the button onClick list, nothing happens when I click the button and I get no errors in the process. Here is how my code looks like:
public GameObject prefab;
public void Generate()
{
for (int i = 0; i < myList.Count; i++)
{
GameObject _t = Instantiate(prefab, myUIPanel.transform) as GameObject;
//Positioning, naming, ...
_t.GetComponent<Button>().onClick.AddListener(delegate { MyFunction(i); });
}
}
public void MyFunction(int index)
{
//...
}
I've created a GUILayout.Button inside an editor script to call the "Generate" method. The buttons are created and I get no errors, but no Event is added in the buttons.
edit: because you ui element is being instantiated from the prefab the button is not hooking up to EventSystem
workaround: use scriptableobject instead of monobeviour, scriptableobjects work independent from scene/gameobject instances
tutorial:
https://docs.unity3d.com/Manual/class-ScriptableObject.html
https://www.raywenderlich.com/2826197-scriptableobject-tutorial-getting-started

Duplicated objects are not working 'per instance' when I intended them to do [duplicate]

This question already has answers here:
How to detect click/touch events on UI and GameObjects
(4 answers)
Closed 5 years ago.
using UnityEngine;
public class LinkEnd : MonoBehaviour
{
public GameObject linkTarget;
private PointEffector2D effector;
private CircleCollider2D contact;
private AimSystem aimer;
private float distFromLink = .2f;
public bool connected;
private void Start()
{
aimer = GetComponent<AimSystem>();
}
private void Update()
{
SyncPosition();
ReactToInput();
}
public void ConnectLinkEnd(Rigidbody2D endRB)
{
HingeJoint2D joint = GetComponent<HingeJoint2D>();
if (GetComponent<HingeJoint2D>() == null)
{
joint = gameObject.AddComponent<HingeJoint2D>();
}
joint.autoConfigureConnectedAnchor = false;
joint.connectedBody = endRB;
joint.anchor = Vector2.zero;
joint.connectedAnchor = new Vector2(0f, -distFromLink);
}
private void SyncPosition()
{
if (linkTarget != null)
{
if (Vector2.Distance(transform.position, contact.transform.position) <= 0.1f)
{
connected = true;
effector.enabled = false;
contact.usedByEffector = false;
}
}
if (connected)
{
GetComponent<Rigidbody2D>().isKinematic = true;
GetComponent<Rigidbody2D>().position = linkTarget.transform.position;
}
else
GetComponent<Rigidbody2D>().isKinematic = false;
}
private void ReactToInput()
{
if (Input.GetKeyUp(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1))
{
connected = false;
}
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.GetComponent<PointEffector2D>() != null)
{
connected = true;
linkTarget = collision.gameObject;
effector = linkTarget.GetComponent<PointEffector2D>();
contact = linkTarget.GetComponent<CircleCollider2D>();
}
}
public void OnTriggerExit2D(Collider2D collision)
{
connected = false;
contact.usedByEffector = true;
effector.enabled = true;
}
}
This is an object that pins its position to another mobile object on collision, and it's supposed to stay that way until it's 'detached' by player action.
It's working almost fine, but it's not working 'per instance.'
Whether this object is a prefab or not, ReactToInput() is affecting all instances of it unlike how I wanted.
I'm missing some per instance specification here and I'm not seeing where.
Any suggestion will help and be appreciated!
++ The method ReactToInput() is triggered by key inputs. I wanted this method to be called when Player's attack 'method' happens which are bound to those key inputs, but I did what I've done only because I couldn't find an elegant way to execute it otherwise, and am really hoping there's a better way rather than using tags or GetComponent to specific object since it's supossed to affect other objects as well.
These methods are what you are looking for.
/// <summary>
/// OnMouseDown is called when the user has pressed the mouse button while
/// over the GUIElement or Collider.
/// </summary>
void OnMouseDown()
{
Debug.Log("Hi!");
}
/// <summary>
/// OnMouseUp is called when the user has released the mouse button.
/// </summary>
void OnMouseUp()
{
Debug.Log("Bye!");
}
MonoBehaviour provides many event callbacks other than Update(), and these two are some of them. The full list of event callbacks you can use for MonoBehaviour is explained in the official Monobehaviour page.
There are two methods for OnMouseUp():
OnMouseUp is called when the user has released the mouse button.
OnMouseUpAsButton is only called when the mouse is released over the
same GUIElement or Collider as it was pressed.
The GameObject your script is attached to is requires to have GUIElement or Collider as described in the manual page to use these functions.
If you do not want to use these methods, you could alternatively write your own custom InputModule, raycast to the mouse position on the screen to find which object is clicked, and send MouseButtonDown() event to a clicked GameObject.
I had to implement a custom input module to do this plus a couple of other stuff and I assure you writing custom InputModules is a headache.
EDIT:
If many different classes need to be notified when something happens, and who listens to such cases is unknown, event is a good option.
If you are using events, each event listener class such as LinkEnd is responsible to register and remove itself to such event.
Below is an example of how you could achieve this behaviour:
class Player
{
public delegate void OnSkillAFiredListener(object obj, SkillAFiredEventArgs args);
public static event OnSkillAFiredListener SkillAPressed = delegate { };
// ...
}
class LinkEnd
{
void OnEnable()
{
Player.SkillAPressed += WhatToDoWhenSkillAFired;
}
void OnDisable()
{
Player.SkillAPressed -= WhatToDoWhenSkillAFired;
}
void OnDestroy()
{
Player.SkillAPressed -= WhatToDoWhenSkillAFired;
}
public void WhatToDoWhenSkillAFired(object obj, SkillAFiredEventArgs args)
{
// get info from args
float someInfo = args.someInfo
// do something..
Bark();
}
// ...
}
It's necessary to deregister from the event in both OnDisable() and OnDestory() to avoid memory leaks (some claim such memory leaks are very minor).
Look for Observer and Publisher/Subscriber pattern to learn more about these approaches. It may not be very related to your case, but the Mediator Pattern is something that's often compared with the Observer Pattern so you might be interested to check it as well.

In VR development ,how can I make a handle shotting?

I want to make an example of shot,
then I wrote this in the handle button event,
using UnityEngine;
using System.Collections;
public class fire : MonoBehaviour {
public GameObject bullet;
SteamVR_TrackedObject trackedObj;
void start() {
trackedObj = GetComponent<SteamVR_TrakedObject>();
}
void Update() {
var device = SteamVR_Controller.Input((int)trackedObj.index);
if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger)) {
GameObejct obj = Instantiate(bullet,transform.position);
Vector3d fwd = transform.TransformDirection(Vector3.forward);
obj.GetComponent.<Rigidbody>().AddForce(fwd*2800);
}
}
}
but when debugging and I press handle button ,it didn't produce a bullet,and had erred at the line
var device = SteamVR_Controller.Input((int)trackedObj.index);,
the error is:
Object reference not set to an instance of an object.
First You should need to confirm that your fire script is attached to your controller object and your controller object also attached SteamVR_TrackedObject script (which provide by steam plugin).
Then, lastly ensure this line is executing
void start() {
trackedObj = GetComponent<SteamVR_TrakedObject>();
}

How to display a name and score list for players using happyfuntimes and unity3d

This is related to happyfuntimes plugin if you have used it .
I am trying to make a game on it and stuck at a point of displaying score along with name to display on large screen while user is playing on his mobile.(i have already tried to display name and score on mobile screens have seen in sample do not need that ). Please suggest how can this be done if you have used happyfuntimes plugin.
I could see the HFTgamepad input having public GameObject player name which I am trying to access ,do I have to make array ?
public string playerName;
I am trying to put these name on array.
Displaying anything in unity is really normal unity issue and not special to happyfuntimes. Games display highscore lists, item lists, inventory lists, etc... A list of players is no different
Use the GUI system display text.
According to the docs if you want to generate UI dynamically you should make a prefab
So basically you'd make a UI hierarchy in the scene. When a player is added to the game Instantiate your name-score prefab, search for the UI hierarchy gameObject (do that at init time), then set the parent of the preafab you just instantiated so it's in the UI hierarchy.
Look up the UI.Text components that represent name and score and set their text properties.
There's various auto layout components to help layout your scores
When a player disconnects Destroy the prefab you instantiated above.
OR ...
Conversely you can use the old immediate mode GUI. You'd make a GameObject, give it a component that has a OnGUI function. Somewhere keep a list of players. Add players to the list as they connect and remove them from the list as they disconnect. In your OnGUI function loop over the list of players and call the GUI.Label function or similar to draw each name and score
Here's a hacked up example.
Assuming you have a PlayerScript script component that's on each player that has a public accessor PlayerName for getting the player's name then you can make a GameObject and put a script like this on it.
using UnityEngine;
using System.Collections.Generic;
public class ExamplePlayerListGUI : MonoBehaviour
{
static ExamplePlayerListGUI s_instance;
List<PlayerScript> m_players = new List<PlayerScript>();
static public ExamplePlayerListGUI GetInstance()
{
return s_instance;
}
void Awake()
{
if (s_instance != null)
{
Debug.LogError("there should only be one ExamplePlayerListGUI");
}
s_instance = this;
}
public void AddPlayer(PlayerScript bs)
{
m_players.Add(bs);
}
public void RemovePlayer(PlayerScript bs)
{
m_players.Remove(bs);
}
public void OnGUI()
{
Rect r = new Rect(0, 0, 100, m_players.Count * 20);
GUI.Box(r, "");
r.height = 20;
foreach(var player in m_players)
{
GUI.Label(r, player.PlayerName);
r.y += 20;
}
}
}
PlayerScript can then call ExamplePlayerListGUI.GetInstance().AddPlayer in its Start function and ExamplePlayerListGUI.GetInstance().RemovePlayer in it's OnDestroy function
public PlayerScript : MonoBehaviour
{
private string m_playerName;
...
public string PlayerName
{
get
{
return m_playerName;
}
}
void Start()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
// Check if it exists so we can use it with or without the list
if (playerListGUI != null)
{
playerListGUI.AddPlayer(this);
}
}
void OnDestroy()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
if (playerListGUI != null)
{
playerListGUI.RemovePlayer(this);
}
}
}