- Home /
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.
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?
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_Level
s, but when I ran it, it was looking at the base class's levels
, rather than the BP_Level
s 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!
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
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> {
}
Hmm, now this is an interesting idea. I'll try implement it and see how it goes. Thanks!
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
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.
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
![](https://koobas.hobune.stream/wayback/20220612091946im_/https://answers.unity.com/themes/thub/images/avi.jpg)