Unity - Assign Button in Scene to Prefab - c#

I'm having trouble assigning two Buttons to the Prefab after it is instantiated.
The buttons are in the scene and i don't know how to assign them.
Drag and Drop doesn't work of course. I'm aware of that.
When I make something like
btnnext = GameObject.Find("Next").GetComponent<Button>();
in the Start() function of the Prefabs script it doesn't work either.
Are there any other workarounds?

As #slowikowskiarkadiusz said GameObject.Find is not the best solution, because it's slow and error prone. But there is an easy solution.
On the script which is placed on the prefab make a public function, called AssignButton:
class ScriptOnPrefab : MonoBehaviour {
public void AssignButton(Button button) {
btnnext = button;
}
}
In the script where you instantite the prefab, you then link the buttons and assign them:
var instance = Instantiate(prefab);
var scriptOnPrefab = instance.GetComponent<ScriptOnPrefab>();
scriptOnPrefab.AssignButton(button);
Note: For this to work that the ScriptOnPrefab has to be on the root of the prefab, note on child objects.
Note: prefab is linked as a GameObject.
Note: If you link the prefab as ScriptOnPrefab you can skip the GetComponent step and immediatelly call the Assign method.

By 'doesn't work either' you mean that btnnext is null or you mean that .GetComponent<Button>() throws an exception? The first case would mean the object named "Next" that was found doesn't have a Button component on it. You can check if you're expecting it to have the Button component or maybe something slightly different. You could also have multiple objects named "Next" and the one you're getting is not the one you expect to get. The second case would mean that your object is most likely inactive. Find(string) doesn't search within inactive objects.
That being said - Find(string) is not reliable in any capacity and I would advice to avoid it (it's also terribly slow). Instead I would create a script to be placed on the object with the button. Inside of that script in the Awake() method I would assign the instance of the Button component to some kind of a public static field, so the other script can later pick it up (if you are dealing with two buttons it might be a list or two separate fields. Depends on your case I guess).

Related

How to group a bunch of objects into one gameobject on a button press

So i challenged myself to make a little game where you build a rocket and then you press the button it launches
i want to it to group up all of the objects and adds a placement script to the parent object all within a script i do not know if this is even possible in unity but if there are other methods or others that are better
using tags did not work i want to let them group up and launch!
Each time you instantiate a piece, cache a reference to it. When it's time to launch, use the first cached reference as the parent and then, for each other piece, set its parent to be the first piece. It'd look something like
for(int i=1; i<parts.Count; i++)
{
parts[i].transform.parent = parts[0].transform;
}

Keep Links between two gameObjects when scenes change in unity

I'm Working on a game, and i want to create a mute button. So i wrote a script for that and i attach this script into a gameObject that don't destroy on load.
I link the script with a UI Button present in a main menu. This UI button is destroy when i'm changing scenes.
when i start my game, and i click on the button, the audio turns off, and when i click back, it turns on.
But when i change to another scene, and i go back to the main menu, my script doesn't have the UI Button attach to it. so when i touch the button the audio doesn't change is behavior
I would like to know if it's possible to maintain the link between the UI Button and the script (attach to a normal GameObject), even if the UI Button is destroyed?
i tried this:
ButtonGameObject = GameObject.Find("UI Button");
but it doesn't work.
How can i fix that?
Thanks a lot.
There are many ways to work around this, but here's one:
Step 1: If you haven't already done so, implement a weak singleton pattern on your mute script (let's call it MuteScript.cs for now).
private static MuteScript singleton {get; private set;}
private void Awake() {
if (singleton == null) singleton = this;
[whatever other stuff you were already doing in Awake()]
}
public static void ToggleMute(Graphic graphic)
{
singleton._ToggleMute(graphic);
}
private void _ToggleMute(Graphic graphic)
{
[whatever code you were running in your original mute method]
}
Step 2: Attach a simple script to your UI button:
public class MuteButton: MonoBehaviour
{
Graphic myGraphic;
private void Awake() {
myGraphic = GetComponent<Graphic>();
}
public void OnClick() {
MuteScript.ToggleMute(myGraphic);
//I assume you want to do something like change the colour of the button when
//the player toggles it. Passing the Graphic to the MuteScript is the easiest
//way of doing this. If you really want to keep your code clean, though,
//I recommend expanding the MuteButton class with methods to take care of
//the UI side of things.
}
}
Step 3: In Unity Editor, setup your button to call its own OnClick() method, not the MuteScript method.
-
Now when you click the button, it will call the static MuteScript.ToggleMute(), which accesses the static-cached singleton reference, which in turn points back to your original object.
Singletons and static accessors are great for efficiency in Unity because they save you from having to call expensive search functions like FindObjectsOfType(). The only gotcha is that you have to be careful about not having multiple copies of a singleton-class object lying around, especially when using DontDestroyOnLoad().
So a better approach rather than having a script search and grab the component is to use a PlayerPrefs class from Unity. Essentially, it will hold onto all important aspects of the game and auto fill information.
This is a great tool for a lot of user customization aspects of a game. When using this, have a script (sceneController would be a good name) that will run when the scene starts (create a blank object at 0,0,0) and then under void Start() have the script grab the mute/unmute button: GameObject.Find("MuteButton") or (my favorite) give it a tag called MuteButton and run: GameOject.FindWithTag("MuteButton"). Also once you get a link to the button, add a listener to it for when the button is pressed.
Also store the sound manager in a gameController that will be passed throughout the game. This would control the soundManager and have access to that. So sceneManager will also need a reference to gameManager.
Using a script that is just for player preferences (a controller if you will) is a better way to organize and contain any preferences for the users. Just better clarity and separation.
Example
SceneController Object Script
class SceneController {
GameObject muteButton;
void Start() {
muteButton = GameObject.FindWithTag("muteButton");
muteButton.AddListener(muteButtonCheck);
}
void muteButtonClick() {
if (PlayerPrefs.GetInt("playerMute")) {
// If 1 (on)
// Set sound off
PlayerPref.SetInt("playerMute", 0);
} else {
// It's 0 (off)
// Set sound on
PlayerPrefs.SetInt("playerMute", 1);
}
}
}

