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.
Related
I am working on a simple weapons controller for my FPS game and have come across an issue while trying to make it dynamic. What I want to happen when the player picks up a weapon is for the weapons stats and effects to be set as default. To do this each weapon has a script which is
weapon.name + "Stats"
But I'm having issues referencing said script. Right now, this is what my code looks like:
string weaponScriptName = weapon.name + "Stats";
weapon.GetComponent<weaponScriptName>().UseWeapon();
where weapon represents the currently equipped weapon's gameobject. Obviously, this doesn't work and various implementations that I've found on the Unity help pages only cause more errors. What can I do to solve this issue?
Thank you.
GetComponent has multiple overloads.
There is the generic version you refer to - the most commonly used one
T GetComponent<T>()
which you can only use with a compile time constant type parameter such as
var renderer = GetComponent<Renderer>();
There is one using a dynamic Type
Component GetComponent (Type type)
like
// Just an example, there are many ways of getting a type
var type = typeof(Renderer);
var renderer = (Renderer) GetComponent(type);
And finally there is one taking a string
Component GetComponent (string typeName);
like
// Again just an example, there are many ways of getting type names
// Especially when dealing with multiple assemblies you might even have to use the AssemblyQualifiedName
var renderer = (Renderer) GetComponent("Renderer");
Note that for any of the dynamic versions you either have to type cast or if it is enough to have only a generic Component reference you can use it of course.
However, as said if anyhow possible don't use string versions at all!
It is always slow and error prone. Rather use e.g. some common base class or interface, or use an enum or Dictionary to decide what to do for which state.
So I would rather have e.g.
public interface IWeapon
{
void UseWeapon();
}
and then every of your different weapons can implement this interface
public class WeaponA : MonoBehaviour, IWeapon
{
public void UseWeapon ()
{
Debug.Log("Used Weapon A");
}
}
public class WeaponB : MonoBehaviour, IWeapon
{
public void UseWeapon ()
{
Debug.Log("Used Weapon B");
}
}
and your code would simply be
var weapon = someObject.GetComponent<IWeapon>(). UseWeapon();
Or if your weapons all share some common implementation such as pickup etc rather have a common base class
public abstract class BaseWeapon : MonoBehaviour
{
// Everything that all weapons share as behavior and properties
// every subclass HAS TO implement and override this method
public abstract void UseWeapon ();
// Alternatively if there even is some common behavior
// subclasses CAN but don't have to override this
//public virtual void UseWeapon ()
//{
// // Implementation that is the default behavior
//}
}
And then
public class WeaponA : BaseWeapon
{
public override void UseWeapon ()
{
Debug.Log("Used Weapon A");
// If using virtual before then this class IGNORES and fully overwrites the default implementation
}
}
public class WeaponB : BaseWeapon
{
public override void UseWeapon ()
{
// If using virtual and you WANT the default behavior then add
//base.UseWeapon();
Debug.Log("Used Weapon B");
}
}
and your code would simply be
var weapon = someObject.GetComponent<BaseWeapon>(). UseWeapon();
If i got it correctly, you want to do something like GetComponent<"Transform">() ?
If so, you should do GetComponent("Transform"); instead
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();
}
}
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 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.
I have a simple Gun class and i want to have attachable components to it. So i use a generic type for my Attatch method since their are different component types.
My problem is i don't know how to assign the value to the object in the method - i think i might have misunderstood the logic of generics. This is my code:
public class Gun : Weapon
{
public Magazine magazine { private set; get; }
public void Attach<T>(T component) //validate if can be attatched
{
if (component is Magazine)
{
if (magazine == null)
Apply(component);
else
{
Drop(component);
Apply(component);
}
}
}
private void Apply<T>(T component) // apply the component to the gun
{
if (component is Magazine)
{
magazine = (Magazine)component; //ERROR: Cannot convert type `T' to `Magazine'
}
}
...// extra non relevant stuff
}
My Magazine type is a class not a struct if that matters:
public class Magazine : Weapon_Component {//code }
So how do i correct for this error so it will be functional for any weapon component type?
It is difficult to understand what design would work best in your scenario. You seem to be asking about a specific syntactical problem. There's not much detail in your question about the broader issues. However, making some inferences, here are a couple of options you might prefer to what you're doing now…
1. Use a dictionary to track components
public class Gun : Weapon
{
private readonly Dictionary<Type, Weapon_Component> _components
= new Dictionary<Type, Weapon_Component>();
public void Attach(Weapon_Component component) //validate if can be attatched
{
if (_components.ContainsKey(component.GetType()))
{
Drop(component);
}
Apply(component);
}
private void Apply(Weapon_Component component) // apply the component to the gun
{
_components.Add(component.GetType(), component);
}
private void Drop(Weapon_Component component)
{
_components.RemoveKey(component.GetType());
}
}
Note that the Attach(), Apply(), and Drop() methods could all be consolidated into a single assignment:
public void Attach(Weapon_Component component)
{
_components[component.GetType()] = component;
}
Though, you might want to keep the Drop() method, for the scenario where you want to remove the component without replacing it. It's not clear from your question whether that ever happens.
2. Design by interface and let components interact directly with weapons
interface IWeapon { }
interface IGun : IWeapon
{
Magazine Magazine { get; set; }
}
interface IWeaponComponent
{
Attach(IWeapon weapon);
}
public class Gun : Weapon, IGun
{
public Magazine Magazine { get; set; }
}
public class Magazine : IWeaponComponent
{
public Attach(IWeapon weapon)
{
IGun gun = weapon as IGun;
if (gun == null)
{
throw new ArgumentException("Cannot attach a magazine to weapon that is not a gun");
}
gun.Magazine = this;
}
}
Each of the above has its pros and cons, and of course these are not by any means your only options. I'm just trying to give you some ideas of what you might do, as well as help you understand, if these are not helpful to you, how you might narrow your question so that it's more answerable.
If you're components must always be of type Magazine and solely that type, than there is absolutely no use of generics here.
If you want any type that is or inherits the type Magazine than yes generics can help, by constraining the generic parameter to inherit the said type like this:
where T : Magazine
But you can still make the type not generic but simply Magazinethis should still do the job.
Or to fix your specific case you can do:
var magazine1 = component as Magazine;
if (magazine1 != null)
{
magazine = magazine1;
}
But in your example it makes more sense to use one of the former 2 options, rather than the solution you already have.