Enum pointing to gameobjects? - c#

So, I have this crazy idea to have enums pointing to gameobjects.
Here's what I want to do:
/* These enums would hold gameobjects instead of ints */
enum exampleEnum{
AddPanel,
ListPanel
}
public class GUIManager : MonoBehaviour {
void Start()
{
EnablePanel(exampleEnum.AddPanel);
}
void EnablePanel(GameObject panel)
{
panel.setActive(true);
}
}
Is there any way to make this work? Or a workaround?
This might be possible with something other than an enum but I don't know of it if there is and I'm looking through the web for a such a solution.

This would satisfy your requirement, works for any amount of enum values or panels.
// Add this to each of your panels, the enum field can be integrated into your other behaviours as well
public class EnumPanel : MonoBehaviour
{
// configurable from the Editor, the unity way.
public ExampleEnum Type;
}
// Assign all your panles in the editor (or use FindObjectsByType<EnumPanel> in Start())
public class GUIManager : MonoBehaviour
{
// configurable from the Editor, the unity way.
public EnumPanel[] Panels;
void Start()
{
// Optionally find it at runtime, if assigning it via the editor is too much maintenance.
// Panels = FindObjectsByType<EnumPanel>();
EnablePanel(ExampleEnum.AddPanel);
}
void EnablePanel(ExampleEnum panelType)
{
foreach(var panel in Panels)
{
if(panel.Type == panelType)
EnablePanel(panel.gameObject);
}
}
void EnablePanel(GameObject panel)
{
panel.setActive(true);
}
}

I don't know why the answer from: #Paradox Forge was wrong but maybe this will help you.
System.Collections.Generic.Dictionary
I don't have a lot of time to explain the dictionary class but this is how you can use it.
This will cost some performance but has really nice readability
public class GUIManager : MonoBehaviour {
public enum exampleEnum{
AddPanel,
ListPanel
}
//For readability you can also add "using System.Collections.Generic;" on the top of your script
private System.Collections.Generic.Dictionary<exampleEnum,GameObject> exampleDictionary = new System.Collections.Generic.Dictionary<exampleEnum, GameObject>();
private GameObject SomeGameObject;
private GameObject SomeOtherGameObject;
void Start()
{
//You have to add all the enums and objects you want to use inside your GUIManager.
exampleDictionary.Add (exampleEnum.AddPanel, SomeGameObject); //Add panel will be linked to SomeGameObject
exampleDictionary.Add (exampleEnum.ListPanel, SomeOtherGameObject); //List Panel will be linked to SomeOtherGameObject
EnablePanel(exampleEnum.AddPanel);
}
void EnablePanel(exampleEnum examplePanel)
{
if (!exampleDictionary.ContainsKey (examplePanel)) //If the given panel does not exist inside the dictionary
return; //Leave the method
GameObject panelToEnable = exampleDictionary [examplePanel]; //Will return the GameObject linked to given panel
panelToEnable.SetActive(true); //Enable the gameobject
}
}
If you want to know more about the Dictionary class go to: Dictionary

Related

How to assign GameObject to child for serialization

