Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by eldorno · Sep 01, 2016 at 05:32 AM · inheritanceoverrideabstract

Overriding a subtype's variable with its own subtype

Hey there,

Long story short, I'm building a sort-of framework with which I can create different game types. The basic structure consists of an abstract Game class, which contains all of the non-game-specific functionality along with an array of [Serializable] abstract Level objects (which contain non-game-specific level info like name and target score to beat). Each of these are subclassed (eg. BP_Game and BP_Level), to implement any game-specific functionality and level information.

Everything works fine, except I'm having to abstract/override the methods which relate to the Level objects in the Game subclasses.

This is a small example of how I have to have it currently (in separate files):

 public abstract class Game : MonoBehaviour 
 {
     public abstract Level[] GetLevels();
     // ...
 }
 
 public class BP_Game : Game 
 {
     public BP_Level[] levels;
     public override Level[] GetLevels()
     {
         return this.levels;
     }
 
     // ...
 }

As you can see, it would be the exact same code no matter what subclass, so it would make sense to put it in the abstract base class... I just can't figure out how to do that! Is it even possible?

I tried adding Level[] levels to the Game class, but the BP_Level[] levels did not override it but rather existed alongside it (calling currentGame.GetLevels() got a nice NullReferenceException because while the subclass's levels variable was set, it was accessing the base class's one, which wasn't).

So... Is it actually possible to do what I'm trying? Or have I just wasted an entire morning :D?

Thanks in advance for any help.

Comment
Add comment · Show 4
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Arkaid · Sep 01, 2016 at 05:51 AM 0
Share

The code looks fine to me. You mention that levels in the base class isn't set, but in your example there isn't anything declared, which is throwing me off a little. Can you share a bit more of code?

avatar image eldorno Arkaid · Sep 01, 2016 at 06:25 AM 0
Share

Thanks for the reply. The code above does work, but in the long run it will result in a lot of code duplication, which I'd very much like to avoid... hence why I'd like to refactor it.

What I'd like is to have it so that I do not have to do any Level-related method overriding in the subclasses (eg. no override for GetLevels() in the BP_Game class).

What I had tried was something like:

 public abstract class Game : $$anonymous$$onobehaviour
 {
     public Level[] levels;
     public Level[] GetLevels()
     {
         return this.levels;
     }
     
     // ...
 }
 
 public class BP_Game : Game
 {
     public BP_Level[] levels;
     
     // ...
 }
 

^ That code is where I was getting the NullReferenceException. Specifically, in the inspector I was able to set up the levels as BP_Levels, but when I ran it, it was looking at the base class's levels, rather than the BP_Levels that I had set up in the inspector.

I hope that's enough detail, if not then I can try to provide some screenshots or something, but I don't know if it'll help much.

Thanks again!

avatar image Arkaid eldorno · Sep 01, 2016 at 06:41 AM 0
Share

Well yea, that code indeed uses the base class' levels, because Game doesn't know about BP_Level. Even though it has the same name, they're are two completely different variables.

The problem is that even though BP_Level inherits from Level, BP_Level [] is not the same as Level []. Arrays don't cast the elements within it. So just typing the variable with the same name as in the base class won't help you.

There are ways around it, like using lists ins$$anonymous$$d of arrays, and the List.Cast method (which indeed casts each element within it)

Although the correct way, I think is to leave levels untouched and populate the array with the correct inherited instances.

Roughly put:

  public abstract class Game : $$anonymous$$onobehaviour
  {
      public Level[] levels;
      public Level[] GetLevels()
      {
          return this.levels;
      }
      
      // ...
  }
  
  public class BP_Game : Game
  {
      // public BP_Level[] levels;  // !< Don't do this...
      // ...
  }
 
 // ...
 
 BP_Game bpGame;
 bpGame.levels = new Level[] { new BP_Level(), .... } // !< this is the correct way
Show more comments

1 Reply

· Add your reply
  • Sort: 
avatar image
1

Answer by NoseKills · Sep 01, 2016 at 06:51 AM

There is no way to override a variable or a function to be/return a different type than what is defined in the base class.

Then again if the return type of GetLevels() is Level[], why would it matter what the internal type of the levels is... Anyone using that function wouldn't see it anyway... Well maybe this was one of the problems you wanted to avoid.

You could use generics if the number of types you need to specify isn't overwhelming. Though because you can't make a generic MonoBrhaviour, you'll have to use an interface.

I'm on mobile so this might not compile but should give you an idea.

 public interface IGame<T> {
     T[] GetLevels();
 }
 
 public abstract class GameClass<T> : MonoBehaviour, IGame<T> {
      public T[] levels;
      public  T[] GetLevels() {
            return levels;
      }
  }
  
  public class BP_Game : GameClass<BP_Level> {
  }

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image eldorno · Sep 01, 2016 at 07:06 AM 0
Share

Hmm, now this is an interesting idea. I'll try implement it and see how it goes. Thanks!

avatar image eldorno · Sep 01, 2016 at 07:24 AM 0
Share

Har har.. and here's where it gets complicated...

$$anonymous$$y public abstract class Game is using a singleton pattern. The class signature has changed, therefore the reference public static Game current; needs to change to public static Game<T> current;...

...And concurrently all times we access it would need to be accessed with Game<T>.current...

...And Unity complains about this when accessing it from another script ( The type or namespace name 'T' could not be found. ).

./cry

avatar image NoseKills eldorno · Sep 01, 2016 at 08:43 PM 0
Share

T needs to be known at compile time, so whatever other classes you make into this system that need to reference Game class directly need to know which generic Game they reference i.e. they need to have a generic parameter too.

 public interface IGame<T> {
     T[] GetLevels();
 }
 
 public abstract class Game<T> : $$anonymous$$onoBehaviour, IGame<T> {
     public static Game<T> current;
 
     public T[] levels;
     public T[] GetLevels() {
         return levels;
     }
 
     public void Other$$anonymous$$ethod() {
         current.GetLevels();
     }
 }
 
 public class BP_Game : Game<BP_Level> {
     public BP_Game() {
         BP_Game.current.GetLevels();
     }
 }
 
 public class AnyOtherClassThatNeedToReference_Game<T> {
     public void Some$$anonymous$$ethod() {
         Game<T>.current.GetLevels();
     }
 }
 
 public class BP_Level {}


If you notice things start to get out of hand like this (every class ends up having 5 generic parameters), it might just be that you are trying to do something too... generic...

After all it is a problematic thought that one could make a game class that would offer a considerable amount of useful logic using a bunch of "WhateverLevels" that can be so different that they can't even share a common interface.

$$anonymous$$aybe it would help to write things down as imaginary interfaces at first... what input do you want your game class to take and what info do you expect to be able to get from it? What kind of an interface would a Level need to offer in order for any Game class to be able to do the things you expect it to do to a bunch of levels.

avatar image eldorno NoseKills · Sep 01, 2016 at 10:06 PM 0
Share

It does look like it might get a bit out of hand... I don't think it's too generic, but perhaps the simplest way would be to just override the methods in every subclass. Not very good program$$anonymous$$g practice but I feel like it would improve the overall readability.

Well yeah, the games have very different gameplay, but the core structure is the same (like the methods which drive the score handling, level progress/unlocking, level select menu generation, etc), hence the superclass/subclass relationship I've set up. The actual level generation is handled using the extra info in the "WhateverLevel" subclasses, but the Level base class contains the basic information like name, description, and score required to unlock next level, that all the games share.

Anyhow, I'll try to get it working with the s again, but I may decide to roll back to the overriding way if things get too confusing. Thanks for the help!

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

5 People are following this question.

avatar image avatar image avatar image avatar image avatar image

Related Questions

Animation Event (Function not supported) for a public overridden function 0 Answers

How to work with a list of base class, if a variable is only found in some subclasses? 2 Answers

"Injecting" an interface into an object 1 Answer

How do I override objects that aren't in the Hierarchy? 0 Answers

Can't Override Virtual Method In Inherited Class 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges