0
\$\begingroup\$

This is my base class

using UnityEngine;

public class Weapon : MonoBehaviour, IInteractable
{
    public void Interact(Player player)
    {
        player.gameObject.AddComponent<Weapon>();
    }
}

It's extended by different children such as Pistol and M16. How can I make it so that the Interact function of the base class adds the child script instead of the base script?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I'd recommend not making separate subclasses for each model of weapon. Variations between an M16 vs an AK47 etc. should typically be represented as data - different clip sizes, fire rates, range values, recoil patterns, etc. - these are all just different sets of numbers, not wholly unique logic that demands bespoke code. \$\endgroup\$
    – DMGregory
    Jan 7 at 1:51

1 Answer 1

2
\$\begingroup\$

You can accomplish this using the Curiously Recurring Template Pattern:

public class Weapon<T> : MonoBehaviour, IInteractable where T: Weapon<T> {

    public void Interact(Player player) {
        player.gameObject.AddComponent<T>();
    }
}

public class RangedWeapon : Weapon<RangedWeapon>
{
    // Put anything the derived weapon class needs here.
}

When the compiler generates the code for RangedWeapon.Interact, the T placeholder type becomes RangedWeapon, and so the component that gets added is this weapon's own type.


However, I'd recommend that you separate the concepts of "Weapon Pickup the player can interact with to pick up / equip the weapon" from "Weapon Behaviour running on the player object that the player can shoot".

These might conceptually be the same thing in terms of the game's fiction (the AK47 sitting on the ground is the same AK47 I pick up and fire), but in terms of the game's implementation they often have very little in common. One needs to shoot and one doesn't. One needs to listen for pick-ups and the other doesn't. They might not even use the same model and texture, if you have high-detail versions to show in the player's hands.

So instead you could do something like this:

public class WeaponPickup : MonoBehaviour, IInteractable {

   public Weapon weaponToEquip;

   public void Interact(Player player) {
       var weaponInstance = Instantiate(weaponToEquip, player);
   }
}

You can then create a prefab for each weapon (possibly many different variants using the same scripts) and use a single script to represent interactable versions of any of them.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ By the way: When you don't want to distribute the properties and stats of each weapon over two separate prefabs, then you can use ScriptableObjects to define weapon type assets and then have both the WeaponPickup and the Weapon refer to the same WeaponType asset to access their data. \$\endgroup\$
    – Philipp
    Jan 7 at 9:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .