Accessing a child variable from aparent

Hello !

My question is quite simple but yet, I can’t answer it. I have a Scriptable Object named Item. From this, I have four derived classes : Weapon, Armor, Food and Material (for crafting for exemple). I need to use it from an exterior script. All I want to do is using a variable contained in the Food child (for exemple the HP amount to give when the player eat it, that doesn’t exist in the others scripts), when I only use the parent in this script. How can I do something like Parent.ChildVariable ?

Thank you for all your answers !

Watch this video on the subject

His exmple is about attacks, but its related to your case

Technically you can check type and cast it like

if (baseItem is FoodItem foodItem) { }

But it’s usually a code smell and wrong way if you have chosen to use inheritance.
You shouldn’t care about underlying type and its data, there should be an interface suitable for each item.
If all of your items can be used, then you should create abstract/virtual Use method which accepts reference to Player, so inside food class you are calling player.Heal() or something.

2 Likes

It also invalidates the O in SOLID. I strongly advice against it.

It’s also good practice in object-oriented programming in general to Tell Dont Ask.

Rather than asking for the value in the HP variable, you should aim to just tell the Item to apply whatever its effects are to the consumer.

public abstract class Item : ScriptableObject
{
	public abstract void OnUsed(Player player);
}

public sealed class Food : Item
{
	[SerializeField]
	private float healAmount;

	public override void OnUsed(Player player)
	{
		player.Health.Heal(healAmount);
	}
}
2 Likes

Thank you for your answer with @AndersMalmgren ! So, if I understand, I must rewrite my items to not use child/parent ?
I must only have a “UsableItem” ScriptableObject and a “NonUsableItem” ScriptableObject ?

I mean the actual answer here should probably depend on the scope of which items and surrounding systems take part in your project. Is there only a handful of items that make up a small part of your game? Or is there going to hundreds if not thousands, and be one of the major focuses?

That said I found that a component-based system provides as much scalability as you could poke a stick at without getting too complicated. Though rather than MonoBehaviour components, you use plain C# objects in a collection serialized with [SerializeReference] to enable polymorphism. That way, you only need one scriptable object class for your items, and can implement any number of component types.

It does require custom inspector stuff to get working, but tools like Odin Inspector can make it work out of the box.

It all depends on other systems etc.

IF not all your items are consumables then you need to adress that. Easiest way is to make all items consumables and have NOOP implementaion on those that do not support being consumed.

YOu can also make a IConsumable interface and make concrete types both implement IItem and IConsumable if they can be consumed. A little tricker using from code, coudl be solved with GetComponent() and similar mechanics.

You can use casting like earlier explained. But difference here is that its not done from a super type and also does not violate SOLID. Example from our game.

public void OnCollisionEnter(Collision collision)
{
    if (NVRInteractables.GetInteractable(collision.collider) is IPhysicalHandCollidable collidable)
    {
        collidable.OnCollide(hand, collision);
    }
}

In your game this would transfer to NVRInteractables.GetInteractable(collision.collider) returning a IItem and IPhysicalHandCollidable being a IConsumable