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.
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.
When I run the game, when I equipt a new body item, it doesnt work, instead showing nothing:
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:
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");
}
}
}