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>();
Related
I'm making a saving system for my mobile game. I have a script for economy that tracks the amount of in-game currency and i'd like to reference a non MonoBehaviour script that will hold the data to save
public class Money : MonoBehaviour
{
public int Gold;
public int Platinum;
public int Tokens;
public DataHolder data;
private void Update()
{
data.Platinum = Platinum;
data.Tokens = Tokens;
data.Gold = Gold;
}
}
public class DataHolder
{
public int[] dragonLevel;
public bool[] dragonMasterLevel;
public int Gold;
public int Platinum;
public int Tokens;
}
Will unity automatically get the reference and will it properly transfer this data?
You are allowed to use your own classes / structs in Unity. Unlike Unity components like ScriptableObject and MonoBehaviour, you need to create them and make sure they get deleted.
ScriptableObject
public class DataHolder : ScriptableObject {
...
}
ScriptableObjects are Unity's solution for exactly your use case. You can then create assets that are instances of DataHolder.
In this solution the data gets serialized with the GameObject so you can have default values in Unity Editor Inspector.
Keep using DataHolder as is
private void Update () {
if (data == null) data = new DataHolder();
...
}
In both these solutions the runtime values will not persist between sessions. To save values and restore them there are different options. Here are a few in order of simplicity:
PlayerPrefs:
Add WriteToPrefs() and LoadFromPrefs() methods to DataHolder.
FileSystem
online databases like Google Firebase
Adding to the other answer, you could just create an object of your DataHolder class in one of your monobehaviours, and call DontDestroyOnLoad on the gameObject. you will also need to use the code below in Awake on your DontDestroyOnLoad() object to make sure only one instance of the MonoBehaviour is available, so you dont create duplicates when you navigate between scenes
public DataHolder dataHolder;
public static MyComponent myComponent;
private void Awake()
{
DontDestroyOnLoad(gameObject);
if (myComponent == null) myComponent = this;
else Destroy(gameObject);
dataHolder = new DataHolder();
}
now you can write to this dataHolder object and keep the GameObject alive in case you want to add any more data to it. It is good practice to have a persistant GameObject in the scene to save in-game data, or just about anything that needs to be preserved between scenes.
Alternatively, you can also make your DataHolder class static and it's members static. although i would advise against this, especially if there are multiple players/characters in your game that use the same component. But this will actually help you reference it from any script, without creating objects, or maintaining a persistent GameObject in the scene.
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"]);
}
}
}
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
}
}
I am making a game where the player first has to choose the type of control to use before playing. The three options being: Keyboard, Controller, Touch
The player must click the button corresponding to his choice. Each button runs this script when clicked on:
public class KeyboardButton : MonoBehaviour {
public static int controller;
public void buttonClick () {
controller = 1;
}
}
In reality, each button as its own script, where the value of controller is different depending on the script ran. The idea is that the value of this integer would be sent over to the script responsible of controlling the player so it will make use of the demanded input type. ie: if the keyboard button is selected, it will run the corresponding script, setting the integer value to 1. After the PlayerController script receives this value, it will know to only accept input from the keyboard.
I have consulted a lot of documentation, but a lot of it contains context-specific C# things that I don't understand and are irrelevant to what I want to do.
Also, I would not like an answer around the lines of: "You don't have to make the player choose a control type, here's how you can make your game accept all types of control at once." I already know all this stuff and there is a reason I want the player to make a choice. Furthermore, I would still like to know a way to transfer integers to be able to be more organized, rather than having a single script that does 90% of the things in the game.
There are three way you can pass value to another script.
GetComponent
You can use GetComponent method to get another script.
public class KeyboardButton : MonoBehaviour {
public int controller;
//this is anotherScript instance
public AnotherScript anotherScript;
Start()
{
anotherScript = GameObject.Find("Name of Object").GetComponent<AnotherScript>();
}
public void buttonClick () {
controller = 1;
anotherScript.sendValue(controller); //send your value to another script
}
}
Singleton
Let AnotherScript be a static Singleton,You can get the instance on other side.
public class AnotherScript : MonoBehaviour
{
//need to be static
public static AnotherScript Current;
Start()
{
if(Current == null)
{
Current = new AnotherScript();
}
}
public void sendValue(int val)
{
//todo
}
}
public class KeyboardButton : MonoBehaviour
{
public int controller;
public void buttonClick () {
controller = 1;
AnotherScript.Current.sendValue(controller);//send your value to another script
}
}
SendMessage
If you want to send a value to otherscript,SendMessage is a simple way you can choose.
ps:SendMessage method can just send a parameter.
public class KeyboardButton : MonoBehaviour
{
public void buttonClick ()
{
controller = 1;
GameObject.Find("name of object").SendMessage("sendValue",controller);
}
}
As pointed out in one of the comments, you already exposed that value, you can refer to is via
Debug.Log(KeyboardButton.controller);
without providing an instance. There's multiple other ways of doing it, as this way is only good to a certain level of complexity, after which it starts to get more muddy, but depending on what you need right know it might get you through. It is one of the valid ways, and probably the simplest one.
You may also want to know when the value has changed, for example you could use UntiyEvent and trigger it when value is changed
public class KeyboardButton : MonoBehaviour {
public UnityEvent OnValueChanged;
public static int controller;
public void buttonClick () {
controller = 1;
OnValueChanged.Invoke();
}
}
this is if you like to wire events in the editor. You could also do:
public class KeyboardButton : MonoBehaviour {
public static UnityEvent OnValueChanged;
public static int controller;
public void buttonClick () {
controller = 1;
OnValueChanged.Invoke();
}
}
the downside is that the event won't show up in the editor,but the upside is that you can set up a trigger without having to have a reference to the KeyboardButton instance that just got clicked.
public class ControllerChangeReactor : MonoBehaviour {
void Start()
{
KeyboardButton.OnValueChanged.AddListener(React); // add event listener
}
void React() // will get called when keyboard is clicked
{
Debug.Log(KeyboardButton.controller);
}
}
This approach can become limiting after you've written a dozen or so of those scripts, but a step up involves tailoring a custom system which is probably not worth it on your level (just yet). You can finish a simple game using the above approach, even if its not the most elegant.
You could also parametrize your script (expose 'WHAT DOES IT CHANGE' in editor), to avoid unnecessary multiplication of code
I'm implementing classes for Effects (something with a duration that applies a behavior in the FixedUpdate loop while it is active) in Unity3D.
I have a base abstract Effect class which has the behavior for keeping track of the duration, removing itself when the duration is up, and calling a protected abstract _doEffect function while its duration is up. In my derived classes, I override _doEffect to create Effects with different behaviors.
public abstract class Effect : MonoBehaviour
{
public virtual float kDuration { get { return 1.0f; }}
public static bool IsStackable { get { return false; }}
private float _elapsed = 0.0f;
protected virtual void Start()
{
_elapsed = kDuration;
}
protected virtual void FixedUpdate()
{
_elapsed -= Time.fixedDeltaTime;
if(_elapsed <= 0) {
Destroy(this);
}
_doEffect();
}
protected abstract void _doEffect();
}
Now, because you can't use constructors with Unity3D, I need a way to do the following for each derived Effect class when I'm applying a new Effect of that type to a game object:
1) If this type of effect is not stackable, then remove all other instances of this monobehaviour from the game object.
2) Create a new component of the effect type to the game object.
3) Do some initialization specific to that effect type.
For these requirements, I was imagining doing something like
public class DerivedEffect : Effect
{
public override float kDuration { get {return 1.0f; }}
public static bool IsStackable { get { return true; }}
private int _derivedData;
public static void Create(GameObject obj, int data)
{
DerivedEffect effect = DerivedEffect.CreateEffect(obj);
effect._data = data;
}
protected override void _doEffect()
{
//Do some stuff
}
}
and then in the base class putting
public static virtual Effect CreateEffect(GameObject obj)
{
//T is somehow magically the type of the class you called this function on
if(!T.IsStackable()) {
//delete all components of type T on obj
}
T effect = obj.AddComponent<T>();
return effect;
}
Obviously this isn't possible unless I do some weird stuff with generics and reflection that seems a bit extreme and probably not that right way to do things.
The crux is that I want a static function that does 1), 2), 3), and I want to share the code that does 1) and 2), and 1) depends on a bool which is different for every derived class.
What is a proper, working design for these desiderata?
What is a proper, working design for these desiderata?
Unity is component based and gets things complicated when you want to use it the way you in a normal C# application.
The simplest way is to use Composition. Make the Effect class it's own class that is not abstract. Just a normal class that inherits from MonoBehaviour. You can easily create new instance of it with AddComponent and get it with GetComponent. This script can also destroy itself directly after the timer is done counting without any problems.
Create a global variable in the DerivedEffect class to hold the instance of the Effect script that is created and this can be re-used over and over again until it becomes null which means that the script is destroyed. Note that there is no inheritance involved here and DerivedEffect script is only used as an example of the script that manages the Effect script.