I've been trying to perfectly structure this project I'm working on in different classes while maximizing the benefits of inheritance. So far however, it's given me more headaches than benefits.
Consider this:
public class SuperClass : MonoBehaviour
{
[SerializeField] protected Camera _camera;
}
and this
public class SubClass : SuperClass
{
}
Both scripts are attached to different game objects in the scene.
The Camera is to be assigned by dragging it in the inspector
I tried this, and unity seemed to tell me that I had to assign the camera to the SuperClass game object AND to the subclass game object, which makes no sense to me.
How can I assign a camera to SuperClass.cs, which is then used and shared by all of its subclasses?
Thanks in advance!
shared by all of its subclasses
Shared by classes could can only be achieved by using "static" (static variable or singleton).
A workaround could be
public class SubClass :SuperClass
{
[SerializeField] Camera camera;
void Awake()
{
if(camera!=null)
{
_camera=camera;
}
}
// Start is called before the first frame update
void Start()
{
camera=_camera;
}
}
To further extend the solution, you could write a editor script or just get the camera from the code.
You need to create public static Camera property somewhere and reference it in your code, using property:
public static class StaticValues
{
public static Camera Camera {get; set;}
}
public class SuperClass : MonoBehaviour
{
[SerializeField] protected Camera _camera
{
get
{
return StaticValues.Camera;
}
set
{
StaticValues.Camera = value;
}
}
}
public class SubClass : SuperClass
{
}
Related
I have basic understanding of inheritance but since every script I want as a component on a game object has to inherit from MonoBehaviour things get tricky.
Let's say I want to have a base class called Character and I want Player and Enemy to derive from Character. How am I suppose to add Player and Enemy script to Player and Enemy objects without getting an error?
I also tried to create empty game object and add CharacterStats to it then populate this class with different character objects with different stats. Guess what, you can't use new keyword to create objects if the script derives from Monobehaviour.
Is there some tutorials about this topic to make it more clear?
Probably the thing to understand is that MonoBehaviour itself inherits Component - A MonoBehaviour IS a Component.
When you look at it as if Components are a part of a thing, it makes at least some sense that you can't just new a Component - could I make a new Arm? If I did, where would it got? But I could make a new Body body and then dobody.AddComponent<Arm>, right? Because then it's clear what happens to the Arm - it gets attached to the body.
Similarly you can't just new a Component because they're supposed to be a part of a GameObject. What you can do is to make a new GameObject and .AddComponent<>() to that object. Again, now it's clear where that Component is going.
I generally agree with UnholySheep's comment that you should prefer composition over inheritance because this generally makes your code more modular. That said, I definitely do also use inheritance.
The question you should be asking yourself when you're outlining your class is, "Is this new class a KIND of _____ or does this new class HAVE a _____." And it's easy I guess to say a Player is a Character and an Enemy is a Character, but do you need to subclass? Or would a Player just be a Character that also has a PlayerController? Maybe you could have an interface like IPlayerController and have a LocalPlayerController for the user and an AIPlayerController for the enemies? Lots of ways to problem-solve with programming.
Or maybe all Characters have a PlayerController and just the .activeControl bit is false for non-player characters. Then you could do things like ghost/spectate NPCs, etc.
But to your question specifically:
Let's say I want to have a base class called Character and I want Player and Enemy to derive from Character. How am I suppose to add Player and Enemy script to Player and Enemy objects without getting an error?
You would start with a Character script that inherits MonoBehaviour and implements any of the logic that is common to both classes:
public class Character : MonoBehaviour
{
// Your common character logic
}
Then you would have subclasses inherit Character:
public class Player : Character
{
// Your Player-specific logic here
}
and
public class Enemy : Character
{
// Your Enemy-specific logic here
}
Then you either add the Enemy and Player scripts to a GameObject in the editor or you can do it programmatically by getting a reference to the target GameObject (by setting the reference in editor or by creating a new GameObject in a script) and then you call targetGameObject.AddComponent<YourComponentToAdd>();.
Using inheritance well comes from planning and iterating. Perhaps your Character class doesn't work well as a class for your enemies. I tend to have the player in their own class (unless its multiplayer) with enemies using their own base class. Below is an example of some inheritance I use in a 2D game I'm working on.
Base Class:
Enemy.cs
public abstract class Enemy : MonoBehaviour
{
public Vector2 flyInPos;
public Vector2 startPos;
public bool isFlyingIn;
public float flyInDuration = 5000f;
public float flyInTime = 0f;
public abstract void Destroy();
public abstract void DestroyWithoutScore();
public abstract void Attack();
public abstract void Attack2();
public abstract void Attack3();
public abstract void FlyOut();
public virtual void FlyIn()
{
isFlyingIn = true;
}
}
Every enemy needs to have these elements. You'll notice most of the methods in my base class are marked abstract so that each class that inherits can have a unique set of Attacks, and Flying patterns. One thing to notice is that the base class is inheriting from MonoBehaviour. Here is how I implemented inheritance in one of my enemies. Sorry its still a WIP.
Inherited Class:
ShootingDrone.cs
public class ShootingDrone : Enemy
{
public GameObject projectileSpawner;
public GameObject projectile;
public void Start()
{
startPos = transform.position;
Scheduler.Invoke(Attack, 5f, gameObject);
}
public void Update()
{
if(isFlyingIn)
{
transform.position = Vector3.Lerp(startPos, flyInPos, flyInTime / flyInDuration);
flyInTime += Time.deltaTime;
}
}
public override void Attack()
{
Shoot();
Scheduler.Invoke(Shoot, 0.25f, gameObject);
Scheduler.Invoke(Shoot, 0.5f, gameObject);
}
public override void Attack2()
{
Attack();
}
public override void Attack3()
{
Attack();
}
public void Shoot()
{
GameObject newProjectile = Instantiate(projectile, projectileSpawner.transform);
newProjectile.transform.parent = null;
}
public override void FlyOut()
{
throw new System.NotImplementedException();
}
public override void DestroyWithoutScore()
{
throw new System.NotImplementedException();
}
public override void Destroy()
{
Destroy(gameObject);
}
}
The base class of Enemy inherits from MonoBehaviour. So when Shooting Drone inherits from Enemy it also is inheriting from MonoBehaviour.
For your problem of trying to add a new MonoBehaviour. The reason for this is that a MonoBehavior HAS to be attached to something. Imagine a Rigidbody with no GameObject, it doesn't work. If you wanted to make a "new" CharacterStats the way you would do that is:
CharacterStats stats = gameObject.AddComponent<CharacterStats>();
If you don't want your CharacterStats as a component, then simply remove the MonoBehavior inheritance from the class and instantiate it as new CharacterStats.
There are a variety of tutorials that cover inheritance, but depending on how new you are to the subject I would start with the Unity official inheritance tutorial. I do think this tutorial is too brief but I also like CircutStreams tutorial since it also mentions implementing Interfaces which can be a better solution to inheritance in many cases.
I am developing a small nuclear reactor simulator game. I have a bunch of reactor component classes: HeatVent, HeatExchanger, UraniumCell etc. They are not deriving from MonoBehaviour since they don't have any Unity logic, but they do implement a shared interface IReactorComponent. What I want to do is to be able to create prefabs of such components (simple heat vent, advanced heat vent, doubled uranium cell etc.) The prefabs would have different sprites and something like that, but the main issue is to define what reactor component class the prefab is related to, because I can't just drag'n'drop a non-MonoBehaviour script on inspector. Also, I want to be able to set settings in the inspector (for example, HeatVent has CoolAmount and HeatCapacity properties, UraniumCell has FuelAmount, HeatProduce and PowerProduce properties).
I have read about factory method pattern and as I understood, I have to create a fabric class that derives from MonoBehaviour for each reactor component class like HeatVentBehaviour, HeatExchangerBehaviour etc. Yes, that completely solves my issue with prefabs but is there any way to not create an additional MonoBehaviour wrap for each class? If I had 15 IReactorComponent classes, I would need to create 15 fabrics which feels like not the greatest solution.
Sounds like what you are looking for is ScriptableObject!
Instances of those are assets so they don't live in a scene but in the Assets folder and basically behave a little bit like prefabs except: They already exist and do not need to be instantiated anymore.
Mostly they are used as just configurable data containers. They have an Inspector so you can easily fill them with your desired data and references to other assets (e.g. the related prefab in your case).
But in addition you can as well let them implement behavior like your interface and thereby change the behavior of your scene objects by using different implementations of a method from different ScriptableObjects!
For the factory you then only need to figure out for which method to use which ScriptableObject instance e.g. either by having different methods or by having a Dictionary where you fill in your SO references.
Just as an example how this might look like (make sure each MonoBehaviour and ScriptableObject has its individual script file with matching name)
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
[SerializeField] private ReactorComponentBehaviour _behaviourPrefab;
[SerializeField] private BaseReactorComponent[] _components;
public bool TrySpawn<T>(out T component, out ReactorComponentBehaviour componentBehaviour) where T : IReactorComponent
{
component = default(T);
componentBehaviour = default;
var foundComponent = components.FirstOrDefault(c => c.GetType() == typeof(T));
if(foundComponent == null)
{
Debug.LogError($"No component found of type {T.GetType().Name}!");
return false;
}
// Here Instantiate doesn't spawn anything into the scene but
// rather creates a copy of the ScriptableObject asset
// This is just to avoid that any changes in the fields during the game
// would change the original ScriptableObject asset and thereby ALL related behavior instances
component = Instantiate ( (T) foundComponent);
// This now indeed spawns the related MonoBehaviour + GameOver
componentBehaviour = Instantiate (behaviourPrefab);
componentBehaviour.Init(component);
return true;
}
}
BaseReactorComponent.cs
public abstract class BaseReactorComponent : ScriptableObject, IReactorComponent
{
public abstract void WhateverIReactorComponentNeeds();
// Common fields and methods e.g.
public Sprite Icon;
}
HeatVent.cs
[CreateAssetMenu]
public class HeatVent : BaseReactorComponent
{
public int CoolAmount;
public int HeatCapacity;
public override void WhateverIReactorComponentNeeds ()
{
// Do something
}
}
UraniumCell.cs
[CreateAssetMenu]
public class UraniumCell : BaseReactorComponent
{
public int FuelAmount;
public int HeatProduce;
public int PowerProduce;
public override void WhateverIReactorComponentNeeds ()
{
// Do something
}
}
And finally you need only one base prefab with the
ReactorComponentBehavior.cs
public class ReactorComponentBehavior : MonoBehaviour
{
[SerializeField] private Image _image;
private IReactorComponent _component;
public void Init(IReactorComponent component)
{
_componemt = component;
// Do other stuff like e.g. adjust visuals according to the component etc
_image.sprite = component.Icon;
}
// And then use whatever this behavior should do with the assigned component
}
So in the end you would use that like e.g.
if(spawManagerReference.TrySpawn<HeatVent>(out var component, out var componentBehaviour)
{
// Do something with the behavior e.g. set its position, parent etc
}
else
{
Debug.LogError($"Failed to get a {nameof(HeatVent)}!");
}
If then at some point you still want different additional behaviours you could let them inherit from the common ReactorComponentBehavior and rather reference the prefabs inside the BaseReactorComponent itself .. then every component can bring its own prefab but still have a common core behaviour
I'm creating an enemy (from EnemyCreator1 class) with both EnemyMove1 and MarkusEnemy scripts (EnemyMove1 is a parent class to MarkusEnemy class). In EnemyCreator1 class I set value mainState of the script EnemyMove1 to "CHASE", but when I'm trying to access it from that class it says that mainState is "IDLE" (Please read my coments below because there are more explanations about what am I trying to achieve)
public class EnemyMove1 : MonoBehaviour
{
public enum mainStates { IDLE, CHASE }
public mainStates mainState;
void Update()
{
Debug.Log(mainState); //mainstate == IDLE, but should be CHASE
}
}
public class EnemyCreator1 : MonoBehaviour
{
[SerializeField] private GameObject enemyPrefab;
public void CreateEnemyAndSetItsStateToChase()
{
GameObject enemy = Instantiate(enemyPrefab);
enemy.GetComponent<EnemyMove1>().mainState = EnemyMove1.mainStates.CHASE;
}
}
public class MarkusEnemy : EnemyMove1
{
void Update()
{
EnemyMove enemyMoveScript = GetComponent<EnemyMove>();
Debug.Log(enemyMoveScript.mainState); //mainstate == CHASE
}
}
From the above code it looks like you are inheriting from a different base class EnemyMove, not EnemyMove1.
Thank you guys for helping me, after searching for the information about base classes I decided that it is impossible to access directly it's variables from another objects' scripts so I just simply call methods with variables as arguments (I put variables in round brackets of the method)
I'm having trouble showing a public field of a ScriptableObject which is a child of the component I'm inspecting. While I can easily do this in another way, I need this method to work for other variables. (ReorderableLists)
I simplified the problem, maybe I was just doing something obvious wrong, but I can't see what I'm doing wrong.
Code + error:
http://answers.unity3d.com/storage/temp/70243-error.png
class SomeComponent : MonoBehaviour{
public MyScriptable scriptable; //instantiated and saved as asset
}
[Serializable] class MyScriptable : ScriptableObject{
[SerializeField] public float value = 0.1f;
}
[CustomEditor(typeof(SomeComponent))] class SomeComponentEditor : Editor{
public override void OnInspectorGUI() {
if((target as SomeComponent).scriptable==null) (target as SomeComponent).scriptable = ScriptableObject.CreateInstance(typeof(MyScriptable)) as MyScriptable;
EditorGUILayout.PropertyField(serializedObject.FindProperty("scriptable"));
//shows the asset
EditorGUILayout.PropertyField(serializedObject.FindProperty("scriptable").FindPropertyRelative("value"));
//error
}
}
To fix your code you can do this:
using UnityEditor;
using UnityEngine;
class SomeComponent : MonoBehaviour
{
public MyScriptable myScriptable;
}
class MyScriptable : ScriptableObject
{
public float myChildValue = 0.1f;
}
[CustomEditor(typeof(SomeComponent))]
class SomeComponentEditor : Editor
{
public override void OnInspectorGUI()
{
SomeComponent someComponent = target as SomeComponent;
if (someComponent.myScriptable == null)
someComponent.myScriptable = CreateInstance<MyScriptable>();
SerializedProperty myScriptableProp = serializedObject.FindProperty("myScriptable");
EditorGUILayout.PropertyField(myScriptableProp);
SerializedObject child = new SerializedObject(myScriptableProp.objectReferenceValue);
SerializedProperty myChildValueProp = child.FindProperty("myChildValue");
EditorGUILayout.PropertyField(myChildValueProp);
child.ApplyModifiedProperties();
}
}
To inspect a child object, you first need to create a SerializedObject version of it, which then can be searched for properties as usual.
Also, the Serializable attribute is not needed on classes which derive from ScriptableObject and the SerializeField attribute is only needed when serializing private fields; public fields are serialized by default in Unity.
Without knowing the original context of your code, your approach seems a little peculiar to me. ScriptableObject instances are meant to be used as asset files in your Unity project. Have you used the CreateAssetMenu attribute yet? Usually, you would create your assets manually via the menu and then plug them into your components. The way you are doing it, it won't be written to disk, so why use ScriptableObject and not just a normal class? But maybe it all makes sense in your actual context, so never mind if I'm wrong.
Hack solution found:
SerializedObject newserobj = new SerializedObject(serializedObject.FindProperty("scriptable").objectReferenceValue );
EditorGUILayout.PropertyField(newserobj.FindProperty("value"));
newserobj.ApplyModifiedProperties();
In order to get variable(s), function(s) in another class, I have known 2 ways of doing this. First, is to use Get Component to the Script that we want to get the variable(s), function(s) into. Second, is to use Instance of the Script itself.
So I have made the following code:
First case: Get Component to the Script itself
public class Manager : MonoBehaviour
{
private AnotherManager _anotherManager;
private void Awake()
{
_anotherManager = GameObject.Find("Managers").GetComponent<AnotherManager>();
}
private void Start()
{
_anotherManager.myIntVariable = 10;
_anotherManager.MyFunction();
}
}
public class AnotherManager : MonoBehaviour
{
public int myIntVariable;
public void MyFunction()
{
}
}
Second case: Use Instance of the Script itself
public class Manager : MonoBehaviour
{
private void Start()
{
AnotherManager.instance.myIntVariable = 10;
AnotherManager.instance.MyFunction();
}
}
public class AnotherManager : MonoBehaviour
{
public static AnotherManager instance;
public int myIntVariable;
private void Awake()
{
instance = this;
}
public void MyFunction()
{
}
}
My question is: Is there any difference between those cases? In terms of good practice of coding for programmer or performance or it is just a matter of programmer's perspective or whatever else?
Thanks
The second example is the what is known as the Singleton Pattern and should be used very sparingly.
I try to never use the first approach either where you find the gameobject and hope it exists.
You can expose a field for the Unity Inspector so that you can wire it up the same as you can expose any other variable
public AnotherManager AnotherManager;
Alternatively, if you hate using public all over the place like that, like me, you can also indicate to Unity that you wish to expose this variable in the inspector with the SerializeField attribute
[SerializeField]
private AnotherManager anotherManager;
With both of these methods, you can then drag an an object that has the AnotherManager component attached into the field in the inspector.
If instantiated objects need access to this, you will need to wire it up when it is instantiated.
If you need help attaching it in unity I can attach some screenshots.