I'm working on a Unity project and i would like to access a non MonoBehaviour script from another script.
I can't use GetComponent cause the script isn't MonoBehabiour, is there a solution ?
Here is some code to help you understand :
public class SomeClass {
public static float coolVar = 1.0f;
private string someVar; // EDIT : I need to access this var too and AnotherClass.someVar won't work obviously
public class AnotherClass {
// i want to be able to access coolVar and change her value
// i know i can do SomeClass.coolVar but i was looking for another way close to a GetComponent approach
My SomeClass class is full of static var i need to edit, i didn't implemented those variables and i can't modify them (i know it's bad practices).
Maybe the reflection is the best way :
typeof(SomeClasse).GetField(name).SetValue(null, value);
you only need to include the script with the not MonoBehaviour class inside the Assets path of the project and you will be able to use it inside other behaviour class.
Take care of use only .NET 2.0 specifications in the external class and if u have used a namespace on it add the using in the behaviour script.
#External class
namespace DefNamespace
{
public class ModelList
{
private static List<GameObject> models;
private ModelList ()
{
}
public static List<GameObject> Models{
get{
if(models == null) models= new List<GameObject>();
return models;
}set{
models=value;
}
}
}
}
And then in a MonoBehaviour or another class:
#Behaviour
using DefNamespace;
public class DefBehaviour : MonoBehaviour
{
Start(){
GameObject go=ModelList.Models[0];
}
}
Related
I need to serialize a class in a custom inspector (using visually the Editor) like as doing in a Monobehaviour script like this:
[System.Serializable]
public class CustomClass
{
int myInt
}
public class OtherClass : MonoBehaviour
{
[SerializeField] CustomClass customClass;
}
which gives this result:
result wanted and given using the code above, where DamageEffect = CustomClass and Damage = myInt
In my custom editor, I'd like something like this:
[CustomEditor(typeof(CardObject))]
class AnotherClassEditor : Editor
{
public override void OnInspectorGUI()
{
[SerializeField] CustomClass customclass;
}
}
but, as expected, it points out an error.
I also tried with EditorGUILayout.ObjectField() but I haven't been able to, I'm not so experienced so please try to keep the answers simple.
Actually, I need this serialization to happen only when an enum is equal to a certain value, the overall script is something like this:
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[CreateAssetMenu(fileName = "Card", menuName = "CardObject")]
public class CardObject : ScriptableObject
{
public List<CardEffectType> effectsTypes;
//other...
[HideInInspector] public List<CardEffect> effects;
//other...
}
#if UNITY_EDITOR
[CustomEditor(typeof(CardObject))]
class CardObjectEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
CardObject cardObject = (CardObject)target;
foreach(CardEffectType effectType in cardObject.effectsTypes)
{
switch (effectType)
{
case CardEffectType.DamageEffect:
{
//!!!
}
}
}
}
}
#endif
public enum CardEffectType
{
DamageEffect, //other...
}
I found some workarounds but the result is not as when a class is serialized in a Monobehaviour.
When you just want to show something like how Unity would, you can use EditorGUILayout.PropertyField(), but it asks for a SerializedProperty, what you need to get from the SerializedObject, not from the actual target. Something like this:
[CustomEditor(typeof(MyType))]
public class MyTypeEditor : Editor
{
private SerializedProperty _variableIWantToShow;
private void OnEnable()
{
_variableIWantToShow = serializedObject.FindProperty("<name-of-the-variable>");
}
public override void OnInspectorGUI()
{
// ...
if (ShowVariable) EditorGUILayout.PropertyField(_variableIWantToShow);
// ...
}
}
You can manage collections (array, list, etc.) as SerializedProperty, but it adds some complexity.
https://docs.unity3d.com/ScriptReference/SerializedProperty.html
https://answers.unity.com/questions/682932/using-generic-list-with-serializedproperty-inspect.html
Just make your int myInt public, or add [SerializeField] attribute to it - inspector is only designed to work with serialisable fields (public fields are serialised by default in unity editor), currently your myInt is private hence only visible from within
As mentioned in an answer by h4i, proper way to display objects in the editor is using SerializedProperty, casting 'target' seems like a good idea at start but is only useful if you want to call methods on the object, it will fail in several other cases.
What you may want to consider is declaring a PropertyDrawer while Editors service a single monobehaviour type, a PropertyDrawer handles displaying every instance of a serializable class, and will be used every time a default editor uses a PropertyField internally. This will probably match your use case better
This might be a super simple question, but for some reason I can't get it to work:
I have two scripts, both attached to the same GameObject.
One script has a dictionary:
public class RPG_Implementierung : MonoBehaviour
{
public Dictionary<string, string> StoryText = new Dictionary<string, string>();
void Start()
{
StoryText.Add("1", "This is the first Entry");
}
}
The other script wants to call that Dictionary. The method SendMessageToChat` is defined in this script and works well as long as it's not referencing the other script.
The first thing I tried didn't work, I get the Error:
CS0120 An object reference is required for the non-static field, method, or property
public class GameManager : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(RPG_Implementierung.StoryText["1"]);
}
}
}
I
this also doesn't work, it gives me the Error
CS0119 'RPG_Implementierung' is a type, which is not valid in the given context
public class GameManager : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(GetComponent(RPG_Implementierung).StoryText["1"]);
}
}
}
Can someone please tell me what I did wrong? In standard C# all I would have to do is to set the other class to public and then I can reference it and access it's objects, why doesn't this work in Unity?
To reference another component on a GameObject, you will need to grab that reference either by serializing the field in the inspector (Making it public or using the attribute [SerializeField].
I am not sure how many places you want to eventually call the method you are trying to invoke, but if it is from a bunch of different places, you might want to consider the Singleton pattern.
To quickly fix your current issue, on your GameManager.cs, do one of these two things:
public class GameManager : MonoBehaviour
{
[SerializeField] private RPG_Implementierung rpgImplement = null;
// OR
public RPG_Implementierung rpgImplement;
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(rpgImplement.StoryText["1"]);
}
}
}
Edit: If you want to use the GetComponent in the Update here is how you would call it. I would advise against this as calling a GetComponent in an Update can be quite costly for performance if called frequently. It is better to store the reference to later use.
public class GameManager : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Y))
{
SendMessageToChat(GetComponent<RPG_Implementierung>().StoryText["1"]);
}
}
}
I'm trying to make a script that reads the variables of another script and stores them in itself, but I canĀ“t get the data stored and I don't know how else to do it, please help.
Unity 2019.1
public class contenedor2 : MonoBehaviour
{
public string elemento1;
public void finalizado()
{
contenedor elementos = GetComponent<contenedor>();
elemento1 = elementos.contenedor1;
}
But the script does not read the variables
(
contenedor is a script associated with an element of another scene which contains the variables that I want to read)
You cannot. If a scene is not loaded, then it effectively does not exist until it is loaded.
What you might try instead is putting the script with the data into a prefab that's used by all scenes that need it. If it's script-only variables, you could also try a static class, but then you need to manage the initialization and shutdown yourself.
With your current code structure, Your contendor class can have the variable as static.
Your contenedor class
public class contenedor : MonoBehaviour
{
public static string elemento1;
}
Your New contenedor2 class:
public class contenedor2 : MonoBehaviour
{
public string elemento1;
public void finalizado()
{
elemento1 = contenedor.elemento1;
}
PS: Code can be improved, Let me know if it helps.
First i'd like to give a short version of my question:
How can i access another code pieces attached to another game object, or how can i initiazlie a class without have an game object attched.
When i making a small game in Unity, i made an unit designer where you give some value such as how many weapon does it carry, and the status of that unit (attack, range, speed, etc.) will be calculated by ComputeValues() and saved when you click confirm. But all those values were adjusted by clicking a button instead of direct input. (I.e. Click a button and add/reduce 1 weapon)
However, when i try to add some template unit at start up it won't work. So i made a CreateDesignWithValue() function. Which takes input for all the related data, and use the ComputeValues() above to compute the value for that object.
The problem is i'm trying to do it in player class. But i can't create new ShipDesigner, and neither can i set it to static. How can i get access to it?
Without knowing you exact usecase and what the methods do you are talking about we can only give a very general answer:
Not all classes have to be of type MonoBehaviour it really depends on your needs.
Extension Methods
If you have a certain calculation for a certain type you can use Extension Methods like
public static class Vector3Extensions
{
public static Vector3 DevideBy(this Vector3 a, Vector3 b)
{
return new Vector(a.x / b.x, a.y / b.y, a.z / b.z);
}
}
which you can use like e.g.
var newVector = transform.position.DevideBy(new Vector(1, 2, 3));
in all other classes.
public static class
In general you can use a public static class to implement methods and store values that shall be executable from everywhere e.g.
public static class Settings
{
private static int _currentInt = 7;
public static void SaySomething(string something)
{
Debug.Log(something);
}
public static void DoubleCurrentInt()
{
_currentInt *= 2;
}
public static int GetSquareOfCurrentInt()
{
return _currentInt * _currentInt;
}
}
which you can call now from everywhere like
Settings.DoubleCurrentInt();
Settings.SaySomething(Settings.GetSquareOfCurrentInt.Tostring);
Instances
Ofcourse sometimes you do not want that something is accessible from everywhere so you can also simply have a normal instanced class for your calculation like
public class Settings
{
private int _currentInt = 7;
public Settings(int initialInt = 0)
{
_currentInt = initialInt;
}
public void SaySomething(string something)
{
Debug.Log(something);
}
public void DoubleCurrentInt()
{
CurrentInt *= 2;
}
public int GetSquareOfCurrentInt()
{
return CurrentInt * CurrentInt;
}
}
So you can use
private Settings settings;
private void Start()
{
new Settings(3);
}
in one MonoBehaviour and
private Settings settings;
private void Start()
{
new Settings(26);
}
in another MonoBehaviour, both have different instances but can use all the implemention in it for calculating and doing stuff individually.
public static void
you can also only "share" one method among all instances of a certain type (static) and also allow other types to access it (public)
public class A : MonoBehaviour
{
// A prefab only this specific component has access to
[SerializeField] private GameObject prefab;
// example for a kind of singleton pattern
private static GameObject prefabSingleton;
private void Start()
{
prefabSingleton = prefab;
}
public static void Spawn(int someIntToAssign, string someTextToAssign)
{
var obj = Instantiate(prefabSingleton)
;
componentReference = obj.GetComponent();
componentReference.someIntField = someIntToAssign;
componentReference.Getcomponent<Text>().text = someTextToAssign;
}
}
this you can call from other types as well like
A.Setup(someExampleReference, "Yeay!");
(in this example you could consider to rather implement it in SomeExampleType, though ^^)
ScriptableObjects
What you described also sounded like ScriptableObjects (Tutorial) might be interesting for you.
ScriptableObjects are kind of assets similar to prefabs but can store values and also methods. You than can reference them in fields of MonoBehaviour components to change their behaviour according to the values or in order to share it as kind of container between multiple instances and different types.
Instance with public method
Last but not least the most "usual" of doing it would be to have a
public class A : MonoBehaviour
{
[SerializeField] private Transform someObject;
public Vector3 GetObjectPosition()
{
return someObject.position;
}
}
and access it via one of the many GetComponent or/and FindObjectOfType variants or simply by referencing the according component like
public class B : MonoBehaviour
{
// drag in via the Inspector
public A AReference;
private void Start()
{
// or get it on runtime e.g.
AReference = GameObject.Find("ObjectWithA").GetComponent<A>();
// or if there is only one e.g.
AReference = FindObjectOfType<A>();
Debug.Log(AReference.GetObjectPosition());
}
}
Answer of short versions:
How can i access another code pieces attached to another game object:
Declare a public field for the script you want to reach e.g. public ExampleScript exampleScript; and assign the gameobject which has ExampleScript to your field in the inspector.
how can i initiazlie a class without have an game object attched: You can't create an instance of a script derived from MonoBehaviour just like new ExampleScript();. But instead you can add that script to your existing gameobject with gameObject.AddComponent<ExampleScript>(); and you can reach this script from another script which is attached the very same gameObject like: gameObject.GetComponent<ExampleScript>();
I wrote little script yesterday but it isn't working. (Serialize fields isn't showing in unity and few errors eg. I can't use reference to non-static member (serialize Field)). Can You help me please.
Eg.
using UnityEngine;
public class sExample : MonoBehaviour
{
[SerializeField] public static GameObject gameObj;
public void serializeUse()
{
//Do something with gameObj
}
}
public class serializeEx : NetworkBehaviour
{
public void Update()
{
If (!isLocalPlayer)
{
sExample.serializeUse()
}
}
}
Thanks alot
That should work.
I think that you can't use static, when you want to expose something to the Editor.
using UnityEngine;
[Serializable]
public class sExample : MonoBehaviour
{
[SerializeField] public GameObject gameObj;
public void serializeUse()
{
//Do something with gameObj
}
}
public class serializeEx : NetworkBehaviour
{
public void Update()
{
If (!isLocalPlayer)
{
sExample.serializeUse()
}
}
}
Edit:
Statics seem to work for JavaScript as mentioned in this post.
To make this work you'll have to switch to the debug view in the inspector.
Like in the image shown below:
Edit2:
The explanation what the Serializeable does is taken from the unity documentation.
The Serializable attribute lets you embed a class with sub properties
in the inspector.
You can use this to display variables in the inspector similar to how
a Vector3 shows up in the inspector. The name and a triangle to expand
its properties. To do this you need create a class that derives from
System.Object and give it the Serializable attribute. In JavaScript
the Serializable attribute is implicit and not necessary.
using UnityEngine;
[System.Serializable]
class Test : System.Object
{
public int p = 5;
public Color c = Color.white;
}