Inheritance? or something else?

Im not sure im using what i need to be using. i’ll make this very simple, and a direct fix would be best. i’m hoping it doesnt become something else that i was hoping not to bother with lol.

the idea: ArmorCore is a base class for each armor type child, i want to create the methods in ArmorCore, and make the fields/properties in the children the numbers it will work with.

public class ArmorCore : MonoBehaviour
{
    private int[] rankHPArray; //get/set too, whatevs.

    private int hp;

    public GetHP()
    {
        //there's stuff about levels, adding ranks, blah blah to set
        //hp to whatever.
    }
}

ok, thats the basic idea, an armor core script that everything in the game can use by calling whatever methods i want to add to it.

public class ArmorType1 : ArmorCore
{
    private int[] rankHPArray = { //long list of numbers}
}

now i’ve tried only having the field/property in the child, the method in the parent doesn’t think rankHPArray exists unless it exists. if i have it in both, it doesnt use the numbers assigned in the child.

pretty sure i’m missing something here, i’ve tried with new keywords in child, and various other things. assume i’m correct at spelling, capitalization and math.

Hi,

I think what you’re getting wrong here are your Access Modifiers. ( Access Modifiers - C# | Microsoft Learn)

Inside your child class ArmorCore, you’re creating a private variable called rankHPArray, which “Only code declared in the same class or struct can access this member.”
If you want to access the variable from a derived class, you should declare it as protected.

public class ArmoreCore : MonoBehaviour
{
    protected int[] rankHPArray; // Accessible inside THIS class and from DERIVED class
    ...
}

Hope this helps !

2 Likes

thank you so much, that was killing me.

aftermath is that it works, and i learned that i forgot that for loops don’t run if… i=0; i<0; i++ lol

thank you thank you thank you!

though the main issue was resolved, the dang thing has suddenly started hitting me with the

The same field name is serialized multiple times in the class or its parent class. This is not supported:

error again, which is obnoxious. i’m not sure anything in both classes is actually serialized concurrently. protected in base class, private new in child class. so now i’m suddenly back to why i tried dinking around in other ways x.x

The point of @Reaktion post was that you don’t serialize it in both the base and derived type. Only define the serialized member in the base type with the protected access modifier, then use the member in the derived type.

1 Like

then maybe i didn’t quite descibe the issue properly in the main post? to clarify:

  1. i can’t have a method in the base class to do maths and such with fields/properties if those properties don’t exist in the base class.

  2. the actual numbers that will be used for said maths are to be stored (since they will all be different) in the child classes.

so, i make a method to multiply what should be in xx since it’s in the base class, but the object holds the child class, so the method uses (childs) xx.

the other options are to break part of the use of inheritance by putting all these methods in the children, thereby copy/pasting probably 500 methods. or putting references to each and every “child” as a separate script into every bullet/enemy/wall/etc, which seems like an even worse option.

it’s WORKING, it’s just tossing that error. well, 60 of them right now, since it’s 6 children with 10 fields.

let me show you the ugly parts lol:

public class ArmorCore : MonoBehaviour
{
    public int armorIndex; //points to which armor in the save file
    //Lookup table for the base stats
    public int armorLevel; //armor's level, 1-300
    public int armorRank; //armor's 'star rank', 0-20 
    public int armorQuality; //armor's color quality, 0-5 (grey, green, blue, purple, red, rainbow)
    public int damageType;

    protected int[] rankHPArray;
    protected float rankArmorAmount; //rank 20 = 20%
    protected float rankDodgeAmount; //rank 20 = 10%
    protected float rankCritAvoid;
    protected float rankCritReduction;

    protected int baseHPPerLevel;
    protected float armorPerLevel; //lv 300 = 30%
    protected float dodgePerLevel; //lv 300 = 7.5%
    protected float levelCritAvoid;
    protected float levelCritReduction;

    private float[] qualityLevelHPMultiplier = { 1f, 1.5f, 2f, 2.5f, 3f, 3.5f };
    private int totalHull; //hull amount
    private float totalArmor; //percentage reduction
    private float totalDodge; //percent chance to dodge
    private float totalCritAvoid;
    private float totalCritReduction;

    public Sprite icon;
    public Sprite[] sprites;
    public Sprite backSprite;
    public Sprite star;
    public int stars;
    

    //sets the stats for the armor according to level and rank.
    public void SetTotalStats()
    {
        armorLevel = Resources.Load<FakeSave>("Data").armorLevels[armorIndex];
        armorRank = Resources.Load<FakeSave>("Data").armorRanks[armorIndex];
        //Get the armor HP for rank
        GetQualityBackground();
        GetStars();
        totalHull = GetRankHP() + GetLevelHP();        
        totalArmor = (armorRank + 1 * rankArmorAmount) + (armorPerLevel * armorLevel);
        totalDodge = (armorRank + 1 * rankDodgeAmount) + (dodgePerLevel * armorLevel);
        totalCritAvoid = (rankCritAvoid * armorRank + 1) + (levelCritAvoid * armorLevel);
        totalCritReduction = (levelCritReduction * armorRank + 1) + (rankCritReduction * armorRank + 1);
    }
ETC
public class ArmorType1 : ArmorCore
{
    private new int[] rankHPArray = { 180, 315, 315, 441, 441, 441, 594, 594, 594, 594, 756, 756, 756, 756, 756, 927, 927, 927, 927, 927, 927 };
    private new float rankArmorAmount = 1f; //rank 20 = 20%
    private new float rankDodgeAmount = 0.5f; //rank 20 = 10%
    private new float rankCritAvoid = 0.5f;
    private new float rankCritReduction = 0.2f;

    private new int baseHPPerLevel = 13;
    private new float armorPerLevel = 0.1f; //lv 300 = 30%
    private new float dodgePerLevel = 0.025f; //lv 300 = 7.5%
    private new float levelCritAvoid = 0.025f;
    private new float levelCritReduction = 0.05f;
}

im not trying to blow this up into a new ticket, i’m just not sure why i get red lines of suck while it’s working JUST FINE.

You don’t need these members in the derived type. Hell you don’t even need the derived type at all if the only difference is going to be some hard-coded values.

Drop the derived type. Serialize all the members you want to modify in the base type. Add it to a game object, then set the values in the inspector. That’s the correct Unity workflow.

2 Likes

so what you’re saying is, that i should just make one class, have x number of rankHParrays, dodge varieties, etc, so like 6 separate types of armor in one class, check every time some kind of impact occurs which armor type it is, have my upgrade buttons repeatedly do so in the main menu section of upgrading, etc? this seems like the best possible way to have an illegible code base. if not illegible, then one to chase a lot of things down in x.x

also, i despise having to type thousands of numbers into the inspector, ten keying in a script is far less odious.

That’s not what I said at all. The idea is to have multiple instances of the component, each set up with different values.

Mind you, if this is all immutable data then you should be using scriptable objects here.

Then every time you want to make a change you have to recompile. You also can’t change the values during gameplay. Having the values serialized avoids both these issues.

im gonna stick with the armors on this, since its only 6, but there are also going to be main weapons, missile launchers, special weapons, and your actual ship as well.

my goal is making a prefab for each of these to reference, with their own core script for the relevant core info for each. those cores are a simple way to interact with the different kinds, since i can derive from it, and put a different one on each prefab. each of those has its OWN unchanging values to math out it’s total values.

i use this to display the various numbers in the scene for show purposes, their ‘next level’ info, etc. when upgrading, it changes only two values which are saved, level and rank, everything else is gotten using those two numbers, which are saved alsewhere in an array to get to those particular prefabs.

but thats 6 different items (again, just going with armor here), and those particular fields such as the hp per level, hp per rank, dodge per level, etc, are unchanging and different for each.

those prefabs are then referenced by the ship to get it’s relevant use cases (armor will change the ships values, the ship will change he weaponry values while in combat.)

so my largest issue comes down to where i can’t make a method in the core script without the fields existing in the core script, and i want to use the values for the fields that are in the child scripts. and again, it’s working. it’s working perfectly. the errors don’t stop anything, except maybe building it. but they show up, and it feels like they’re there for no good reason at all.

Because you’re using inheritance wrong; or more directly: you shouldn’t be using it here. Inheritance should not be used to only have different data. You inherit from a type when you want to modify or reuse behaviour (reusing alone is not a good reason, imo), and so that you can substitute base types with derived types and not have to worry about their specific implementation.

When the only thing different here is data (particularly immutable data), you only need an object to store that data and to make multiple instances of said data. That’s what we have scriptable objects for. You can wrap up all this data in said object, and if you need to do any calculations based on it, you can pass said object as a parameter into a method.

1 Like

i know how that works, but it makes me sad. i don’t know why i hate using the inspector to set values, but i do. i guess i’ll do it that way. lot of work to rework now.

While I agree with spiney199 that you should ideally be using just one class and changing the data for each armor type in inspector, if you really want to do it your way and just want to get rid of the errors, you can just convert the array fields into properties instead so that they don’t get serialized.

Change the parent class field into:
protected virtual int[] rankHPArray { get; set; }

And the child class fields into:
protected override int[] rankHPArray { get; set; } = { values... }

The “virtual” and “override” keywords aren’t required but they are good practice as they make sure that the child arrays are always used when accessing the property even from the parent class.

You’d need to do this same thing to all the other fields in your classes.

1 Like

just reminded me why i was doing it, it’s because there are going to be various different methods in each as well.

had to change the “solved”, the guy above this actually hit me with a more thorough answer.

EDIT:
and those errors start popping up again, holy crap lol. now they’re whining about the k_BackingField at the end of the “serialized multiple times blahblah.” and it will build and play perfectly anyway, what the actual hell? xD

If you’re hard coding values, why do you have them serialized anyway? Just don’t serialize the fields or property’s backing members.

you might have to define serialized for me in this context. they aren’t public, so they aren’t showing in inspector, which i usually associate with serialized, nor am i using a [SerializeField].

1 Like

I believe private (and protected) fields are still serialized under the hood even if they don’t show in the inspector. Properties are not serializable though, so changing all the fields into properties should fix the issue. The simplest way to do this is by adding { get; set; } right after each variable name like I showed in my previous reply.

Again, ideally this should just be one class with the values for each armor type set via inspector. If you need different methods for different types like you explained earlier, then these methods should be handled in a separate class that itself has different child classes, which can be added as a second component in the prefabs.

Or decorate the fields with [System.NonSerialized]. But seriously, there are better ways to do this. There’s no need for inheritance here at all.

all of the properties that the error are showing for are lol.

public class ArmorCore : MonoBehaviour
{
    public int armorIndex; //points to which armor in the save file
    //Lookup table for the base stats
    public int armorLevel; //armor's level, 1-300
    public int armorRank; //armor's 'star rank', 0-20 
    public int armorQuality; //armor's color quality, 0-5 (grey, green, blue, purple, red, rainbow)
    public int damageType;

    protected virtual int[] rankHPArray { get; set; }// = { 180, 315, 315, 441, 441, 441, 594, 594, 594, 594, 756, 756, 756, 756, 756, 927, 927, 927, 927, 927, 927 };
    protected virtual float rankArmorAmount { get; set; }//= 1f; //rank 20 = 20%
    protected virtual float rankDodgeAmount { get; set; }//= 0.5f; //rank 20 = 10%
    protected virtual float rankCritAvoid { get; set; }//= 0.5f;
    protected virtual float rankCritReduction { get; set; }//= 0.2f;

    protected virtual int baseHPPerLevel { get; set; }//= 13;
    protected virtual float armorPerLevel { get; set; }//= 0.1f; //lv 300 = 30%
    protected virtual float dodgePerLevel { get; set; }//= 0.025f; //lv 300 = 7.5%
    protected virtual float levelCritAvoid { get; set; }//= 0.025f;
    protected virtual float levelCritReduction { get; set; }//= 0.05f;

    private float[] qualityLevelHPMultiplier = { 1f, 1.5f, 2f, 2.5f, 3f, 3.5f };
    private int totalHull; //hull amount
    private float totalArmor; //percentage reduction
    private float totalDodge; //percent chance to dodge
    private float totalCritAvoid;
    private float totalCritReduction;

    public Sprite icon;
    public Sprite[] sprites;
    public Sprite backSprite;
    //public Sprite star;
    public int stars;
public class ArmorType1 : ArmorCore
{
    protected override int[] rankHPArray { get; set; } = { 180, 315, 315, 441, 441, 441, 594, 594, 594, 594, 756, 756, 756, 756, 756, 927, 927, 927, 927, 927, 927 };
    protected override float rankArmorAmount { get; set; } = 1f; //rank 20 = 20%
    protected override float rankDodgeAmount { get; set; } = 0.5f; //rank 20 = 10%
    protected override float rankCritAvoid { get; set; } = 0.5f;
    protected override float rankCritReduction { get; set; } = 0.2f;

    protected override int baseHPPerLevel { get; set; } = 13;
    protected override float armorPerLevel { get; set; } = 0.1f; //lv 300 = 30%
    protected override float dodgePerLevel { get; set; } = 0.025f; //lv 300 = 7.5%
    protected override float levelCritAvoid { get; set; } = 0.025f;
    protected override float levelCritReduction { get; set; } = 0.05f;

The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(ArmorType6) k__BackingField

that is very specifically pointed at the final property.

possibly no need, true, but it should also just be as simple as this with no need to keep going with more and more scripts, to fill up my IDE with, when it can all be handled like this very easily lol

This seems to be referring to a different class than the ones shown here, ArmorType6? Is this k__BackingField field familar to you?