Having Issue with Setting Up multiple Input Field in Unity3d - c#

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.

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;

How to Draw a list of non-MonoBehaviour Objects, that Inherits same Class in Unity Editor?

I have a List<AbilityEffect> effects and a lot of sub-classes of AbilityEffect, such as DamageEffect, HealEffect e.t.c. that HAVE [System.Serializable] property on it.
If I create class with field such as DamageEffect - Default editor will draw it perfectly! (And other effects too!)
I've added a ContextMenu Attribute to this function in AbilityData.cs
[ContextMenu(Add/DamageEffect)]
public static void AddDamageEffect()
{
effects.Add(new DamageEffect());
}
BUT default Unity Editor draws it if was an AbilityEffect, NOT a DamageEffect!
I've write some Custom Editor for class, that contains List<AbilityEffect> effects = new List<AbilitiEffect>(), write code that draws a custom list! But how do I tell a Editor to draw a DamageEffect specifically, NOT AbilityEffect?
I'll put some code below:
Ability Data Class
using UnityEngine;
using System.Collections.Generic;
[CreateAssetMenu(fileName = "New Ability", menuName = "ScriptableObject/Ability")]
public class AbilityData : ScriptableObject
{
public int cooldown = 0;
public int range = 1;
public List<AbilityEffect> effects = new List<AbilityEffect>();
public bool showEffects = false;
[ContextMenu("Add/DamageEffect")]
public void AddDamageEffect()
{
effects.Add(new DamageEffect());
}
}
Ability Data Editor Class
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
[CustomEditor(typeof(AbilityData))]
public class AbilityEditor : Editor
{
public override void OnInspectorGUI()
{
var ability = (AbilityData)target;
DrawDetails(ability);
DrawEffects(ability);
}
private static void DrawEffects(AbilityData ability)
{
EditorGUILayout.Space();
ability.showEffects = EditorGUILayout.Foldout(ability.showEffects, "Effects", true);
if (ability.showEffects)
{
EditorGUI.indentLevel++;
List<AbilityEffect> effects = ability.effects;
int size = Mathf.Max(0, EditorGUILayout.IntField("Size", effects.Count));
while (size > effects.Count)
{
effects.Add(null);
}
while (size < effects.Count)
{
effects.RemoveAt(effects.Count - 1);
}
for (int i = 0; i < effects.Count; i++)
{
DrawEffect(effects[i], i);
}
EditorGUI.indentLevel--;
}
}
private static void DrawDetails(AbilityData ability)
{
EditorGUILayout.LabelField("Details");
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Cooldown", GUILayout.MaxWidth(60));
ability.cooldown = EditorGUILayout.IntField(ability.cooldown);
EditorGUILayout.LabelField("Range", GUILayout.MaxWidth(40));
ability.range = EditorGUILayout.IntField(ability.range);
EditorGUILayout.EndHorizontal();
}
private static void DrawEffect(AbilityEffect effect, int index)
{
//if (effect is DamageEffect)
// effect = EditorGUILayout
// HOW??
}
}
Ability Effect class (NOT ABSTRACT)
[System.Serializable]
public class AbilityEffect
{
public virtual void Affect() { }
}
Damage Effect Class
[System.Serializable]
public class DamageEffect : AbilityEffect
{
public int damageAmout = 1;
public override void Affect() { ... }
}
Because of how Serialization works, once you deserialize some data Unity will try to populate an object instance based on the type specified in the class definition. If you have a List<AbilityEffect> Unity won't be able to differentiate which specific AbilityEffect you previously serialized. There is really one solution, change AbilityEffect to be a ScriptableObject, so that Unity doesn't actually serialize them as raw data but as GUID references, so that the referenced assets know by themselves what subtype of AbilityEffect they are. The downside is that this way all your effects will have to be assets in your Assets folder.
First of all: Note that since Unity 2021 the foldout is built-in default for all lists and arrays so actually I see absolutely no need for a custom editor at all really (at least for the list part) ;)
There is a couple of problems with your approach.
BUT default Unity Editor draws it if was an AbilityEffect, NOT a DamageEffect.
Yes, because it is serialized only as a AbilityEffect! For the Serializer all items in that list are of type AbilityEffect and it won't go any further.
So even if you can manage to add subclass items it will only be temporary! After e.g. saving, closing Unity and reopening all the subtypes should be converted to AbilityEffect because that's tue only type the Serializer actually sees for those.
My recommendation would be to rather make your AbilityEffect also of type ScriptableObject. This way you don't even have to bother with a custom drawer for them at all and could have as many instances with different types and configurations as you want, reuse them etc.
This said now a general thing: Don't directly go through the target in editors! (except you know exactly what you are doing)
This doesn't mark this object as "dirty", doesn't work with Undo/Redo and worst of all - it won't save these changes persistently!
Always rather go through the serializedObject and the SerializedPropertys.
[CustomEditor(typeof(AbilityData))]
public class AbilityEditor : Editor
{
SerializedProperty cooldown;
SerializedProperty range;
SerializedProperty effects;
SerializedProperty showEffects;
private void OnEnable ()
{
// Link up the serialized fields you will access
cooldown = serializedObject.FindProperty(nameof(AbilityData.cooldown));
range = serializedObject.FindProperty(nameof(AbilityData.range));
effects = serializedObject.FindProperty(nameof(AbilityData.effects));
showEffects = serializedObject.FindProperty(nameof(AbilityData.showEffects));
}
public override void OnInspectorGUI()
{
// refresh current actual values into the editor
serializedObject.Update();
DrawDetails();
DrawEffects();
// write back any changed values from the editor back to the actual object
// This handles all marking dirty, saving and handles Undo/Redo
serializedObject.ApplyModifiedProperties();
}
private void DrawEffects()
{
// Now always ever only read and set values via the SerializedPropertys
EditorGUILayout.Space();
showEffects.boolValue = EditorGUILayout.Foldout(showEffects.boolValue, effects.displayName, true);
if (showEffects.boolValue)
{
EditorGUI.indentLevel++;
// This already handles all the list drawing by default
EditorGUILayout.PropertyField(effects, GUIContent.none, true);
EditorGUI.indentLevel--;
}
}
private void DrawDetails()
{
EditorGUILayout.LabelField("Details");
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(cooldown.displayName, GUILayout.MaxWidth(60));
cooldown.intValue = EditorGUILayout.IntField(cooldown.intValue);
EditorGUILayout.LabelField(range.displayName, GUILayout.MaxWidth(40));
range.intValue = EditorGUILayout.IntField(range.intValue);
EditorGUILayout.EndHorizontal();
}
}
Now if you really really want to customize the behavior of the list drawing you could use a ReorderableList and can then implement a drawer for each element and there you could indeed perform a type check.
But as said I wouldn't go this way at all since the Serializer doesn't support it anyway.

Programmatically Add Objects to a UnityEvent and set the Function

I am trying to add an object to a UnityEvent listener and set that listeners function to Raise() function as the trigger. Like the below screenshot. That way I won't forget to drag and drop the object, or by chance selected the wrong function;
I have some a custom GameEvent that I use through out my game that contains the Raise() function;
public class GameEvent : ScriptableObject
{
private readonly List<GameEventListener> eventListeners = new List<GameEventListener>();
public void Raise()
{
for(int i = eventListeners.Count -1; i >= 0; i--)
eventListeners[i].OnEventRaised();
}
public void RegisterListener(GameEventListener listener)
{
if (!eventListeners.Contains(listener))
eventListeners.Add(listener);
}
public void UnregisterListener(GameEventListener listener)
{
if (eventListeners.Contains(listener))
eventListeners.Remove(listener);
}
}
To make my life easier, I have created an Editor script that will populate two UnityEvents for me.
I can find the two custom GameEvents I want to add. OnCameraWillTransition and OnCameraFinishedTransition by using:
string[] eventAssets = AssetDatabase.FindAssets("OnCamera t:GameEvent", new[] { "Assets/_Scripts/Library/Camera/Events" });
Then I loop through each of the two events and check their names, and add them to whatever class needs them set. In this example, I am using ProCamera2D and it has two events that I want to set. It's referenced in the screenshot above. This is just an example. I would like to be able to pre-poulate any class that has some UnityEvent.
ProCamera2DRooms pr = m_cam.gameObject.AddComponent<ProCamera2DRooms>();
foreach (string s in eventAssets)
{
string path = AssetDatabase.GUIDToAssetPath(s);
if (path.Contains("OnCameraFinishedTransition"))
{
Debug.Log("OnCameraFinishedTransition");
GameEvent e = (GameEvent)AssetDatabase.LoadAssetAtPath(path, typeof(GameEvent));
// This doesn't work, and I have tried different methods
pr.OnStartedTransition.AddListener( (e)=> {e.Raise()} ) );
}
//if (path.Contains("OnCameraWillTransition"))
//Haven't Started
}
Is there any way to pre-populate UnityEvent's programmatically?
I tried to do the same just a few days ago.
I had an example script made just to activate/deactivate a unityevent:
void SetEventTriggerState(EventTrigger ET, EventTriggerType ETType, string MethodName, UnityEventCallState NewState) {
for (int i = 0; i < ET.triggers.Count; i++) {
EventTrigger.Entry Trigger = ET.triggers[i];
EventTrigger.TriggerEvent CB = Trigger.callback;
for (int j = 0; j < CB.GetPersistentEventCount(); j++) {
if (CB.GetPersistentMethodName(j) == MethodName && Trigger.eventID == ETType) {
CB.SetPersistentListenerState(j, NewState);
}
}
}
}
You call it like that (the button that activate the trigger event, event type and event name that you need to change, new state that you need to set)
SetEventTriggerState(actionButtonEventTrigger, EventTriggerType.PointerDown, "JumpButton", UnityEventCallState.EditorAndRuntime);
I tried to add a line like this:
CB.SetPersistentTarget(j, newGameObject); //after the SetPersistentListenerState line
But I discovered that there is no way to do something like that (there is only the GetPersistentTarget method and I found no alternative way to change the target object at runtime).
Not sure if you can add it at runtime but there is an alternative way to do everything at runtime that I found:
First you need to implement the interface you need:
IDragHandler, IPointerUpHandler, IPointerDownHandler
Those are just examples (I'm not sure what you need for the ProCamera2D since I'm not using it), the first one is for the Drag Event, the second one is for the PointerUp event and the last is for the PointerDown event.
After that I just implement the method I want to use with the object I need to use:
public virtual void OnPointerDown(PointerEventData ped) {
currentObject.GetComponent<ScriptOfThatObject>().MethodIWantToUse();
}
to decide what Object will use that method I created a setter method
public GameObject CurrentObject {
set => CurrentObject= value;
}
And I set that GameObject OnTriggerEnter
public void OnTriggerEnter(Collider other) {
publicScriptOfMyButtonReference.CurrentObject = other.gameObject;
}
This is just an example of how to implement Event without using the UnityEvent.EventTrigger component and just use code.
I will not be able to be more specific since I don't use that Camera script but you should be able to implement a solution with those few things in mind.

