Armor using Modular Fantasy Hero Characters

Please can you help me with my armor scripts? I have hit a brick wall.

I have successfully managed to adjust the fighter script by creating new Classes for a Shield and Necklace (rather than 1 specific item class as per the video instructions)

Next to do are the Gloves, Boots, Trousers and Body. The rest of the armor will be more difficult, as the skeleton structure is difficult to instantiate assets onto.

My thoughts to get around this were to effectively show / hide child objects based on the Modular Character structure as per below.

image

to do this, I set a string on my BodyConfig file (similar to WeaponConfig) to name the body object I would like to set active.

Then in the fighter script, I have tried to hide all children and show the specified body item.

On the player I then set where the Body transform is (the Male_03_Torso) and the default body item.

image

When I run the game, when I equipt a new body item, it doesnt work, instead showing nothing:

image

I have added some debug logs and I believe this is because the bodyConfig is null. I am unsure why this is as I am setting the location of the parent and I am using the same wording as the name of the child object:

image

My BodyConfig script is:

using UnityEngine;
using RPG.Attributes;
using GameDevTV.Inventories;
using RPG.Stats;
using System.Collections.Generic;

namespace RPG.Combat
{
    [CreateAssetMenu(fileName = "Body", menuName = "Armor/Make New Body", order = 0)]
    public class BodyConfig : EquipableItem, IModifierProvider
    {
        [SerializeField] string childObjectName = "";
        [SerializeField]
        Modifier[] additiveModifiers;
        [SerializeField]
        Modifier[] percentageModifiers;

        public GameObject selectedBody { get; set; }

        [System.Serializable]
        struct Modifier
        {
            public Stat stat;
            public float value;
        }

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

        const string bodyName = "Body";

        public Body Spawn(Transform bodyTransform)
        {
            DestroyOldBody(bodyTransform);
            Body body = null;
            if (!string.IsNullOrEmpty(childObjectName))
            {
                Transform childTransform = bodyTransform.Find(childObjectName);
                if (childTransform != null)
                {
                    body = childTransform.GetComponent<Body>();
                    if (body != null)
                    {
                        body.gameObject.SetActive(true);
                        body.gameObject.name = bodyName;
                    }
                }
            }
            return body;
        }

        private static void DestroyOldBody(Transform bodyTransform)
        {
            Transform oldBody = bodyTransform.Find(bodyName);
            if (oldBody == null)
            {
                oldBody = bodyTransform.Find(bodyName);
            }
            if (oldBody == null) return;
            oldBody.name = "DESTROYING";
            Destroy(oldBody.gameObject);
        }
    }
}

My fighter script is:

