- Home /
The question is answered, right answer was accepted
Inheritance and Component Types in C#
This will be a tremendous simplification of my problem, but I'll try to break it down as much as possible. I have two parent classes that declare common functionality for my GameObjects.
class Movement():MonoBehaviour
{
float HorizontalMove;
float VerticalMove;
}
class Animation():MonoBehaviour
{
Animator _Animator;
void Start()
{
_Animator = GetComponent<Animator>();
}
}
A third class is intended to get these components, and perform actions on them.
class Controller():MonoBehavior
{
Movement _movement;
Animation _animation;
void Awake()
{
_movement = GetComponent<Movement>();
_animation = GetComponent<Animation>();
}
}
Now, assume I derive classes from these with different implementations-
class MovementUp():Movement
{
void MoveUpEachFrame()
{
VerticalMove += 1;
}
}
class MovementDown():Movement
{
void MoveDownEachFrame()
{
VerticalMove -= 1;
}
}
And then derive a controller for individual actors to implement those classes.
class ControllerUp():Controller
{
//GameObject has the "MovementUp" script attached
//GetComponent<Movement>() will assign the MovementUp script to the _movement variable, as it is a child of the Movement() class
void Update()
{
//change animation with _animation
MovementUp()
}
}
Now, the problem is that, since the "_movement" variable of Controller is an instance of the parent rather than of the child, MovementUp does not exist within that context. Another example would be that, using classes deriving from Animation, different GameObjects could have different Animation start and stop conditions, yet still inherit common functionality from a parent (such as Getting the Animator component.)
Interfaces are no good in this situation since I need to be able to inherit the actual implementation of the methods. Hiding the parent variables and reimplementing them in the child won't work since the parent might still need to call methods that override methods in the parent.
I'm not quite sure what to do from here. I'm at a complete loss for any possible approach to this problem.
Answer by NoseKills · Apr 07, 2016 at 10:39 PM
It's starting to sound like you can't do what you want with simple inheritance. Either that or you just have trouble figuring out how to organize your code so that the controller wouldn't have to know what kind of Animation and Movement it has. It's hard to say without seeing the whole picture and all the use cases
E.g. to me it doesn't make sense to have this in your example
class MovementUp():Movement {
void MoveUpEachFrame() {
VerticalMove += 1;
}
}
class MovementDown():Movement {
void MoveDownEachFrame() {
VerticalMove -= 1;
}
}
Why have 2 different classes whose name describe the direction of movement and then have 2 different methods in them that also describe the direction of movement? Either have just "Movement" class with both of the methods or "MoveEachFrame()" method that's overridden to do what the derived class name says. But maybe it's just a flaw in the quick example you gave... :)
If what you describe is really what is needed, then the next thing to consider would be "generics". It would be a lot easier though if the classes in question wouldn't have to be monobehaviours :-S
The idea is this
public class Animation {}
public class UpAnimation : Animation {
public float upAnimFloat;
}
public class Movement {}
public class UpMovement : Movement {
public float upMoveFloat;
}
public class Controller<M, A> where M:Movement where A:Animation {
// controller class needs 2 types to define which kind of Movement and Animation it has
protected M Movement;
protected A Animation;
}
public class UpController : Controller<UpMovement, UpAnimation> {
public void SomeMethod() {
// these variable references to derived vars are ok because based
// on the generic parameters UpController knows Movement's and Animation's exact types
Debug.Log(this.Movement.upMoveFloat);
Debug.Log(this.Animation.upAnimFloat);
}
}
That was just an example; you can change the names of any class or variable and the problem is still the same.
Generics, though, sounds like exactly the solution I am looking for.
Since this seems to have solved your issue, I converted it into an answer. If it has indeed solved it, please accept his answer as correct so your question is marked as solved. Glad you guys figured it out, I misinterpreted your question. :)
@landon91235 Thanks.
you can change the names of any class or variable and the problem is still the same.
@tubswitch No, I didn't mean the na$$anonymous$$g convention. I meant that it doesn't make sense logically to use both inheritance and a different method name to differentiate the a similar behaviour in 2 classes that have a common base type. It defeats the purpose of inheritance to have classes Dog and Cat that inherit class WalkingAnimal, if you then have to call WalkDog() method for a Dog and WalkCat() method for a Cat to make them walk.
If you know that an object with UpController always has UpAnimation and Up$$anonymous$$ovement components, why not just have methods $$anonymous$$ove() and Animate() that do the right things based on whatever the implementation is in the derived class?
All classes that derive from Animation need this method, so a Virtual methid cannot be used- this functionality must be inherited.
I don't understand. "Virtual" doesn't mean that the method disappears from the base class. You can do.
class Animation():$$anonymous$$onoBehaviour {
protected Animator _Animator;
protected virtual void Start() {
_Animator = GetComponent<Animator>();
}
}
class UpAnimation():Animation {
protected override void Start() {
base.Start();
_Animator.SetTrigger("upAnimation");
}
}
class DownAnimation():Animation {
protected override void Start() {
base.Start();
_Animator.SetTrigger("downAnimation");
}
}
Anyways. I'm not insisting that this can be solved without generics :) Just saying the see$$anonymous$$g oddities in your examples suggest there's a chance for that.
Again, it was an example to display what I needed through inheritance. The names of the classes and methods were completely irrelevant to the problem.
Answer by landon912 · Apr 07, 2016 at 11:59 AM
You've got quite a few fundamental issues going on here. Instead of addressing your mistakes, I will simply explain how to go about this(it doesn't really make sense here, but I'll go along with your inheritance idea).
abstract class Movement : MonoBehavior
{
protected float speed;
public virtual void Move() { //do some general movement stuff }
}
So, we declare an abstract class. This restricts us from creating an Object of Movement
and instead forces us to create a class that implements this base abstract class.
class MoveUp : Movement
{
speed = 5f;
public override void Move()
{
//call base implementation of Move
base.Move();
//special move up code
Debug.Log("C# Magic!");
}
}
Here we create a usable implementation of the base class Movement
. Notice the override
keyword. Otherwise, we would be hiding the base class' implementation instead of overriding. Also notice that we call the base's version of Move(). This allows us to have shared general functionality between our special implementations. We will latter create an instance of this object, yet access it through it's base type.
class Controller : MonoBehavior
{
Movement baseMovement;
void Start()
{
baseMovement = GetComponent<Movement>();
baseMovement.Move();
}
}
Take special notice that baseMovement
is an field of type Movement
not MoveUp
. This allows us to store references to these classes that share the base class. Here's the catch, even though baseMovement
is of type Movement
, the reference is also of type MoveUp
(Or any other class that derives from Movement). Therefore, when we call Move()
we will be calling Move()
within the base class that is overridden by MoveUp()
(or any other implementations) therefore calling each derivative's implementation.
Hopefully this helps. Let me know if you have any questions. The code is all untested(I'm at work), so if anyone catches any issues please let me know.
A few typoish issues :)
An abstract method can't have a body.
And base$$anonymous$$ovement = ($$anonymous$$ovement)GetComponent<$$anonymous$$oveUp>();
should have GetComponent<$$anonymous$$ovement>()
because if you need to know the derived type at compile time, it sort of defeats the whole purpose ofinheritance here:)
Thanks for catching those oversights! It's hard to not test code. :p
Sorry, I guess my example wasn't clear. The implementation in my example would already store the child $$anonymous$$oveUp class as an instance of the $$anonymous$$ovement class within the _movement variable, as you can see here
class Controller():$$anonymous$$onoBehavior
{
$$anonymous$$ovement _movement;
Animation _animation;
void Awake()
{
_movement = GetComponent<$$anonymous$$ovement>();
_animation = GetComponent<Animation>();
}
}
GetComponent will get any component of that type attached to the GameObject, including derived types. Therefore, if $$anonymous$$oveUp is attached to the object, it will be assigned to the _movement variable, but any variables or methods unique to the derived type are no longer available.
Classes that have a Controller will always have a both a $$anonymous$$ovement and Animation component, and classes that derive from Controller need to be able to access the common functionality shared between them, while still being able to work with the unique functionality that might be added to a derived class.
Let me provide another example:
The Animation class stores the Animator component of a class. All classes that derive from Animation need this method, so a Virtual methid cannot be used- this functionality must be inherited.
class Animation():$$anonymous$$onoBehaviour
{
Animator _Animator;
void Start()
{
_Animator = GetComponent<Animator>();
}
}
However, each unit's Animator component has a different set of variables it must access, and different conditions for those animations.
I would also like to know what fundamental issues you can see, as those are equally important for me to address.
Follow this Question
Related Questions
An OS design issue: File types associated with their appropriate programs 1 Answer
Losing object reference 2 Answers
inheritance - problem setting class variable from external call 0 Answers
How to trigger udpate in editor mode only when I modify component parameters 1 Answer
FPS weapons class structure 1 Answer