Why can this script access another script without it being instantiated? - c#

I followed a tutorial in which someone wrote "public Inventory Container;" with which he could access data from the Inventory-Script. Why is this possible? I thought you can only access a non-instantiated script if it's static. Thanks for the help
First Script:
[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory System/Inventory")]
public class InventoryObject : ScriptableObject
{
public Inventory Container;
public void AddItem(Item _item, int _amount)
{
if (_item.buffs.Length > 0)
{
Container.Items.Add(new InventorySlot(_item.Id, _item, _amount));
return;
}
for (int i = 0; i < Container.Items.Count; i++) //Checks if already has the item.
{
if(Container.Items[i].item.Id == _item.Id)
{
Container.Items[i].AddAmount(_amount); /*If the item already exits, it just increases the number of it.
AddAmount works, because the Container consists of InventorySlot.*/
return;
}
}
Container.Items.Add(new InventorySlot(_item.Id, _item, _amount)); /*If Item doesn't exist, it adds it using the InventorySlot Constructor.*/
}
}
Script being accessed:
[System.Serializable]
public class Inventory
{
public List<InventorySlot> Items = new List<InventorySlot>();
}

Inventory is Serializable, which means when you change the Container in the inspector when editing your game, Unity will serialize that instance.
Then, when the game starts Unity will deserialize it and assign it back to Container for whichever instance of InventoryObject you were editing.
See The Unity documentation for further details:
Inspector window
When you view or change the value of a GameObject’s component field in the Inspector window, Unity serializes this data and then displays it in the Inspector
window.

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 access and disable a GameObject from a different script - Unity2D

I am currently working on a game, and while designing a Main Menu for the game, I need to be able to access different GameObjects and enable/disable them for the user to access different parts of the menu. The different parts are held under their respective parent GameObjects, for example, all the GameObjects related to the "Options Menu" will be under a single empty GameObject called "Options".
Here is the code pertaining to a script that holds the references to all the different parent objects and the methods to enable them:
public class MenuSwitch : MonoBehaviour
{
public GameObject Main;
public GameObject Options;
public GameObject About;
public GameObject Load;
public void MainMenu()
{
Main.SetActive(true);
Options.SetActive(false);
About.SetActive(false);
Load.SetActive(false);
}
public void OptionsMenu()
{
Main.SetActive(false);
Options.SetActive(true);
About.SetActive(false);
Load.SetActive(false);
}
However. the problem is the navigation of the Menu has to be done by the player by typing 'commands'. For example, they would type '/options' to go to the options menu. This functionality will remain throughout the game, so I made another script to handle player commands from the in-game console. Now how do I call the methods from MenuSwitch, because making a MenuSwitch object inside my console script does not seem to work, it gives an error: "Object reference not set to an instance of an object".
Here is the script handling the commands specified in the main menu:
// Commands
string[] MainMenuCommands = { "./start", "./load", "./about", "./options", "./end" };
private void Update()
{
if (Input.GetKeyDown(KeyCode.Return))
{
// Reset the console
playerInput = console.text;
console.text = "";
// Main Menu Input
if (CheckCommand(playerInput) == true)
{
RunCommand(playerInput, scene);
}
}
}
void RunCommand(string _command, int _scene)
{
// Main Menu commands
if (_scene == 0)
{
if (_command == MainMenuCommands[0]) { StartCoroutine(MM_Start()); }
else if (_command == MainMenuCommands[1]) { MM_Load(); }
else if (_command == MainMenuCommands[2]) { MM_About(); }
else if (_command == MainMenuCommands[3]) { MM_Options(); }
else if (_command == MainMenuCommands[4]) { MM_End(); }
}
}
The MM methods are empty currently, that is where the enabling/disabling will take place. How do I enable or disable the GameObjects inside the MenuSwitch script? The objects are referenced in that script via the Unity inspector.
You could create an empty object and call it MenuManager, attach the MenuScript to it. Then in a script you want to call MenuSwitch functions from declare a class member like so:
public MenuSwitch menuManager;
Drag and drop the MenuManager empty gameObject in the inspector and then you can easily use all the public functions from MenuSwitch script by just calling them from the menuManager variable like so:
menuManager.MainMenu();
menuManager.OptionsMenu();
I hope it was helpful :)

Need Help Creating an inventory system using a List of ScriptableObjects

SOLVED
thank you for taking the time to view my question.
I am currently working on a project using Unity3D and have ran into a bit of difficulty whilst creating the inventory system. Upon doing some research I have decided that it would be in my best interest to use a List of ScriptableObjects (Item) to create this inventory system due to the ease of management and the expand-ability of Lists.
Just as an example I have an Item titled "Skull." When the player points towards the skull my code prompts the user to press F to pick up the item which in turn should remove the item instance from the game-world and add an instance of that Item into the inventory.
The part that is causing me the most trouble is my InventoryEditor script which controls the RayCast feature I mentioned previously.
Here is the code in my InventoryEditor Script with the exception of Update and Start methods as they are working as intended
using UnityEngine;
public class InventoryEditor : MonoBehaviour
{
private bool isOpen = false;
public TMPro.TMP_Text itemIDTM;
public GameObject currentObject;
public GameObject inventoryUI;
new Inventory playerInventory;
public void CurrentObject()
{
RaycastHit itemHit;
Ray itemFinder = new Ray(this.transform.position, transform.TransformDirection(Vector3.forward));
if (Physics.Raycast(itemFinder, out itemHit))
{
if (itemHit.collider.tag == "Item")
{
// Rather than itemHit.collider.name I would prefer if it uses the Identifier of the
//Item ScriptableObject V
itemIDTM.text = "F - Pick up: " + itemHit.collider.name;
if (Input.GetKeyDown(KeyCode.F))
{
/*
I need to Add an Item When F is pressed
Something along the lines of:
playerInventory.AddItem(Item that is hit by RayCast);
*/
}
}
else
{
itemIDTM.text = "";
}
}
}
}
Here is my ScriptableObject which has not been causing me any issues
using UnityEngine;
[CreateAssetMenu]
public class Item : ScriptableObject
{
public string identifier;
public GameObject model;
public int value;
public double weight;
Item(string identifierStr)
{
this.identifier = identifierStr;
}
}
Here is my Inventory Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : List<Item>
{
public List<Item> itemList = new List<Item>();
// Default Constructor
Inventory()
{
}
public override string ToString()
{
string itemListString = " ";
for(int i = 0; i < itemList.Count; i++)
{
itemListString = itemListString += itemList[i].name;
}
return itemListString;
}
public void AddItem(Item itemToAdd)
{
for (int i = 0; i < itemList.Count + 1; i++)
{
if (itemList[i] == null)
{
itemList[i] = itemToAdd;
}
}
}
// Remove Item Not Finished
public void RemoveItem(Item itemToRemove)
{
for (int i = 0; i < itemList.Count; i++)
{
if (itemList[i] == itemToRemove)
{
itemList.Remove(itemToRemove);
}
}
}
}
The only solution that I can think of is to use ScriptableObject.CreateInstance to populate my game-world with the items that I wish to add to the inventory but even then I am unsure how to both: reference those Item Instances in code and, use a RayCast to determine that an Item Instance is being hit. Is it possible to create instances of a ScriptableObject in unity without code or will I have to populate my world through code?
Any help would be greatly appreciated I have been struggling with this for quite some time now.
So as with most things there are many ways to skin a cat. There are some brilliant and complicated inventory systems in the asset store that you can use if you feel like this is taking too much time. If it were me settings up this structure i would make an interface called something like "IInteractable" for you items that you want to the player to have the ability to pick up.That interface would look something like this
public interface IInteractable
{
//once a player has reference to this object they call this
//method to get an item back (which is your scriptable object)
Item GetItemInfo();
}
So in your OnCollision method instead of checking for a tag you could instead check against an interface like this.
IInteractable item = itemhit.collider.gameobject.GetComponent<IInteractable>();
if (item != null)
{
//Then you have found an item that needs to be picked up.
string name = item.GetItemInfo().identifier;
}
From here there are several ways you can go depending on what effects you want your inventory to have on your character. With some inventory you want items to have immediate effect while with others you want the effect only to be active while equipped.As far as spawning these items it would probably be easiest to just make prefabs with SO's attached and spawn those. Let me know if you have any other questions.

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