With a heavy, bruised ego I beseech advice again. I wanted to implement a few new features not covered (thus far) in the series. After refactoring keenly, I have somehow made a previously functional OnTriggerEnter to forget whom it answers to.
PREAMBLE / TLDR :: When I began this project I was using a UnitManager as an insulated container of common stats and pass through functions. I am just beginning to migrate that class towards BaseClass. It stands for now to avoid sledgehammer refactoring. So, That explains that.
My “ Projectile ” and “ Weapon “ classes are just ugly cousins to those in the course. They do work as intended other than this little time consumer of a headache. You see, I just had to have a throwable spear that can be retrieved and reused. And, an arced trajectory! No girl will ever talk to me if my arrows don’t simulate physics. After seriously crash coursing some trigonometry and calculus, I am now fully convinced that the operands used in both disciplines were taken directly from the Necronomicon. There is itching madness if you stare too long. But! I finally got it to work swimmingly* with some snooping and brain souping.
*It doesn’t work swimmingly.
PROBLEM
The snag points immediately to Ballistics.OnTriggerEnter. When the call to the enemy’s health is made, she nullifies. After that discovery I sprayed and prayed Debug bullets all over.
Code Blocks from CombatManager:
private void ContinueRangedAttack()
{
if (enemy.GetComponent().GetIsDead() == false)
{
Debug.Log(“CombatManager - ContinueRangedAttack() - Nobody dead.”);
equippedWeapon.MarkEnemy(enemy);
Debug.Log("CombatManager - ContinueRangedAttack() - Marked enemy = " + enemy.name.ToString());
firingRangedWeapon = true;
StartCoroutine(FireBow(enemy));
lastAttack = 0;
}
}
On To
IEnumerator FireBow(UnitManager target)
{
Debug.Log(“CombatManager, coroutine fire bow started”);
if (equippedWeapon == null || equippedWeapon.GetIsBow() == false)
{
firingRangedWeapon = false;
yield return null;
}
/* Since the bow fires projectiles and is not a projectile itself
the AttackRange of the bow is used instead of the seperate ProjectileRange
used for how far a weapon can be thrown */
scheduler.StartAction(this);
movement.MoveToDestination(target.transform.position);
float range = equippedWeapon.GetBowRange();
if (Vector3.Distance(transform.position, target.transform.position) < range)
{
firingRangedWeapon = true;
movement.CancelAction();
transform.LookAt(target.transform.position);
handler.OnHandleBowAnimations();
yield return new WaitForSeconds(animationDelay);
equippedWeapon.FireProjectile(target);
/* To avoid repeat fire */
CancelAction();
}
firingRangedWeapon = false;
}
Next up is the Weapon Handler
public class WeaponHandler : ScriptableObject
{
[Header("Weapon Components")]
[SerializeField] AnimatorOverrideController overrideController = null;
[SerializeField] Rigidbody equippedWeapon = null;
[SerializeField] Ballistics projectile = null;
[SerializeField] GameObject hitFX = null;
[Header("Weapon Class")]
[SerializeField] bool ranged = false;
[SerializeField] bool majik = false;
[SerializeField] bool canThrow = false;
[Header("Ranged Attack")]
[SerializeField] float attackRange = 0f;
[SerializeField] float majikRange = 0f;
[SerializeField] float throwRange = 0;
[Header("Damage")]
[SerializeField] int damage = 0;
Transform handTransform = null;
UnitManager enemy;
const string storedWeaponData = "EquippedWeapon";
#region Common Functions
public void MarkEnemy(UnitManager enemyTarget)
{
enemyTarget = enemy;
}
public void DisplayHitFX(Vector3 position)
{
if (hitFX != null)
{
GameObject fx = Instantiate(hitFX, position, Quaternion.identity);
}
}
#endregion Common Funcions
#region Ranged Attack Functions
public void FireProjectile(UnitManager enemy)
{
if (projectile == null)
{
return;
}
if (ranged == false && canThrow == false)
{
return;
}
Ballistics missle = Instantiate(projectile, handTransform.position, Quaternion.identity);
missle.SetHandTarget(handTransform);
missle.TargetEnemy(enemy, damage);
}
#endregion Ranged Attack Functions
#region Arm And Remove Equipment Functions
private Transform GetHandTarget(Transform leftHandTarget, Transform rightHandTarget)
{
Transform selectedTarget;
if (ranged == false)
{
selectedTarget = rightHandTarget;
}
else
{
selectedTarget = leftHandTarget;
}
return selectedTarget;
}
public void EquipWeapon(Transform leftHandTarget, Transform rightHandTarget, Animator animator)
{
ClearWeapon(leftHandTarget, rightHandTarget);
if (equippedWeapon != null)
{
handTransform = GetHandTarget(leftHandTarget, rightHandTarget);
Rigidbody weapon = Instantiate(equippedWeapon, handTransform);
weapon.name = storedWeaponData;
equippedWeapon.useGravity = false;
equippedWeapon.isKinematic = true;
}
AnimatorOverrideController controller =
overrideController.runtimeAnimatorController as AnimatorOverrideController;
if (overrideController != null)
{
animator.runtimeAnimatorController = overrideController;
}
else if (controller != null)
{
animator.runtimeAnimatorController = controller.runtimeAnimatorController;
}
}
private static void ClearWeapon(Transform rightHand, Transform leftHand)
{
Transform equippedWeapon = rightHand.Find(storedWeaponData);
if (equippedWeapon == null)
{
equippedWeapon = leftHand.Find(storedWeaponData);
}
if (equippedWeapon == null) return;
equippedWeapon.name = "GameObjectToDestroy";
Destroy(equippedWeapon.gameObject);
}
public void DisconnectWeapon()
{
equippedWeapon.useGravity = true;
equippedWeapon.isKinematic = false;
equippedWeapon.GetComponent<Collider>().enabled = true;
equippedWeapon.transform.parent = null;
}
#endregion Arm And Remove Equipment Functions
Another Heavy. Ballistics.
public class Ballistics : MonoBehaviour
{
UnitManager enemy = null;
[Header(“Class”)]
[SerializeField] bool tracking = false;
[SerializeField] bool thrown = false;
[SerializeField] bool majik = false;
[Header(“If thrown weapon, add this game object to recover after the throw.”)]
[SerializeField] Rigidbody recoveredWeapon = null;
[SerializeField] QuadraticCurve trajectory;
[SerializeField] GameObject hitFX = null;
[SerializeField] float speed = 0f;
[SerializeField] float flightRangeModifier = 0;
[SerializeField] float lift = 0f;
[SerializeField] float lifetime = 1f;
[SerializeField] int damage = 0;
[SerializeField] int burstForce = 8;
Transform handTarget = null;
int netDamage = 0;
bool itemDropped = false;
float evaluationTime = 0f;
private void Start()
{
if (enemy == null)
{
Destroy(gameObject);
}
SetFlightPath();
}
private void Update()
{
if (enemy == null)
{
return;
}
if (tracking)
{
trajectory.SetTarget(GetImpactZone());
}
evaluationTime += speed * Time.deltaTime;
transform.position = trajectory.FollowTrajectory(evaluationTime);
transform.forward = trajectory.FollowTrajectory(evaluationTime + 0.001f) - transform.position;
Destroy(gameObject, lifetime);
}
#region Targeting Instructions
public void TargetEnemy(UnitManager selectedTarget, int damage)
{
Debug.Log("Ballistics - TargetEnemy() = " + selectedTarget.name.ToString());
Debug.Log("Ballistics - TargetEnemy() - damage input = " + damage);
enemy = selectedTarget;
netDamage = Random.Range(1, damage) + damage;
Debug.Log("Ballistics - TargetEnemy() - net damage = " + netDamage);
}
/* <<<<< THIS RIGHT HERE >>>>> */
Vector3 PeakOfFlight()
{
float halfway = 0.5f;
Vector3 scaledPosition = halfway * Vector3.Normalize(enemy.transform.position - transform.position)
+ transform.position;
scaledPosition.y += lift;
return scaledPosition;
}
/* <<<<< IS SOLID GOLD >>>>> */
/* NEVER REMOVE NEVER DESROY */
Vector3 GetImpactZone()
{
BoxCollider targetCollider = enemy.GetComponent<BoxCollider>();
if (targetCollider == null)
{
return enemy.transform.position;
}
Debug.Log("Ballistics - GetImpactZone() - enemy = " + enemy.name.ToString());
Debug.Log("Ballistics - GetImpactZone() - enemy position = " + enemy.transform.position);
return enemy.transform.position + Vector3.up * targetCollider.size.y / 2;
}
#endregion Targeting Instructions
#region Impact Instuctions
void OnTriggerEnter(Collider collider)
{
Debug.Log("OnTriggerEnter() - collider = " + collider.gameObject.name.ToString());
StartCoroutine(HandleImpact(collider));
}
IEnumerator HandleImpact(Collider collider)
{
if (enemy == null || collider.GetComponent<UnitManager>() != enemy)
{
yield return null;
}
/* THINGS GO CLUNK HERE */
Debug.Log("HandleImpact() enemy is identified as " + enemy.name.ToString());
if (enemy.GetComponent<Health>().GetIsDead())
{
tracking = false;
Destroy(gameObject);
yield return null;
}
if (thrown && recoveredWeapon != null && itemDropped == false)
{
if (enemy.GetComponent<Health>().GetIsDead())
{
Rigidbody droppedWeapon = Instantiate(recoveredWeapon, enemy.transform.position, transform.rotation);
Debug.Log("Ballistics - HandleImpact() - enemy supposedly died. Weapon dropped.");
}
else
{
Rigidbody droppedWeapon = Instantiate(recoveredWeapon, GetImpactZone(), transform.rotation);
Debug.Log("Ballistics - HandleImpact() - Weapon dropped.");
}
itemDropped = true;
}
if (collider.GetComponent<UnitManager>() == enemy)
{
Debug.Log("Ballistics - HandleImpact() - DamageEnemy(). The enemy is " + enemy.name.ToString());
DamageEnemy();
}
Destroy(gameObject, lifetime);
}
public void DamageEnemy()
{
Debug.Log("DamageEnemy() - enemy == " + enemy.name.ToString());
int evasion = enemy.GetEvasion();
Debug.Log("DamageEnemy() - evasion = " + evasion);
int hitRoll = Random.Range(1, 21);
Debug.Log("DamageEnemy() - hitroll = " + hitRoll);
if (hitRoll > evasion)
{
Debug.Log("Ballistics - hit roll = " + hitRoll + " Vs. evasion of " + evasion);
int attackDamage = Random.Range(1, netDamage);
enemy.GetComponent<Health>().TakeDamage(attackDamage);
Debug.Log("Ballistics - DamaegeEnemy() - TakeDamage(" + attackDamage + ")");
if (hitFX != null)
{
Debug.Log("Ballistics - DamageEnemy() - hit fx called for.");
GameObject fx = Instantiate(hitFX, GetImpactZone(), transform.rotation);
}
if (burstForce >= 0)
{
Debug.Log("Ballistics - DamageEnemy() - burst force = " + burstForce);
Push(enemy);
}
}
}
void Push(UnitManager target)
{
Vector3 heading = enemy.transform.position - transform.position;
float distance = heading.magnitude;
Vector3 direction = heading / distance;
enemy.GetComponent<Rigidbody>().AddForce(direction * burstForce);
}
public int GetProjectileDamage()
{
return damage;
}
#endregion Impact Instructions
#region FlightInstructions
public void SetFlightPath()
{
trajectory.SetLaunchPoint(handTarget.position);
trajectory.SetControlPoint(PeakOfFlight());
trajectory.SetTarget(enemy.transform.position);
Debug.Log("Ballistics - SetFlightPath() - " + enemy.name.ToString() + " at " + enemy.transform.position);
}
public float GetRangeModifier()
{
return flightRangeModifier;
}
public void SetHandTarget(Transform target)
{
Debug.Log("Ballistics - SetHandTarget(" + target.name.ToString() + ")");
handTarget = target;
}
#endregion FlightInstructions
Throwing actions are handled just a little differently so that logic can be ignored for now.
With all that I have a console full of:
My Inspector worn out from double checking. I have moved things all around to no avail. I have tried removing Cancel calls, being crazy person redundant with identifying colliders and game objects (like every other line sort of OCD). I am somehow missing the Big Simple.
I am positive that there is much to sneer at with my code efficiency and logic. Be gentle kind friends.