0

I am trying to understand inheritance for my unity project but seem to have found a limitation to my setup. I got my self confused whilst writing it as i am still learning to understand C# properly.

I have a set of classes that inherit, and they split based on two different behaviors that way i have the correct reference.

I then need to cast them so i can have access to a method in one of these classes. So my structure looks like this:

public class Behaviour : Position {
    public Handler reference;

    public Behaviour(int tx, int ty, Handler refer) : base (tx,ty){
        reference = refer;
    }
    // overload
    public Behaviour(int tx, int ty) : base (tx,ty){}
}

public class Behaviour2 : Position {
      public SettingsHandler reference;

    public Behaviour2(int tx, int ty, SettingsHandler refer) : base (tx,ty) {
        reference = refer;
    }
}


public class SettingsHandler : Handler {
    public Settings level {get;set;}
}
public class Handler : MonoBehaviour{
    virtual public void Enter(List<Node> n,Vector3 p){}
    virtual public void Exit(List<Node> n, Node curNode){}
}

Now this was working fine until i had to access Handler.Enter or Handle.Exit. Then i got lost on how to set the type properly.

So I was doing something like this:

//need to set temp :

 ??? temp;
if(path[i] is Behaviour2){
   temp = (Behaviour2)path[i];
} else {
   temp = (Behaviour)path[i];
}
temp.reference.Enter();

What should temp type be set to here?

I am thinking i might have misunderstood inheritance as i seem to get type issues. Does C# have a solution for this - i can't be the only one who has got stuck. But my brain is getting confused trying to follow it all.

Sir
  • 8,135
  • 17
  • 83
  • 146
  • 2
    There are a sufficient number of bad practices in this short code sample that it is really hard to know how to advise you effectively. Public fields are a bad code smell. Making decisions based on runtime types is a bad code smell. And so on. – Eric Lippert May 15 '16 at 00:53
  • If they were private i wouldn't be able to access them ? What do you mean making decisions on run time types? How else do distinguish different types of nodes on a map when its compiled at run time - the code has no idea what type the path is until i check and thus set the relevant settings/behavior. – Sir May 15 '16 at 00:55
  • 1
    The fact that you think that doing an "is" check followed by a cast to get some common type represented by "temp" is problematic. Moreover, **how is a behaviour a special kind of position**? This type hierarchy makes no sense. A cat is a kind of mammal, so cat extends mammal. A customer is a kind of person, so customer extends person. A behaviour is not a kind of position, so **why is behaviour extending position*? This whole thing needs to be redesigned I think. – Eric Lippert May 15 '16 at 00:57
  • Because position is a grid position. And if a player lands on that position it needs to run a behavior that effects the player in some way. It is random each time. Each behavior is different so the references are of different types (which is stored in `handler`) so in this case it makes sense that there is a behavior for each node position. – Sir May 15 '16 at 01:36
  • 2
    So read what you just wrote: **there is a behaviour for each position**. Not "a behaviour is a kind of position" but rather either "a behaviour has a position" or "a position has a behaviour". Which of those two better models your scenario, I don't know, but either of them are better than "a behaviour is a position". **Make the relationships in your code follow the logical relationships in your business domain**. – Eric Lippert May 15 '16 at 12:21
  • 1
    Or perhaps neither of those things. Even better would be "a **position** does one thing well; it describes a position. A **behaviour** does one thing well; it describes a behaviour. A **game policy** does one thing well; it associates a behaviour with every position." When you face a problem like this, don't think that everything has to go in one class; that's the "god object" pattern. If you have a rule like "there is a behaviour for each position" consider whether the code for that rule should go in a class of its own. – Eric Lippert May 15 '16 at 12:25
  • @EricLippert so a game policy would be essentially another class that stores the Node and the behaviour together ? Where by node and behavior are not inheriting each other? – Sir May 15 '16 at 19:02

2 Answers2

6

Your problem stems from the fact that the base classes are poorly designed in the first place, in the following ways:

  • The hierarchy makes no sense. A behaviour is not a special kind of position. Prefer composition to inheritance.

  • Fields should never be public. Use properties, not fields.

  • "is" checks are runtime type checks; don't do runtime type checks for polymorphic behaviour; use virtual methods.

Let's redesign your hierarchy.

abstract class MyBehaviour
{
    public Position Position { get; private set; }
    public Handler Handler { get; private set; }
    protected MyBehaviour(int x, int y, Handler handler) {
        this.Position = new Position(x, y);
        this.Handler = handler;
    }
}
class Behaviour1 : MyBehaviour {
  /* Whatever */
}
class Behaviour2 : MyBehaviour {
  /* Whatever */
}

All right, and now when we want to execute the handler...

 MyBehaviour b = whatever;
 b.Handler.Enter();

Done. No temporary variable needed. No runtime type check. No "if". The behaviour provides a service; you use the service. You should not have to ask the behaviour its type in order to use the service it provides; if you do, something is probably wrong at the design level.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thanks, i'm going to start again fresh with your idea, with my code hopefully with get a better results! – Sir May 15 '16 at 05:06
  • 1
    Hi @Dave you are facing a common problem of today. Folks who are beginner programmers are trying to learn to program in **Unity3D**. It causes an incredible amount of problems. Unity (being an "ECS" component-based, scene-based, and frame-based system) has unique issues such as "public" Inspector variables, coroutines, the frame loop, and the overwhelming issue of MonoBehavior. So, learners are in a situation of having "no clue" about all that stuff AND learning c# from scratch! Additionally you've jumped ahead to Inheritance! **Ouch.** – Fattie May 15 '16 at 11:46
  • @JoeBlow yeah it didn't take long for my game's complexity to require inheritance ! None of the unity tutorials ever go to such complexity involving these things. It seemed relatively easy up until i had to think more carefully about code structure. – Sir May 15 '16 at 19:04
  • I would just say it is difficult to learn to code while learning Unity. Unity is a mess and you have to have great command of OO to *use* OO "on top of" an *ECS* system. Unity is inherently *non* OO although by all means you use OO, err, "on" the Unity ECS system to write behaviors (as indeed you are doing). – Fattie May 15 '16 at 19:31
  • @JoeBlow where is a better place to start learning C# if not unity for the purpose of game development ? – Sir May 26 '16 at 05:20
6

In game engines, you simply take a "game object" and add behaviors.

It's that simple - end of story.

{In Unity "adding a behavior" is literally adding what they call a Component class.}

You can't "derive" or "subclass" a game object - it's totally, completely, meaningless.

Unity is not a "programming language". Unity has no connection, at all, in any way, to "Object Oriented" - or anything else, at all, to do with programming. A game engine is not a programming language and has no connection to it.

To simply repeat: You can't "derive" or "subclass" a game object - that would be totally, completely, meaningless.

Say you have the following "behaviors" which you have written:

(You may have written them in c#, COBOL, machine code, whatever. That's totally irrelevant. They are behaviors .. Component items in Unity.)

  • can fly through air
  • causes splash damage
  • causes melting damage
  • has glowing sphere around it
  • has an icon attached
  • explodes after ten seconds
  • explodes on touching the ground
  • has a whooshing sound

Say you have a game object "weapon".

To make a "grenade" you'd add these behaviors:

  • can fly through air
  • causes splash damage
  • explodes on touching the ground

However, to make a "laser bomb", you'd instead add these behaviors:

  • can fly through air
  • explodes after ten seconds
  • causes melting damage
  • has glowing sphere around it

That's how game development works.

There is no connection, at all, to subclassing, etc.

In game engines, you simply take a "game object" and add behaviors.

In Unity "adding a behavior" is literally adding what they call a Component class.

Fattie
  • 27,874
  • 70
  • 431
  • 719