Instead of LoadLevel, how do i use SceneManager - unity3d

I super new in coding and I couldn't find the right answer on the web. All I want is just change scene in my game. I've already had the buttons etc. But i can't choose the "menu" script from the On click function menus.
All answer is welcome!
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class Menu : MonoBehaviour {
public void ChangeScene(string sceneName)
{
SceneManager.LoadScene("sceneName");
}
}

Several problems here. First, the function "ChangeScene"
public void ChangeScene(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
Then, you may have a problem in your scripts since you can't add any listener to the OnClick delegate. Fix every problem thrown by the console. A strange thing is that the name of the Menu script does not appear in the Menu component. Make sure the filename is "Menu.cs"
Finally, drag & drop the button into the field under "Runtime Only" and select "Menu > ChangeScene"

You can't just plug in the Menu script to the left slot.
You have to attache Menu to a GameObject then plug that GameObject to the left slot.You will then be able to chose which script and function to send the event to on the right.
The Image below shows the wrong way to do this(This is how you are currently doing it):
The is the correct way to do it:
You can also do this from code:
public class Menu : MonoBehaviour
{
public Button playButton;
void Start()
{
//Add Button Event
playButton.onClick.AddListener(() => buttonCallBack(playButton));
}
public void ChangeScene(string sceneName)
{
SceneManager.LoadScene("sceneName");
}
private void buttonCallBack(Button buttonPressed)
{
if (buttonPressed == playButton)
{
ChangeScene("myscene");
}
}
}

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 Triggering a Method Once Only at the Beginning of the Game

I'd like to show dialogue text at the beginning of my unity game as an introduction narration.
I have a method which holds the dialogue text then calls another method which writes the text out to a UI text box. This works fine if I have a button. However, I want it to happen when the scene loads for the first time, but because I'm leaving the scene and coming back within the same game, I don't want to welcome the player back to the scene they just left everytime they reenter.
button => method => another method writes to UI textbox
How could I do this without a button at the beginning?
Code
[System.Serializable]
public class DialogueTrigger : MonoBehaviour
{
public Dialogue dialogue; // array of strings past in
public void Start()
{
FindObjectOfType<DialogueManager>().StartDialogue(dialogue); //find the dialogue manager and plays the startdialogue method I suppose, should do this once the script is loaded because it is the start method
}
using UnityEngine;
using UnityEngine.UI;
public class DialogueManager : MonoBehaviour
{
private Queue<string> sentences;
public Text dialogueText;
void Start() // dialogue manager turns sentences into queue for the sentence input into dialogue manager
{
sentences = new Queue<string>();
}
public void StartDialogue(Dialogue dialogue)
{
//sentences.Clear(); // clears sentences
foreach (string sentence in dialogue.sentences) //each "sentence in the dialogue goes through and enters the queue one by one
{
sentences.Enqueue(sentence);
}
DisplayNextSentence(); //display next sentence method is called once all sentences are in the queue
}
public void DisplayNextSentence()
{
if(sentences.Count == 0) //once there are no sentences left (queue or otherwise?) the end dialogue method is called
{
EndDialogue(); //prints out that the queue has ended... ok
return; // this return is what really ends the dialogue
}
string sentence = sentences.Dequeue(); //new sentence for sentences that are being dequeued...
dialogueText.text = sentence; //dialogue text is supposed to be the same as the sentence that was just dequeued and this should print out to whatever text was assigned to the variable dialogue text
}
Thank you for reading my question.
You should use DontDestroyOnLoad. Create a new script called DialogueTrigger. Then attach it to an empty GameObject in the scene.
using UnityEngine;
using System;
public class DialogueTrigger : MonoBehaviour
{
// Array of strings past in
public Dialogue dialogue;
public void Start()
{
// Keeps the gameobject alive on scene changes
DontDestroyOnLoad(gameObject);
// Find the dialogue manager and plays the start dialogue
FindObjectOfType<DialogueManager>().StartDialogue(dialogue);
}
}

Unity says that 'Button' is not a Component and doesn't derive from Monobehavior

This was originally coded in unity 2018 then ported to unity 2019 because my main project is on that version. It works perfectly in unity 2018 (aside from that button.clicked += used to be button.AddListener())
The only relevant bit of the code is lines 12 - 17 but who knows.
Script is attached to object with the Button component.
Unity 2019.4.11f1
Returns the following error with the setup listed above:
ArgumentException: GetComponent requires that the requested component 'Button' derives from MonoBehaviour or Component or is an interface.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using UnityEngine.Events;
public class ButtonProperties : MonoBehaviour {
public ButtonType buttonType;
public string dest;
Button button;
void Start()
{
button = GetComponent<Button>();
//perform different functions dependant on the button type
if (buttonType == ButtonType.Scene)
{
//add ButtonLoadScene as a listener to the the button
button.clicked += ButtonLoadScene;
}
else if (buttonType == ButtonType.Menu)
{
button.clicked += ButtonLoadMenu;
}
//change button text to "Load Scene" + the name of the scene
string str = "Load " + dest;
GetComponentInChildren<TextElement>().text = str;
}
//private scene loader because event listeners cant take arguments >:(
void ButtonLoadScene()
{
SceneManager.LoadScene(dest);
}
void ButtonLoadMenu()
{
//array of all canvases in the scene
Canvas[] canvases = GameObject.FindObjectsOfType<Canvas>();
//foreach canvas
foreach (Canvas canvas in canvases)
{
//if the canvases name is the desired name
if (canvas.name == dest)
{
//turn all canvases off
foreach (Canvas c in canvases)
{
c.enabled = false;
}
//turn current canvas on
canvas.enabled = true;
//break loop
break;
}
}
}
}
public enum ButtonType
{
Scene,
Menu
};
I also tested this by adding a NewBehaviourScript to the button with Button button; and, in the start function, Start() {button = GetComponent<Button>();}
Same error
I believe you are using the wrong namespace for your button. Try changing:
using UnityEngine.UIElements;
to
using UnityEngine.UI;
There is a Button in both namespaces, but the one in UnityEngine.UI is likely the component you're working with.
Had somebody else on the team test the build, as I started to think from the comment thread, I was missing the UI library. Changing it to the UI library was the solution but not for me until I restarted the computer. Thank you Thomas Finch for the help.

custom inspector not drawing correctly

GameContainer script:
public class GameContainer : MonoBehaviour
{
public List<Game> Games;
public void AddGame()
{
Games.Add(new Game());
}
}
Game Class:
[System.Serializable]
public class Game
{
public List<GameType> gameTypes;
public void addGameType()
{
gameTypes.Add(new GameType());
}
}
GameType Class
[System.Serializable]
public class GameType
{
}
and my OnInspectorGUI method in custom editor
public override void OnInspectorGUI()
{
var targetScript = target as GameContainer;
var centeredStyle = GUI.skin.GetStyle("Label");
centeredStyle.alignment = TextAnchor.UpperCenter;
centeredStyle.fontStyle = FontStyle.Bold;
EditorGUILayout.LabelField("Games", centeredStyle);
for(int i = 0;i<targetScript.Games.Count; i++)
{
Game game = targetScript.Games[i];
//here is the LINE CAUSING A PROBLEM
Debug.Log(game.gameTypes.Count);
GUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.Space();
GUILayout.BeginVertical("Game Types", "window");
if (GUILayout.Button("+"))
{
game.addGameType();
}
GUILayout.EndVertical();
GUILayout.EndVertical();
EditorGUILayout.Space();
}
if (GUILayout.Button("+"))
{
targetScript.AddGame();
}
}
the problem is with this line:
//here is the LINE CAUSING A PROBLEM
Debug.Log(game.gameTypes.Count);
when i hit AddGame Button, all draw calls after this line will be ignored for newly added element and its not shown till next change in code and refresh in the editor, if i remove this line, everything works just fine.
but if i try to use gameType list by any mean, it will not show correct view in inspector.
what the problem is?
I recommend using EditorGUILayout instead of old GUILayout class.
here's link to document for it:
https://docs.unity3d.com/ScriptReference/EditorGUILayout.html
Although unity introduced a new way to make custom editors lately that is called UI Elements.
You can create your own editors with layered architecture with xml,css like language.
here's some useful YouTube links for you:
https://www.youtube.com/watch?v=sVEmJ5-dr5E
https://www.youtube.com/watch?v=MNNURw0LeoQ
https://www.youtube.com/watch?v=GSRVI1HqlF4
And lastly you can check this beautiful editor attributes too:
https://github.com/dbrizov/NaughtyAttributes

Is there any way to hide the "Object picker" of an EditorGUILayout.ObjectField in Unity Isnpector?

I'm just asking if there is any possibility to hide the "Object Picker" (The little knob/menu next to an ObjectField) in a custom Inspector. I have some cases where changes are disabled (DisableGroup) and I would like to also hide the knob while the content can not be changed anyway.
Also to make things easier for users I think about making the field higher (EditorGUIUtility.SingleLineHeight * 2) -> the picker gets stretched as well what looks kind of shitty ^^
example:
using UnityEditor;
using UnityEngine;
public class Bla : MonoBehaviour {
[CustomEditor(typeof(Bla))]
public class BlaEditor : Editor
{
private AudioClip _clip;
public override void OnInspectorGUI()
{
EditorGUI.BeginDisabledGroup(true);
// do some magic to hide the object picker
_clip = (AudioClip) EditorGUILayout.ObjectField("some label", _clip, typeof(AudioClip), false);
EditorGUI.EndDisabledGroup();
}
}
}
I want to stick with an ObjectField rather than a simple Label for two reasons:
Even on a disabled `ObjectField| the "ping" functionality is still working. (If you click on it, the according asset gets highlighted in the Hierarchy.) This is not the case obviously with a label.
The user should not get confused with completely different looking controls but I rather only want to remove some unnecessary clutter.
You might find a solution to hide the object picker by usage of stylesheets.
If all you want is just to display some reference, you can use a simple button basically styled as text field, adding an image and ping the object from code yourself.
using UnityEngine;
namespace Test
{
public class TestBehaviour : MonoBehaviour
{
[SerializeField] private bool _audioEnabled;
[SerializeField] private AudioClip _audioClip;
}
}
editor:
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEngine;
namespace Test
{
[CustomEditor(typeof(TestBehaviour))]
public class TestBehaviourEditor : Editor
{
private SerializedProperty _clipProp;
private SerializedProperty _audioEnabledProp;
private ObjectField m_ObjectField;
private const BindingFlags FIELD_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private void OnEnable()
{
_clipProp = serializedObject.FindProperty("_audioClip");
_audioEnabledProp = serializedObject.FindProperty("_audioEnabled");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_audioEnabledProp);
if(_audioEnabledProp.boolValue)
EditorGUILayout.PropertyField(_clipProp);
else
{
//TODO: calculate proper layout
var type = target.GetType().GetField(_clipProp.propertyPath, FIELD_BINDING_FLAGS).FieldType;
var clip = _clipProp.objectReferenceValue;
var guiContent = EditorGUIUtility.ObjectContent(clip, type);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Fake ObjectField Button");
var style = new GUIStyle("TextField");
style.fixedHeight = 16;
style.imagePosition = clip ? ImagePosition.ImageLeft : ImagePosition.TextOnly;
if (GUILayout.Button(guiContent, style ) && clip)
EditorGUIUtility.PingObject(clip);
EditorGUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}
}
}
I've got another solution: Ignore the pick result of this object picker, and although the picker is still here and can show the picker window, pick up will not work.
(I still don't know how to hide this button and the pickerwindow, > <), and this answer was posted at unity answers as well.
Here is the code:
// Register another callback of this object field
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
// In this callback, is a trick
private void DefaultAssetFieldCallback(ChangeEvent<UnityEngine.Object> evt) {
// unregister the callback first
myObjectField.UnregisterValueChangedCallback(DefaultAssetFieldCallback);
// trick: set back to the old value
m_ConfigAssetField.value = evt.previousValue;
// register the callback again
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
}
I needed to do something similar and found a way to do this by stepping through the ObjectField in the UIToolkit Debugger. The type of the little object selector button is hidden, so we cant really work with the class itself.
This solution is using UIToolkit, so unfortunately it won't work with Unity Editor IMGUI, but hopefully it will be helpful to someone.
The Solution in easy steps:
Find out what the uss style class of the ObjectFieldSelector is.
Recursively search the children of the ObjectField for a VisualElement containing the uss style class.
Set visibility to false.
All done!
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
[CustomPropertyDrawer(typeof(MyClass))]
public class MyClassDrawer: PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var field = new ObjectField(property.displayName);
field.objectType = typeof(MyClass);
var button = FindChild(field, "unity-object-field__selector");
button.visible = false;
return field;
}
private VisualElement FindChild(VisualElement parent, string ussClass)
{
foreach(var child in parent.Children())
{
if (child.ClassListContains(ussClass))
return child;
var subChild = FindChild(child, ussClass);
if (subChild != null)
return subChild;
}
return null;
}
}