Game Text Inputfield and Button not responding - c#

I am trying to build a word guessing game. My input field is supposed to accept a string and go to the next page if right or return back to the menu page if not right. Currently, my scene is going to the next scene for both the right and wrong answer.
public class anser : MonoBehaviour {
private string secretWord = "eje";
private string guessWord;
public void GetInput(string guessWord) {
//Invoke ("CompareGuesses", 1f);
if (secretWord == guessWord) {
SceneManager.LoadScene ("Front Page");
} else if (secretWord != guessWord) {
SceneManager.LoadScene ("Question2");
}
}
}

I'm not sure what the guessWord field is for, all you need is the guessWord parameter for your GetInput() method.
Also, you need to make sure the scene names are correct (check for capitals and spaces), and make sure that some other script is actually calling the GetInput() method (you didn't post the code for anything that calls this method).
Here is a cleaned up version of your code that should do what you want:
// The class name "anser" was misspelled. Also you typically use PascalCase for class names.
public class Answer : MonoBehaviour
{
private string secretWord = "eje";
public void GetInput(string guessWord)
{
if (secretWord == guessWord.ToLower())
{
SceneManager.LoadScene("Question2");
return; // eliminates the need for an else clause
}
SceneManager.LoadScene("Front Page");
}
}
Edit: Per Scriven's suggestions in the comments, I changed the LoadScene() arguments to reflect the OP's desired behavior. Also added a little bit of input validation for the guessWord parameter.

Related

Understanding why Unity C# passing custom class object that return null pointer error

can someone help me understand what I'm missing?
My problem is, I have a script that manages the scene, and when it starts up it creates a bunch of buttons from a prefab.
void InitializeCharacters()
{
int index = 0;
foreach (CharacterSelectionItem character in CharacterSelectionData.CharacterList)
{
CharacterButton.GetComponent<CharacterButton>().Character = character;
CharacterButton.GetComponent<CharacterButton>().fullSource = character.FullCharacterSource;
GameObject newButton = Instantiate(CharacterButton) as GameObject;
newButton.transform.SetParent(CharacterPanel.transform, false);
}
}
CharacterButton is the prefab, assigned from editor to the script, with this script attached:
using static CharacterSelectionItem;
public class CharacterButton : MonoBehaviour
{
public CharacterSelectionItem Character;
public string fullSource;
void Start()
{
Debug.Log("Full" + fullSource);
Debug.Log("Character image source" + Character.FullCharacterSource);
}
}
public class CharacterSelectionItem
{
public string Description;
public string ImageSource;
public string FullCharacterSource;
public string CharacterHistory;
public CharacterSelectionItem(
string descr,
string img,
string fullSource,
string histroy,
Race race
)
{
Description = descr;
ImageSource = img;
FullCharacterSource = fullSource;
CharacterHistory = histroy;
Race = race;
}
};
Now, the first debug prints the string, while the second returns "Object reference not set to an instance of an object", and I can't really understand why. I tried to pass character in a SetCharacter(ref CharacterSelectionItem character) to have the reference but still don't work. The objective is not to pass every field on of CharacterSelectionItem but the whole object.
It's my first project in unity and I was loosely following a tutorial. and hope I could find an answer there but it didn't come.
I think I'm missing something pretty basic but don't know what, can someone help?
I had a similar situation lately and discovered that while Instantiate basically creates a deep clone of the prefab it only seems to take over custom fields that are serialized.
At least from the code you shared that wouldn't be the case for
public CharacterSelectionItem Character;
since the type CharacterSelectionItem isn't [Serializable]
Try to make it
[Serializable]
public class CharacterSelectionItem
{
...
}
If you then do not want it to appear in the Inspector you can still decorate the field additionally
[HideInInspector] public CharacterSelectionItem Character;
Alternatively instead of assigning the field on the prefab before Instantiate rather assign it on the instance in the first place:
var newButton = Instantiate(CharacterButton);
newButton.transform.SetParent(CharacterPanel.transform, false);
newButton.GetComponent<CharacterButton>().Character = character;
newButton.GetComponent<CharacterButton>().fullSource = character.FullCharacterSource;

Event function not working as expected

First I will explain whats happening, then what I am expecting to happen, and finally the code behind it
So whats happening is when I press enter the color of the text is green
What I expect to happen is the color turn red
This is based on if i type "Bad" into the field
//Please note I have edited uni9mportant code out
//Event Listener
inputField.onEndEdit.AddListener (delegate {
VerifyWords();
});
//Clss that handles the dictionary
public abstract class WordDictionary: MonoBehaviour{
public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();
private void Start(){
_wordDictionary.Add ("Bad",true);
}
}
//Function that handles the word verification
private void VerifyWords(){
if (openChat == false) { //If we done have open chat
bool hasBadWords = false; //Reset boolean
string[] stringSplit = inputField.text.Split (' '); //Split text string
for (int i = 0; i < stringSplit.Length; i++) { // Go through each word in the string array
if (WordDictionary._wordDictionary.ContainsKey (stringSplit[i])) { //If the word is in the dictionary
hasBadWords = true; //Then there is a bad word
}
}
if (hasBadWords == true) { //If a bad word was found
inputField.textComponent.color = Color.red; //Then the text should be red
} else {
inputField.textComponent.color = Color.green; //The text should be green
}
}
}
EDIT
I edited the code with comments to kinda put my thinking into perspective
The problem is that the class is marked as abstract. An abstract class cannot be instantiated, and therefore Unity can't call Start on a class that it cannot instantiate. The easiest fix is to simply remove the abstract from the class definition:
public class WordDictionary: MonoBehaviour{
public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();
private void Start(){
_wordDictionary.Add ("Bad",true);
}
}
However you now have a new problem. WordDictionary has a static member that is initialized by a non-static method. This means that each time you create a new WordDictionary, Start() will be called and it will add all the words into the dictionary again (or at least it will attempt to, you will get a duplicate key exception in this case, to avoid that you can also write _wordDictionary["Bad"] = true which replaces an existing key if it exists).
The better option here is to use a static constructor. This will make sure that the dictionary is only initialized once:
public class WordDictionary: MonoBehaviour{
public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();
static WordDictionary() {
_wordDictionary.Add ("Bad",true);
}
private void Start(){
}
}
Now you can use WordDictionary without worrying about the dictionary growing each time the class is instantiated. But at this point there really is no use in making WordDictionary a MonoBehavior because really it is just a holder for a bunch of words. So your class now just becomes:
public class WordDictionary: {
private static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();
public static Dictionary<string, bool> Words {
get { return _wordDictionary; }
}
static WordDictionary() {
_wordDictionary.Add ("Bad",true);
}
}
I added a property here because really you should be using properties, and having the underscore names (in my code world) means that it is a private field. You can extend your dictionary to do other things instead:
public class WordDictionary: {
private static List<string> _wordList = new List<string> ();
static WordDictionary() {
_wordList.Add ("Bad");
}
public static Contains(string word) {
return _wordList.Contains(word);
}
public static ContainsAny(IEnumerable<string> words) {
return words.Any(w => Contains(w));
}
}
I don't see any reason to use a Dictionary here, if it contains the word then it is "bad", if it doesn't contain the word then it would be "good". So changing to a list makes things simpler. If you hide how the "Dictionary" works in the background and just expose the "contains" and "contains any" methods, you get two advantages, use becomes simpler, and you can change the underlying "engine" without changing the interface and downstream code.
And now your colorization function becomes much simpler:
private void VerifyWords() {
if (openChat)
return;
var stringSplit = inputField.text.Split(' ');
if (WordDictionary.ContainsAny(stringSplit))
inputField.textComponent.color = Color.red;
else
inputField.textComponent.color = Color.green;
}
First I would recomend that you dont use MonoBehaviour for your WordDictionary. I see no reason to do so. Insted use ScriptableObject.
Second it doesn't need to be abstract and I dont think you need a dictionary at all a list would do just fine assuming you only store the 'bad' words.
e.g. (and I used dicitonary here, could be list assuming your requirements would work with only the bad words being stored)
[CreateAssetMenu(menuName = "Custom Object/Word Dictionary")]
public class WordDictionary : ScriptableObject
{
public Dictionary<string, bool> Values = new Dictionary<string, bool>();
}
Scriptable objects are created in your assets and can be referenced by GameObjects in your scenes. So in gameObjects that need to use the dicitonary just add a
public WordDictionary gameDictionary;
And set it to the dictionary you created in your assets. In general singletons/static classes and similar approches of making a monoBehaviour act like a static object leads to problems.
Scriptable Objects are a great workaround. They are available at start of game without initalization like a singleton, they can contain data or funcitons like any other class but unlike MonoBehaviour they do not have Start, Update, etc. ... doesn't mean you can call a AtStart() you write in the Scriptable object from some specific initalization behaviour in your game however.
1 important note, in editor the data updated in a scriptable object persists, at runtime it does not. e.g. when testing your game in editor the 'bad' words will be retained between sessions ... in build it will not but I assume you are preparing your dictionary on initalization of the game anyway so this should be a non-issue.
Maybe this will help you
//Please note I have edited uni9mportant code out
//Event Listener
inputField.onEndEdit.AddListener (delegate {
VerifyWords();
});
//Clss that handles the dictionary
public abstract class WordDictionary: MonoBehaviour{
public static Dictionary<string,bool> _wordDictionary = new Dictionary<string,bool> ();
private void Start(){
_wordDictionary.Add ("Bad",true);
}
}
//Function that handles the word verification
private void VerifyWords(){
if (openChat == false) { //If we done have open chat
bool hasBadWords = false; //Reset boolean
string[] stringSplit = inputField.text.Split (' '); //Split text string
int i=0;
for (i = 0; i < stringSplit.Length; i++) { // Go through each word in the string array
if (WordDictionary._wordDictionary.ContainsKey (stringSplit[i])) { //If the word is in the dictionary
inputField.textComponent.color = Color.red; //Then the text should be red
}
}
if (i == stringSplit.Length) { //If a bad word was found
inputField.textComponent.color = Color.green; //The text should be green
}
}
}

Unity C#, How script know when public variable(not property) changed

For example of UnityEngine.UI.Text
It's contains text variable for set the value of text as string.
Now, In my own class I use property(get;set) instead directly access the variable, This let me able to update views or fire some events when new value have set. The problem is when using property(get set) it'll not show this field in inspector and even in integration test tools.
But Unity's text component decided not to use get set. Which in my opinion I feel not comfortable for deal with value changed.
Question is how original UnityEngine.UI.Text in text variable can dealing with this?
I think It not just OnValidate() because it can only work on editor. But text variable also work on runtime scripting.
This function is called when the script is loaded or a value is
changed in the inspector (Called in the editor only).
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnValidate.html
If you are not using a monobehavior (so you can do OnValidate()) or want to do polling than you could opt for going for an propertyattribute string to method reflection solution along the lines of this gist. Also below a simple implementation and example:
public class OnChangedCallAttribute : PropertyAttribute
{
public string methodName;
public OnChangedCallAttribute(string methodNameNoArguments)=> methodName = methodNameNoArguments;
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(OnChangedCallAttribute))]
public class OnChangedCallAttributePropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(position, property);
if (EditorGUI.EndChangeCheck())
{
OnChangedCallAttribute at = attribute as OnChangedCallAttribute;
MethodInfo method = property.serializedObject.targetObject.GetType().GetMethods().Where(m => m.Name == at.methodName).First();
if (method != null && method.GetParameters().Count() == 0)// Only instantiate methods with 0 parameters
method.Invoke(property.serializedObject.targetObject, null);
}
}
}
#endif
Example:
using UnityEngine;
public class OnChangedCallTester : MonoBehaviour
{
public bool UpdateProp = true;
[SerializeField]
[OnChangedCall("ImChanged")]
private int myPropVar;
public int MyProperty
{
get => myPropVar;
set { myPropVar = value; ImChanged(); }
}
public void ImChanged() => Debug.Log("I have changed to" + myPropVar);
private void Update()
{
if (UpdateProp)
MyProperty++;
}
}
Another solution I used:
public T car;
private T _car;
public Car { get => _car; set => _car = value; }
public void OnValidate() => Car = car;
There is no real way other than to check if the text has changed with respect to a cached "previous" value. In the absence of extension properties you'll simply have to do so manually.
You just can't. That's why most components in Unity poll the value they are relying on every single frame.
If you really want to detect changes, you can do something like this:
public sometype Field;
private sometype _previousValueOfField;
public void Awake()
{
_previousValueOfField = Field;
}
public void Update()
{
if(Field != _previousValueOfField)
{
_previousValueOfField = Field;
// Execute some logic regarding the change of the field there
}
}
(By the way that's another reason why public fields are evil and I hate Unity for relying on them.)

Having Issue with Setting Up multiple Input Field in Unity3d

I'm currently trying to set up multiple Input Field in Unity and having issue do so. I can only get one input to work (intPressure) but don't know how to add a second one (drillpipe). It seem to me that unity only allow one Input Field at a time. I would think that just changing it to InputField2, ect will do the trick, but that doesn't seem to work. Other have mentioned creating an empty gameobject and inserting the inputfield to each gameobject.
To be honest, I still quite confuse about how to setup the second input field.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class UserInput : MonoBehaviour {
InputField input;
int intPressure = 0;
int drillpipe = 0;
public DataManager data;
void Start ()
{
var input = gameObject.GetComponent<InputField>();
var se= new InputField.SubmitEvent();
se.AddListener(SubmitName);
input.onEndEdit = se;
}
private void SubmitName(string pressure)
{
var pressureScript =
GameObject.FindObjectOfType(typeof(DataManager)) as DataManager;
if (int.TryParse(pressure, out intPressure))
{
pressureScript.pressure = intPressure;
}
else
{
// Parse fail, show an error or something
}
}
}
I am not sure about the way you want to implement this (using single GameObject), however you most certainly would be able to do this if you had a couple of GameObjects (One object per control) nested within another GameObject (let's call it UserInputRoot) like this:
UserInputRoot (has UserInputController script)
|
+--- InputPressure (has InputField component)
|
+--- InputDrillpipe (has InputField component)
The controlling script would then have either a couple of public InputField's or a couple of private ones, initialized within the Start() or Awake() method:
class UserInputController : MonoBehaviour {
//These can be set from the inspector and controlled from within the script
public InputField PressureInput;
public InputField DrillpipeInput;
// It would be better to pass this reference through
// the inspector instead of searching for it every time
// an input changes
public DataManager dataManager;
private void Start() {
// There is no actual need in creating those SubmitEvent objects.
// You can attach event handlers to existing events without a problem.
PressureInput.onEndEdit.AddListener(SubmitPressureInput);
DrillpipeInput.onEndEdit.AddListener(SubmitDrillpipeInput);
}
private void SubmitDrillpipeInput(string input) {
int result;
if (int.TryParse(input, out result)) {
dataManager.drillpipe = result;
}
}
private void SubmitPressureInput(string input) {
int result;
if (int.TryParse(input, out result)) {
dataManager.pressure = result;
}
}
}
And by the way, the formatting of your code is absolutely atrocious. You MUST fix it.
It looks like the script you are showing is attached to an input field gameobject is that correct? You have a member variable in your object named input but then you create a new variable input in your Start method. Rather than have a script attached to your first InputField, create an empty gameobject in the editor and attach a script to that. In the script add two public members:
public class UserInput : MonoBehaviour {
public InputField PressureInput;
public InputField DrillPipeInput;
Now pop back to the editor, you should see the two input fields showing up when you select your empty gameobject. Drag and drop your two input fields into the slots for each inputfield. Now when the scene starts your UserInput script will be set with the input fields and you can use them both.

this is null in the inside method, how is this even possible?

I have a class Star that inherits from Object. It has a GameObject field.
Here is code:
public class Star : Object {
public GameObject starObject;
// code omitted
}
It has a method that I use for selection.
public void selectStar () {
Debug.Log ("Inside star selection");
mapManager.selectionStarDidChange(this);
selected = true;
}
I have another script from which I call that method, it detects input.
public class StarObjectHandler : MonoBehaviour {
public Star starScript;
void OnMouseOver()
{
if (Input.GetMouseButtonDown(0)) {
Debug.Log ("Mouse registered star selection");
starScript.selectStar();
}
}
}
But when I call the first method, somehow this becomes null. How can I even call methods on null and they work?
What is wrong here? I checked the starScript in second class, it is not null.
In the log I get "Mouse registered.." and then "Inside star selection" as expected.
EDIT1:
Here is the method that is getting invoked from this:
public void selectionStarDidChange(Star newlySelected)
{
if (newlySelected) {
starSelector.transform.position = newlySelected.position;
} else {
starSelector.transform.position = new Vector2(-10, -10);
}
if (lastSelectedStar) {
lastSelectedStar.deselectStar();
}
if (lastSelectedShip) {
lastSelectedShip.deselectShip();
}
lastSelectedStar = newlySelected;
}
It always goes the else route, and logging here says that newlySelected is null.
EDIT2:
Here is the project if anyone feels interested to see what is wrong:
https://www.dropbox.com/s/napsqknlw3hjuku/Orion2.zip?dl=0
EDIT3:
I managed to localize the error to this code:
Star star = new Star();
Debug.Log ("Is star null? __" + (star == null) + "__");
Somehow this returns true, I will have to look up object instantiation.
Yes looks like you have declared variable without assigning it
Star star = new Star();
Will solve the issue
Apparently the problem was that this code returned null instead of an object. I have no idea how the rest of the program worked with this being null, and I can't understand why it returns null.
It is a subclass of Object class. I changed it to no superclass and it worked.
Star star = new Star();

Categories