2
\$\begingroup\$

I have an enemy shooting around. My game mechanic involves hovering over the enemy with the mouse to drain its health. While the enemy is moving, you need to follow it with your mouse. If you don't succeed to follow it and the mouse leaves the enemy's collider, the enemy's health should be refilled.

The trouble is that the enemies' bullets have also a collider, and OnMouseOver acts weird when more colliders appear on top of each other.

How can I only select a particular Layer for the enemy to detect only that layer without acknowledging the bullets layer?

My Code so far:

public class FightDarkalls : MonoBehaviour
{

    [Header("Guidance Light On?")]
    public bool _guidanceLight = false;
    [Header("Target Settings")]
    public GameObject _monster;
    public GameObject _targetEffect;
    public Animator _TargetEffectAnim;
    public LayerMask _monsterLayer;
    int _layer;
    [Space]
    [Header("Damage Darkall")]
    public float damage = 0.1f;
    public float damageSpeed = 2.5f;
    [Header("OBJs to deactivate when Darkall dead")]
    public GameObject[] _deactivateOBJsOnDeath;
    [Header("Health Settings")]
    public GameObject _healthBar;
    public float startingHealth;
    public float health;
    public GameObject _deathDarkallParticle;
    //Animator
    private Animator _anim;

    Vector2 mousePosition;
    public LayerMask _interactLayer;

    private void Start()
    {
        _targetEffect.SetActive(false);
        _anim = GetComponent<Animator>();
        _TargetEffectAnim = _targetEffect.GetComponent<Animator>();
    }

    private void Update()
    {
        CheckIfGuidanceIsActive();

        _healthBar.transform.localScale = new Vector2(health, 1f);

        _deathDarkallParticle.transform.position = Vector2.Lerp(_deathDarkallParticle.transform.position, _monster.transform.position, 100f * Time.deltaTime);

        SetMousePosition();
    }

    void CheckIfGuidanceIsActive()
    {
        if (GameObject.Find("_GuidanceLight").GetComponent<FollowCursor>   ()._guidanceActivated == true)
        {
            _guidanceLight = true;
        }
        else
            _guidanceLight = false;
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawSphere(mousePosition, 1f);
    }

    private void OnMouseOver()
    {
        if (_guidanceLight)
        {
            //[DAMAGE]\\
            health -= damage * Time.deltaTime * damageSpeed;
            _anim.SetBool("TakeDamage", true);
            //Activate Target Effect
            _targetEffect.SetActive(true);
            _TargetEffectAnim.SetBool("OnDarkallOver", true);
            //Damage the target
            //Kills the target
            if (health <= 0f)
            {
                foreach (GameObject _dOBJs in _deactivateOBJsOnDeath)
                {
                    _dOBJs.GetComponent<SpriteRenderer>().enabled = false;
                }
                //Change Cam Shake to another OBJ
                GameObject.Find("Main Camera").GetComponent<CameraShake>().CamShakeEffect(0.1f, 0.5f);
                health = 0f;
                _deathDarkallParticle.SetActive(true);
                _deathDarkallParticle.GetComponent<ParticleSystem>().Play();
                _monster.gameObject.SetActive(false);
                Destroy(_monster.gameObject, 3f);
            }
        }
    }
    private void OnMouseExit()
    {
        _anim.SetBool("TakeDamage", false);
        //If failed to kill, reset health
        health = startingHealth;
        //Deactivate Target
        _targetEffect.SetActive(false);
        _TargetEffectAnim.SetBool("OnDarkallOver", false);
    }


    void SetMousePosition()
    {
        mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Please be careful of code formatting when authoring and editing questions. Your most recent edit removed indentation required for proper formatting of the top level of your code, so I've rolled it back. Please make sure to use the preview feature to ensure your code is formatted correctly. \$\endgroup\$
    – DMGregory
    Jan 30, 2019 at 17:49

1 Answer 1

4
\$\begingroup\$

First, configure your Layers so that you have a layer for bullets and a layer for enemies. Ensure you've set the bullet prefabs to use the bullet layer, and the enemies/enemy prefabs to use the enemies layer.

Next, you can apply your hovering damage using a script like this one. Main things to note about this strategy:

  • We check for objects overlapping the mouse position with Physics2D.OverlapPoint - this says "Give me a collider in one of the selected layers that's touching this point, or null if there's no such collider touching this point"

  • We use a public LayerMask parameter configured in the inspector to include only the enemies layer in this check, and ignore bullets

  • We do this search for any overlapping collider only when we don't currently have a target we're damaging

  • When we already have a target, we do a cheaper check to see if we're still within that collider

    In addition to being cheaper (checking just one collider instead of multiple), this also makes this damage mechanic "sticky" - if two enemies overlap, this will pick one and stick with it instead of flickering between them.


public class HoverDamage : MonoBehaviour {

     // Set this to exclude your bullets and include your damageable enemies.
     public LayerMask layersToDamage;

     public float damageRate;

     // Keep track of our current target. 
     // I recommend making a DamageTaker component to keep track of health transactions.
     DamageTaker _currentTarget;
     Collider2D _targetCollider;

     void Update() {         
         if(_currentTarget == null) {
             // If we don't have a valid target, try to find a new one.
             FindTarget();
         } else {
             // Otherwise, follow and damage the target we have.
             TrackAndDamageTarget();
         }
     }

     Vector2 MousePositionInWorld() {
         // You could cache a reference to the camera,
         // or get this position from a cursor/reticle object instead.
         return Camera.main.ScreenToWorldPoint(Input.mousePosition);
     }

     void FindTarget() {
         // Find any collider in the right layer overlapping our mouse.
         _targetCollider = Physics2D.OverlapPoint(MousePositionInWorld(), layersToDamage);
         if(_targetCollider == null)
             return;  // No such collider! We'll go without a target for now.

         // Try to get something we can damage on this object.
         _currentTarget = _targetCollider.GetComponent<DamageTaker>();
         if(_currentTarget == null)
             return;  // Nuts - the thing we found isn't damageable. Still no target.

         // We have a valid target now! Remember it for next frame,
         // and deal it some damage right away!
         DamageTarget();
     }

     void TrackAndDamageTarget() {
         // Check if our mouse is still over the target's collider from a previous frame.
         // You could use a distance query here instead to give a little tolerance.
         if(_targetCollider.OverlapPoint(MousePosition)) {
             // We're still on-target - deal damage!
             DamageTarget();
         } else {
             // We slipped off the collider. Stop tracking & damaging it.
             _targetCollider = null;
             _currentTarget = null;
             // Next frame we'll search for a new target.
         }
     }

     void DamageTarget() {
         _currentTarget.TakeDamage(damageRate * Time.deltaTime);
     }
}
\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .