Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

"walk is a type of run" function inheritance..

Discussion in 'Scripting' started by yoonitee, Oct 3, 2016.

  1. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    I was implementing a few functions such as "run" "walk" and so on. These belong to a "mover" class.

    But in fact "run", "walk", "swim" etc. are all types of "movement". So really I would want to encapsulate this and somehow inherit run/walk/swim from a base function.

    Now I know you can have class inheritance. But I don't think C sharp has such as thing as function inheritance. What it would be would be a new function. I envision something like this:

    Code (CSharp):
    1. void Movement(Position from, Position to){
    2. ///
    3. }
    4. void Run (speed s) : Movement {
    5.  
    6. }
    7. void Swim(stroke S): Movement {
    8.  
    9. }
    10.  
    11. Swim(STROKE.CRAWL, from=here, to=there);
    In other words the functions would "inherit" the arguments of the base function and you could add more or remove some just like properties in classes.

    Just out of interest are there any languages that have this kind of thing?
    e.g. not just encapsulate "cat" is type of "animal" for nouns/classes. But also "run" is a type of "movement" for verbs/functions?
     
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    How would that kind of inheritance be usable?

    Inheritance is neat because you can write the same algorithm for different types of things. So you can say animal.Bark(), and not have to care what kind of animal this is.

    You can't do that with what you're proposing, since the arguments are different. So you can't use a Swim wherever you'd use Movement, as they have different arguments.

    There are things that are similar, closures comes to mind. But it's not the same thing, as those essentially create a more specialized function by pre-binding arguments. So you can make a closure Swim which is Movement with speed and animation parameters pre-bound, but you don't get a Swim-is-Movement relationship.

    C# delegates are also highly polymorphic - any two functions with the same signature can fit into the same delegate. So you could have:

    Code (csharp):
    1. public delegate void Move(Vector3 from, Vector3 to);
    2.  
    3. public void ApplyMovement(Move move, Vector3 from, Vector3 to) {
    4.     //do something with from and to
    5.     move(from, to);
    6. }
    7.  
    8. public void Swim(Vector3 from, Vector3 to, Stroke stroke) { }
    9.  
    10. public void DoTheCrawl(Vector3 from, Vector3 to) {
    11.     ApplyMovement((v1, v2) => Swim(v1, v2, Stroke.Crawl), from, to);
    12. }
    Which achieves kind of what you want to do, I guess?
     
    LiterallyJeff likes this.
  3. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    Yeah I guess you could just do something like:
    Code (CSharp):
    1. void Swim(from,to){
    2.      Move(from,to);
    3.      animation.Play("swim");
    4. }
    5.  
    6. void Run(from,to){
    7.      Move(from,to);
    8.      animation.Play("run");
    9. }
    However, it doesn't really encapsulate it. For example what if I wanted a function to get all types of movement like frog.GetFunctionsOfType<Move>(); of something like that. Its just something I noticed that objects (which are like nouns) have a type hierarchy whereas functions (which are like verbs) don't.
     
  4. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    You can use reflection and attributes to aggregate functions by "type".
     
    Kiwasi likes this.
  5. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    8,986
    Interfaces.
     
    Kiwasi likes this.
  6. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    I think implementing something like that in any language would run into the problem of benefit versus cost. The only reasonable thing to inherit is function parameters. At this point the its a lot of effort and work behind the scenes to save a few keystrokes on the function declaration end. I mean the caller of this method still has to put in the inherited parameters.

    Additionally since in just about every language methods are the core Unit of a block of code, your not really inheriting any behaviour from your parent. Swim would still have to recode all the logic to move, even if some of it overlapped the logic Run, and Walk used. There is no way in any modern language to encapsulate a bit of code inside a function for inheritance. If you did, then that small block of code would become your new "method" and your methods that could inherit would be some sort of new object that was a cross between a Class and a method, which seems to be unnecessary.
     
  7. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    @zombiegorilla Interfaces would be a good choice if Objects in his world could only Walk or Run or Swim. But usually couldn't do all 3. So some object might implement ISwim, and IWalk but not IRun. or maybe SlowDude only implemented IWalk. However, I suspect all the mobile objects in his game will be walking, running , and swimming and he was trying to find some Generic way to call one of those 3 functions. If every object inherits all 3 interfaces then your probably shouldn't be using interfaces.
     
  8. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    8,986
    Interfaces can inherit. And they work well when used in conjunction with a state machine. While a entity may have all 3, more than likely locomotive events would be/could be similar to a degree. State run,walk and swim may all have pause, move, turn, ect. So the entity itself may not actually implement IWalk, IRun, etc, directly, you can use classes as states that implement them. This gives you an even further degree of customization (DamagedRun, AlertRun, PanicRun) implementing IRun, extending BaseRun extending BaseMove or whatever. Reducing duplication of shared code. Interfaces can be fun
     
    Kiwasi, yoonitee and takatok like this.
  9. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Hadn't realized interfaces could inherit
    Good idea then.
     
  10. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    Yep interfaces seem more or less what I was thinking of. But IMO there is a lot of extra unnecessary duplicated code you have to write. e.g.
    Code (CSharp):
    1.  
    2. interface IMove{
    3.      Move();
    4. }
    5. interface IWalk: IMove{
    6.      Walk();
    7. }
    8. class Animal:IWalk{
    9.     Walk(){
    10.  
    11.    }
    12. }
    13.  
    When surely a nice language would have it much simpler like this:
    Code (CSharp):
    1.  
    2. class Animal{
    3.     Walk (): Move{
    4.    }
    5.    Swim () : Move{
    6.   }
    7. }
    8.  
    for the special case where your interface only has one function with the same name as the interface. Just a thought. Not that anyone can do anything about it!

    I suppose the reasoning is that you might want to use IWalk in a different class. But I would say that IWalk is only appropriate to the Animal class and so is used only once.

    I suppose the nearest thing would be to turn each function into a class all inheriting from a "function" class with one method "Do".

    Code (CSharp):
    1. class Function{
    2.    object[] args;
    3.    void Do(object[] args){}
    4. }
    5. class Move: Function{
    6.   overload Do(args){...}
    7. }
    8. class Walk: Move{
    9.   overload Do(args){...}
    10. }
    11. class Run: Move{
    12.   overload Do(args){...}
    13. }
    14. class Animal{
    15.     Walk walk;
    16.    Run run;
    17. }
    18.  
    19. (new Animal()).walk.Do(from, to)
    But again a bit messy! It kind of feels like your creating your own implementation of a new language!
     
    Last edited: Oct 4, 2016
  11. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    If the base functionality of movement is the same for all 3 movement types. The speed and animation simply change. I would just do something simple like the following...

    Pseudocode:
    Code (csharp):
    1. public abstract class BaseMovementController : MonoBehaviour
    2. {
    3.    protected void Move(float force, Vector3 direction, string animation)
    4.    {
    5.      //move the character
    6.      //play animation
    7.    }
    8. }
    Code (csharp):
    1. public class SpecialMovementController : BaseMovementController
    2. {
    3.    private Vector3 direction;
    4.  
    5.    [SerializeField]
    6.    private float swimSpeed;
    7.    [SerializeField]
    8.    private string swimAnimation;
    9.  
    10.    [SerializeField]
    11.    private float walkSpeed;
    12.    [SerializeField]
    13.    private string walkAnimation;
    14.  
    15.    [SerializeField]
    16.    private float runSpeed;
    17.    [SerializeField]
    18.    private string runAnimation;
    19.  
    20.    public bool IsUnderwater { get; }
    21.  
    22.    void Update()
    23.    {
    24.      if (Input.GetKey(forwardKey))
    25.      {
    26.        if (this.IsUnderwater)
    27.        {
    28.          //calculate some "stroke" modifier for speed based on swim animation with Animator.GetFloat
    29.          float swimSpeedStrokeModifier = 1.2f;
    30.          this.Move(this.swimSpeed * swimSpeedStrokeModifier, this.direction, this.swimAnimation);
    31.        }
    32.        else if (Input.GetKey(runKey))
    33.        {
    34.          this.Move(this.runSpeed, this.direction, this.runAnimation);
    35.        }
    36.        else
    37.        {
    38.          this.Move(this.walkSpeed, this.direction, this.walkAnimation);
    39.        }
    40.      }
    41.    }
    42. }
     
    Last edited: Oct 4, 2016
    zombiegorilla likes this.
  12. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    8,986
    If you are only using it once, then you don't need any of this. The value in interfaces is having a contract, knowing that you call a method or acess a variable on any class using. It also means that you can identify a class by its interface. Kinda the whole point is that you will use it in multiple classes.

    Keep things simple, don't worry about how things might work, learn how they do work first. @Baste post is probably spot on. Delegates are solid place to start.
     
    Kiwasi likes this.
  13. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    There are several ways to do that in C#: events, Actions, Funcs, delegates, lambdas, etc. For example:

    Code (csharp):
    1. public class AnimalMover {
    2.     public void FlockExample(Vector3 moveFrom, Vector3 moveTo) {
    3.         var animals = GetAllTheAnimals();
    4.         foreach(var animal in animals) {
    5.              var movement = animal.GetMyMovementFunction();
    6.              movement(moveFrom, moveTo);
    7.         }
    8.     }
    9. }
    10.  
    11. public class Animal {
    12.       public virtual Action<Vector3, Vector3> GetMyMovementFunction() {
    13.            return (from, to) => throw new Exception("Not implemented!");
    14.       }
    15. }
    16.  
    17. public class Duck : Animal {
    18.        public override Action<Vector3, Vector3> GetMyMovementFunction() {
    19.             return SomeOtherClass.SwimFunctionDefinedElsewhere;
    20.        }
    21. }
    22.  
    23. public class Kangaroo : Animal {
    24.         public override Action<Vector3, Vector3> GetMyMovementFunction() {
    25.                return (from, to) =>
    26.                    {
    27.                         //write some custom hopping code right here
    28.                    };
    29.          }
    30. }
    As for "languages that do this", you should take a look at Functional Programming. F# is a .NET functional language sort of like C# that works with Unity. Though most of what you can do with F# can be done with C#, it's just a little more verbose.
     
    zombiegorilla likes this.
  14. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    Yeah, but I like to worry about such things. :)
     
  15. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    I've thought of quite a nice way:
    Code (csharp):
    1.  
    2. class Movement(){
    3.    public Position from;
    4.    public Position to;
    5.    void do(Thing a){
    6.       a.animate(from,to);
    7.    }
    8. }
    9.  
    10. class Jump:Movement{
    11.    public float height;
    12.    public do(Animal a){
    13.  
    14.    }
    15. };
    16.  
    17. class Swim:Movement{
    18.    public Stroke stroke;
    19.    public do(Animal a){
    20.  
    21.    }
    22.    public do(Submarine b){
    23.  
    24.    }
    25. }
    26.  
    27. class Animal{
    28.    public void begin(Movement move){
    29.      move.do(this);
    30.    }
    31. }
    32.  
    33. Animal catfish = new Animal();
    34.  
    35. catfish.begin(new Swim(){ from=here, to=there, stroke=CRAWL  });
    36. catfish.begin(new Jump(){ from=here, to=there, height=23     });
    37.  
    The bad thing about this though, is that it moves all the functions out to other classes. Which I is frowned upon! :eek: .
    You could have it all backwards and have Swim as the object acting on an subject:

    Code (csharp):
    1.  
    2. (new Swim(){ from=here, to=there, stroke=CRAWL  }).do(catfish);
    3.  
     
  16. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    The way you're implementing movement is so incredibly weird... Can you explain the implementation differences you envision for the three movement types? Why can't you just use a single function and use parameters/variables to handle differentials?
     
    Last edited: Oct 6, 2016
  17. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,363
    I don't think its that weird. Just a different way of thinking about things.

    One is Object-Orientated the other is Action-Orientated.
    The first has objects as the main thing which contain list of actions.
    The second has actions with a list of objects it can be applied to.

    So either we have:

    Man
    >Climb
    >Swim

    Monkey
    >Climb

    Fish
    >Swim

    -----Or we could have----

    Climb
    >Man
    >Monkey

    Swim
    >Man
    >Fish
    >Submarine

    I was just trying to work out how best to do the second way in C#. Not because I need to necessarily but just because its interesting. :)
     
  18. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This is where Unity's components come in handy ;)
     
    zombiegorilla likes this.
  19. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    8,986
    Or, as more commonly called... a state machine. ;).

    Though, from a logic/relationship stand point, it almost always makes more sense for actors to have states, not states have actors.
     
    Kiwasi likes this.
  20. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    The simplest way to do this would be to go all data orientated design.

    In order to do this you have to build your data structures in very specific, ways. It actually makes sense if you are doing a lot of the same thing over and over again. But it gets very messy if you are doing a lot of different things. And most developers minds don't really do DOD. OOP is a more natural way of thinking.
     
    zombiegorilla likes this.