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.
Related
This is in the context of creating an RTS, in which the aim is to have several Units (Buildings; Movers : Workers, Soldiers...) that one can select as one or a group. When selected, the units can be moved (Mover), and can have specific methods (Building: can create a Mover...).
I have (heavily) based the selection system on:
https://www.youtube.com/watch?v=vAVi04mzeKk
I have a class Unit, which is added upon Start within the unitList field of the class UnitSelections (this makes all Unit objects of the Scene accessible for selection):
public class Unit : MonoBehaviour
{
public int maxHealth;
public int health;
public bool isSelected = false;
public bool canMove = false;
public bool canCreateUnits = false;
public bool canAttack = false;
public bool canGatherResources = false;
void Start()
{
//add this unit to the list
UnitSelections.Instance.unitList.Add(this);
health = maxHealth;
}
void OnDestroy()
{
//remove it from the list when destroyerd
UnitSelections.Instance.unitList.Remove(this);
}
}
The class UnitSelections holds the data on all Unit objects in the Scene, and all Unit objects that are currently selected. It includes methods on how to select them (via left click, drag + left click, shift + left click...):
public class UnitSelections : MonoBehaviour
{
public List<Unit> unitList = new List<Unit>();
public List<Unit> unitsSelected = new List<Unit>();
private static UnitSelections _instance;
public static UnitSelections Instance { get { return _instance; }
//more code with methods to add units to the unitsSelected
}
And then a class Building, child of Unit, which has a method that can be triggered if the field isSelected is TRUE (create a Mover object). This is to say, when the Building is selected, only then can you trigger its methods via e.g. keyboard input:
public class Building : Unit
{
void Start()
{
canCreateUnits = true;
maxHealth = 1000;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.B) && this.isSelected)
{
CreateMover();
}
}
void CreateMover()
{
// behaviour here
}
}
In the unity editor, I have a prefab which corresponds to the Building subClass:
If I add as a component the script Building.cs to the GameObject Building, does it inherit everything from Unit.cs ?
Or does it need to have the Unit script as a component too ?
--> For fields and "normal" methods (not Start/Awake/Update), yes. So the GameObject Building only needs to have the Script Component Building
However, the Start method of Unit is not called in the Building class, which makes it not possible to add it to the unitList field of the class UnitSelections.
How can the Start method of Unit be called for an object with the Building Script Component ?
--> Need protected virtual void Start() for the TopClass, and can call base.Start() within the protected override void Start() of the ChildClass
See solution answer for details
Just as Unit inherits from MonoBehaviour, Building inherits Unit (which inherits MonoBehaviour). As such, you should ONLY add Building as a component to your GameObject. If you add both, you'll end up with two distinct components on your game object - the Building component AND a Unit component. Each component would have it's own set of variables, and listen and react to the Unity messages independently, for example Update() if it were present if both classes.
It's also interesting to note that MonoBehaviour inherits from Behaviour, which inherits from Component, which inherits from Object (UnityEngine.Object, not System.Object). So Building.cs in this case is just another subclass of a component and is treated as such. You can check out the lineage on the Unity docs pages.
This image demonstrates the problem, where each component has it's own set of field values.
Now, if we create a small test script to look at the GameObject:
We get this as the result:
As you can see, there are two "Unit" components on the HumanBarracks GameObject, a Unit and a Building which is also of type Unit.
Extra
public class Unit : MonoBehaviour
{
public int maxHealth;
public int health;
public bool isSelected = false;
public bool canMove = false;
public bool canCreateUnits = false;
public bool canAttack = false;
public bool canGatherResources = false;
// By making Start protected virtual, child classes will run it and can override it as well if they require a specific implementation.
protected virtual void Start()
{
Debug.Log("This Start method will now be called by all children.");
}
}
public class Building : Unit
{
protected override void Start()
{
// call the base Start method on Unit.
base.Start();
// Now add this building’s particular Start code.
Debug.Log (“Building specific Start code!”);
}
}
As you can see the building component has all fields of unit component. So yes it inherits everything. The building component is unit component + all inside (start, update, CreateMover). If you add building component and unit component then you have 2 unit components where one of them is extended with start, update and CreateMover.
TLDR: How can I have a script that inherits from a public abstract class have access to an often changing Enemy gameObject variable (so it can't be static) without passing it through several other scripts first?
In my game, I have a battle system where a different "Battle Event" gets loaded for each battle. Each "Battle Event" gets its own script, and each of those events inherits from the same BattleEvent parent (which is public abstract).
The code structure basically goes:
BattleSystem (main brain of battles which holds the Enemy
gameObject) ->
BattleEventsManager (handles both which BattleEvent to load, and which methods to run on that BattleEvent) ->
a random BattleEvent (BattleEventOne or BattleEventTwo etc)
public class BattleSystem : MonoBehaviour
{
BattleEventsManager battleEventsManager;
public Enemy currentEnemy;
// the Enemy data is passed when the battle starts
public void Start(Enemy enemyToLoad)
{
battleEventsManager = GetComponent<BattleEventsManager>();
currentEnemy = enemyToLoad;
}
public void BeginPlayerTurn()
{
battleEventsManager.SetupEvent(currentEnemy);
}
}
public class BattleEventsManager : MonoBehaviour
{
BattleEvent currentBattleEvent;
private void Awake()
{
// define this battleEvent
currentBattleEvent = GetComponent<BattleEventOne>();
}
public void SetupEvent(Enemy currentEnemy)
{
// start the battleEvent with its Setup function
currentBattleEvent.Setup(currentEnemy);
}
}
// inherits from `BattleEvent` parent class, shown below
public class BattleEventOne : BattleEvent
{
// override the method from the parent
public override void Setup(Enemy currentEnemy) {
// we can now use the data we need in `currentEnemy`
// all I wanted was to get access to `BattleSystem.currentEnemy`
// but i had to pass it down all the way here. Is there a better way?
}
}
// parent of all `BattleEvents`
public abstract class BattleEvent : MonoBehaviour
{
public abstract void Setup(Enemy currentEnemy);
} // end BattleEvent class
As you can see, the the currentEnemy variable needs to be passed down through 2 classes in order to get to where it needs to be: BattleEventOne.Setup().
Furthermore, I needed to add the Enemy currentEnemy param to the parent BattleEvent, which is problematic because not all BattleEvents will need this information.
I originally wanted to just call BattleSystem.currentEnemy from BattleEventOne (using a property or something), but because the BattleSystem is abstract/static, it can't access it. And because currentEnemy contains new data each battle, I can't make that a static variable.
So, how can I have BattleEventOne here access BattleSystem.currentEnemy without having to pass it down as I've done above?
(I still struggle a lot with passing information between scripts, so any help here is really appreciated!)
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 Unity I make use of interfaces. I set a logic for components which are totally different to each other.
Examples:
A car, a dog and a aircraft would implement IMovable. I can call Move() from each component but these components execute different code.
Same for ISavable, each component, that has to save data to the database could save the stuff when looping through all savables.
The problem:
Some people in forums say that interfaces are bad for Unity.
When destroying a gameobject and call its interface method this still gets executed.
No error would come up because Destroy() does not destroy objects. Unity as a C++ driven Engine would setup a C# wrapper for the objects. These objects just get a flag destroyed which is a bool.
Destroyed gameobjects will not get destroyed immediately, they will be destroyed later on at the end of the frame.
Until this end of the frame is not reached the method can still get called from the destroyed object.
The best way would be using abstract classes only and never use interfaces because of the bad behaviour coming up when destroying objects.
I tested this with a small example, I created the following scripts:
public interface IIntfacable
{
void DoSomething();
void DestroyComponent();
}
public class bar : MonoBehaviour
{
private IIntfacable i;
private void Start()
{
i = FindObjectOfType<foo>().GetComponent<IIntfacable>();
}
private void Update()
{
i.DoSomething();
i.DestroyComponent();
i.DoSomething();
}
}
public class foo : MonoBehaviour, IIntfacable
{
public void DoSomething()
{
Debug.Log("=> DoSomething");
}
public void DestroyComponent()
{
Debug.Log("=> DestroyComponent");
Destroy(gameObject);
}
}
When executing this code I get the following result
Workaround:
I could create an abstract base class and choose between
public abstract void Foo();
and
public virtual void Bar()
{
return;
}
but this might lead to overengineering. Because all Scripts would need this base class whether they need this method or not.
Conclusion:
Should I prevent using interfaces?
I am confident in saying there is no harm in using interfaces.
The underlying fear is about keeping track of unmanaged references, a problem which will still be there weather you are using interfaces, abstract classes or whatever. You simply have to make sure that your game code will not try to access any objects which have been Destroy()ed.
Basically, I just construct a collection of objects that I know are not destroyed in my scene, and remove them after destruction.
With risk of answering an xy-problem, If you are scared to miss out on your reference count anyway or there is something in particular which wont allow creating such a list, there is not really any magic wand here, but there are a few precedent patterns in the .net framework with the IDisposable interface/pattern that may lead the way.
Many implementations of these patterns checks a flag in a few public-facing methods of the object. IDisposable.Dispose() would set the flag to true and throw an ObjectDisposedException on some public method if this is set to true, analog to MissingReferenceException in this case. Some patterns will then expose the flag IsDisposed, so that other objects that use the implementation can check instead of doing a try-catch on any access to the object. Your analog could be IsDestroyed, and you should set it in the override of OnDestroy.
You could change your method update like this (well it's not really a use case, why would you try to use it after destroying it, but to show my point):
private void Update()
{
i.DoSomething();
i.DestroyComponent();
if (!i.IsDestroyed) {
// This will not be called
i.DoSomething();
}
}
and implementation could be
public interface IIntfacable : IDestroyable
{
void DoSomething();
}
public interface IDestroyable
{
void DestroyComponent();
bool IsDestroyed { get; }
}
public class foo : MonoBehaviour, IIntfacable
{
bool IsDestroyed { get; private set; }
public void DoSomething()
{
Debug.Log("=> DoSomething");
}
public void DestroyComponent()
{
Debug.Log("=> DestroyComponent");
Destroy(gameObject);
}
public override OnDestroy() {
base.OnDestroy();
IsDestroyed = true;
}
}
I'm working on a game for WP7 with XNA. Here is my structure:
public abstract class enemy
{}
Child elements:
public class genericEnemy : enemy{}
...
public class snake : enemy {}
etc...
In WP7, a lot of things have been moved around and/or removed (especially with Serialization) it seems. Despite much searching, I haven't been able to find a solution. I'm trying to duplicate the child elements.
For example: On loading a level, I pass an array of three different enemies into the loading phase. During loading, I need to duplicate each of those enemies so that 20 of each are flying around doing their own thing during gameplay.
All the solutions I've seen refer to things that are not present in the WP7 library.
There's no "library" way of doing this as far as I know. One solution would be:
1) Declare a Clone() method in enemy that returns a copy of that enemy.
abstract class Enemy {
public abstract Enemy Clone();
}
2) Implement it in every concrete type, so a Snake creates a new Snake, etc. Example:
class Snake : Enemy {
int speed;
public override void Enemy Clone() {
var clone = new Snake();
clone.speed = speed;
return clone;
}
}
3) Now any object of a concrete type knows how to clone itself, so if you have an array of Enemies, you can call Clone() on each and it will create the proper concrete type in the proper way.
Create an enemy factory that can create enemies from an id of some sorts. While loading your level, you can then call the factory when you need to create an enemy:
class EnemyFactory
{
Enemy CreateEnemy(int id)
{
if (id == 0)
return new Snake();
return new GenericEnemy();
}
}
void LoadLevel()
{
// bla bla
Level level = new Level();
int enemyId = LoadFromFile();
level.AddEnemy(EnemyFactory.CreateEnemy(enemyId));
}
This way you get rid of the nasty cloning code, and you can control all enemy instantiation in the factory class.
use an abstract method that calls a copy constructor:
public abstract class Enemy
{
private readonly int mEnemyData;
protected Enemy(Enemy pEnemy)
{
mEnemyData = pEnemy.mEnemyData;
}
public abstract Enemy Clone();
}
public sealed class GenericEnemy : Enemy
{
private readonly double mGenericEnemyData;
private GenericEnemy(GenericEnemy pGenericEnemy)
: base(pGenericEnemy)
{
mGenericEnemyData = pGenericEnemy.mGenericEnemyData;
}
public override Enemy Clone()
{
return new GenericEnemy(this);
}
}