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

,

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
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);
       
    }

I tried, logically should replace the base armor… No despawn game object(armor).

And the defaultarmorBody/Helmet/Gloves, etc are all assigned to an ArmorConfig with the appropriate category assigned and an empty armor bject?

1 Like

Thanks, Brian! The reason for the error was simple, I figured it out thanks to you … I will delete old videos except this last one(Or should they not be removed?).
I still have a question, but it does not belong to this topic, should I create another one?
Thanks again! And thanks for your time!

Probably best to create a new topic. we’ve hijacked this one sufficiently, and it’s better if questions are in an Ask category

1 Like

First off, thanks a bunch to Thomas and Brian for this. I’ve gotten this implementation working fairly well at this point, but I need to make adjustments for the goal of my project.

Namely, I’d like to not have to designate specific weapons as “dual wield” but rather allow a weapon tagged as “one-handed” to be equipped in either slot, which will allow for dual wielding with any type of 1 handed weapon.

Also, slightly off-topic but I’m having trouble finding a solution for equipping multiple rings using the same enum value. I’m trying to figure out how to do this without using a Ring (Lefthand) Ring (Righthand) method on the rings themselves.

Here’s a quick markup of what I’m going for

Any tips would be appreciated! Happy to post code, but realistically it looks fairly similar to what Thomas has going.

1 Like

Thank you for your question, I know here in this thread we kinda cheese the equipment system to get the results to appear as a “dual-wield” system. Thankfully I address this in my tutorial for the turn-based game course, which is an attempt to integrate the inventory system from the RPG series here.

I essentially create another EquipLocation in the EquipableItem.cs and then slightly reconfigure the MaxAcceptable() method in the EquipmentSlotUI.cs so that if the slot doesn’t accept the first EquipLocation then it will check for the second Equiplocation and equip the item. So for the Dual wielding, you just need to make a left & right weapon slot location. You will probably need to do the same for the ring slots.

Hope this helps!

1 Like

I think Thomas may be on the right track with this…

I did experiment when the Inventory course first came out with rings… my solution was less than elegant, as I wound up using

public enum EquipPosition
{
     Primary,
     Secondary
}
Dictionary<EquipLocation, Dictionary<EquipPosition, EquipableItem>>

Then storing everything in the Primary unless it was a EquipLocation.Ring or EquipLocation.Trinket (my own design at the time)
This, of course, required some checking for these corner cases.
It’s not unworkable, I just think I executed it poorly 3 years ago. Probably something to explore again, as I’m working on a build from the ground up rework of the Inventory system for a future tutorial.

2 Likes

I’ll check this out, thanks! :slight_smile:

1 Like

Well… I just gave this whole thing a shot. I don’t know about the rest of you guys, but the results for me were terrible, so I deleted it off my code

  • Animations were suddenly flipped
  • I can wield stuff where it wasn’t supposed to be, in my equipment slots
  • Some unequipped stuff was not de-rendered when I took them off my hands
  • My Cursors got a bit messy
  • My equipment limiter (what stops the player from wielding stuff way above his combat level) suddenly stopped working…

These are only the bugs I caught from my own testing. The game was basically a buggy mess, so I’ll slowly retry this whole thing and try again to figure out what went wrong, at one point in time

I got things working regarding the dual wield/shield etc. and rings

That’s after you finished the entire RPG series?

Mm no just core combat and inventory. For your issue with the equipment slots I would double check that the UI slots are set to the correct equipment type in the inspector. Whenever I mess with the equipment type enums those tend to get messed up. I actually have the same issue with the de-rendering weapons, I’ll take a look into that.

After you finish the entire series, the bugs multiply and become a serious problem. I did request @Brian_Trotter to have a professional remake for us regarding this one, now we just have to wait (and probably have a look into it ourselves as well :slight_smile: )

Privacy & Terms