Game Text Inputfield and Button not responding

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.

Accessing elements in a list property from a seperate class.

I have one class that is going to contain multiple lists that hold a variety of game objects. These lists will get populated by JSON scripts further in development.
Right now, I'm trying to access 1 element in 1 list. I have filled my class out as so:
public class ShopManager : MonoBehaviour
{
public List<GameObject> primaryWeapons = new List<GameObject>();
public List<GameObject> PrimaryWeapons
{
get { return primaryWeapons; }
}
public GameObject gameObj1, gameObj2;
// Use this for initialization
void Start ()
{
FillList();
}
public void FillList()
{
primaryWeapons.Add(gameObj1);
primaryWeapons.Add(gameObj2);
}
}
In my second class I'm trying to access one of the game objects I have placed in the list. This what I have so far:
ShopManager primary;
public GameObject temp;
public List<GameObject> tmpList;
void Awake()
{
primary = new ShopManager();
primary.FillList();
tmpList= new List<GameObject>();
}
// Use this for initialization
void Start()
{
for (int i = 0; i < primary.PrimaryWeapons.Count; i++)
{
tmpList.Add(primary.PrimaryWeapons[i]);
}
Debug.Log(primary.PrimaryWeapons.Count);
}
public void SelectWeapon1()
{
temp = primary.PrimaryWeapons.Where(obj => obj.gameObject.name == "DoorParts_1").SingleOrDefault();
}
}
In my list in the shop manager class I am manually setting the objects myself, so I know the names of them. However, whilst I can get a count returning correctly, I am unable to access this named object.
When I run the code I get a null reference pointing to the following line:
temp = primary.PrimaryWeapons.Where(obj => obj.gameObject.name == "DoorParts_1").SingleOrDefault();
Additonally I even created another list with the idea of passing the contents from my List property in the ShopManager class to this temp one. However, this lists populates with 2 empty positions.
I'm still not 100% on using properties like this. Espcially with lists. Could someone please tell me what it is I'm doing wrong?
You didn't initialize your objects in ShopManager.
Replace this:
public GameObject gameObj1, gameObj2;
With this:
public GameObject gameObj1 = new GameObject(), gameObj2 = new GameObject();
Check GameObject initialization. Especially gameObject field.
primary.PrimaryWeapons.Where(obj => obj.gameObject.name == "DoorParts_1").SingleOrDefault(); can fail with null reference in just few conditions:
primary is null
primary.PrimaryWeapons is null
obj.gameObject is null
The first two look covered in your code. So, check how GameObject.gameObject field is initialized.

Categories