Unity RPG Offhand/Shield equipment slot (W/ added Dual Wielding)

,

That would do it. Good job getting that sorted!

2 Likes

Iā€™ll leave a note here to other people so that the shield cannot be equipped with dual weaponsā€¦(EquipmentSlotUI.cs) Just had to copy the solution for two-handed weapons.

public int MaxAcceptable(InventoryItem item)
    {
        EquipableItem equipableItem = item as EquipableItem;
        if (equipableItem == null) return 0;
        if ((equipLocation == EquipLocation.Weapon) && item is WeaponConfig weaponConfig)
        {
            if (weaponConfig.isTwoHanded && (playerEquipment.GetItemInSlot(EquipLocation.Offhand) != null))
                return 0;
            if (weaponConfig.isDualWield && (playerEquipment.GetItemInSlot(EquipLocation.Offhand) != null)) // this
                return 0;
        }

        if (equipLocation == EquipLocation.Offhand)
        {
            EquipableItem mainHand = playerEquipment.GetItemInSlot(EquipLocation.Weapon);
            if (mainHand is WeaponConfig mainHandConfig)
            {
                if (mainHandConfig.isTwoHanded) return 0;
                if (mainHandConfig.isDualWield) return 0; //and this
            }
        }
        //The line below only makes sense after completing the Shops and Abilities course
        //if (!equipableItem.CanEquip(equipLocation, playerEquipment)) return 0; 
        if (GetItem() != null) return 0;

        return 1;
    }
1 Like

Hello, Iā€™m stuck with one problem, I made armor according to the principle of weapons. All armor slots work correctly spawn and are destroyed. But only the armor on the body does not want to be removed, I just donā€™t understand what the problem is, the code for all parts is identical. Based on your variant weapon code.

public class ArmorConfig : EquipableItem, IModifierProvider
    {
        [SerializeField] Armor equippedArmor = null;
        [SerializeField] Armor equippedArmor2 = null;

        [SerializeField]
        Modifier[] additiveModifiers;
        [SerializeField]
        Modifier[] percentageModifiers;

        [System.Serializable]
        struct Modifier
        {
            public Stat stat;
            public float value;
        }
        public enum ArmorVariant { Helmet, Necklace, Body, Trousers, Boots, Gloves}
        [SerializeField] ArmorVariant armorVarianter; 
       
        const string EquipNameHelmet = "Helmet";
        const string EquipNameNecklace = "Necklace";
        const string EquipNameBody = "Body";
        const string EquipNameTrousers = "Trousers";
        const string EquipNameBoots = "Boots";//right
        const string EquipNameBootsL = "BootsL";//left
        const string EquipNameGloves = "Gloves"; //right
        const string EquipNameGlovesL = "GlovesL"; //left

        Transform oldEquip;
        Transform oldEquip2;
        public Armor Spawn(Transform Helmet, Transform Necklace, Transform Body, Transform Trousers, Transform Boots, Transform Left_Boots, Transform Gloves, Transform Left_Gloves)
        {
            //Debug.Log("Spawn");
            DestroyOldArmor(Helmet, Necklace, Body, Trousers, Boots, Left_Boots, Gloves, Left_Gloves);

            Armor equipment = null;
            Armor equipment2 = null;
            if (equippedArmor != null)
            {
                Transform armorTransform = GetTransform(Helmet, Necklace, Body, Trousers, Boots, Gloves);
                Transform armorTransfromDouble = GetDoubleTransfrom(Left_Boots, Left_Gloves);
                switch (armorVarianter)
                {
                    case ArmorVariant.Helmet:
                        equipment = Instantiate(equippedArmor, armorTransform);
                        equipment.gameObject.name = EquipNameHelmet;
                        break;
                    case ArmorVariant.Necklace:
                        equipment = Instantiate(equippedArmor, armorTransform);
                        equipment.gameObject.name = EquipNameNecklace;
                        break;
                    case ArmorVariant.Body:
                        equipment = Instantiate(equippedArmor, armorTransform);
                        equipment.gameObject.name = EquipNameBody;
                        break;
                    case ArmorVariant.Trousers:
                        equipment = Instantiate(equippedArmor, armorTransform);
                        equipment.gameObject.name = EquipNameTrousers;
                        break;
                    case ArmorVariant.Boots:
                        equipment = Instantiate(equippedArmor, armorTransform);
                        equipment2 = Instantiate(equippedArmor2, armorTransfromDouble);
                        equipment.gameObject.name = EquipNameBoots;
                        equipment2.gameObject.name = EquipNameBootsL;
                        break;
                    case ArmorVariant.Gloves:
                        equipment = Instantiate(equippedArmor, armorTransform);
                        equipment2 = Instantiate(equippedArmor2, armorTransfromDouble);
                        equipment2.gameObject.name = EquipNameGlovesL;
                        equipment.gameObject.name = EquipNameGloves;
                        break;
                }             

            }

            return equipment;
        }
       
        private void DestroyOldArmor(Transform Helmet, Transform Necklace, Transform Body, Transform Trousers, Transform Boots, Transform Left_Boots, Transform Gloves, Transform Left_Gloves)
        {
            switch (armorVarianter)
            {
                case ArmorVariant.Helmet:
                    Debug.Log("Destroing Helmet.");
                    oldEquip = Helmet.Find(EquipNameHelmet);
                    break;
                case ArmorVariant.Necklace:
                    Debug.Log("Destroing Necklace.");
                    oldEquip = Necklace.Find(EquipNameNecklace);
                    break;
                case ArmorVariant.Body:
                    Debug.LogWarning("Destroing Body???");
                    oldEquip = Body.Find(EquipNameBody);
                    break;
                case ArmorVariant.Trousers:
                    Debug.Log("Destroing Trousers.");
                    oldEquip = Trousers.Find(EquipNameTrousers);
                    break;
                case ArmorVariant.Boots:
                    Debug.Log("Destroing Boots.");
                    oldEquip = Boots.Find(EquipNameBoots);
                    oldEquip2 = Left_Boots.Find(EquipNameBootsL);
                    break;
                case ArmorVariant.Gloves:
                    Debug.Log("Destroing Gloves.");
                    oldEquip = Gloves.Find(EquipNameGloves);
                    oldEquip2 = Left_Gloves.Find(EquipNameGlovesL);
                    break;
            }
            if (oldEquip == null) return;
            if (oldEquip2 != null)
            {
                oldEquip2.name = "DESTROYING2";
                Destroy(oldEquip2.gameObject);
            }

            oldEquip.name = "DESTROYING";
            Destroy(oldEquip.gameObject);
        }
        private Transform GetDoubleTransfrom(Transform L_Boots, Transform L_Gloves)
        {
            if (armorVarianter == ArmorVariant.Boots) return L_Boots;
            if (armorVarianter == ArmorVariant.Gloves) return L_Gloves;
            else return null;
        }
        private Transform GetTransform(Transform Helmet, Transform Necklace, Transform Body, Transform Trousers, Transform Boots, Transform Gloves)
        {
            //Transform armorTransform;
            if (armorVarianter == ArmorVariant.Helmet) return Helmet;
            if (armorVarianter == ArmorVariant.Necklace) return Necklace;
            if (armorVarianter == ArmorVariant.Body) return Body;
            if (armorVarianter == ArmorVariant.Trousers) return Trousers;
            if (armorVarianter == ArmorVariant.Boots)  return Boots;
            if (armorVarianter == ArmorVariant.Gloves) return Gloves;
            else return null;
        }

        public IEnumerable<float> GetAdditiveModifiers(Stat stat)
        {
            foreach (var modifier in additiveModifiers)
            {
                if (modifier.stat == stat)
                {
                    yield return modifier.value;
                }
            }
        }

        public IEnumerable<float> GetPercentageModifiers(Stat stat)
        {
            foreach (var modifier in percentageModifiers)
            {
                if (modifier.stat == stat)
                {
                    yield return modifier.value;
                }
            }
        }
    }

Honestly, Iā€™m not sure. The code is identical between the different locations. Is the body armor being renamed to DESTROYING?

1 Like

Now Iā€™m completely confused, I didnā€™t change anything at all, Yesterday only the armor on the body did not work, now everything is not removedā€¦ And the helmet does not even spawn, itā€™s absurdā€¦ No, no renamedā€¦
I will write all parts of the armor scriptā€¦
Fighter.cs

public class Fighter : MonoBehaviour, IAction
{

    
    [SerializeField] Transform armorTransformHelmet = null;
    [SerializeField] Transform armorTransformNecklace = null;
    [SerializeField] Transform armorTransformBody = null;
    [SerializeField] Transform armorTransformTrousers = null;
    [SerializeField] Transform armorTransformBoots_Left = null;
    [SerializeField] Transform armorTransformBoots_Right = null;
    [SerializeField] Transform armorTransformGloves_Left = null;
    [SerializeField] Transform armorTransformGloves_Right = null;


    [SerializeField] ArmorConfig defaultarmorHelmet = null;
    [SerializeField] ArmorConfig defaultarmorNecklace = null;
    [SerializeField] ArmorConfig defaultarmorBody = null;
    [SerializeField] ArmorConfig defaultarmorTrousers = null;
    [SerializeField] ArmorConfig defaultarmorBoots = null;
    [SerializeField] ArmorConfig defaultarmorGloves = null;


    ArmorConfig currentArmorConfig;
    LazyValue<Armor> currentArmor;
   

    private void Awake()
    {
        
        currentArmorConfig = defaultarmorHelmet;
        currentArmor = new LazyValue<Armor>(SetupDefaultArmor);
        

        

        equipment = GetComponent<Equipment>();
        if (equipment)
        {
            equipment.equipmentUpdated += UpdateWeapon;
            equipment.equipmentUpdated += UpdateEquipment;
        }
    }
    //armor
    private Armor SetupDefaultArmor()
    {
        return AttachArmor(defaultarmorHelmet);
    }
    public void EquipArmor(ArmorConfig armor)
    {
        currentArmorConfig = armor;
        currentArmor.value = AttachArmor(armor);

    }
    private Armor AttachArmor(ArmorConfig armor)
    {
        

        return armor.Spawn(armorTransformHelmet, armorTransformNecklace, armorTransformBody, armorTransformTrousers, armorTransformBoots_Left, armorTransformBoots_Right, armorTransformGloves_Right, armorTransformGloves_Left);
    }
    public void UpdateEquipment()
    {
        var Helmet = equipment.GetItemInSlot(EquipLocation.Helmet) as ArmorConfig;
        var Body = equipment.GetItemInSlot(EquipLocation.Body) as ArmorConfig;
        var Trousers = equipment.GetItemInSlot(EquipLocation.Trousers) as ArmorConfig;
        var Boots = equipment.GetItemInSlot(EquipLocation.Boots) as ArmorConfig;
        var Gloves = equipment.GetItemInSlot(EquipLocation.Gloves) as ArmorConfig;
        var Necklace = equipment.GetItemInSlot(EquipLocation.Necklace) as ArmorConfig;
        if (Helmet == null) EquipArmor(defaultarmorHelmet);        
        else EquipArmor(Helmet);
        if (Body == null) EquipArmor(defaultarmorBody);
        else EquipArmor(Body);
        if (Trousers == null) EquipArmor(defaultarmorTrousers);
        else EquipArmor(Trousers);
        if (Boots == null) EquipArmor(defaultarmorBoots);
        else EquipArmor(Boots);
        if (Gloves == null) EquipArmor(defaultarmorGloves);
        else EquipArmor(Gloves);
        if (Necklace == null) EquipArmor(defaultarmorNecklace);
        else EquipArmor(Necklace);
       
    }

One thing I didnā€™t enquire aboutā€¦ as most armor systems (like the Synty Modular Hero Set or InfinityPBRā€™s Armor system) actually use individual skinned mesh components that are all set up to work with the skeleton, and components are added/removed by activating/deactivating those componentsā€¦ Is this system a collection of armor that is attached to the skeleton, or is it more like what I just described?

1 Like

Fully weapon system principle, same spawn.


All parts of the armor are separate objects. These parts are tied to body parts. And I didnā€™t use skeleton animation.

Iā€™m not seeing a reason these wouldnā€™t be spawning/unspawning from the code presented. I would probably refactor this a bit, howeverā€¦

private void DestroyOldArmor(Transform slot, string armorName)
{
    var oldItem = slot.Find(armorName);
    if(oldItem)
    {
        oldItem.Name = "DESTROYING";
        Destroy(oldItem);
    }
}

private Armor Spawn(Transform slot, string itemName, bool equip2 = false)
{
     if(!slot)
     {
          Debug.Log($"Attemping to spawn slot {itemName}, but no transform is defined");
          return null;
      }
     DestroyOldArmor(slot, itemName);
     var ItemToInstantiate = equip2? equippedArmor2:equippedArmor;
     Armor armor = Instantiate(ItemToInstantiate, slot);
     armor.name = itemName;
     return armor;
}

public Armor Spawn(Transform Helmet, Transform Necklace, Transform Body,  Transform Trousers, Transform Boots, Transform Left_Boots, Transform Gloves, Transform Left_Gloves);
{
     switch(armorVarianter)
     case ArmorVariant.Helmet:
          return Spawn(Helmet, EquipNameHelmet);
     case ArmorVariant.Necklace:
          return Spawn(Necklace, EquipNameNecklace);
     case ArmorVariant.Body:
          return Spawn(Body, EquipNameBody);
     case ArmorVariant.Trousers:
          return Spawn(Trousers, EquipNameTrousers;
     case ArmorVariant.Boots:
          Spawn(Left_Boots, EquipNameBootsL, true); //spawns left boot
          return Spawn(Boots, EquipNameBoots);
     case ArmorVariant.Gloves:
          Spawn(Left_Gloves, EquipNameGlovesL, true);
          return Spawn(Gloves, EquipNameGloves);
}
1 Like

Tried to change to your version, still does not work completely. I just canā€™t figure out what to doā€¦
Iā€™m sorry to bother you so muchā€¦
The weapon just now stopped working, after the changes.

ā€œThe Object you want to instantiate is nullā€
This would seem to indicate that in one or more of your ArmorConfig instances, the equippedArmor or equippedArmor2 is nullā€¦

Add this to Armor.Spawn (the private one with the single transform and string)

if(equippedArmor==null)
{
    Debug.Log($"Armor {name} is trying to equip an {itemName}, but does not have an equippedArmor assigned.");
    return null;
}
if(equip2 && equippedArmor2==null)
{
    Debug.Log($"Armor {name} is trying to equip {itemName}, but has no equippedArmor2 assigned");
    return null;
} 
1 Like

Thanks, Spawn now work, but destroing no work and no renamed to ā€œDESTROYINGā€ā€¦

When the items are spawned, are they being renamed to the itemName?

1 Like

Yes, items renamed to the itemName, maybe after ranamed no work find this item?

That is, I believe, the problem. What Iā€™m unsure of is why.

Letā€™s make a brute force method to try to ensure that the item is found:

private Transform FindByName(Transform slot, string nameToFind)
{
    foreach(Transform child in slot)
    {
         if(child.name == nameToFind) return child;
    }
    return null;
}

Then replace

var oldItem = slot.Find(armorName);

with

var oldItem = FindByName(slot, armorName);

Itā€™s all strangeā€¦

public class ArmorConfig : EquipableItem, IModifierProvider
    {
        [SerializeField] Armor equippedArmor = null;
        [SerializeField] Armor equippedArmor2 = null;

        [SerializeField]
        Modifier[] additiveModifiers;
        [SerializeField]
        Modifier[] percentageModifiers;

        [System.Serializable]
        struct Modifier
        {
            public Stat stat;
            public float value;
        }
        public enum ArmorVariant { Helmet, Necklace, Body, Trousers, Boots, Gloves}
        [SerializeField] ArmorVariant armorVarianter; 
       
        const string EquipNameHelmet = "Helmet";
        const string EquipNameNecklace = "Necklace";
        const string EquipNameBody = "Body";
        const string EquipNameTrousers = "Trousers";
        const string EquipNameBoots = "Boots";//right
        const string EquipNameBootsL = "BootsL";//left
        const string EquipNameGloves = "Gloves"; //right
        const string EquipNameGlovesL = "GlovesL"; //left

        Transform oldEquip;
        Transform oldEquip2;
        
         private Armor Spawn(Transform slot, string itemName, bool equip2 = false)
         {

             if (!slot)
             {
                 Debug.Log($"Attemping to spawn slot {itemName}, but no transform is defined");
                 return null;
             }
             if (equippedArmor == null)
             {
                 Debug.Log($"Armor {name} is trying to equip an {itemName}, but does not have an equippedArmor assigned.");
                 return null;
             }
             if (equip2 && equippedArmor2 == null)
             {
                 Debug.Log($"Armor {name} is trying to equip {itemName}, but has no equippedArmor2 assigned");
                 return null;
             }
             DestroyOldArmor(slot, itemName);
             var ItemToInstantiate = equip2 ? equippedArmor2 : equippedArmor;
             Armor armor = Instantiate(ItemToInstantiate, slot);
             armor.name = itemName;
             return armor;
         }
         public Armor Spawn(Transform Helmet, Transform Necklace, Transform Body, Transform Trousers, Transform Boots, Transform Left_Boots, Transform Gloves, Transform Left_Gloves)
         {
             switch (armorVarianter) {
                 case ArmorVariant.Helmet:
                     return Spawn(Helmet, EquipNameHelmet);
                 case ArmorVariant.Necklace:
                     return Spawn(Necklace, EquipNameNecklace);
                 case ArmorVariant.Body:
                     return Spawn(Body, EquipNameBody);
                 case ArmorVariant.Trousers:
                     return Spawn(Trousers, EquipNameTrousers);
                 case ArmorVariant.Boots:
                     Spawn(Left_Boots, EquipNameBootsL, true); //spawns left boot
                     return Spawn(Boots, EquipNameBoots);
                 case ArmorVariant.Gloves:
                     Spawn(Left_Gloves, EquipNameGlovesL, true);
                     return Spawn(Gloves, EquipNameGloves);
             }
             return null;
         }
        private Transform FindByName(Transform slot, string nameToFind)
        {
            foreach (Transform child in slot)
            {
                if (child.name == nameToFind) return child;
            }
            return null;
        }
        private void DestroyOldArmor(Transform slot, string armorName)
         {
             var oldItem = FindByName(slot, armorName);
             if (oldItem)
             {
                 oldItem.name = "DESTROYING";
                 Destroy(oldItem.gameObject);
             }
         }
        

        public IEnumerable<float> GetAdditiveModifiers(Stat stat)
        {
            foreach (var modifier in additiveModifiers)
            {
                if (modifier.stat == stat)
                {
                    yield return modifier.value;
                }
            }
        }

        public IEnumerable<float> GetPercentageModifiers(Stat stat)
        {
            foreach (var modifier in percentageModifiers)
            {
                if (modifier.stat == stat)
                {
                    yield return modifier.value;
                }
            }
        }
    }
public class Fighter : MonoBehaviour, IAction
{
    [SerializeField] float timeBetweenAttacks = 1f;
    [SerializeField] Transform rightHandTransform = null;
    [SerializeField] Transform leftHandTransform = null;
    [SerializeField] WeaponConfig defaultWeapon = null;
    [SerializeField] ShieldConfig defaultShield = null;
    [SerializeField] float autoAttackRange = 4f;

    //armor
    [SerializeField] Transform armorTransformHelmet = null;
    [SerializeField] Transform armorTransformNecklace = null;
    [SerializeField] Transform armorTransformBody = null;
    [SerializeField] Transform armorTransformTrousers = null;
    [SerializeField] Transform armorTransformBoots_Left = null;
    [SerializeField] Transform armorTransformBoots_Right = null;
    [SerializeField] Transform armorTransformGloves_Left = null;
    [SerializeField] Transform armorTransformGloves_Right = null;


    [SerializeField] ArmorConfig defaultarmorHelmet = null;
    [SerializeField] ArmorConfig defaultarmorNecklace = null;
    [SerializeField] ArmorConfig defaultarmorBody = null;
    [SerializeField] ArmorConfig defaultarmorTrousers = null;
    [SerializeField] ArmorConfig defaultarmorBoots = null;
    [SerializeField] ArmorConfig defaultarmorGloves = null;


    ArmorConfig currentArmorConfig;
    LazyValue<Armor> currentArmor;
    //armor

    Health target;
    Equipment equipment;
    float timeSinceLastAttack = Mathf.Infinity;
    WeaponConfig currentWeaponConfig;
    LazyValue<Weapon> currentWeapon;

    ShieldConfig currentShield;
    LazyValue<Shield> currentEquippedShield;
    

    private void Awake()
    {
        
        currentArmorConfig = defaultarmorHelmet;
        currentArmor = new LazyValue<Armor>(SetupDefaultArmor);

        currentWeaponConfig = defaultWeapon;
        currentWeapon = new LazyValue<Weapon>(SetupDefaultWeapon);

        currentShield = defaultShield;
        currentEquippedShield = new LazyValue<Shield>(null);

        equipment = GetComponent<Equipment>();
        if (equipment)
        {
            equipment.equipmentUpdated += UpdateWeapon;
            equipment.equipmentUpdated += UpdateEquipment;
        }
    }
    //armor
    private Armor SetupDefaultArmor()
    {
        return AttachArmor(defaultarmorHelmet);
    }
    public void EquipArmor(ArmorConfig armor)
    {
        currentArmorConfig = armor;
        currentArmor.value = AttachArmor(armor);

    }
    private Armor AttachArmor(ArmorConfig armor)
    {
        //Debug.Log("AttachArmor");

        return armor.Spawn(armorTransformHelmet, armorTransformNecklace, armorTransformBody, armorTransformTrousers, armorTransformBoots_Left, armorTransformBoots_Right, armorTransformGloves_Right, armorTransformGloves_Left);
    }
    public void UpdateEquipment()
    {
        var Helmet = equipment.GetItemInSlot(EquipLocation.Helmet) as ArmorConfig;
        var Body = equipment.GetItemInSlot(EquipLocation.Body) as ArmorConfig;
        var Trousers = equipment.GetItemInSlot(EquipLocation.Trousers) as ArmorConfig;
        var Boots = equipment.GetItemInSlot(EquipLocation.Boots) as ArmorConfig;
        var Gloves = equipment.GetItemInSlot(EquipLocation.Gloves) as ArmorConfig;
        var Necklace = equipment.GetItemInSlot(EquipLocation.Necklace) as ArmorConfig;
        if (Helmet == null) EquipArmor(defaultarmorHelmet);        
        else EquipArmor(Helmet);
        if (Body == null) EquipArmor(defaultarmorBody);
        else EquipArmor(Body);
        if (Trousers == null) EquipArmor(defaultarmorTrousers);
        else EquipArmor(Trousers);
        if (Boots == null) EquipArmor(defaultarmorBoots);
        else EquipArmor(Boots);
        if (Gloves == null) EquipArmor(defaultarmorGloves);
        else EquipArmor(Gloves);
        if (Necklace == null) EquipArmor(defaultarmorNecklace);
        else EquipArmor(Necklace);
       
    }
    //armor
}

Letā€™s modify this a bit to see what information weā€™re getting from the FindByName

        private Transform FindByName(Transform slot, string nameToFind)
        {
            Debug.Log($"{name} removing existing {nameToFind} from {slot}");
            foreach (Transform child in slot)
            {
                Debug.Log($"Testing child {child.name} against {nameToFind}");
                if (child.name == nameToFind) return child;
            }
            return null;
        }

If you install a different helmet, does the old one remain with the new one?

It occurs to me, we may not even be attempting to unequip the item in the first place if it is dragged awayā€¦
With the weapon, if you drag away the sword, then the default weapon of Unarmored is generally spawned in itā€™s placeā€¦ thatā€™s a WeaponConfig with a blank GameObject (just the Weapon script, no model).

Are you checking to see if the slot is now empty, and if so, equipping an ā€œunarmoredā€/empty WeaponConfg?

1 Like

No, the old one is destroy, the new one is spawn.
Now i will try checking.

So hereā€™s what I believe is happeningā€¦ if your default equipment is null, then nothing is telling the old armor (or weapon) to destroy itselfā€¦
For each armor slot, create an Unarmored type ArmorConfigā€¦ So an UnarmoredHelment, UnarmoredNecklace, UnarmoredBody, etc.
In those Unarmored configs, fill everything out as far as ArmorVariant, and fill the needed Armor fields with Armor Prefabs with no models in them. You wonā€™t need the models for this.

Then in every characterā€™s defaultarmorxxx field, put the appropriate Unarmored ArmorConfig.

Now when you remove a piece of armor, but donā€™t replace it, it should despawn correctly.

1 Like

Privacy & Terms