using UnityEngine;
using RPG.Movement;
using RPG.Core;
using GameDevTV.Saving;
using RPG.Attributes;
using RPG.Stats;
using System.Collections.Generic;
using GameDevTV.Utils;
using System;
using GameDevTV.Inventories;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] Transform rightHandTransform = null;
        [SerializeField] Transform leftHandTransform = null;
        [SerializeField] WeaponConfig defaultWeapon = null;
        [SerializeField] float autoAttackRange = 4f;
        [SerializeField] Transform bodyTransform = null;
        [SerializeField] BodyConfig defaultBody = null;
        [SerializeField] Transform bootsTransform = null;
        [SerializeField] BootsConfig defaultBoots = null;
        [SerializeField] Transform glovesTransform = null;
        [SerializeField] GlovesConfig defaultGloves = null;
        [SerializeField] Transform helmetTransform = null;
        [SerializeField] HelmetConfig defaultHelmet = null;
        [SerializeField] Transform necklaceTransform = null;
        [SerializeField] NecklaceConfig defaultNecklace = null;
        [SerializeField] Transform shieldTransform = null;
        [SerializeField] ShieldConfig defaultShield = null;
        [SerializeField] Transform trousersTransform = null;
        [SerializeField] TrousersConfig defaultTrousers = null;

        Health target;
        Equipment equipment;
        float timeSinceLastAttack = Mathf.Infinity;
        WeaponConfig currentWeaponConfig;
        LazyValue<Weapon> currentWeapon;
        BodyConfig currentBodyConfig;
        LazyValue<Body> currentBody;
        BootsConfig currentBootsConfig;
        LazyValue<Boots> currentBoots;
        GlovesConfig currentGlovesConfig;
        LazyValue<Gloves> currentGloves;
        HelmetConfig currentHelmetConfig;
        LazyValue<Helmet> currentHelmet;
        NecklaceConfig currentNecklaceConfig;
        LazyValue<Necklace> currentNecklace;
        ShieldConfig currentShieldConfig;
        LazyValue<Shield> currentShield;
        TrousersConfig currentTrousersConfig;
        LazyValue<Trousers> currentTrousers;

        private void Awake()
        {
            currentWeaponConfig = defaultWeapon;
            currentWeapon = new LazyValue<Weapon>(SetupDefaultWeapon);
            currentBodyConfig = defaultBody;
            currentBody = new LazyValue<Body>(SetupDefaultBody);
            currentBootsConfig = defaultBoots;
            currentBoots = new LazyValue<Boots>(SetupDefaultBoots);
            currentGlovesConfig = defaultGloves;
            currentGloves = new LazyValue<Gloves>(SetupDefaultGloves);
            currentHelmetConfig = defaultHelmet;
            currentHelmet = new LazyValue<Helmet>(SetupDefaultHelmet);
            currentNecklaceConfig = defaultNecklace;
            currentNecklace = new LazyValue<Necklace>(SetupDefaultNecklace);
            currentShieldConfig = defaultShield;
            currentShield = new LazyValue<Shield>(SetupDefaultShield);
            currentTrousersConfig = defaultTrousers;
            currentTrousers = new LazyValue<Trousers>(SetupDefaultTrousers);
            equipment = GetComponent<Equipment>();
            if (equipment)
            {
                equipment.equipmentUpdated += UpdateWeapon;
                equipment.equipmentUpdated += UpdateBody;
                equipment.equipmentUpdated += UpdateBoots;
                equipment.equipmentUpdated += UpdateGloves;
                equipment.equipmentUpdated += UpdateHelmet;
                equipment.equipmentUpdated += UpdateNecklace;
                equipment.equipmentUpdated += UpdateShield;
                equipment.equipmentUpdated += UpdateTrousers;
            }
        }

        private Weapon SetupDefaultWeapon()
        {
            return AttachWeapon(defaultWeapon);
        }

        private Body SetupDefaultBody()
        {
            return AttachBody(defaultBody);
        }

        private Boots SetupDefaultBoots()
        {
            return AttachBoots(defaultBoots);
        }

        private Gloves SetupDefaultGloves()
        {
            return AttachGloves(defaultGloves);
        }

        private Helmet SetupDefaultHelmet()
        {
            return AttachHelmet(defaultHelmet);
        }

        private Necklace SetupDefaultNecklace()
        {
            return AttachNecklace(defaultNecklace);
        }

        private Shield SetupDefaultShield()
        {
            return AttachShield(defaultShield);
        }

        private Trousers SetupDefaultTrousers()
        {
            return AttachTrousers(defaultTrousers);
        }

        private void Start()
        {
            currentWeapon.ForceInit();
            currentBody.ForceInit();
            currentBoots.ForceInit();
            currentGloves.ForceInit();
            currentHelmet.ForceInit();
            currentNecklace.ForceInit();
            currentShield.ForceInit();
            currentTrousers.ForceInit();
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;
            if (target == null) return;
            if (target.IsDead())
            {
                target = FindNewTargetInRange();
                if (target == null) return;
            }

            if (!GetIsInRange(target.transform))
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        public void EquipWeapon(WeaponConfig weapon)
        {
            currentWeaponConfig = weapon;
            currentWeapon.value = AttachWeapon(weapon);
        }

        public void EquipBody(BodyConfig body)
        {
            currentBodyConfig = body;
            currentBody.value = AttachBody(body);
        }

        public void EquipBoots(BootsConfig boots)
        {
            currentBootsConfig = boots;
            currentBoots.value = AttachBoots(boots);
        }

        public void EquipGloves(GlovesConfig gloves)
        {
            currentGlovesConfig = gloves;
            currentGloves.value = AttachGloves(gloves);
        }

        public void EquipHelmet(HelmetConfig helmet)
        {
            currentHelmetConfig = helmet;
            currentHelmet.value = AttachHelmet(helmet);
        }

        public void EquipNecklace(NecklaceConfig necklace)
        {
            currentNecklaceConfig = necklace;
            currentNecklace.value = AttachNecklace(necklace);
        }

        public void EquipShield(ShieldConfig shield)
        {
            currentShieldConfig = shield;
            currentShield.value = AttachShield(shield);
        }

        public void EquipTrousers(TrousersConfig trousers)
        {
            currentTrousersConfig = trousers;
            currentTrousers.value = AttachTrousers(trousers);
        }

        private void UpdateWeapon()
        {
            var weapon = equipment.GetItemInSlot(EquipLocation.Weapon) as WeaponConfig;
            if (weapon == null)
            {
                EquipWeapon(defaultWeapon);
            }
            else
            {
                EquipWeapon(weapon);
            }
        }

        private void UpdateBody()
        {
            var body = equipment.GetItemInSlot(EquipLocation.Body) as BodyConfig;
            if (body == null)
            {
                EquipBody(defaultBody);
            }
            else
            {
                currentBodyConfig = body;
                currentBody.value = AttachBody(body);
                ShowSelectedBody(body);
            }
        }

        private void UpdateBoots()
        {
            var boots = equipment.GetItemInSlot(EquipLocation.Boots) as BootsConfig;
            if (boots == null)
            {
                EquipBoots(defaultBoots);
            }
            else
            {
                EquipBoots(boots);
            }
        }

        private void UpdateGloves()
        {
            var gloves = equipment.GetItemInSlot(EquipLocation.Gloves) as GlovesConfig;
            if (gloves == null)
            {
                EquipGloves(defaultGloves);
            }
            else
            {
                EquipGloves(gloves);
            }
        }

        private void UpdateHelmet()
        {
            var helmet = equipment.GetItemInSlot(EquipLocation.Helmet) as HelmetConfig;
            if (helmet == null)
            {
                EquipHelmet(defaultHelmet);
            }
            else
            {
                EquipHelmet(helmet);
            }
        }

        private void UpdateNecklace()
        {
            var necklace = equipment.GetItemInSlot(EquipLocation.Necklace) as NecklaceConfig;
            if (necklace == null)
            {
                EquipNecklace(defaultNecklace);
            }
            else
            {
                EquipNecklace(necklace);
            }
        }

        private void UpdateShield()
        {
            var shield = equipment.GetItemInSlot(EquipLocation.Shield) as ShieldConfig;
            if (shield == null)
            {
                EquipShield(defaultShield);
            }
            else
            {
                EquipShield(shield);
            }
        }

        private void UpdateTrousers()
        {
            var trousers = equipment.GetItemInSlot(EquipLocation.Trousers) as TrousersConfig;
            if (trousers == null)
            {
                EquipTrousers(defaultTrousers);
            }
            else
            {
                EquipTrousers(trousers);
            }
        }

        private void ShowSelectedBody(BodyConfig bodyConfig)
        {
            // Hide all body objects
            for (int i = 0; i < bodyTransform.childCount; i++)
            {
                bodyTransform.GetChild(i).gameObject.SetActive(false);
                Debug.Log(bodyTransform.GetChild(i));
            }

            // Show the selected body object
            if (bodyConfig != null && bodyConfig.selectedBody != null)
            {
                bodyConfig.selectedBody.SetActive(true);
                Debug.Log(bodyConfig.selectedBody);
            }

            else
            {
                Debug.Log("bodyConfig is null");
            }
        }

        private Weapon AttachWeapon(WeaponConfig weapon)
        {
            Animator animator = GetComponent<Animator>();
            return weapon.Spawn(rightHandTransform, leftHandTransform, animator);
        }

        private Body AttachBody(BodyConfig bodyConfig)
        {
            return bodyConfig.Spawn(bodyTransform);
        }

        private Boots AttachBoots(BootsConfig bootsConfig)
        {
            return bootsConfig.Spawn(bootsTransform);
        }

        private Gloves AttachGloves(GlovesConfig glovesConfig)
        {
            return glovesConfig.Spawn(glovesTransform);
        }

        private Helmet AttachHelmet(HelmetConfig helmetConfig)
        {
            return helmetConfig.Spawn(helmetTransform);
        }

        private Necklace AttachNecklace(NecklaceConfig necklaceConfig)
        {
            return necklaceConfig.Spawn(necklaceTransform);
        }

        private Shield AttachShield(ShieldConfig shieldConfig)
        {
            return shieldConfig.Spawn(shieldTransform);
        }

        private Trousers AttachTrousers(TrousersConfig trousersConfig)
        {
            return trousersConfig.Spawn(trousersTransform);
        }

        public Health GetTarget()
        {
            return target;
        }

        public Transform GetHandTransform(bool isRightHand)
        {
            if (isRightHand)
            {
                return rightHandTransform;
            }
            else
            {
                return leftHandTransform;
            }
        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                // This will trigger the Hit() event.
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private Health FindNewTargetInRange()
        {
            Health best = null;
            float bestDistance = Mathf.Infinity;
            foreach (var candidate in FindAllTargetsInRange())
            {
                float candidateDistance = Vector3.Distance(
                    transform.position, candidate.transform.position);
                if (candidateDistance < bestDistance)
                {
                    best = candidate;
                    bestDistance = candidateDistance;
                }
            }
           return best;
        }

        private IEnumerable<Health> FindAllTargetsInRange()
        {
            RaycastHit[] raycastHits = Physics.SphereCastAll(transform.position,
                                                autoAttackRange, Vector3.up);
            foreach (var hit in raycastHits)
            {
                Health health = hit.transform.GetComponent<Health>();
                if (health == null) continue;
                if (health.IsDead()) continue;
                if (health.gameObject == gameObject) continue;
                yield return health;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        // Animation Event

        void Hit()
        {
            if (target == null) { return; }
            float damage = GetComponent<BaseStats>().GetStat(Stat.Damage);
            BaseStats targetBaseStats = target.GetComponent<BaseStats>();
            if (targetBaseStats != null)
            {
                float defence = targetBaseStats.GetStat(Stat.Defence);
                damage /= 1 + defence / damage;
            }
            if (currentWeapon.value != null)
            {
                currentWeapon.value.OnHit();
            }
            if (currentWeaponConfig.HasProjectile())
            {
                currentWeaponConfig.LaunchProjectile(rightHandTransform, leftHandTransform, target, gameObject, damage);
            }
            else
            {
                target.TakeDamage(gameObject, damage);
            }
        }

        void Shoot()
        {
            Hit();
        }

        private bool GetIsInRange(Transform targetTransform)
        {
            return Vector3.Distance(transform.position, targetTransform.position) < currentWeaponConfig.GetRange();
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            if (!GetComponent<Mover>().CanMoveTo(combatTarget.transform.position) &&
                !GetIsInRange(combatTarget.transform))
            {
                return false;
            }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}

I’m a bit curious about the useage of the DestroyOldBody() since for the most part, we’re using the existing skinned meshes. We don’t want to be destroying those, or it won’t take long to have nothing to wear! Is the Body representing some of the additions like the Backs and Shoulders… The problem here is that these are also skinned meshes (under All_Gender_Parts). Without knowing what the Body class is, it’s hard to diagnose completely, but I think I’m going to do something that will spin your head a bit…

In the FMH configurations, generally speaking the Chest armor consists of the Torso, the shoulder attachments (right and left) and the Arm_Upper Right and left…

For right now, though, we’ll just focus on the Torso
Your bodyTransform is fine in Fighter, so let’s focus on the Spawn, where the magic happens:
Instead of trying to remember Chr_Torso_Male every time, use the final 00/01/02, etc instead… This also gives us an opportunity to create female characters that use the exact same BodyConfigs!

const MaleBody = "Chr_Torso_Male_";
const FemaleBody = "Chr_Torso_Female_";
[Range(0,28)]
[SerializeField] int bodyIndex = 0;

private string ChildObjectName(Transform bodyTransform)
{
     string suffix = bodyIndex.ToString();
     if(bodyIndex<10) suffix = "0"+suffix;
     if(bodyTransform.name.Contains("Female") return FemaleBody + suffix;
     return MaleBody + suffix;
}

//Without knowing enough about Body, I'm going to leave it aside.  Is it needed for sound effects?  If 
//this is the case, it needs to be attached to the skeleton in an appropriate place, not the Skinned mesh 
public void Spawn(Transform bodyTransform)
{
    string bodyToActivate = ChildObjectName(bodyTransform);
    foreach(Transform child in bodyTransform)
    {
         child.gameObject.enabled = child.gameObject.name == bodyToActivate;
    }
}

That’s it. At least as far as spawning the Torso. If you’re also wanting to spawn Arm_Upper, then you’ll need similar logic.
So what did I do here? I simply went through all of the children of Male_03_Torso (or Female_03_Torso) and either enabled or disabled them based on whether or not their name was the same as the created bodyToActivate.

Now all of that being said, this is not the approach I took to using the FMH in Beneath and SpellbornHunter. I made a class responsible for outfitting the character. Whenever it gets an equipmentUpdated event, it polls each EquipableItem to get a list of Categories and specific indexes to turn on (with the assumption that any other item in that category will be turned off), as well as any color changes (material.setColor) needed.

That was a massive undertaking, as this will be, but it took a lot of the responsibility off of Fighter (leaving Fighter only responsible for spawning the Sword and Shield, things that should be attached to the proper skeleton Transform)

thanks for your help.

I have updated the code as follows:

using UnityEngine;
using RPG.Attributes;
using GameDevTV.Inventories;
using RPG.Stats;
using System.Collections.Generic;

namespace RPG.Combat
{
    [CreateAssetMenu(fileName = "Body", menuName = "Armor/Make New Body", order = 0)]

    public class BodyConfig : EquipableItem, IModifierProvider
    {
        [SerializeField]
        Modifier[] additiveModifiers;
        [SerializeField]
        Modifier[] percentageModifiers;
        public GameObject selectedBody { get; set; }

        [System.Serializable]
        struct Modifier
        {
            public Stat stat;
            public float value;
        }

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

        const string MaleBody = "Chr_Torso_Male_";
        const string FemaleBody = "Chr_Torso_Female_";

        [Range(0, 28)]
        [SerializeField] int bodyIndex = 0;
        private string ChildObjectName(Transform bodyTransform)
        {
            string suffix = bodyIndex.ToString();
            if (bodyIndex < 10) suffix = "0" + suffix;
            if (bodyTransform.name.Contains("Female")) return FemaleBody + suffix;
            return MaleBody + suffix;
        }

        public Body Spawn(Transform bodyTransform)
        {
            string bodyToActivate = ChildObjectName(bodyTransform);
            foreach (Transform child in bodyTransform)
            {
                child.gameObject.SetActive(child.gameObject.name == bodyToActivate);
            }
            return bodyTransform.GetComponent<Body>();
        }
    }
}

and fighter:

using UnityEngine;
using RPG.Movement;
using RPG.Core;
using GameDevTV.Saving;
using RPG.Attributes;
using RPG.Stats;
using System.Collections.Generic;
using GameDevTV.Utils;
using System;
using GameDevTV.Inventories;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] Transform rightHandTransform = null;
        [SerializeField] Transform leftHandTransform = null;
        [SerializeField] WeaponConfig defaultWeapon = null;
        [SerializeField] float autoAttackRange = 4f;
        [SerializeField] Transform bodyTransform = null;
        [SerializeField] BodyConfig defaultBody = null;
        [SerializeField] Transform bootsTransform = null;
        [SerializeField] BootsConfig defaultBoots = null;
        [SerializeField] Transform glovesTransform = null;
        [SerializeField] GlovesConfig defaultGloves = null;
        [SerializeField] Transform helmetTransform = null;
        [SerializeField] HelmetConfig defaultHelmet = null;
        [SerializeField] Transform necklaceTransform = null;
        [SerializeField] NecklaceConfig defaultNecklace = null;
        [SerializeField] Transform shieldTransform = null;
        [SerializeField] ShieldConfig defaultShield = null;
        [SerializeField] Transform trousersTransform = null;
        [SerializeField] TrousersConfig defaultTrousers = null;

        Health target;
        Equipment equipment;
        float timeSinceLastAttack = Mathf.Infinity;
        WeaponConfig currentWeaponConfig;
        LazyValue<Weapon> currentWeapon;
        BodyConfig currentBodyConfig;
        LazyValue<Body> currentBody;
        BootsConfig currentBootsConfig;
        LazyValue<Boots> currentBoots;
        GlovesConfig currentGlovesConfig;
        LazyValue<Gloves> currentGloves;
        HelmetConfig currentHelmetConfig;
        LazyValue<Helmet> currentHelmet;
        NecklaceConfig currentNecklaceConfig;
        LazyValue<Necklace> currentNecklace;
        ShieldConfig currentShieldConfig;
        LazyValue<Shield> currentShield;
        TrousersConfig currentTrousersConfig;
        LazyValue<Trousers> currentTrousers;

        private void Awake()
        {
            currentWeaponConfig = defaultWeapon;
            currentWeapon = new LazyValue<Weapon>(SetupDefaultWeapon);
            currentBodyConfig = defaultBody;
            currentBody = new LazyValue<Body>(SetupDefaultBody);
            currentBootsConfig = defaultBoots;
            currentBoots = new LazyValue<Boots>(SetupDefaultBoots);
            currentGlovesConfig = defaultGloves;
            currentGloves = new LazyValue<Gloves>(SetupDefaultGloves);
            currentHelmetConfig = defaultHelmet;
            currentHelmet = new LazyValue<Helmet>(SetupDefaultHelmet);
            currentNecklaceConfig = defaultNecklace;
            currentNecklace = new LazyValue<Necklace>(SetupDefaultNecklace);
            currentShieldConfig = defaultShield;
            currentShield = new LazyValue<Shield>(SetupDefaultShield);
            currentTrousersConfig = defaultTrousers;
            currentTrousers = new LazyValue<Trousers>(SetupDefaultTrousers);
            equipment = GetComponent<Equipment>();
            if (equipment)
            {
                equipment.equipmentUpdated += UpdateWeapon;
                equipment.equipmentUpdated += UpdateBody;
                equipment.equipmentUpdated += UpdateBoots;
                equipment.equipmentUpdated += UpdateGloves;
                equipment.equipmentUpdated += UpdateHelmet;
                equipment.equipmentUpdated += UpdateNecklace;
                equipment.equipmentUpdated += UpdateShield;
                equipment.equipmentUpdated += UpdateTrousers;
            }
        }

        private Weapon SetupDefaultWeapon()
        {
            return AttachWeapon(defaultWeapon);
        }

        private Body SetupDefaultBody()
        {
            return AttachBody(defaultBody);
        }

        private Boots SetupDefaultBoots()
        {
            return AttachBoots(defaultBoots);
        }

        private Gloves SetupDefaultGloves()
        {
            return AttachGloves(defaultGloves);
        }

        private Helmet SetupDefaultHelmet()

        {
            return AttachHelmet(defaultHelmet);
        }

        private Necklace SetupDefaultNecklace()
        {
            return AttachNecklace(defaultNecklace);
        }

        private Shield SetupDefaultShield()
        {
            return AttachShield(defaultShield);
        }

        private Trousers SetupDefaultTrousers()
        {
            return AttachTrousers(defaultTrousers);
        }

        private void Start()
        {
            currentWeapon.ForceInit();
            currentBody.ForceInit();
            currentBoots.ForceInit();
            currentGloves.ForceInit();
            currentHelmet.ForceInit();
            currentNecklace.ForceInit();
            currentShield.ForceInit();
            currentTrousers.ForceInit();
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;
            if (target == null) return;
            if (target.IsDead())
            {
                target = FindNewTargetInRange();
                if (target == null) return;
            }

            if (!GetIsInRange(target.transform))
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        public void EquipWeapon(WeaponConfig weapon)
        {
            currentWeaponConfig = weapon;
            currentWeapon.value = AttachWeapon(weapon);
        }

        public void EquipBody(BodyConfig body)
        {
            currentBodyConfig = body;
            currentBody.value = AttachBody(body);
        }

        public void EquipBoots(BootsConfig boots)
        {
            currentBootsConfig = boots;
            currentBoots.value = AttachBoots(boots);
        }

        public void EquipGloves(GlovesConfig gloves)
        {
            currentGlovesConfig = gloves;
            currentGloves.value = AttachGloves(gloves);
        }

        public void EquipHelmet(HelmetConfig helmet)
        {
            currentHelmetConfig = helmet;
            currentHelmet.value = AttachHelmet(helmet);
        }

        public void EquipNecklace(NecklaceConfig necklace)
        {
            currentNecklaceConfig = necklace;
            currentNecklace.value = AttachNecklace(necklace);
        }

        public void EquipShield(ShieldConfig shield)
        {
            currentShieldConfig = shield;
            currentShield.value = AttachShield(shield);
        }

        public void EquipTrousers(TrousersConfig trousers)
        {
            currentTrousersConfig = trousers;
            currentTrousers.value = AttachTrousers(trousers);
        }

        private void UpdateWeapon()
        {
            var weapon = equipment.GetItemInSlot(EquipLocation.Weapon) as WeaponConfig;
            if (weapon == null)
            {
                EquipWeapon(defaultWeapon);
            }
            else
            {
                EquipWeapon(weapon);
            }
        }

        private void UpdateBody()
        {
            var body = equipment.GetItemInSlot(EquipLocation.Body) as BodyConfig;
            if (body == null)
            {
                EquipBody(defaultBody);
            }

            else
            {
                currentBodyConfig = body;
                currentBody.value = AttachBody(body);
                ShowSelectedBody(body);
            }
        }

        private void UpdateBoots()
        {
            var boots = equipment.GetItemInSlot(EquipLocation.Boots) as BootsConfig;
            if (boots == null)
            {
                EquipBoots(defaultBoots);
            }
            else
            {
                EquipBoots(boots);
            }
        }

        private void UpdateGloves()
        {
            var gloves = equipment.GetItemInSlot(EquipLocation.Gloves) as GlovesConfig;
            if (gloves == null)
            {
                EquipGloves(defaultGloves);
            }
            else
            {
                EquipGloves(gloves);
            }
        }

        private void UpdateHelmet()
        {
            var helmet = equipment.GetItemInSlot(EquipLocation.Helmet) as HelmetConfig;
            if (helmet == null)
            {
                EquipHelmet(defaultHelmet);
            }
            else
            {
                EquipHelmet(helmet);
            }
        }

        private void UpdateNecklace()
        {
            var necklace = equipment.GetItemInSlot(EquipLocation.Necklace) as NecklaceConfig;
            if (necklace == null)
            {
                EquipNecklace(defaultNecklace);
            }
            else
            {
                EquipNecklace(necklace);
            }
        }

        private void UpdateShield()
        {
            var shield = equipment.GetItemInSlot(EquipLocation.Shield) as ShieldConfig;
            if (shield == null)
            {
                EquipShield(defaultShield);
            }
            else
            {
                EquipShield(shield);
            }
        }

        private void UpdateTrousers()
        {
            var trousers = equipment.GetItemInSlot(EquipLocation.Trousers) as TrousersConfig;
            if (trousers == null)
            {
                EquipTrousers(defaultTrousers);
            }
            else
            {
                EquipTrousers(trousers);
            }
        }

        private void ShowSelectedBody(BodyConfig bodyConfig)
        {
            // Hide all body objects
            for (int i = 0; i < bodyTransform.childCount; i++)
            {
                bodyTransform.GetChild(i).gameObject.SetActive(false);
                Debug.Log(bodyTransform.GetChild(i));
            }

            // Show the selected body object

            if (bodyConfig != null && bodyConfig.selectedBody != null)

            {
                bodyConfig.selectedBody.SetActive(true);
                Debug.Log(bodyConfig.selectedBody);
            }
            else
            {
                Debug.Log("bodyConfig is null");
            }
        }

        private Weapon AttachWeapon(WeaponConfig weapon)
        {
            Animator animator = GetComponent<Animator>();
            return weapon.Spawn(rightHandTransform, leftHandTransform, animator);
        }

        private Body AttachBody(BodyConfig bodyConfig)
        {
            return bodyConfig.Spawn(bodyTransform);
        }

        private Boots AttachBoots(BootsConfig bootsConfig)
        {
            return bootsConfig.Spawn(bootsTransform);
        }

        private Gloves AttachGloves(GlovesConfig glovesConfig)
        {
            return glovesConfig.Spawn(glovesTransform);
        }

        private Helmet AttachHelmet(HelmetConfig helmetConfig)
        {
            return helmetConfig.Spawn(helmetTransform);
        }

        private Necklace AttachNecklace(NecklaceConfig necklaceConfig)
        {
            return necklaceConfig.Spawn(necklaceTransform);
        }

        private Shield AttachShield(ShieldConfig shieldConfig)

        {
            return shieldConfig.Spawn(shieldTransform);
        }

        private Trousers AttachTrousers(TrousersConfig trousersConfig)
        {
            return trousersConfig.Spawn(trousersTransform);
        }

        public Health GetTarget()
        {
            return target;
        }

        public Transform GetHandTransform(bool isRightHand)
        {
            if (isRightHand)
            {
                return rightHandTransform;
            }
            else
            {
                return leftHandTransform;
            }
        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                // This will trigger the Hit() event.
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private Health FindNewTargetInRange()
        {
            Health best = null;
            float bestDistance = Mathf.Infinity;
            foreach (var candidate in FindAllTargetsInRange())
            {
                float candidateDistance = Vector3.Distance(
                    transform.position, candidate.transform.position);
                if (candidateDistance < bestDistance)
                {
                    best = candidate;
                    bestDistance = candidateDistance;
                }
            }
            return best;
        }

        private IEnumerable<Health> FindAllTargetsInRange()
        {
            RaycastHit[] raycastHits = Physics.SphereCastAll(transform.position,
                                                autoAttackRange, Vector3.up);
            foreach (var hit in raycastHits)
            {
                Health health = hit.transform.GetComponent<Health>();
                if (health == null) continue;
                if (health.IsDead()) continue;
                if (health.gameObject == gameObject) continue;
                yield return health;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        // Animation Event

        void Hit()
        {
            if (target == null) { return; }
            float damage = GetComponent<BaseStats>().GetStat(Stat.Damage);
            BaseStats targetBaseStats = target.GetComponent<BaseStats>();
            if (targetBaseStats != null)
            {
                float defence = targetBaseStats.GetStat(Stat.Defence);
                damage /= 1 + defence / damage;
            }
            if (currentWeapon.value != null)
            {
                currentWeapon.value.OnHit();
            }
            if (currentWeaponConfig.HasProjectile())
            {
                currentWeaponConfig.LaunchProjectile(rightHandTransform, leftHandTransform, target, gameObject, damage);
            }
            else
            {
                target.TakeDamage(gameObject, damage);
            }
        }

        void Shoot()
        {
            Hit();
        }

        private bool GetIsInRange(Transform targetTransform)
        {
            return Vector3.Distance(transform.position, targetTransform.position) < currentWeaponConfig.GetRange();
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            if (!GetComponent<Mover>().CanMoveTo(combatTarget.transform.position) &&
                !GetIsInRange(combatTarget.transform))
            {
                return false;
            }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}

This now works when the game is loaded - the players default outfit is the 0X number set in the unarmed body.

When I change the body piece, I am getting the “bodyConfig is null” debug log and the body piece is missing again.

Potentially it might be worth just following a tutorial to implement the system you used? Is this detailed anywhere / can you please share a link?

You might take a look at how I reformatted the code in your prior post… start with a line by itself with three backwards apostrophes (the ones right next to the 1 on your keyboard, like this
```
SomeCode();
```
becomes

SomeCode();

and retains the original formatting of the code.

1 Like

Can you post the text of the Null Reference Error?

It might, but it’s so specific to a $80.00 product that it hasn’t been a priority to make a tutorial…
Here’s the relevant scripts. It looks fairly complex but ultimately is fairly simple.

SyntyEquipableObject.cs -- Equipment with defnitions for building/coloring armor
using System.Collections.Generic;
using System.Linq;
using GameDevTV.Inventories;
using UnityEditor;
using UnityEngine;

namespace TkrainDesigns.Inventories
{
    [CreateAssetMenu(menuName = "Equipment/SyntyEquipableItem", fileName = "SyntyEquipableItem", order = 0)]
    public class SyntyEquipableItem : StatsEquipableItem
    {
        [System.Serializable]
        public class ItemPair
        {
            public string category = "";
            public int index;
        }

        [System.Serializable]
        public class ItemColor
        {
            public string category = "";
            public Color color = Color.magenta;
        }

        [Header("The name of the object in the Modular Characters Prefab representing this item.")] [SerializeField]
        List<ItemPair> objectsToActivate = new List<ItemPair>();

        [SerializeField] List<ItemColor> colorChanges = new List<ItemColor>();

        [Header("Slot Categories to deactivate when this item is activated.")] [SerializeField]
        List<string> slotsToDeactivate = new List<string>();

        public List<string> SlotsToDeactivate => slotsToDeactivate;
        public List<ItemPair> ObjectsToActivate => objectsToActivate;
        public List<ItemColor> ColorChangers => colorChanges;
    }
}
SyntyStatics.cs -- pre defined constants to make Editors and the Generator function correctly
using UnityEngine;

public static class SyntyStatics
{
    public const string HairColor = "_Color_Hair";
    public const string SkinColor = "_Color_Skin";
    public const string PrimaryColor = "_Color_Primary";
    public const string SecondaryColor = "_Color_Secondary";
    public const string LeatherPrimaryColor = "_Color_Leather_Primary";
    public const string LeatherSecondaryColor = "_Color_Leather_Secondary";
    public const string MetalPrimaryColor = "_Color_Metal_Primary";
    public const string MetalSecondaryColor = "_Color_Metal_Secondary";
    public const string MetalDarkColor = "_Color_Metal_Dark";
    public static readonly string HeadCoverings_Base_Hair = "HeadCoverings_Base_Hair";
    public static readonly string HeadCoverings_No_FacialHair = "HeadCoverings_No_FacialHair";
    public static readonly string HeadCoverings_No_Hair = "HeadCoverings_No_Hair";
    public static readonly string All_01_Hair = "All_01_Hair";
    public static readonly string Helmet = "Helmet";
    public static readonly string Back_Attachment = "All_04_Back_Attachment";
    public static readonly string Shoulder_Attachment_Right = "All_05_Shoulder_Attachment_Right";
    public static readonly string Shoulder_Attachment_Left = "All_06_Shoulder_Attachment_Left";
    public static readonly string Elbow_Attachment_Right = "All_07_Elbow_Attachment_Right";
    public static readonly string Elbow_Attachment_Left = "All_08_Elbow_Attachment_Left";
    public static readonly string Hips_Attachment = "All_09_HipsAttachment";
    public static readonly string Knee_Attachment_Right = "All_10_Knee_Attachement_Right";
    public static readonly string Knee_Attachment_Left = "All_11_Knee_Attachement_Left";
    public static readonly string Elf_Ear = "Elf_Ear";

    public static readonly string[] AllGenderBodyParts = new string[]
                                                         {
                                                             "HeadCoverings_Base_Hair",
                                                             "HeadCoverings_No_FacialHair",
                                                             "HeadCoverings_No_Hair",
                                                             "All_01_Hair",
                                                             "Helmet",
                                                             "All_04_Back_Attachment",
                                                             "All_05_Shoulder_Attachment_Right",
                                                             "All_06_Shoulder_Attachment_Left",
                                                             "All_07_Elbow_Attachment_Right",
                                                             "All_08_Elbow_Attachment_Left",
                                                             "All_09_Hips_Attachment",
                                                             "All_10_Knee_Attachement_Right",
                                                             "All_11_Knee_Attachement_Left",
                                                             "Elf_Ear"
                                                         };

    public static readonly string Female_Head_All_Elements = "Female_Head_All_Elements";
    public static readonly string Female_Head_NoElements = "Female_Head_No_Elements";
    public static readonly string Female_Eyebrows = "Female_01_Eyebrows";
    public static readonly string Female_Torso = "Female_03_Torso";
    public static readonly string Female_Arm_Upper_Right = "Female_04_Arm_Upper_Right";
    public static readonly string Female_Arm_Upper_Left = "Female_05_Arm_Upper_Left";
    public static readonly string Female_Arm_Lower_Right = "Female_06_Arm_Lower_Right";
    public static readonly string Female_Arm_Lower_Left = "Female_07_Arm_Lower_Left";
    public static readonly string Female_Hand_Right = "Female_08_Hand_Right";
    public static readonly string Female_Hand_Left = "Female_09_Hand_Left";
    public static readonly string Female_Hips = "Female_10_Hips";
    public static readonly string Female_Leg_Right = "Female_11_Leg_Right";
    public static readonly string Female_Leg_Left = "Female_12_Leg_Left";

    public static readonly string[] FemaleBodyCategories = new string[]
                                                           {
                                                               "Female_Head_All_Elements",
                                                               "Female_Head_No_Elements",
                                                               "Female_01_Eyebrows",
                                                               "Female_03_Torso",
                                                               "Female_04_Arm_Upper_Right",
                                                               "Female_05_Arm_Upper_Left",
                                                               "Female_06_Arm_Lower_Right",
                                                               "Female_07_Arm_Lower_Left",
                                                               "Female_08_Hand_Right",
                                                               "Female_09_Hand_Left",
                                                               "Female_10_Hips",
                                                               "Female_11_Leg_Right",
                                                               "Female_12_Leg_Left",
                                                           };

    public static readonly string Male_Head_All_Elements = "Male_Head_All_Elements";
    public static readonly string Male_Head_No_Elements = "Male_Head_No_Elements";
    public static readonly string Male_Eyebrows = "Male_01_Eyebrows";
    public static readonly string Male_FacialHair = "Male_02_FacialHair";
    public static readonly string Male_Torso = "Male_03_Torso";
    public static readonly string Male_Arm_Upper_Right = "Male_04_Arm_Upper_Right";
    public static readonly string Male_Arm_Upper_Left = "Male_05_Arm_Upper_Left";
    public static readonly string Male_Arm_Lower_Right = "Male_06_Arm_Lower_Right";
    public static readonly string Male_Arm_Lower_Left = "Male_07_Arm_Lower_Left";
    public static readonly string Male_Hand_Right = "Male_08_Hand_Right";
    public static readonly string Male_Hand_Left = "Male_09_Hand_Left";
    public static readonly string Male_Hips = "Male_10_Hips";
    public static readonly string Male_Leg_Right = "Male_11_Leg_Right";
    public static readonly string Male_Leg_Left = "Male_12_Leg_Left";

    public static readonly string[] MaleBodyCategories = new string[]
                                                         {
                                                             "Male_Head_All_Elements",
                                                             "Male_Head_No_Elements",
                                                             "Male_01_Eyebrows",
                                                             "Male_02_FacialHair",
                                                             "Male_03_Torso",
                                                             "Male_04_Arm_Upper_Right",
                                                             "Male_05_Arm_Upper_Left",
                                                             "Male_06_Arm_Lower_Right",
                                                             "Male_07_Arm_Lower_Left",
                                                             "Male_08_Hand_Right",
                                                             "Male_09_Hand_Left",
                                                             "Male_10_Hips",
                                                             "Male_11_Leg_Right",
                                                             "Male_12_Leg_Left",
                                                         };

    public static string[] GearColors = new string[]
                                        {
                                            PrimaryColor,
                                            SecondaryColor,
                                            LeatherPrimaryColor,
                                            LeatherSecondaryColor,
                                            MetalPrimaryColor,
                                            MetalSecondaryColor,
                                            MetalDarkColor
                                        };

    public static Color[] primary =
    {
        new Color(0.2862745f, 0.4f, 0.4941177f), new Color(0.4392157f, 0.1960784f, 0.172549f),
        new Color(0.3529412f, 0.3803922f, 0.2705882f), new Color(0.682353f, 0.4392157f, 0.2196079f),
        new Color(0.4313726f, 0.2313726f, 0.2705882f), new Color(0.5921569f, 0.4941177f, 0.2588235f),
        new Color(0.482353f, 0.4156863f, 0.3529412f), new Color(0.2352941f, 0.2352941f, 0.2352941f),
        new Color(0.2313726f, 0.4313726f, 0.4156863f), Color.magenta, Color.blue, Color.green, Color.red,
        new Color32(165, 42, 42, 255), new Color32(255, 165, 0, 255)
    };

    public static Color[] secondary =
    {
        new Color(0.7019608f, 0.6235294f, 0.4666667f), new Color(0.7372549f, 0.7372549f, 0.7372549f),
        new Color(0.1647059f, 0.1647059f, 0.1647059f), new Color(0.2392157f, 0.2509804f, 0.1882353f), Color.magenta,
        Color.blue, Color.green, Color.red, new Color32(165, 42, 42, 255), new Color32(255, 165, 0, 255)
    };

    public static Color[] metalPrimary =
    {
        new Color(0.6705883f, 0.6705883f, 0.6705883f), new Color(0.5568628f, 0.5960785f, 0.6392157f),
        new Color(0.5568628f, 0.6235294f, 0.6f), new Color(0.6313726f, 0.6196079f, 0.5568628f),
        new Color(0.6980392f, 0.6509804f, 0.6196079f), Color.magenta, Color.blue, Color.green, Color.red,
        new Color32(165, 42, 42, 255), new Color32(255, 165, 0, 255)
    };

    public static Color[] metalSecondary =
    {
        new Color(0.3921569f, 0.4039216f, 0.4117647f), new Color(0.4784314f, 0.5176471f, 0.5450981f),
        new Color(0.3764706f, 0.3607843f, 0.3372549f), new Color(0.3254902f, 0.3764706f, 0.3372549f),
        new Color(0.4f, 0.4039216f, 0.3568628f), Color.magenta, Color.blue, Color.green, Color.red,
        new Color32(165, 42, 42, 255), new Color32(255, 165, 0, 255)
    };

    public static Color[] metalDark =
    {
        new Color32(0x2D, 0x32, 0x37, 0xff), new Color32(0x1D, 0x22, 0x27, 0xff),
        new Color32(0x50, 0x50, 0x50, 0xff), new Color32(0x90, 0x90, 0x90, 0xff),
        new Color32(0x0c, 0x37, 0x63, 0xff), new Color32(0x63, 0x37, 0x0c, 0xff),
        new Color32(0x0c, 0x63, 0x37, 0xff), Color.magenta, Color.blue, Color.green, Color.red,
        new Color32(165, 42, 42, 255), new Color32(255, 165, 0, 255)
    };

    public static Color[] leatherPrimary =
    {
        new Color(0.282353f, 0.2078432f, 0.1647059f), new Color(0.342549f, 0.3094118f, 0.2384314f),
        new Color32(0x4D, 0x2C, 0x18, 0xFF), Color.magenta, Color.blue, Color.green, Color.red,
        new Color32(165, 42, 42, 255), new Color32(255, 165, 0, 255)
    };

    public static Color[] leatherSecondary =
    {
        new Color(0.372549f, 0.3294118f, 0.2784314f), new Color(0.5566038f, 0.4759794f, 0.4759794f), Color.magenta,
        Color.blue, Color.green, Color.red, new Color32(165, 42, 42, 255), new Color32(255, 165, 0, 255)
    };

    public static Color[] skinColors =
    {
        new Color(1f, 0.8000001f, 0.682353f), new Color(0.8196079f, 0.6352941f, 0.4588236f),
        new Color(0.5647059f, 0.4078432f, 0.3137255f)
    };

    public static Color[] hairColors =
    {
        new Color(0.3098039f, 0.254902f, 0.1764706f), new Color(0.1764706f, 0.1686275f, 0.1686275f),
        new Color(0.8313726f, 0.6235294f, 0.3607843f), new Color(0.9339623f, 0.3688644f, 0.06608222f), Color.magenta,
        Color.blue, Color.green, Color.red
    };

    public static Color whiteScar = new Color(0.9294118f, 0.6862745f, 0.5921569f);
    public static Color brownScar = new Color(0.6980392f, 0.5450981f, 0.4f);
    public static Color blackScar = new Color(0.4235294f, 0.3176471f, 0.282353f);
    public static Color elfScar = new Color(0.8745099f, 0.6588235f, 0.6313726f);

    public static Color[] bodyArt =
    {
        new Color(0.0509804f, 0.6745098f, 0.9843138f), new Color(0.7215686f, 0.2666667f, 0.2666667f),
        new Color(0.3058824f, 0.7215686f, 0.6862745f), new Color(0.9254903f, 0.882353f, 0.8509805f),
        new Color(0.3098039f, 0.7058824f, 0.3137255f), new Color(0.5294118f, 0.3098039f, 0.6470588f),
        new Color(0.8666667f, 0.7764707f, 0.254902f), new Color(0.2392157f, 0.4588236f, 0.8156863f)
    };

    public enum Gender
    {
        Male,
        Female
    }

    public enum Race
    {
        Human,
        Elf
    }

    public static Color GetColor(string parameterString, int value)
    {
        Color colorToSet;
        switch (parameterString)
        {
            case SyntyStatics.LeatherPrimaryColor:
                colorToSet = SyntyStatics.leatherPrimary[value];
                break;
            case SyntyStatics.LeatherSecondaryColor:
                colorToSet = SyntyStatics.leatherSecondary[value];
                break;
            case SyntyStatics.MetalPrimaryColor:
                colorToSet = SyntyStatics.metalPrimary[value];
                break;
            case SyntyStatics.MetalSecondaryColor:
                colorToSet = SyntyStatics.metalSecondary[value];
                break;
            case SyntyStatics.PrimaryColor:
                colorToSet = SyntyStatics.primary[value];
                break;
            case SyntyStatics.SecondaryColor:
                colorToSet = SyntyStatics.secondary[value];
                break;
            case SyntyStatics.MetalDarkColor:
                colorToSet = SyntyStatics.metalDark[value];
                break;
            default:
                colorToSet = Color.white;
                break;
        }

        return colorToSet;
    }

    public static int GetColorCount(string parameterString)
    {
        int cycleValue;
        switch (parameterString)
        {
            case SyntyStatics.LeatherPrimaryColor:
                cycleValue = SyntyStatics.leatherPrimary.Length;
                break;
            case SyntyStatics.LeatherSecondaryColor:
                cycleValue = SyntyStatics.leatherSecondary.Length;
                break;
            case SyntyStatics.MetalPrimaryColor:
                cycleValue = SyntyStatics.metalPrimary.Length;
                break;
            case SyntyStatics.MetalSecondaryColor:
                cycleValue = SyntyStatics.metalSecondary.Length;
                break;
            case SyntyStatics.PrimaryColor:
                cycleValue = SyntyStatics.primary.Length;
                break;
            case SyntyStatics.SecondaryColor:
                cycleValue = SyntyStatics.secondary.Length;
                break;
            case SyntyStatics.MetalDarkColor:
                cycleValue = SyntyStatics.metalDark.Length;
                break;
            default:
                cycleValue = 0;
                break;
        }

        return cycleValue;
    }
}
"CharacterGenerator.cs" -- Sits on character, responds to changes in equipment
using System.Collections.Generic;
using System.Linq;
using GameDevTV.Inventories;
using GameDevTV.Saving;
using Newtonsoft.Json.Linq;
using UnityEngine;

namespace TkrainDesigns.Inventories
{
    [DisallowMultipleComponent]
    [RequireComponent(typeof(Equipment))]
    public class CharacterGenerator : MonoBehaviour, ISaveable, IJsonSaveable
    {

    #region Fields

        /// <summary>
        /// A dictionary containing all of the modular parts, organized by category.
        /// Use this catalogue, not the static one when actually customizing the character.
        /// </summary>
        Dictionary<string, List<GameObject>> characterGameObjects;

        /// <summary>
        /// A dictionary containing all of the modular parts, organized by category.
        /// Use this catalogue, not the static one when actually customizing the character.
        /// </summary>
        Dictionary<string, List<GameObject>> CharacterGameObjects
        {
            get
            {
                InitGameObjects(); //This will build the dictionary if it hasn't yet been initialized.
                return characterGameObjects;
            }
        }

        [SerializeField] private bool playerClone = false;
        [SerializeField] SyntyStatics.Gender gender = SyntyStatics.Gender.Male;
        [SerializeField] SyntyStatics.Race race = SyntyStatics.Race.Human;
        [Range(-1, 37)] [SerializeField] int hair = 0;
        [Range(-1, 21)] [SerializeField] int head = 0;
        [Range(-1, 6)] [SerializeField] int eyebrow = 0;
        [Range(-1, 17)] [SerializeField] int facialHair = 0;
        [Range(-1, 27)] [SerializeField] int defaultTorso = 1;
        [Range(-1, 20)] [SerializeField] int defaultUpperArm = 0;
        [Range(-1, 17)] [SerializeField] int defaultLowerArm = 0;
        [Range(-1, 16)] [SerializeField] int defaultHand = 0;
        [Range(-1, 27)] [SerializeField] int defaultHips = 0;
        [Range(-1, 18)] [SerializeField] int defaultLeg = 0;
        public bool isMale => gender == SyntyStatics.Gender.Male;
        [SerializeField] private bool pickup = false;
        public bool isPickup => pickup;

        int hairColor = 0;
        int skinColor = 0;

        Equipment equipment;

    #endregion

    #region Initialization

        void Awake()
        {
            playerClone = (CompareTag("PlayerPreview"));
            equipment = playerClone
                ? GameObject.FindWithTag("Player").GetComponent<Equipment>()
                : GetComponent<Equipment>();
            equipment.equipmentUpdated += LoadArmor;
            LoadDefaultCharacter();
        }

        private void Start()
        {
            LoadDefaultCharacter();
            if (pickup)
            {
                Pickup pickup = GetComponent<Pickup>();
                InventoryItem item = pickup.GetItem();
                if (item is EquipableItem equipable)
                    equipment.AddItem(equipable.GetAllowedEquipLocation(), equipable);
            }
        }

        private void GetConfiguration()
        {
            CharacterGenerator otherGenerator = equipment.GetComponent<CharacterGenerator>();
            gender = otherGenerator.gender;
            race = otherGenerator.race;
            hair = otherGenerator.hair;
            head = otherGenerator.head;
            eyebrow = otherGenerator.eyebrow;
            facialHair = otherGenerator.facialHair;
            defaultTorso = otherGenerator.defaultTorso;
            defaultHand = otherGenerator.defaultHand;
            defaultHips = otherGenerator.defaultHips;
            defaultLeg = otherGenerator.defaultLeg;
            defaultLowerArm = otherGenerator.defaultLowerArm;
            defaultUpperArm = otherGenerator.defaultUpperArm;
        }

        public void InitGameObjects()
        {
            if (characterGameObjects != null) return;
            characterGameObjects = new Dictionary<string, List<GameObject>>();
            BuildCharacterGameObjectFromCatalogue(SyntyStatics.AllGenderBodyParts);
            BuildCharacterGameObjectFromCatalogue(SyntyStatics.MaleBodyCategories);
            BuildCharacterGameObjectFromCatalogue(SyntyStatics.FemaleBodyCategories);
        }

        void BuildCharacterGameObjectFromCatalogue(string[] catalogue)
        {
            foreach (string category in catalogue)
            {
                List<GameObject> list = new List<GameObject>();
                Transform t = GetComponentsInChildren<Transform>().FirstOrDefault(x => x.gameObject.name == category);
                if (t)
                {
                    for (int i = 0; i < t.childCount; i++)
                    {
                        Transform tr = t.GetChild(i);
                        if (tr == t) continue;
                        {
                            list.Add(tr.gameObject);
                            tr.gameObject.SetActive(false);
                        }
                    }

                    characterGameObjects[category] = list;
                }
                else
                {
                    Debug.Log($"BuildFromCatalogue - {name} - has no {category} category!");
                }

                if (characterGameObjects.ContainsKey(category))
                {
                    //Debug.Log($"Category {category}, objects {characterGameObjects[category].Count()}");
                }
            }
        }

    #endregion

    #region Character Generation

        /// <summary>
        /// Should only be called when creating the character or from within RestoreState()
        /// </summary>
        /// <param name="female"></param>
        public void SetGender(bool female)
        {
            gender = female ? SyntyStatics.Gender.Female : SyntyStatics.Gender.Male;
            LoadDefaultCharacter();
        }

        /// <summary>
        /// Should only be called when creating the character or from within RestoreState()
        /// </summary>
        /// <param name="index"></param>
        public void SetHairColor(int index)
        {
            if (index >= 0 && index < SyntyStatics.hairColors.Length)
            {
                hairColor = index;
            }

            SetColorInCategory(SyntyStatics.All_01_Hair, SyntyStatics.HairColor, SyntyStatics.hairColors[hairColor]);
            SetColorInCategory(SyntyStatics.Male_FacialHair, SyntyStatics.HairColor,
                SyntyStatics.hairColors[hairColor]);
            SetColorInCategory(SyntyStatics.Female_Eyebrows, SyntyStatics.HairColor,
                SyntyStatics.hairColors[hairColor]);
            SetColorInCategory(SyntyStatics.Male_Eyebrows, SyntyStatics.HairColor, SyntyStatics.hairColors[hairColor]);
        }

        /// <summary>
        /// Should only be called when creating the character.
        /// </summary>
        /// <param name="index"></param>
        public void CycleHairColor(int index)
        {
            hairColor += index;
            if (hairColor < 0) hairColor = SyntyStatics.hairColors.Length - 1;
            hairColor = hairColor % SyntyStatics.hairColors.Length;
            SetHairColor(hairColor);
        }

        /// <summary>
        /// Should only be called when creating the character.
        /// </summary>
        /// <param name="index"></param>
        public void CycleSkinColor(int index)
        {
            skinColor += index;
            if (skinColor < 0) skinColor += SyntyStatics.skinColors.Length - 1;
            skinColor = skinColor % SyntyStatics.skinColors.Length;
            SetSkinColor(skinColor);
        }

        /// <summary>
        /// Should only be called when creating the character.
        /// </summary>
        /// <param name="index"></param>
        public void CycleHairStyle(int index)
        {
            hair += index;
            if (hair < -1) hair = characterGameObjects[SyntyStatics.All_01_Hair].Count - 1;
            hair %= CharacterGameObjects[SyntyStatics.All_01_Hair].Count;
            ActivateHair(hair);
        }

        /// <summary>
        /// Should only be called when creating the character.
        /// </summary>
        /// <param name="index"></param>
        public void CycleFacialHair(int index)
        {
            facialHair += index;
            int maxHair = CharacterGameObjects[SyntyStatics.Male_FacialHair].Count;
            if (facialHair < -1) facialHair = maxHair - 1;
            if (facialHair >= maxHair) facialHair = -1;
            ActivateFacialHair(facialHair);
        }

        /// <summary>
        /// Should only be called when creating the character.
        /// </summary>
        /// <param name="index"></param>
        public void CycleHead(int index)
        {
            head += index;
            if (head < 0) head += CharacterGameObjects[SyntyStatics.Female_Head_All_Elements].Count - 1;
            head %= CharacterGameObjects[SyntyStatics.Female_Head_All_Elements].Count;
            ActivateHead(head);
        }

        /// <summary>
        /// Should only be called when creating the character.
        /// </summary>
        /// <param name="index"></param>
        public void CycleEyebrows(int index)
        {
            eyebrow += index;
            if (eyebrow < 0) eyebrow += CharacterGameObjects[SyntyStatics.Female_Eyebrows].Count - 1;
            eyebrow %= CharacterGameObjects[SyntyStatics.Female_Eyebrows].Count;
            ActivateEyebrows(eyebrow);
        }

        /// <summary>
        /// Should only be called when creating the character.
        /// </summary>
        /// <param name="category"></param>
        /// <param name="shaderVariable"></param>
        /// <param name="colorToSet"></param>
        void SetColorInCategory(string category, string shaderVariable, Color colorToSet)
        {
            if (!CharacterGameObjects.ContainsKey(category)) return;
            foreach (GameObject go in CharacterGameObjects[category])
            {
                Renderer rend = go.GetComponent<Renderer>();
                rend.material.SetColor(shaderVariable, colorToSet);
            }
        }

        /// <summary>
        /// Should only be called when creating the character or from RestoreState
        /// </summary>
        /// <param name="index"></param>
        public void SetSkinColor(int index)
        {
            if (index >= 0 && index < SyntyStatics.skinColors.Length)
            {
                skinColor = index;
            }

            foreach (var pair in CharacterGameObjects)
            {
                SetColorInCategory(pair.Key, "_Color_Skin", SyntyStatics.skinColors[skinColor]);
            }
        }

    #endregion

    #region CharacterActivation

        /// <summary>
        /// This sets the character to the default state, assuming no items in the EquipmentManager. 
        /// </summary>
        public void LoadDefaultCharacter()
        {
            foreach (var pair in CharacterGameObjects)
            {
                foreach (var item in pair.Value)
                {
                    item.SetActive(false);
                }
            }

            if (pickup) return;
            ActivateHair(hair);
            ActivateHead(head);
            ActivateEyebrows(eyebrow);
            ActivateFacialHair(facialHair);
            ActivateTorso(defaultTorso);
            ActivateUpperArm(defaultUpperArm);
            ActivateLowerArm(defaultLowerArm);
            ActivateHand(defaultHand);
            ActivateHips(defaultHips);
            ActivateLeg(defaultLeg);
        }


        public void LoadArmor()
        {
            if (equipment == null) equipment = GetComponent<Equipment>();
            LoadDefaultCharacter();
            foreach (var pair in equipment.EquippedItems)
            {
                if (pair.Value is SyntyEquipableItem item)
                    // Debug.Log(pair.Key.GetDisplayName());
                {
                    foreach (string category in item.SlotsToDeactivate)
                    {
                        DeactivateCategory(category);
                    }

                    var colorChanger = item.ColorChangers;
                    foreach (SyntyEquipableItem.ItemPair itemPair in item.ObjectsToActivate)
                    {
                        //Debug.Log($"{itemPair.category}-{itemPair.index}");
                        switch (itemPair.category)
                        {
                            case "Leg":
                                ActivateLeg(itemPair.index, colorChanger);
                                break;
                            case "Hips":
                                ActivateHips(itemPair.index, colorChanger);
                                break;
                            case "Torso":
                                ActivateTorso(itemPair.index, colorChanger);
                                break;
                            case "UpperArm":
                                ActivateUpperArm(itemPair.index, colorChanger);
                                break;
                            case "LowerArm":
                                ActivateLowerArm(itemPair.index, colorChanger);
                                break;
                            case "Hand":
                                ActivateHand(itemPair.index, colorChanger);
                                break;
                            default:
                                ActivatePart(itemPair.category, itemPair.index, colorChanger);
                                break;
                        }
                    }
                }
            }
        }


        void ActivateLeg(int selector, List<SyntyEquipableItem.ItemColor> colorChanges = null)
        {
            ActivatePart(gender == SyntyStatics.Gender.Male ? SyntyStatics.Male_Leg_Left : SyntyStatics.Female_Leg_Left,
                selector, colorChanges);
            ActivatePart(
                gender == SyntyStatics.Gender.Male ? SyntyStatics.Male_Leg_Right : SyntyStatics.Female_Leg_Right,
                selector, colorChanges);
            DeactivateCategory(isMale ? SyntyStatics.Female_Leg_Left : SyntyStatics.Male_Leg_Left);
            DeactivateCategory(isMale ? SyntyStatics.Female_Leg_Right : SyntyStatics.Male_Leg_Right);
        }

        void ActivateHips(int selector, List<SyntyEquipableItem.ItemColor> colorChanges = null)
        {
            ActivatePart(gender == SyntyStatics.Gender.Male ? SyntyStatics.Male_Hips : SyntyStatics.Female_Hips,
                selector, colorChanges);
            DeactivateCategory(isMale ? SyntyStatics.Female_Hips : SyntyStatics.Male_Hips);
        }

        void ActivateHand(int selector, List<SyntyEquipableItem.ItemColor> colorChanges = null)
        {
            ActivatePart(
                gender == SyntyStatics.Gender.Male ? SyntyStatics.Male_Hand_Right : SyntyStatics.Female_Hand_Right,
                selector, colorChanges);
            ActivatePart(
                gender == SyntyStatics.Gender.Male ? SyntyStatics.Male_Hand_Left : SyntyStatics.Female_Hand_Left,
                selector, colorChanges);
            DeactivateCategory(isMale ? SyntyStatics.Female_Hand_Right : SyntyStatics.Male_Hand_Right);
            DeactivateCategory(isMale ? SyntyStatics.Female_Hand_Left : SyntyStatics.Male_Hand_Left);
        }

        void ActivateLowerArm(int selector, List<SyntyEquipableItem.ItemColor> colorChanges = null)
        {
            ActivatePart(
                gender == SyntyStatics.Gender.Male
                    ? SyntyStatics.Male_Arm_Lower_Right
                    : SyntyStatics.Female_Arm_Lower_Right, selector,
                colorChanges);
            ActivatePart(
                gender == SyntyStatics.Gender.Male
                    ? SyntyStatics.Male_Arm_Lower_Left
                    : SyntyStatics.Female_Arm_Lower_Left, selector,
                colorChanges);
            DeactivateCategory(isMale ? SyntyStatics.Female_Arm_Lower_Right : SyntyStatics.Male_Arm_Lower_Right);
            DeactivateCategory(isMale ? SyntyStatics.Female_Arm_Lower_Left : SyntyStatics.Male_Arm_Lower_Left);
        }

        void ActivateUpperArm(int selector, List<SyntyEquipableItem.ItemColor> colorChanges = null)
        {
            ActivatePart(isMale ? SyntyStatics.Male_Arm_Upper_Right : SyntyStatics.Female_Arm_Upper_Right, selector,
                colorChanges);
            ActivatePart(isMale ? SyntyStatics.Male_Arm_Upper_Left : SyntyStatics.Female_Arm_Upper_Left, selector,
                colorChanges);
            DeactivateCategory(isMale ? SyntyStatics.Female_Arm_Upper_Right : SyntyStatics.Male_Arm_Upper_Right);
            DeactivateCategory(isMale ? SyntyStatics.Female_Arm_Upper_Left : SyntyStatics.Male_Arm_Upper_Left);
        }

        void ActivateTorso(int selector, List<SyntyEquipableItem.ItemColor> colorChanges = null)
        {
            ActivatePart(isMale ? SyntyStatics.Male_Torso : SyntyStatics.Female_Torso, selector, colorChanges);
            DeactivateCategory(isMale ? SyntyStatics.Female_Torso : SyntyStatics.Male_Torso);
        }

        void ActivateFacialHair(int selector)
        {
            if (!isMale)
            {
                DeactivateCategory(SyntyStatics.Male_FacialHair);
                return;
            }

            ActivatePart(SyntyStatics.Male_FacialHair, selector);
        }

        void ActivateEyebrows(int selector)
        {
            ActivatePart(isMale ? SyntyStatics.Male_Eyebrows : SyntyStatics.Female_Eyebrows, selector);
            DeactivateCategory(isMale ? SyntyStatics.Female_Eyebrows : SyntyStatics.Male_Eyebrows);
        }

        void ActivateHead(int selector)
        {
            ActivatePart(isMale ? SyntyStatics.Male_Head_All_Elements : SyntyStatics.Female_Head_All_Elements,
                selector);
            DeactivateCategory(isMale ? SyntyStatics.Female_Head_All_Elements : SyntyStatics.Male_Head_All_Elements);
        }


        void ActivateHair(int selector)
        {
            ActivatePart(SyntyStatics.All_01_Hair, selector);
        }


        private Dictionary<GameObject, Material> materialDict = new Dictionary<GameObject, Material>();

        void ActivatePart(string identifier, int selector, List<SyntyEquipableItem.ItemColor> colorChanges = null)
        {
            if (selector < 0)
            {
                DeactivateCategory(identifier);
                return;
            }

            if (!CharacterGameObjects.ContainsKey(identifier))
            {
                Debug.Log($"{name} - {identifier} not found in dictionary");
                return;
            }

            if ((CharacterGameObjects[identifier].Count < selector))
            {
                Debug.Log($"Index {selector}out of range for {identifier}");
                return;
            }

            DeactivateCategory(identifier);
            GameObject go = CharacterGameObjects[identifier][selector];
            go.SetActive(true);
            if (colorChanges == null) return;
            foreach (var pair in colorChanges)
            {
                SetColor(go, pair.category, pair.color);
            }
        }

        void DeactivateCategory(string identifier)
        {
            if (!CharacterGameObjects.ContainsKey(identifier))
            {
                Debug.LogError($"Category {identifier} not found in database!");
                return;
            }

            foreach (GameObject g in CharacterGameObjects[identifier])
            {
                g.SetActive(false);
            }
        }

    #endregion

    #region StaticDictionary

        /// <summary>
        /// This static dictionary is for a hook for the custom editors for EquipableItem and other Editor windows.
        /// Outside of this, it should not be used as a reference to get/set items on the character because it is
        /// terribly inefficient for this purpose.
        /// </summary>
        static Dictionary<string, List<string>> characterParts;

        public static Dictionary<string, List<string>> CharacterParts
        {
            get
            {
                InitCharacterParts();
                return characterParts;
            }
        }

        public static void InitCharacterParts()
        {
            if (characterParts != null) return;
            GameObject character = Resources.Load<GameObject>("PolyFantasyHeroBase");
            if (character == null) Debug.Log("Unable to find Character!");
            characterParts = new Dictionary<string, List<string>>();
            BuildCategory(SyntyStatics.AllGenderBodyParts, character);
            BuildCategory(SyntyStatics.FemaleBodyCategories, character);
            BuildCategory(SyntyStatics.MaleBodyCategories, character);
            character = null;
        }

        static void BuildCategory(IEnumerable<string> parts, GameObject source)
        {
            foreach (string category in parts)
            {
                List<string> items = new List<string>();
                if (source == null)
                {
                    Debug.Log("Source Not Loaded?");
                }
                else
                {
                    Debug.Log($"Source is {source.name}");
                }

                Debug.Log($"Testing {category}");
                Transform t = source.GetComponentsInChildren<Transform>().First(x => x.gameObject.name == category);
                if (t == null)
                {
                    Debug.Log($"Unable to locate {category}");
                }
                else
                {
                    Debug.Log($"Category {t.name}");
                }

                foreach (Transform tr in t.gameObject.GetComponentsInChildren<Transform>())
                {
                    if (tr == t) continue;
                    GameObject go = tr.gameObject;
                    Debug.Log($"Adding {go.name}");
                    items.Add(go.name);
                }

                characterParts[category] = items;
                Debug.Log(characterParts[category].Count);
            }
        }

    #endregion

    #region ISaveable

        public struct ModularData
        {
            public bool isMale;
            public int hair;
            public int facialHair;
            public int head;
            public int eyebrow;
            public int skinColor;
            public int hairColor;

            public ModularData(bool _isMale, int _hair, int _facialHair, int _head, int _eyebrow, int _skinColor,
                               int _hairColor)
            {
                isMale = _isMale;
                hair = _hair;
                facialHair = _facialHair;
                head = _head;
                eyebrow = _eyebrow;
                skinColor = _skinColor;
                hairColor = _hairColor;
            }
        }

        public JToken CaptureState()
        {
            return JToken.FromObject(new ModularData(isMale, hair, facialHair, head, eyebrow, skinColor, hairColor));
        }

        public JToken CaptureAsJToken()
        {
            return JToken.FromObject(new ModularData(isMale, hair, facialHair, head, eyebrow, skinColor, hairColor));
        }

        public void RestoreState(JToken state)
        {
            equipment.equipmentUpdated -= LoadArmor; //prevent issues
            ModularData data = state.ToObject<ModularData>();
            gender = data.isMale ? SyntyStatics.Gender.Male : SyntyStatics.Gender.Female;
            hair = data.hair;
            facialHair = data.facialHair;
            head = data.head;
            eyebrow = data.eyebrow;
            skinColor = data.skinColor;
            hairColor = data.hairColor;
            SetHairColor(hairColor);
            SetSkinColor(skinColor);
            equipment.equipmentUpdated += LoadArmor;
            Invoke(nameof(LoadArmor), .1f);
        }

        public void RestoreFromJToken(JToken state)
        {
            equipment.equipmentUpdated -= LoadArmor; //prevent issues
            ModularData data = state.ToObject<ModularData>();
            gender = data.isMale ? SyntyStatics.Gender.Male : SyntyStatics.Gender.Female;
            hair = data.hair;
            facialHair = data.facialHair;
            head = data.head;
            eyebrow = data.eyebrow;
            skinColor = data.skinColor;
            hairColor = data.hairColor;
            SetHairColor(hairColor);
            SetSkinColor(skinColor);
            equipment.equipmentUpdated += LoadArmor;
            Invoke(nameof(LoadArmor), .1f);
        }

    #endregion

        /* This section is used by the EquipmentBuilder scene only */

    #region EquipmentBuilder

        public int SetParameter(string parameterString, int value, int i)
        {
            if (!CharacterGameObjects.ContainsKey(parameterString))
                return TryAlternateParameter(parameterString, value, i);
            int available = CharacterGameObjects[parameterString].Count;
            value += i;
            if (value >= available) value = -1;
            else if (value < -1) value = available - 1;
            ActivatePart(parameterString, value);
            return value;
        }

        int TryAlternateParameter(string parameterString, int value, int i)
        {
            switch (parameterString)
            {
                case "Torso":
                    value = CycleValue(SyntyStatics.Male_Torso, value, i);
                    ActivateTorso(value);
                    break;
                case "UpperArm":
                    value = CycleValue(SyntyStatics.Male_Arm_Upper_Left, value, i);
                    ActivateUpperArm(value);
                    break;
                case "LowerArm":
                    value = CycleValue(SyntyStatics.Male_Arm_Lower_Left, value, i);
                    ActivateLowerArm(value);
                    break;
                case "Hand":
                    value = CycleValue(SyntyStatics.Male_Hand_Left, value, i);
                    ActivateHand(value);
                    break;
                case "Hips":
                    value = CycleValue(SyntyStatics.Male_Hips, value, i);
                    ActivateHips(value);
                    break;
                case "Leg":
                    value = CycleValue(SyntyStatics.Male_Leg_Left, value, i);
                    ActivateLeg(value);
                    break;
                default:
                    value = -999;
                    break;
            }

            return value;
        }

        int CycleValue(string parameterString, int value, int i)
        {
            int available = CharacterGameObjects[parameterString].Count;
            value += i + available;
            value %= available;
            return value;
        }

        void SetColor(GameObject item, string parameterString, Color colorToSet)
        {
            //Color colorToSet = Color.white;
            //colorToSet = SyntyStatics.GetColor(parameterString, value);

            {
                Material mat;
                if (materialDict.ContainsKey(item) && materialDict[item] != null)
                {
                    mat = materialDict[item];
                }
                else
                {
                    mat = Instantiate(item.GetComponent<Renderer>().sharedMaterial);
                    item.GetComponent<Renderer>().material = mat;
                    materialDict[item] = mat;
                }

                mat.SetColor(parameterString, colorToSet);
                //item.GetComponent<Renderer>().material.SetColor(parameterString, colorToSet);
            }
        }

        
        

        public int CycleColor(string parameterString, int value, int modifier)
        {
            int cycleValue = 0;
            cycleValue = SyntyStatics.GetColorCount(parameterString);

            value += modifier + cycleValue;
            value %= cycleValue;

            foreach (var itemList in CharacterGameObjects.Values)
            {
                foreach (GameObject item in itemList)
                {
                    SetColor(item, parameterString, Color.white);
                }
            }

            return value;
        }

    #endregion
    }
}

Thanks Brian, I think I will go with your tutorial.

I get a small error in your script that I am unsure how to fix, relating to a Lambda expression:


        public void LoadArmor()
        {
            if (equipment == null) equipment = GetComponent<Equipment>();
            LoadDefaultCharacter();
            foreach (var pair in equipment.EquippedItems)
            {
                if (pair.Value is SyntyEquipableItem item)
                // Debug.Log(pair.Key.GetDisplayName());
                {
                    foreach (string category in item.SlotsToDeactivate)
                    {
                        DeactivateCategory(category);
                    }

                    var colorChanger = item.ColorChangers;
                    foreach (SyntyEquipableItem.ItemPair itemPair in item.ObjectsToActivate)
                    {
                        //Debug.Log($"{itemPair.category}-{itemPair.index}");
                        switch (itemPair.category)
                        {
                            case "Leg":
                                ActivateLeg(itemPair.index, colorChanger);
                                break;
                            case "Hips":
                                ActivateHips(itemPair.index, colorChanger);
                                break;
                            case "Torso":
                                ActivateTorso(itemPair.index, colorChanger);
                                break;
                            case "UpperArm":
                                ActivateUpperArm(itemPair.index, colorChanger);
                                break;
                            case "LowerArm":
                                ActivateLowerArm(itemPair.index, colorChanger);
                                break;
                            case "Hand":
                                ActivateHand(itemPair.index, colorChanger);
                                break;
                            default:
                                ActivatePart(itemPair.category, itemPair.index, colorChanger);
                                break;
                        }
                    }
                }
            }
        }

I get the error The first operand of an ‘is’ or ‘as’ operator may not be a lambda expression, anonymous method, or method group. [Assembly-CSharp]

any ideas? (and thanks for your help so far!)

Thanks Brian, managed to get this to work.

I assume the Character Generator needs to go onto the player?

When I go to start the game I get this error:

image

Thanks for your help so far!

Is that on the line if (pair.Value is SyntyEquipableItem item)? There shouldn’t be a lambda expression there, the pair is a KeyValuePair<EquipLocation, EquipableItem> from the Dictionary. The is expression is just casting the the EquipableItem returned by the Equipment to a SyntyEquipableItem. If it’s not (not all items deserve a costume change, like Rings, for instance), then the method won’t do anything. Unless your Equipment is returning something other than an EquipableItem, perhaps?

The PlayerPreview tag is for a custom editor, because handling the character in an Editor preview window (that little bit at the bottom of the inspector where you can see animations and such has some different handling requirements. For now, just add a tag PlayerPreview and ignore it.

Thanks Brian,

I searched the error on google and it came up with this tutorial (which I think you were involved in) RPG Character Creator & Modular Armor Tutorial - Unity Courses / Show - GameDev.tv

but it has more comments in than the code you shared (I think this must be a more finalised version).

It works perfectly! thanks for all your help,

1 Like

I forgot about that one. That started out with the very scripts I just posted. He did a fantastic job of annotating them and presenting them in a tutorial format.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms