Hello all, I have been hard at work expanding upon the RPG course (mainly the combat and inventory systems thus far), but with the help of @Brian_Trotter I was able to expand the equipment system to include equipping a Shield, Dual-Wielding, and Two-handed weapons and I wanted to share my results on here to help guide anyone who may wish to do so in their own RPG in the future.
So for the Shield, I had to create a separate config similar to the WeaponConfig.cs with 2 scripts Shield.cs & ShieldConfig.cs
Shield.cs
using UnityEngine;
using UnityEngine.Events;
namespace RPG.Combat
{
public class Shield: MonoBehaviour
{
[SerializeField] UnityEvent onHit;
public void OnHit()
{
onHit.Invoke();
}
}
}
ShieldConfig.cs
using System.Collections.Generic;
using UnityEngine;
using GameDevTV.Inventories;
using RPG.Stats;
namespace RPG.Combat
{
[CreateAssetMenu(fileName = "Shield", menuName = "Shields/Make New Shield", order = 0)]
public class ShieldConfig : EquipableItem, IModifierProvider
{
[SerializeField] Shield equippedPrefab;
[SerializeField] bool isRightHanded;
[SerializeField] float defenseBonus = 0;
[SerializeField] float percentageBonus = 0;
const string shieldName = "Shield";
public Shield SpawnEquipableItem(Transform rightHand, Transform leftHand)
{
DestroyOldEquipableItem(rightHand, leftHand);
Shield shield = null;
if (equippedPrefab != null)
{
Transform handTransform = GetTransform(rightHand, leftHand);
shield = Instantiate(equippedPrefab, handTransform);
shield.gameObject.name = shieldName;
}
return shield;
}
public Transform GetTransform(Transform rightHand, Transform leftHand)
{
Transform handTransform;
if (isRightHanded) handTransform = rightHand;
else handTransform = leftHand;
return handTransform;
}
void DestroyOldEquipableItem(Transform rightHand, Transform leftHand)
{
Transform oldWeapon = rightHand.Find(shieldName);
if (oldWeapon == null)
{
oldWeapon = leftHand.Find(shieldName);
}
if (oldWeapon == null) return;
oldWeapon.name = "DESTROYING";
Destroy(oldWeapon.gameObject);
}
//Add Armor to enum in Stats.cs
public IEnumerable<float> GetAdditiveModifiers(Stat stat)
{
if (stat == Stat.Armor)
{
yield return defenseBonus;
}
}
public IEnumerable<float> GetPercentageModifiers(Stat stat)
{
if (stat == Stat.Armor)
{
yield return percentageBonus;
}
}
}
With these, you would be able to create a shield prefab similarly to a weapon prefab in the unity editor.
Note: Don’t forget to add a EquipmentSlot UI that can accept a shield/offhand as an equipable item.
For Dual-wielding and Two-handed weapons, I modified the WeaponConfig.cs for Dual-wielding you would have to duplicate the processes in Spawn() to add a secondary weapon prefab to instantiate. For Two-handed weapons, I added a public enum to differentiate between lefthand, righthand, or both as well as a bool.
WeaponConfig.cs
using System.Collections.Generic;
using UnityEngine;
using RPG.Attributes;
using GameDevTV.Inventories;
using RPG.Stats;
namespace RPG.Combat
{
[CreateAssetMenu(fileName = "Weapon", menuName = "Weapons/Make New Weapon", order = 0)]
public class WeaponConfig : EquipableItem, IModifierProvider
{
[SerializeField] Weapon equippedPrefab = null;
[SerializeField] Weapon equippedPrefab2 = null;
public bool isTwoHanded = false;
public bool isDualWield = false;
[SerializeField] Projectile projectile = null;
[SerializeField] float attackBonus = 1;
public enum WeaponHands { right, left, both }
[SerializeField] WeaponHands mainHand;
const string weaponName = "Weapon";
const string weaponName2 = "Weapon2";
public Weapon Spawn(Transform rightHand, Transform leftHand, Animator animator)
{
DestroyOldWeapon(rightHand, leftHand);
DestroyOtherWeapon(rightHand, leftHand);
Weapon weapon = null;
Weapon weapon2 = null;
if (equippedPrefab != null)
{
Transform handTransform = GetMainHand(rightHand, leftHand);
if (mainHand == WeaponHands.left)
{
weapon = Instantiate(equippedPrefab, handTransform);
}
if (mainHand == WeaponHands.right)
{
weapon = Instantiate(equippedPrefab, handTransform);
}
if (mainHand == WeaponHands.both)
{
weapon = Instantiate(equippedPrefab, rightHand);
if (equippedPrefab2 != null && isDualWield)
{
weapon2 = Instantiate(equippedPrefab2, leftHand);
weapon2.gameObject.name = weaponName2;
}
}
//No changes in Spawn() beyond this point...
}
private void DestroyOtherWeapon(Transform rightHand, Transform leftHand)
{
Transform oldWeapon2 = leftHand.Find(weaponName2);
if (oldWeapon2 == null)
{
oldWeapon2 = rightHand.Find(weaponName2);
}
if (oldWeapon2 == null) return;
oldWeapon2.name = "DESTROYING";
Destroy(oldWeapon2.gameObject);
}
//Replaced GetTransform() with GetMainHand()
private Transform GetMainHand(Transform rightHand, Transform leftHand)
{
Transform handTransform;
if (mainHand == WeaponHands.right) return rightHand;
if (mainHand == WeaponHands.left) return leftHand;
if (mainHand == WeaponHands.both)
{
if (rightHand && !leftHand)
{
return rightHand;
}
if (leftHand && !rightHand)
{
return leftHand;
}
}
return handTransform = rightHand;
}
//No further changes beyond this point...
}
Finally, the changes that need to be made to the Inventory system to accept the shield and other item configs are by modifying MaxAcceptable() in EquipmentSlotUI.cs in order to ensure that you cannot equip a shield while wielding a dual/two-handed weapon and vice-versa.
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 (equipLocation == EquipLocation.Offhand)
{
EquipableItem mainHand = playerEquipment.GetItemInSlot(EquipLocation.Weapon);
if (mainHand is WeaponConfig mainHandConfig)
{
if (mainHandConfig.isTwoHanded) return 0;
}
}
//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;
}
}
Edit: It was brought to my attention by @Maximilien that you will need to go into the Fighter.cs and practically copy the EquipWeapon() methods, but calling the Shield Config instead.
//add these to configurables
ShieldConfig currentShield;
LazyValue<Shield> currentEquippedShield;
private void Awake()
{
currentWeaponConfig = defaultWeapon;
currentWeapon = new LazyValue<Weapon>(SetupDefaultWeapon);
currentShield = defaultShield;
currentEquippedShield = new LazyValue<Shield>(null);
equipment = GetComponent<Equipment>();
if (equipment)
{
equipment.equipmentUpdated += UpdateWeapon;
}
}
Add Underneath EquipWeapon()
public void EquipShield(ShieldConfig shield)
{
currentShield = shield;
currentEquippedShield.value = AttachShield(shield);
}
private Shield AttachShield(ShieldConfig shield)
{
return shield.SpawnEquipableItem(rightHandTransform, leftHandTransform);
}
// use to destroy Shield/offhand prefab when the item is removed
private void DestroyOffHandItem()
{
Shield offHandWeapon = leftHandTransform.GetComponentInChildren<Shield>();
if(offHandWeapon == null)
{
return;
}
DestroyImmediate(offHandWeapon.gameObject);
}
Then Modify UpdateWeapon
private void UpdateWeapon()
{
var weapon = equipment.GetItemInSlot(EquipLocation.Weapon) as WeaponConfig;
var shield = equipment.GetItemInSlot(EquipLocation.Offhand) as ShieldConfig;
if (weapon == null)
{
EquipWeapon(defaultWeapon);
}
else
{
EquipWeapon(weapon);
}
if (shield != null)
{
EquipShield(shield);
}
else
{
DestroyOffHandItem();
}
}
and that should be all that you need to do, hope this helps anyone on their Gamedev journey as they will be able to add more depth with items such as shields and various weapon combinations now available.
Any comments or concerns are appreciated and Thank you for your time and huge Thanks to the Gamedev.TV team for their support and guidance.