I want to assign each item I create with a GameObject of some sort and then when I create a new object (called Item) it will have the assigned GameObject as a child.
For that I have a class of a scriptable object which holds a public GameObject called "gameObj" within it:
public abstract class ItemObject : ScriptableObject
{
public int id;
public GameObject gameObj;
}
Then, in another class I want to have something of this sort:
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
this.gameObject.transform.GetChild(0) = item.gameObj; //WRONG CODE, NEED HELP HERE
}
}
The purpose is to set the gameObj from the given ItemObject.item as the GameObject for the GroundItem.
The purpose is in the end to have lots of scriptable items of all sorts (like bread, sword, stone etc) and each one will have a GameObject assigned to it, and once I create a new GroundItem game object I will simply assign the scriptable object item and its child will have the game object itself (which includes all the visuals, special scripts etc).
For reference, in the following link the person is doing this from minute 4 to minute 6, but with a sprite instead of a game object.
Anyone knows how it should be written? Is it even possible?
You would probably mean
public class GroundItem : MonoBehaviour, ISerializationCallbackReceiver
{
public ItemObject item;
public void OnBeforeSerialize()
{
if(!item) return;
if(!item.gameObj) return;
// make according object a child of this object
item.gameObj.transform.parent = transform;
// make it the firs child
item.gameObj.transform.SetSiblingIndex(0);
}
}
However, in general ScriptableObjects are assets and GameObjects are instances in the scene hierarchy -> you can't simply reference scene objects in assets, doing so anyway might lead to unexpected behavior.
Is there a good reason why this has to be done in ISerializationCallbackReceiver?
I think what you actually rather want to achieve would be e.g.
public class GroundItem : MonoBehaviour
{
public ItemObject item;
#if UNITY_EDITOR
[HideInInspector]
[SerializeField]
private ItemObject _lastItem;
// This is called whenever something is changed in this objects Inspector
// like e.g. item ;)
private void OnValidate()
{
// Delay the call in order to not get warnings for SendMessage
UnityEditor.EditorApplication.delayCall += DelayedOnValidate;
}
private void DelayedOnValidate()
{
// remove the callback since we want to be sure it is always added only once
UnityEditor.EditorApplication.delayCall -= DelayedOnValidate;
// if item hasn't changed nothing to do
if (item == _lastItem) return;
// otherwise first destroy current child if any
if (_lastItem && transform.childCount > 0)
{
if (Application.isPlaying) Destroy(transform.GetChild(0).gameObject);
else DestroyImmediate(transform.GetChild(0).gameObject);
}
// is an item referenced and does it even have a gameObject ?
if (item && item.gameObj)
{
// instantiate the new one as child of this object
var obj = Instantiate(item.gameObj, transform);
// set as first child (if needed)
obj.transform.SetSiblingIndex(0);
if (!Application.isPlaying)
{
// if in edit mode mark this object as dirty so it needs to be saved
UnityEditor.EditorUtility.SetDirty(gameObject);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
_lastItem = item;
}
#endif
}
Which would now look like

How to set a GameObject active on one scene form a button click function on second scene that is loaded additive C#, Unity

I'm new to coding and Unity, I'm working on a simple click style game to learn the basics of both. I've created several scenes: MainMenu, UI, 1st level and 2nd level. After pressing 'Start' in main menu i'm loading UI and 1st level additively.
In UI layer I have a shop UI and other bits that I never want to unload. On the 1st and 2nd level i have the bits that i want to have only on those scenes.
So, what I'm trying to do is when i purchase an item or upgrade for the 1st level i want a GameObject (sprite) to be set as active.
What I've tried to do is to call a function in one script that is attached to GameObject in 1st level from script that is attached to a purchase button in UI scene, but from what I was able to understand from messing with it will set that game object active if it's assigned in the UI scene - so the whole thing is basicly pointless and i can do it much easier.
Code in script attached to GameObject in 1st level scene
public class Work_Button : MonoBehaviour
{
public GameObject hubert12;
public void Huberd()
{
hubert12.SetActive(true);
}
}
Code in script attached to GameObject in UI scene
public class Shop : MonoBehaviour
{
public GameObject buyHubertOnebutton;
public GameObject test;
public void UnlockHubert1()
{
if (Global_Cash.CashCount >= 20)
{
Global_Cash.CashCount -= 20;
buyHubertOnebutton.GetComponent<UnityEngine.UI.Button>().interactable = false;
Work_Button sn = test.GetComponent<Work_Button>();
sn.Huberd();
}
}
}
If you have any remarks anout how i've spit scenes or anything else they will be more than welcome!
Thanks!
There's something you need to be aware of, and that is that you can't reference an object from one scene, in another. You CAN reference the same Prefab though, but that's a slightly different issue.
One way I find effective is to do something like this:
public class Work_Button : MonoBehaviour
{
public GameObject hubert12;
public void Awake ( )
{
Shop.Register ( this );
}
public void Huberd ( )
{
hubert12.SetActive ( true );
}
}
Then you can have a Shop manager that has instance and static components.
public class Shop : MonoBehaviour
{
// Static variables
private static Work_Button _workButton;
// Instance variables
public GameObject buyHubertOnebutton;
public static void Register ( Work_Button workButton )
{
_workButton = workButton;
}
public void UnlockHubert1 ( )
{
if ( Global_Cash.CashCount >= 20 )
{
Global_Cash.CashCount -= 20;
buyHubertOnebutton.GetComponent<UnityEngine.UI.Button> ( ).interactable = false;
if ( _workButton != null )
_workButton.Huberd ( );
}
}
}
This works by having your button register with the shop. And because we're using the static variable here, we don't need to "find" the Shop object. The instance method UnlockHubert1 will check to make sure there is a button registered.
This represents just one of many ways to accomplish this. It's also the most basic of basic implementations. But as of right now I'm fond of this method. Extending this, you could store the registered items in a Dictionary collection, and you could do some cleanup/deregister when a button is destroyed (i.e. OnDestroy ).
The assumption here is that there is only one Work_Button in the scene, otherwise you'll need a way to differentiate them (i.e. a Dictionary with am identifier as the key).

Create Scriptable Object with constant, Unique ID

I use Scriptable Objects to create Items for my Unity Game. Let's say I create a Stick, a Sword and a Helmet. Now to use them, I need to drag them into a list and on game start, each item get's an ID assigned which is also the position in the list, as I simply loop through the list to assign ID's. Now I can access the item by using the index it has or, as shown in most of the tutorials about ScriptableObjects, we can then add the item and the id to a Dictionary to better access them.
Stick is 0, Sword is 1 and Helmet is 2. Now I decide to remove the sword with a patch so helmet gets the ID of 1. Now every savegame is broken as swords become helmets.
How can I handle assigning fixed ID's which wont change and wont be taken by another item until I really want to assign it again? I could of course hard-code the id for every ScriptableObject I create but as you can imagine, managing hard-coded IDs seems not like a good idea.
Here is my take on it :
Create a custom attribute "ScriptableObjectIdAttribute", you can use it in front of whatever you need to be an id.
Make a custom property drawer in order to initialize the id the first time you access the object in the inspector and also to make it non-editable by the inspector.
(Optional) Make a base class with that Id property and extend all your ScriptableObjects which need a unique Id from it.
BaseScriptableObject.cs :
using System;
using UnityEditor;
using UnityEngine;
public class ScriptableObjectIdAttribute : PropertyAttribute { }
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(ScriptableObjectIdAttribute))]
public class ScriptableObjectIdDrawer : PropertyDrawer {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
GUI.enabled = false;
if (string.IsNullOrEmpty(property.stringValue)) {
property.stringValue = Guid.NewGuid().ToString();
}
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
#endif
public class BaseScriptableObject : ScriptableObject {
[ScriptableObjectId]
public string Id;
}
Weapon.cs :
using System;
using UnityEngine;
[CreateAssetMenu(fileName = "weapon", menuName = "Items/Weapon")]
public class Weapon : BaseScriptableObject {
public int Attack;
public int Defence;
}
Result :
Create a string "ID" variable, check if it is empty when the Scriptable Objects Validates. If it is empty Create a GUID, if it is not, do nothing.
public string UID;
private void OnValidate()
{
#if UNITY_EDITOR
if (UID == "")
{
UID = GUID.Generate().ToString();
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
This is how i did it, it also sets the scriptable object to dirty so it does not reset when you restart the unity editor.
You can go a step further and make the UID field read only to prevent changing it by accident.
This is what I ended up using for a simple, duplicate safe method.
public class YourObject : ScriptableObject
{
public string UID;
private void OnValidate()
{
if (string.IsNullOrWhiteSpace(UID))
{
AssignNewUID();
}
}
private void Reset()
{
AssignNewUID();
}
public void AssignNewUID()
{
UID = System.Guid.NewGuid().ToString();
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
}

How can I create a dialogue system

using System.Collections;
using UnityEngine;
using TMPro;
public class NPCscript : Interactable {
public GameObject dialougeObj;
public TMP_Text dialouge;
public int Size;
public string dialougeSt;
public override void Interact () {
base.Interact ();
dialougeObj.SetActive (true);
NPCInteraction();
}
void OnTriggerExit(Collider other) {
dialougeObj.SetActive(false);
}
void NPCInteraction(){
dialouge.SetText(dialougeSt);
}
}
Please tell how can I have an array or list in which I can do use my size variable and loop it until all dialogues are said.
I tried but mine doesn't work.
HELP!
A Dialogue system in itself is a very broad topic. However, I don't think you will be able to pull it off with just a List of strings. If you are talking about giving the player choices to choose from then you will need to create a few custom classes.
First, you will need a class to define your Character's Options. This will be a very simple class. It will contain the string response and an integer that will represent a destination node.
public class DialogueOption {
public string Text;
public int DestinationNodeID;
}
Next, you will need a DialogueNode class. Consider this what the NPC is telling the character and then it also contains a list of, you guessed it, Dialogue Options. The other thing it will have is a NodeID. This is an integer that gives us a place to send us to from our Options. It will look a little like this:
public class DialogueNode {
public int NodeID = -1; //I use -1 as a way to exit a conversation.
//NodeId should be a positive number
public string Text;
public List<DialogueOption> Options;
}
Finally, you will need to create your Dialogue class, which is the simplest of them all, just a list of DialogueNodes and looks something like this:
public class Dialogue {
public List<DialogueNode> Nodes = new List<DialogueNode>();
}
You can have it where your character has just one dialogue script but, I did it differently where each character then had another class called DialogueList, which basically is a list of dialogues and it also contained the character's name that I could display and depending on the situation I could pick which dialogue I wanted my character to have with the player at the time.
public class DialogueList
{
public string name;
public List<Dialogue> dialogues = new List<Dialogue>();
}
This also has the added benefit of being easily converted to a Dictionary using the name as a key returning a list of dialogues if you wanted to set it up in that direction.
And somewhere else in your project you will need a DialogueManager class that will control everything. I typically make this a Singleton just so I can easily call it from anywhere. I won't show you all of it but I will show you the displaying part the rest is just setting the text and turning on and off objects.
public void RunDialogue(string name, Dialogue dia)
{
nameText.text = name;
StartCoroutine(run(dia));
}
IEnumerator run(Dialogue dia)
{
DialoguePanel.SetActive(true);
//start the convo
int node_id = 0;
//if the node is equal to -1 end the conversation
while (node_id != -1 )
{
//display the current node
DisplayNode(dia.Nodes[node_id]);
//reset the selected option
selected_option = -2;
//wait here until a selection is made by button click
while (selected_option == -2)
{
yield return new WaitForSeconds(0.25f);
}
//get the new id since it has changed
node_id = selected_option;
}
//the user exited the conversation
EndDialogue(node_id);
}
My buttons basically just had this simple method attached to them in their OnClick event that I set under the DisplayNode method. Basically, I take my buttons and give them this method and their parameter is whatever their DialogueOption.DestinationId is
public void SetSelectedOption(int x)
{
selected_option = x;
}
Hmm if you are asking for a Dialogue System it is very easy to create on Unity using UGUI.
1.) Create a canvas with a plane and a text I guess will do
*scale the plane ofcourse to your satisfaction and put it infront of the camera
*and about the text you must put it infront of the plane like the plane is on the back
*and the text is infront of the plane.
2.) Now setup your heirarchy like this
Canvas
Dialogue //Empty GameObject
Plane
Text
I'll make it pop out whenever you collides into an NPC something like that
[SerializeField] GameObject dialogueObject; //here drag the canvas
[SerializeField] GameObject text; //we will get its component for changing the text later on
void Start(){
text = GetComponent<Text>().text = "Sample Dialogue";
dialogueObject.setActive(false);
}
void OnCollisionEnter(collision collide){
if(collide.gameObject.name == "NPC"){
dialogue.setActive(true);
}
}
Simple as that . I hope it helps

How to create a custom inspector showing a list of lists in Unity?

well, I'm working on a specific part of a game in which I need to create a list of lists, a list of Game levels and in each list node a list of triggers (The level lists and each list inside it are variable in size).
This is all in the script LevelController.cs.
This is the Trigger class (it's simple like this for now)
[System.Serializable]
public class TriggerClass
{
public GameObject Trigger;
public bool Active;
public int Type;
public TriggerClass(GameObject obje, bool active = false, int type = 1)
{
Trigger = obje;
Active = active;
Type = type;
}
}
And a wrapper class to create the Levels List
[System.Serializable]
public class WrapperClass
{
public List<TriggerClass> Triggers = new List<TriggerClass>();
}
And of course
public List<WrapperClass> Levels = new List<WrapperClass>();
I serialized both and they show ok in standard Inspector draw in Unity, but I need to customize the Inspector showing this list, so I made a InspectorCustomizer.cs and a EditorList.cs in Editor folder.
For now I'm just trying to simulate the default draw that Unity does with the lists (an expand arrow, a size field that creates that amount of list nodes, and in each node the same thing, as I said, the default that happens if you uncomment the DrawDefaultInspector and comment the other 3 lines in InspectorCustomizer.cs)
The InspectorCustomizer:
[CustomEditor(typeof(LevelController))]
public class InspectorCustomizer : Editor
{
public override void OnInspectorGUI()
{
//DrawDefaultInspector();
serializedObject.Update();
EditorList.Show(serializedObject.FindProperty("Levels"));
serializedObject.ApplyModifiedProperties();
}
}
And here the EditorList.cs:
public static class EditorList
{
public static void Show (SerializedProperty list)
{
EditorGUILayout.PropertyField(list);
EditorGUI.indentLevel += 1;
if (list.isExpanded)
{
EditorGUILayout.PropertyField(list.FindPropertyRelative("Array.size"));
for (int i = 0; i < list.arraySize; i++)
{
EditorGUILayout.PropertyField(list.GetArrayElementAtIndex(i));
}
}
EditorGUI.indentLevel -= 1;
}
}
But this just shows the "Levels" list with empty elements, I click in the expande arrow in each element and nothing shows up, I know this happens because the script is iterating only through the Levels list and not each element within, but I don't know how to iterate through every element inside the Levels list to show the same thing.
I'm not really used to serialization so forgive me if this sounds easy.
I cannot do EditorList.Show(list[i]); inside the for loop because it cannot apply indexing to a serializedProperty of course, so how do I procced?
No, You don't need the EditorList.cs.
EditorGUILayout.PropertyField has a parameter includeChildren to show its children properties.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(LevelController))]
public class InspectorCustomizer : Editor
{
public override void OnInspectorGUI()
{
//DrawDefaultInspector();
serializedObject.Update();
EditorGUILayout.PropertyField(serializedObject.FindProperty("Levels"), true);
serializedObject.ApplyModifiedProperties();
}
}

Categories

Resources