An Alternate Solution (Can Damage Any Enemy, Not Just the Target)

Evening all :slight_smile: I was working on my own solution to Rick’s challenge in Projectile.cs and thought I’d share it. My code doesn’t check if the target hit is the target; instead, it just looks for a target with a Health component, and if one exists, applies damage as appropriate. This way, if a damageable target comes in between you and your true target, it will take damage first. (I’d like to say I was trying to design it this way, but I didn’t know that you could compare two similar components to see if they were the same… silly me! forehead smack and thoughts of V8) Anyway, the downside of this was that fewer than half of the arrows made it far enough away from the player to damage the enemy, the rest triggering (and damaging) said player, so my solution was to start each projectile with its box collider disabled, and then enable it after a set time using a Coroutine. (I’ll post the code in a moment). I also cached the reference to the target’s Capsule Collider in the SetTarget() method so that it won’t be called repeatedly during Update (this may be done in a later refactor, but I’ve heard it’s good practice to avoid getting components in Update, so voila!) Anyway, here’s the code. I’d appreciate any comments, suggestions, corrections, quips, or mild sass :slight_smile: Cheers!

using System.Collections;
using RPG.Core;
using UnityEngine;

namespace RPG.Combat {

    public class Projectile : MonoBehaviour {
    
        [SerializeField] float projectileSpeed = 5f;
        [SerializeField] float targetHeightDivisor = 1.5f;
        [SerializeField] float enableColliderAfter = 0.5f;

        CapsuleCollider targetCollider;
        Transform target;

        float damage = 0f;

        void Update() {
            if (targetCollider == null) { return; }

            transform.LookAt(GetTargetPosition());
            transform.Translate(Vector3.forward * projectileSpeed * Time.deltaTime);
        }

        public void SetTarget(Transform target, float damage) {
            this.target = target;
            this.damage += damage;
            targetCollider = target.GetComponent<CapsuleCollider>();
            StartCoroutine(ActivateCollider());
        }

        Vector3 GetTargetPosition() {
            if (targetCollider == null) { return target.position; }

            return target.position + Vector3.up * targetCollider.height / targetHeightDivisor;
        }

        void OnTriggerEnter(Collider other) {
            if (other.TryGetComponent<Health>(out Health targetHealth)) {
                targetHealth.TakeDamage(damage);
                Destroy(gameObject);
            }
        }

        IEnumerator ActivateCollider() {
            yield return new WaitForSeconds(enableColliderAfter);
            GetComponent<BoxCollider>().enabled = true;
        }
    }
}

A simpler solution to the hitting yourself connundrum caused by selecting any target is to pass along the shooter in the SetTarget method:

Health shooter;
public void SetTarget(Transform target, float damage, Health damageDealer)
{
    shooter = damageDealer;
    this.target = target;
    this.damage += damage;
    targetCollider = target.GetComponent<CapsuleCollider>();
}

void OnTriggerEnter(Collider other)
{
    if(other.TryGetComponent(out Health targetHealth) && targetHealth!=shooter)
    {
1 Like

Oh! That’s lovely, thanks Brian! I had started fearing that my solution could still potentially break, since it’s just relying on object velocity and timing to work (which I’d already had to tune for closer ranged combat) but your solution comes from the other direction and makes it impossible to fail! I still seem to have a mental block about passing arguments through to methods, so thank-you very much!

Nice!

I’m thinking one should make this a flag so one could have projectiles that just fly along their path until they hit (standard arrow for example), or one might have other projectiles that are “smart” and will only hit their designated target (a magic missile for example could do that).

Privacy & Terms