Load scene using canvas button

void OnMouseDown() {
SceneManager.LoadScene ("Scene2");
}
I have tried every conceivable method. The method posted has worked for me using GameObjects with colliders. Instead, this time I am using a button on a 2D canvas. It does not work in this context.
How do I load a new scene using a button in a canvas? I have tried so many different things. This should be simple.
Thanks for any advice.
Here (link: Unity page) you can find a video tutorial how to use Button on canvas in UnityGUI. It's for Unity 4.6 but its really simillar to newest (5.3.1).
It's quite simple. U can make a script with public method e.g
public void LoadScene2()
{
SceneManager.LoadScene ("Scene2");
}
Attach this script to some GameObject e.g Controller. And add event in Button inspector.
In my opinion there is a better solution for the one shown by #Paweł Marecki
I use this in my projects.
OK so you will simply create a script called ButtonManager and inside it you can make a method like this
public void ChangeToScene(string sceneName)
{
Application.LoadLevel(sceneName);
OR
SceneManager.LoadScene(sceneName);
}
Now you have your canvas button, you will select it and look for "Event Trigger"
(i got this image from google to help) add a new mouse down event.
Create an empty GameObject on your Scene, name it "ButtonManager" and drag it onto the event box.
Now you need to click that dropDown list and find your "ChangeToScene" method.
You will see that an editor field appears below, type your desired scene name and hit play :P
This way you will always use this script when you want to change scenes.
You can add other methods and add functionality, but the beautiful part is that you dont need to create a method each time the name of the scene changes.

How would I implement a User Control swapping method?

I'm trying to swap "Scenes", the equivalent of levels in a game. I already know that Forms is not good for gaming, but what I'm making isn't so much a game as it is an interactive movie built out of some dialog options and animated .gifs.
I plan on building each "Scene" in a separate User Control, and then load up those User Controls onto the main form with a separate script that handles all of the scenes. In this separate script, I was going to add the "ChangeScene" method, so I could essentially trigger a scene to change once it finished, and have all scene changes in one script. I've run into two problems so far.
1: I can't seem to call anything from this Scene Handler script. I've been trying to use "Form1.SuspendLayout" and "Form1.ResumeLayout" to swap between Scenes smoothly, but I can't seem to call that, and then when I try to set a boolean in a Scene to true once the scene finishes, I can't actually check that bool from the Scene Handler script. How can I let this one script use the bools from each of the Scenes?
2: Can I call the "Form1.SuspendLayout" method from this Scene Handler script? So far, I was told I needed a reference to 'System.Windows.Forms.Control.SuspendLayout()', although I don't know how to reference that. It's not a namespace or assembly, so how do I reference it?
Sorry if this question seems like a simple mistake. I'm new to Windows Forms and not too well experienced with C#, so any question I could find about this used terms too complicated for me to understand.
Scene Handler here: class SceneHandler :
{
public virtual UserControl changeScene(UserControl currentScene, UserControl newScene)
{
Form1.SuspendLayout();
currentScene.Visible = false;
currentScene.Dock = DockStyle.None;
newScene.Visible = true;
newScene.Dock = DockStyle.Fill;
return newScene; //use "this.SuspendLayout" and "ResumeLayout"
}
}
Forget SuspendLayout.
Load all the UCs upfront but hidden.
Don't worry about performance before you run into problems!
Put all methods that involve more than one UC in the Form as public methods.
Put a public reference to your mainform in each UC and set it right after loading the UC.
Now each control can call mainform.someMethod().
Feel free to ask for more advice and help!

Modify GameObject Component values from custom EditorWindow

I've created a custom window with a range of buttons. When a game objects are selected, and a button is pressed, the myPrefab value of the MyScript component of the selected game objects are set to a particular prefab (according to which button is pressed).
This is working up until the point where I press play. I may have 10 game objects with the MyScript component, and using the buttons set each of them to contain a particular prefab. In the inspector, I can see the prefab value has updated for each of the game objects.
However, as soon as I press the play button, any modifications made by the buttons is undone.
To clarify, if I set the prefab in the inspector, then with a button, it will revert to the one set in the inspector. If I simply set it by the inspector, it won't revert. If it is by default null, and only set by the buttons, it will revert to null. (Only the button results are being reverted).
Could anyone work out why this is the case? Is there some sort of confirm or save method I should be calling to "lock in" my choice?
Simply add "EditorUtility.SetDirty(component);" after the value is modified.
This marks the object as needing to be stored (instead of simply being the value shown in the editor).
In the inspector, things are marked as dirty as soon as you change them. Changing them through other editors like this however, means that the the object must manually be marked as dirty.
if (GUI.Button(rectangle, GUIContent.none))
{
foreach (GameObject go in Selection.gameObjects)
{
MyScript component = go.GetComponent<MyScript>();
if (component == null)
continue;
component.myPrefab = firstPrefab;
EditorUtility.SetDirty(component);
}
}

Categories