I'm trying to create my first bigger project in Unity and I am struggling to understand how abstract implementations away from the monobehaviour classes attached to gameobjects.
I am very familiar with dependency injection (using ASP.NET) but it seems DI is not a great idea to include in Unity projects according to a number of different articles. These articles say Unity has to handle Inversion of Control already, which is the Service Locator principal. I cannot find any built in implementation aside from methods like GameObject#Find or GameObject#GetComponent, etc.
An example would be a class handling interaction with files:
public interface IFileHandler { }
public class FileHandler : IFileHandler { }
public class FileHandling : MonoBehaviour
{
private IFileHandler fileHandler;
private void Awake()
{
this.fileHandler = new FileHandler();
}
}
Now the constructor of FileHandler changes. I would have to change it in every class.
How can I decouple the FileHandler from FileHandling?
Thanks in advance
I recall that in Asp.net there's Program.cs with main method that boots everything up. In unity you could have GameObject with similar Program.cs script that has been assigned a very low Script Execution Order. The script simply initialises your game systems if they have not been initialised yet.
During initialization you can create system instances and store to a static class like toolbox from where you can later reference them from anywhere. If your system needs to listen to unity events like update you can create a new GameObject with the script and set DontDestroyOnLoad enabled and store it to the Toolbox as well.
Toolbox is basically singleton for storing your other "singletons" so they don't have to be singletons. If you use interfaces you can easily swap out the implementation for the SaveSystem for example.
// Enum to allow multiple instances if needed
enum GameSystems { SaveSystem, GameManager }
public class GameTB {
//usage GameTB.toolBox.SetTool(GameSystems.SaveSystem, new SaveSystem());
public static ToolBox<GameSystems> toolBox = new ToolBox<GameSystems>();
//usage GameTB.SaveSystem.SaveGame();
public static ISaveSystem SaveSystem
{
get
{
return toolBox.GetTool<ISaveSystem>(GameSystems.SaveSystem);
}
}
public static IGameManager GameManager
{
get
{
return toolBox.GetTool<IGameManager>(GameSystems.GameManager);
}
}
}
public class ToolBox<T>
{
private Dictionary<T, object> Tools { get; } = new Dictionary<T, object>();
public K GetTool<K>(T key)
{
if (Tools.ContainsKey(key))
return (K)Tools[key];
else
return default(K);
}
public void SetTool(T key, object tool)
{
if (!Tools.ContainsKey(key))
Tools[key] = tool;
else
Tools.Add(key, tool);
}
public bool ContainsTool(T key)
{
if (Tools.ContainsKey(key) && Tools[key] != null)
return true;
else
return false;
}
public void ClearTools()
{
Tools.Clear();
}
}
Related
I am not sure if this is possible with C#, but is it possible to store information in an attribute that is related to the instance of a class?
So, I have the following class with the field Initialized, as seen here:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class GameObjectAttribute : Attribute {
internal bool Initialized = false;
}
I then use this to add the attribute to the class:
[GameObject]
public class Player {
}
Now, in this class, is it possible to modify the data in the attribute for each individual instance of the class like in this pseudo code:
internal class Core {
async void Tick() {
while (isRunning) {
foreach (var gameObject in gameObjects) {
// Get attribute information
var refToAttribute = gameObject.... // Somehow get information
if (!refToAttribute.Initialized) {
// Do some stuff
refToAttribute.Initialized = true;
}
}
await Task.Delay(1);
}
}
}
Edit
When coming from a TypeScript world, I can return a new instance of a class when the class is created:
export function GameObject() {
return function (constructor: T) {
return class extends constructor {
initialized = false;
}
}
}
#GameObject
export class Player {
}
so now, in my loop I have access to instance, however Player does not have access.
Now, in this class, is it possible to modify the data in the attribute for each individual instance of the class
No, attributes are attached to the class, not to instances of said class. There is only a single instance of the attribute and that instance is attached to the class Player. You can access the attribute from the player instance, but only by looking at the attributes that are attached to the type. So it is really not anything that you could use to provide additional information for any particular player instance.
What you can do in a statically typed language is to wrap the value. Something like this would achieve what you are trying to do:
public class PlayerGameObject
{
public bool IsInitialized { get; set; }
public Player Player { get; set; }
}
// …
foreach (var gameObject in gameObjects)
{
if (!gameObject.IsInitialized)
{
var player = gameObject.Player;
// do some stuff
gameObject.IsInitialized = true;
}
}
is it possible to store information in an attribute that is related to the instance of a class?
No. Attributes are part of the definition of a class, not instances of it. It's impossible for the same reason that a method can't be public for one instance of a class but private for another instance of the same class.
Information about an instance of a class can only be stored in its fields and properties.
I suggest you use a base class instead.
//Your framework
abstract class GameObject
{
internal bool Initialized { get; set; } = false;
}
//Inside program that uses the framework
class Player : GameObject
{
}
Now the Player class has a property than only your code can access.
//Your framework
void Initialize(GameObject obj)
{
if (!obj.Initialized)
{
//Do something
obj.Initialized = true;
}
}
//Inside program that uses the framework
var player = new Player();
if (player.Initialized) //Compile-time error
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>();
In my project, I have a class structure as shown in the image.
The green classes are old codes, that runs very well. The classes in red boxes are newly added codes. There're no compiler errors, however when click play in Unity and runs into the new code, the three classes can't be initialized correctly.
And unity console gives warning that says "The class named 'DataMgrBase`2' is generic. Generic MonoBehaviours are not supported! UnityEngine.GameObject:AddComponent()" at this line: "instance = obj.AddComponent ();"
How can I solve this problem?
Following are some code for your reference, thanks!
Implementation of singleton base class:
using UnityEngine;
using System.Collections;
public class UnitySingletonPersistent<T> : MonoBehaviour where T : Component
{
private static T instance;
public static T Instance {
get {
if (instance == null) {
instance = FindObjectOfType<T> ();
if (instance == null) {
GameObject obj = new GameObject ();
obj.name = typeof(T).Name;
obj.hideFlags = HideFlags.DontSave;
instance = obj.AddComponent<T> ();
}
}
return instance;
}
}
public virtual void Awake ()
{
DontDestroyOnLoad (this.gameObject);
if (instance == null) {
instance = this as T;
} else {
Destroy (gameObject);
}
}
}
Implementation of DataMgrBase:
public class DataMgrBase<TKey, TValue>: UnitySingletonPersistent<DataMgrBase<TKey, TValue>> {
protected Dictionary<TKey, TValue> dataDict;
public override void Awake()
{
base.Awake();
dataDict = new Dictionary<TKey, TValue>();
}
public TValue GetDataForKey(TKey key)
{
TValue data;
if (dataDict.TryGetValue(key, out data))
{
return data;
}
else
{
data = LoadDataForKey(key);
if (data != null)
{
dataDict.Add(key, data);
}
return data;
}
}
virtual protected TValue LoadDataForKey(TKey key)
{
if (dataDict.ContainsKey(key))
{
return GetDataForKey(key);
}
else
{
return default(TValue);
}
}
}
I've solved it by myself as following:
Change of the base class to get a new generic type(of the class that will derive from it, and pass this type to singleton base class)
public class DataMgrBase<TKey, TValue, TClass>: UnitySingletonPersistent<TClass> where TClass: Component
For all the other three classes that want to derive from it, change them as following form:
public class MobSettingDataMgr : DataMgrBase<int, MobSettingData, MobSettingDataMgr>
You want something like:
public abstract class UnitySingletonPersistent<T> : MonoBehaviour where T:UnitySingletonPersistent<T>
{
...
}
Then in your concrete class:
public class DataMgrBase<TKey, TValue> : UnitySingletonPersistent<DataMgrBase<TKey, TValue> >
{
...
}
This is somehow answer that is not solving your problem, but will explain the problem.
MonoBehaviour cannot be generic for at least two reason:
1. Imagine you want to add generic component in Inspector from Unity3D editor. Now engine needs to know exactly all types in this component, not only casue it is going to be compiled in this moment, but also cause you could have public fields with undeclered types. Try to assign your UnitySingletonPersistent directly in Inspector, and you will see it is imposible.
2. Using AddComponent<T> where T is generic looks like could work, but also in this engine you can make so called prefabs out of instantiated GameObjects, and if this GameObject contains generic component Unity3D engine would need to support some kind of baking types, and in practice this would lead to generating scripts, each with diffrent types, and making big mess inside project. I hope you follow me.
But why it works for the components you marked with green color? Simply cause Unity3D engine knows all types when adding this component to GameObject.
To support all this Unity Technologies would need to make core changes in Unity3D engine, how it works now. It would make Unity3D completly diffrent engine as it is now.
So to solve your problem, there is only one way: no adding in runtime generic components, and getting rid of DataMgrBase class. So you will need to implement DataMgrBase logic in each component.
I'm currently attempting to use a Singleton as a global data structure for Task organization in a game I'm making in Unity.
My Singleton class code is as follows:
public class TaskManager : MonoBehaviour
{
private List<Task> Tasks;
private static TaskManager instance;
private TaskManager()
{
Tasks = new List<Task>();
}
public static TaskManager Instance
{
get
{
if(instance == null)
{
instance = new TaskManager();
}
return instance;
}
}
}
I used this example as a basis for my class:
https://msdn.microsoft.com/en-us/library/ff650316.aspx
However, the problem is, when I try to access the TaskManager in different scripts, the values don't get saved.
For example, in one script I do:
TaskManager tm = TaskManager.Instance;
Task newTask = new Task();
tm.PushTask(newTask);
print(tm.GetTaskList().Count);
Through this I can see that the TaskManager has a count of 1, showing the new task.
But then my other script attempts to read from the TaskManager:
TaskManager tm = TaskManager.Instance;
List<Task> l = tm.GetTaskList();
print(l.Count);
When I do this, the Count is returned as 0, showing that the above task from the world has not been saved to the TaskManager.
I'm pretty sure the error is resulting from me misunderstanding how to use Singletons. Do I need to implement a set property for TaskManager Instance? Or is there another mistake I'm making?
Thanks!
Edit:
The PushTask() code is as follows:
public void PushTask(Task t)
{
Tasks.Add(t);
}
Personally, I don't think that solution that you chose is a perfect one. For some time, I tried using "abstract" classes that didn't inherit from MonoBehaviours, to decouple logic from Unity mechanics — but found that this makes code bloated and unnecessary complicated. In fact, every class that doesn't only contain data but has some logic of its own ends up being a MonoBehaviour sooner or later, in my experience.
So, instead of removing inheritance from MonoBehaviour, I'd solve it by implementing the usual MonoBehaviour singleton pattern:
using UnityEngine;
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
public static T Instance { get; private set; }
protected virtual void Awake()
{
if (Instance == null)
{
Instance = (T) this;
}
else
{
Debug.LogError("Got a second instance of the class " + this.GetType());
}
}
}
And then just inheriting your class from it.
I've encountred a confusing problem while developping a user library in c# in which i've created two classes that implement an interface that is called by the API as a separated plugin...
(that means that after the compilation the API detect 2 plugins although they are from same project)
What i'm trying to do is to enable communication between those two plugins. Accuratly i want to transfer an object (it's reference not a copy) from a plugin to another... but i'm failing!
I tried to make one of those plugins Singleton and reach it from the other, but since the API require a public constructor, I was forced to imitate the singleton work, and effectively i've reached the instance of the plugin, but i'm enable to reach its properties...
Let me schematize that through simplified code:
let's say this is class A (the one that imitate the singleton)
Class A:IPlugin
{
private static volatile A _instance;
public static A Instance
{
get { return _instance; }
}
public A()
{
if (_instance == null) _instance = this; // as i'm sure it's called once
}
public Foo F{get;set} // THIS IS INITIALIZED SOMEWHERE IN THAT PLUGIN'S CONTEXT
}
and this is the class that tries to extract objects from A
Class B:IPlugin
{
FindFoo()
{
Foo Fb = A.Instance.F; // THAT IS ALWAYS NULL
}
}
A very important indication and the one that may create the problem is that:
A.F is bound to a WPF control...
I hope I've clearly transmitted my issue and you'll be able to help me because i'm stuck !
Try the following ;
public interface IPlugin {
string Foo { get; set;}
}
public class A : IPlugin {
private static A _inslance { get; set;}
public static A Instance {
get {
if (_inslance == null){
_inslance = new A();
}
return _inslance;
}
}
public string Foo { get; set;}
}
public class B : IPlugin {
public string GetMeFooOfA {
get {
return A.Instance.Foo;
}
}
public string Foo { get; set;}
}
void Main()
{
A.Instance.Foo = "Test 123";
var b = new B();
Console.WriteLine(b.GetMeFooOfA);
}
You may want to look at a DI (Dependency Injection), such as Unity, Ninject, etc.. framework which would offer you a good platform to work on when you develop modular code