Made arrows 'stick'

(edit: gif was too big!)
arrows3

I’m sure this is a bad way to do it and no doubt I’ll regret it later, but I had fun making the arrows stay in the enemy model instead of destroying on collision.

If anyone wants to recreate it, I added:

  1. A bool so that movement only occurs if the arrow is still intended to be travelling, i.e. wrapped an IF statement around the transform lines in Update
  2. A line to change the parent of the projectile to be the ‘Spine_01’ object of the target upon collision with a disgustingly long GetChild() chain. I didn’t want to do it by string-search but if arrows are ever meant to hit other models, this will stop working. The reason I wanted to use the Spine_01 object was so that the arrows move with the model as it jostles about during animations. Parenting it at the root level made it look as if it were floating inside the model
  3. An “ArrowProjectile” tag since it looks like non-arrow projectiles are going be implemented next, and I wouldn’t want to have fireballs stick to enemies in the same way

Here’s my code:

public class Projectile : MonoBehaviour
{
    [SerializeField] float projectileSpeed = 1f;
    Health target = null;
    float damage = 0;
    bool inFlight = true;

    void Update()
    {
        if(target == null) return;
        
        if(inFlight)
        {
            transform.LookAt(GetAimLocation());
            transform.Translate(Vector3.forward * projectileSpeed * Time.deltaTime);
        }         
    }

    public void SetTarget(Health target, float damage)
    {
        this.target = target;
        this.damage = damage;
    }

    private Vector3 GetAimLocation()
    {
        CapsuleCollider targetCapsule = target.GetComponent<CapsuleCollider>();
        if(targetCapsule == null) return target.transform.position;
        return target.transform.position + Vector3.up * targetCapsule.height / 2;
    }

    private void OnTriggerEnter(Collider other) {

        if(other.GetComponent<Health>() != target) return;        
        target.TakeDamage(damage);
        

        inFlight = false;
        
        //arrow childed to Spine_01 object, will probably break later!
        if(gameObject.tag == "ArrowProjectile")
        {
            gameObject.transform.SetParent(target.transform.GetChild(0).GetChild(1).GetChild(0).GetChild(2));
        }     
        else
        {
            Destroy(gameObject);
        }       
    }
}

There’s a few quirks of course, like that arrows seem to ‘stick’ at differing frames which honestly makes for some nice randomisation :stuck_out_tongue:

5 Likes

Wow this is awesome! Great job! This will help a lot of people who want to do the something.

1 Like

Oh, I don’t know, that could be a realistic mechanic… fireball hits you and you’re on fire!
Well done!

1 Like

haha that’s a great point, actually

Rather than the fireball itself, I’d probably leave a flaming particle effect at the hit location.

1 Like

This is a great idea! I feel like a lot of games have arrows that “stick” like this.

1 Like

FWIW I did something similar but I grabbed the bone right in Start() already:

        private void Start()
        {
            if (null != target)
            {
                // Find() apparently is not recursive, so we have to climb down the hierarchy...
                targetRootBone = target.transform.GetChild(0).Find("Root").Find("Hips");
            }

            // We do this only once, for performance and since arrows are no
            // homing missiles. We may have to get more fancy if we add magic
            // missiles that *might* be following the target...
            targetAimLocation = GetAimLocation();
        }

I used one GetChild(0) to be independent on which model the target enemy might have…

Something that would need a bit of fixing is that right now the target position is a fixed value once the arrow is on its way. If I shoot at an enemy that’s moving away from the player (which happens if I lured a guard away and then left their perception range) then the arrow will be stuck in the air…
So the way to do it would be to determine the direction once in SetTarget() and then stick by it until some obstacle was hit. Buildings may not be damageable but should still be stopping a flying arrow… (and an arrow flying through a brick wall would be rather silly…)

Omg thank you very much, I have been wanting to implement this, but couldn’t figure it out due to the childs.

Privacy & Terms