How do I add serialized field for a function in Unity? - c#

I need to put a function from one class into another. And I want to put in the serialized field not the whole GameObject of the desired class, but one function from this class. How should I do it?
For example, I have a class GameActions describing game events.
public class GameActions : MonoBehaviour
{
public void actionStart()
{
SceneManager.LoadScene("Game");
}
}
And I want to be able to fire a function actionStart in another class by putting it into serialized field (inside of toggleEnd, for example).
public class Countdown : MonoBehaviour
{
[SerializeField] private SomeType toggleEnd;
[SerializeField] private float currTime;
}

To use the Unity Inspector to store and serialise classes, you would expose the class as a field. The Inspector will then let you drag and drop a class into the object field.
public class Countdown : MonoBehaviour
{
// A reference to the GameActions class is serialised here.
[SerializeField] private GameActions _gameActions;
[SerializeField] private float currTime;
// This is an example of a countdown timer.
private void Update ( )
{
currTime -= Time.deltaTime;
if ( currTime < 0 )
{
// This is how you call the GameActions actionStart method.
_gameActions.actionStart ( );
this.enabled = false;
}
}
}

Related

How do I reference a variable from another class in the same script?

I am trying to make a wave spawn system in a serialized list in the inspector, but I can't figure out how to make it work. I can't seem to reference variables in other classes within the same script, and I need that data to make it work.
Right now it looks something like this, with classes nested within each other:
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class WaveSystemScript : MonoBehaviour
{
[SerializeField] public WaveBubbleScriptableObject waveBubbleSO; //This contains the list of valid bubbles, but I can't reference it from the other classes.
[SerializeField] public List<GameObject> validBubbles = new List<GameObject>(); //So does this
[SerializeField] List<Waves> waveList = new List<Waves>();
}
[System.Serializable]
class Bubble
{
[SerializeField] GameObject bubbleSelection; //select the bubble to spawn repeatedly
[SerializeField] float spawnInterval;
}
[System.Serializable]
class Waves
{
[SerializeField] List<Bubble> waveBubbles = new List<Bubble>(); //each wave may have multiple bubbles, with different variable each
float waveLength;
}
`
The main issue is setting the bubbleSelection gameobject in the bubble class. Anything in the monobehaviour class can't be referenced in the other classes for a reason I'm not aware of. I tried lists, arrays and scriptableobjects but couldn't figure out how. I could reference the prefab directly using an empty GameObject variable, but I would like it to work such that all the valid spawnable objects are already present in an array/list or dropdown for easy selection.
Well, both fields within Bubbles as well as within Wave have the default member accessibility private and therefore are not visible for any other type.
(See access modifiers)
You either simply want to make those public (in which case the [SerializeField] becomes redundant as it is only needed to serialize fields that are not public)
// [System.Serializable] <- this is also redundant as MonoBehaviour or anything
// derived from UnityEngine.Object already is serialized anyway
public class WaveSystemScript : MonoBehaviour
{
public WaveBubbleScriptableObject waveBubbleSO;
public List<GameObject> validBubbles = new List<GameObject>();
public List<Waves> waveList = new List<Waves>();
}
[System.Serializable]
class Bubble
{
public GameObject bubbleSelection;
public float spawnInterval;
}
[System.Serializable]
class Waves
{
public List<Bubble> waveBubbles = new List<Bubble>();
public float waveLength;
}
or for encapsulation, depending on your use case, you can keep them as private so they are only editable via the Inspector but via code only provide them as public readonly properties like e.g.
[System.Serializable]
public class Waves
{
[SerializeField] private List<Bubble> _waveBubbles = new List<Bubble>();
[SerilaizeField] private float _waveLength;
public IReadOnlyList<Bubble> waveBubbles => _waveBubbles;
public float waveLength => _waveLength;
}

cannot access non static method "GetComponent" in static context, despite me not declaring the class as static

I am currently working on making a class accessible from other scripts. Below is what I have coded.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(BoxCollider2D))]
// Start is called before the first frame update
public class _move
{
private float _sizex;
private float _sizey;
public _move(float sizx, float sizy....etc)
{
_sizex = sizx;
_sizey = sizy;
}
public void regularmove(float sizex, float sizey...etc)
{
GameObject _player;
_player = GameObject.GetComponent<GameObject>();
BoxCollider2D bc2d;
bc2d = _player.GetComponent <BoxCollider2D>();
bc2d.offset = new Vector2(locationx + 1, locationy);
}
}
however, when I try to compile it, it gives me an error.
cannot access non-static method"GetComponent"
on this line of code
_player = GameObject.GetComponent<GameObject>();
I do not know why this is happening because I have not declared the class as static, and do not really know the properties of GetComponent that well. Can someone tell me what is happening and how I can solve this?
Also, when I change
GameObject _player;
to
public GameObject player;
the scripts below suddenly cease to work, giving me errors like
cannot resolve symbol _player
what exactly is happening here?
Thanks in advance!
Whatever script creating the instance of the _move class (This is bad naming btw, it should be Move) should also pass its gameObject to the constructor.
// attributes here are not valid if your class is not a MonoBehaviour
public class Move
{
GameObject m_object;
public Move(GameObject obj, ...)
{
m_object = obj;
}
}
if your class is meant to be a MonoBehaviour component, then it has a GameObject member already and you can use the attributes:
[RequireComponent(typeof(BoxCollider2D),typeof(Rigidbody2D))]
// Start is called before the first frame update
public class Move : MonoBehaviour
{
void Start()
{
Debug.Log(gameObject.name);
}
}

How can i re-use a code in monobehaviour class?

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>();

How to create a dynamic variable system with Scriptable Objects

In this talk I learned how to create variables with scriptable objects, creating classes like FloatVariable, DoubleVariable, StringVariable and others. But, in the same talk, the guy said that he uses a more dynamic variable system, that prevents creating several classes to handle all variable types.
Using the first system, I had a C# script called ImageFillSetter, that given two float variables and a Image script, it returns the division of the two variables to the fillAmount variable of the image.
But, when I get a Double Variable, and I'd like to set a progress bar with this value, I need to create another script called ImageFillSetterDouble, and put in these variables. And if I needed to create one with Integers? Every time I create a script like this, I will need to create two duplicates to handle the other number variable types?
With this dynamic variable system, this problem should be solved, but I have no idea how to start/create this system.
The code looks like this:
[CreateAssetMenu(menuName="Variable/Float")]
public class FloatVariable : ScriptableObject, ISerializationCallbackReceiver
{
public float initialValue;
[NonSerialized]
public float value;
public void OnAfterDeserialize()
{
value = initialValue;
}
public void OnBeforeSerialize() { }
}
What I want is something like this (Totally hypothetical, I know that this doesn't works)
[CreateAssetMenu(menuName="Variable")]
public class Variable : ScriptableObject, ISerializationCallbackReceiver
{
public var initialValue;
[NonSerialized]
public var value;
public void OnAfterDeserialize()
{
value = initialValue;
}
public void OnBeforeSerialize() { }
}
I know there is an accepted answer that works, but I feel that the usage of ScriptableObject variables as described in the linked video was misinterpreted.
I think you would be better off making your FloatVariable independent of the calculation.
Let's say the calculation is for player health and your fill value would be calculated by currentHealth/maxHealth.
public class PlayerHealth: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private float maxHealth;
[SerializeField] private float currentHealth;
void Update()
{
this.floatReference.value = currentHealth/maxHealth;
}
}
public class ImageFillSetter: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private Image imageReference;
void Update()
{
this.imageReference.fill = this.floatReference.value;
}
}
Or let's say that player health is stored as double:
public class PlayerHealth: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private double maxHealth;
[SerializeField] private double currentHealth;
void Update()
{
this.floatReference.value = (float)(currentHealth/maxHealth);
}
}
Now let's say that you add an input field where the fill value can be entered as a percentage string (like '76'):
public class FillInput: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
[SerializeField] private Input input;
void Update()
{
if(Input.GetKeyDown(KeyCode.Enter))
{
this.floatReference.value = float.Parse(input.text)/100f;
}
}
}
The ImageFillSetter will 'observe' the FloatVariable without being aware of how that float was calculated.
This way you only ever have to have one ImageFillSetter that can be used for any image and any data source, while having 1 or more ways of altering the fill that does not require any changes to be made to ImageFillSetter.
For example, let's say that you want to use the same approach to indicate async level load progress:
public class FillInput: MonoBehaviour
{
[SerializeField] private FloatVariable floatReference;
private AsyncOperation loadOperation;
void LoadLevelAsync(string levelName)
{
this.loadOperation = SceneManager.LoadLevelAsync(levelName, LoadSceneMode.Additive);
}
void Update()
{
this.floatReference.value = this.loadOperation?.progress ?? 0;
}
}
This will work without making any other changes as long as your ImageFillSetter references the same FloatVariable.
Think of the FloatVariable (or whichever primitive you have eg. DoubleVariable) as a value stored in a database. Anyone can read the value and anyone can save a new value. It would be strange to store all possible calculations for the value in the database instead of doing the calculation and just storing the answer.
This does not change the fact that you need Scriptable implementations for each primitive:
FloatVariable
DoubleVariable
StringVariable
BoolVariable
etc
but you will only need one of each as demonstrated in the first section of derHugo's answer.
Have a look at Generics
Have one abstract class like
public abstract class ValueAsset<T> : ScriptableObject
{
public T value;
// Add your methods
// Here some more examples also using the T value. They might also be abstract but they don't have to be
// return a T
public T GetValue()
{
return value;
}
// or pass a T
public void SetValue(T input)
{
value = input;
}
}
This class you will never instantiate but now derive multiple implementations from it e.g.
[CreateAssetMenu(fileName = "new int", menuName = "ValueAssets/int")]
public class IntValue : ValueAsset<int>
{
// Maybe constructors here or additional fields and methods
}
[CreateAssetMenu(fileName = "new float", menuName = "ValueAssets/float")]
public class FloatValue : ValueAsset<float>
{
// Maybe constructors here or additional fields
}
You can also have multiple generic values like
public abstract class OtherExample<TKey, TValue> : ScriptableObject
{
// Note that this is just an example
// Dictionary is not serializable
public Dictionary<TKey, TValue> values = new Dictionary<TKey, TValue>();
public void AddPair(TKey key, TVakue value)
{
values.Add(key, value);
}
}
And implement something like
public OneImplementation : OtherExample<string, GameObject>
{
//...
}
The same way this can be used for reference values (components, GameObject etc)
So for IntValue the method GetValue will return an int and SetValue will take an int as parameter. The same way they take and return a float in FloatValue.
Doing the same thing with an ImageFillSetter<T> you can than make your method abstract and implement different behaviours for different T values (like e.g. a different parsing etc)
Note: I don't know why exactly but in the past I noticed that
public ValueAsset<T> valueAsset;
will not be serialized in the inspector even if later implemented so you have to implement the field with the correct type in the implementation instead. You also still could override it on runtime but you can skip the whole FetchValue part if you don't need it and anyway use valueReference instead - just added it for completeness.
public abstract class ImageFillSettet<T> : MonoBehaviour
{
// Will not appear in the Inspector
public ValueAsset<T> ValueAsset;
// Override this in implementation
protected abstract void FetchValue();
// Use it for Initializing the value
private void Awake ()
{
FetchValue();
}
public abstract void SetFill();
}
Than later
public class ImageFillSetterFloat : ImageFillSetter<float>
{
// Show in the inspector
[SerializeField] private FloatValue valueReference;
// Provide the reference to the base class
protected override void Fetch value()
{
valueAsset = valueReference;
}
public override void SetFill()
{
// Use valueReference for something
}
}

Is it possible to show static fields in the Unity Editor?

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;
}

Categories

Resources