Search code examples
c#unity-game-engineraycasting

OnPointerEnter and OnPointerExit not being triggered Unity


Alright so basically the issue that I've been having is that for some reason a GameObject is interfering with the OnPointerEnter function. I'm pretty sure that OnPointerEnter detects only UI. So that's why I'm extremely confused when seeing that a specific GameObject in this case the PlayerLogic GameObject (which you can see in the screenshot) is for some reason interfering with the detection of UI elements. The reason I believe it is this specific GameObject is because once I do PlayerLogic.SetActive(false); OnPointerEnter starts to work again, and I'm also sure that it isn't any of the children of PlayerLogic because I've tried turning them off specifically and it still didn't work.

Inspector of the PlayerLogic object

Hierarchy

The code I'm using to test OnPointerEnter

After some testing I've realized that its the specific issue lies within the Player script on the PlayerLogic GameObject. Now what confuses me is that once I turn off the Player component OnPointer doesn't work, but if I were to remove the Player component completely from the PlayerLogic GameObject OnPointerEnter works.

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections;
using System.Collections.Generic;

public class Player : MonoBehaviour, TakeDamage {

    [SerializeField] private Animator playerAnimator;

    [SerializeField] private Transform mainCameraTransform;
    private bool isRunning = false; 

    [SerializeField] private CharacterController controller;
    public float speed = 10f;
    [SerializeField] private float jumpForce = 3f;
    [SerializeField] private float gravity = -10000.81f;
    Vector3 velocity;
    Vector3 desiredMoveDirection;

    private float dashSpeed = 30f;

    private float mouseX;
    private float mouseY;
    [SerializeField]
    private Transform Target;
    [SerializeField]
    private Transform player;
    
    private float turnSmoothVelocity;    

    private float time = 0f;
    public bool playerIsAttacking = false;

    [SerializeField] private Slider playerHealth, playerMana;
    [SerializeField] private TextMeshProUGUI healthText, manaText; 

    private Vector3 originalSpawnPos;
    private bool playerIsDead = false;

    [SerializeField] private LayerMask enemyLayerMask;
    [SerializeField] private Transform playerLook;

    private ShowHPBar obj;
    private bool HPBarShown = false;
    private bool unshowingHPBar = false;
    public bool lookingAtEnemy = false;
    public RaycastHit hit;

    [SerializeField] private Canvas abilityCanvas;
    [SerializeField] private Slider CD1;
    [SerializeField] private Slider CD2;
    [SerializeField] private Slider CD3;
    public List<Ability> currentlyEquippedAbilites = new List<Ability>();
    public List<string> abilityTexts = new List<string>();
    public float[] abilityCooldowns = new float[3]; 
    private float manaRegenTime;
    //public List<Image> abilityImages = new List<Image>();

    private void Awake() {
        Cursor.visible = false;
        Cursor.lockState = CursorLockMode.Locked;
    }

    private void Start() {
        playerHealth.onValueChanged.AddListener(delegate {OnValueChangedHealth(); });
        playerMana.onValueChanged.AddListener(delegate {OnValueChangedMana(); });
        originalSpawnPos = transform.position;
    }

    private void Update() {
        if (!playerIsDead) {
            PlayerMovementAndRotation();
        }
        PlayerDash();
        PlayerRun();
        PlayerSeeEnemyHealth();
        PlayerActivateAbility();

        if (manaRegenTime > 0.5f) {
            playerMana.value += playerMana.maxValue/100; 
            manaRegenTime = 0;
        }
        playerLook.rotation = mainCameraTransform.rotation;
        time += Time.deltaTime;
        manaRegenTime += Time.deltaTime;

        #region Ability Cooldowns
        if (currentlyEquippedAbilites.Count > 0) {
            if (currentlyEquippedAbilites[0].cooldown <= abilityCooldowns[0])
            {
                currentlyEquippedAbilites[0].isOnCooldown = false;
                abilityCooldowns[0] = 0;
                CD1.value = 0;
            }
            else if (currentlyEquippedAbilites[0].isOnCooldown) { 
                abilityCooldowns[0] += Time.deltaTime; 
                CD1.value = currentlyEquippedAbilites[0].cooldown - abilityCooldowns[0];
            }
        }

        if (currentlyEquippedAbilites.Count > 1) {
            if (currentlyEquippedAbilites[1].cooldown <= abilityCooldowns[1])
            {
                currentlyEquippedAbilites[1].isOnCooldown = false;
                abilityCooldowns[1] = 0;
                CD2.value = 0;
            }
            else if (currentlyEquippedAbilites[1].isOnCooldown) { 
                abilityCooldowns[1] += Time.deltaTime; 
                CD2.value = currentlyEquippedAbilites[1].cooldown - abilityCooldowns[1];
            }
        }

        if (currentlyEquippedAbilites.Count > 2) {
            if (currentlyEquippedAbilites[2].cooldown <= abilityCooldowns[2])
            {
                currentlyEquippedAbilites[2].isOnCooldown = false;
                abilityCooldowns[2] = 0;
                CD3.value = 0;
            }
            else if (currentlyEquippedAbilites[2].isOnCooldown) { 
                abilityCooldowns[2] += Time.deltaTime; 
                CD3.value = currentlyEquippedAbilites[2].cooldown - abilityCooldowns[2];
            }
        }
        #endregion
    }

    private void PlayerRun() {
        if (Input.GetKey(KeybindsScript.RunKey) && (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0)) {
            playerAnimator.SetInteger("isRunning", 1);
            playerAnimator.SetInteger("isIdle", 0);
            playerAnimator.SetInteger("isWalking", 0);
            speed = 15f;
        }
        else if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0) {
            playerAnimator.SetInteger("isWalking", 1);
            playerAnimator.SetInteger("isIdle", 0);
            playerAnimator.SetInteger("isRunning", 0);
            speed = 10f;
        }
        else {
            playerAnimator.SetInteger("isRunning", 0);
            playerAnimator.SetInteger("isWalking", 0);
            playerAnimator.SetInteger("isIdle", 1);
            speed = 10f;
        }
    }

    private void PlayerMovementAndRotation() {
        bool isGrounded = controller.isGrounded;

        if (isGrounded && velocity.y < 0) {
            velocity.y = -2f;
        }
        float horizontal = Input.GetAxisRaw("Horizontal");
        float vertical = Input.GetAxisRaw("Vertical");
        Vector3 moveDir = (transform.right*horizontal+transform.forward*vertical).normalized;

        controller.Move(moveDir*Time.deltaTime*speed);
        transform.eulerAngles = new Vector3(0f, mainCameraTransform.eulerAngles.y, 0f);
        

        if (Input.GetKeyDown(KeybindsScript.JumpKey) && isGrounded) {
            velocity.y = Mathf.Sqrt(jumpForce * -2f * gravity);
        }

        velocity.y += gravity * Time.deltaTime;

        controller.Move(velocity * Time.deltaTime);
    }

    private void PlayerDash() {
        if (Input.GetKeyDown(KeybindsScript.DashKeybind) && isRunning == false) {
            if (Input.GetKey(KeybindsScript.MovementKeyBackward)) {
                StartCoroutine(PlayerDashTiming(2));
            }
            else if (Input.GetKey(KeybindsScript.MovementKeyRight)) {
                StartCoroutine(PlayerDashTiming(3));
            }
            else if (Input.GetKey(KeybindsScript.MovementKeyLeft)) {
                StartCoroutine(PlayerDashTiming(4));
            }
            else {
                StartCoroutine(PlayerDashTiming(1));
            }
        }
    }

    private void PlayerSeeEnemyHealth() {
        if (Physics.Raycast(playerLook.position, playerLook.forward, out hit, 1000f, enemyLayerMask)) {
            obj = hit.transform.gameObject.GetComponent<ShowHPBar>();
            if (obj != null && !HPBarShown && !unshowingHPBar) {obj.ShowHPBarFunction(); HPBarShown = true;}
            lookingAtEnemy = true;
        }
        else {
            if (obj != null && HPBarShown) {StartCoroutine(UnShowHPBar(obj)); HPBarShown = false;}
        }
    }

    public void PlayerEquipAbility(Ability ability, int place) {
        if (currentlyEquippedAbilites.Count < place) {currentlyEquippedAbilites.Add(ability);}
        else {currentlyEquippedAbilites[place-1] = ability;}

        //if (abilityImages.Count < place) {abilityImages.Add(ability.icon);}
        //else {abilityImages.Add(ability.icon);}
        if (abilityTexts.Count < place) {abilityTexts.Add(ability.name);}
        else {abilityTexts[place-1] = ability.name;}
        for (int i=0;i < abilityTexts.Count;++i) {
            abilityCanvas.transform.GetChild(i).GetChild(0).GetComponent<TextMeshProUGUI>().text = abilityTexts[i];
            abilityCanvas.transform.GetChild(i).GetChild(1).GetComponent<Slider>().maxValue = ability.cooldown;
        }
    }

    private void PlayerActivateAbility() { 
        if (Input.GetKeyDown(KeyCode.Alpha1)) {
            if (currentlyEquippedAbilites[0] != null) {
                if (currentlyEquippedAbilites[0].manaCost <= playerMana.value && !currentlyEquippedAbilites[0].isOnCooldown) {
                    ParticleEffect pe = currentlyEquippedAbilites[0].script.gameObject.GetComponent<ParticleEffect>();
                    playerMana.value -= currentlyEquippedAbilites[0].manaCost;
                    currentlyEquippedAbilites[0].isOnCooldown = true;
                    if (pe != null) {pe.PlayAnimation();}
                }
            }
        }
    }


    public void OnValueChangedHealth() {
        healthText.text = playerHealth.value + "/" + PlayerStats.HealthPoints;
    }

    public void OnValueChangedMana() {
        manaText.text = playerMana.value + "/" + PlayerStats.ManaAmount;
    }

    public void TakeDamageFunction(float damage) {
        playerHealth.value -= damage;
        if (playerHealth.value <= 0) {
            StartCoroutine(PlayerDeath());
        }
    }


    IEnumerator PlayerDashTiming(int x) {
        isRunning = true;
        float time = 0f;
        Vector3 savedVector = Vector3.zero;
        switch (x) {
            case 1: 
            savedVector = transform.forward;
            break;
            case 2:
            savedVector = -transform.forward;
            break;
            case 3:
            savedVector = transform.right;
            break;
            case 4:
            savedVector = -transform.right;
            break;
        }
        while(time < .3f)
        {
            time += Time.deltaTime;
            controller.Move(savedVector * dashSpeed * Time.deltaTime);
            yield return null; 
        }
        yield return new WaitForSeconds(1.5f);
        isRunning = false;
    }

    IEnumerator PlayerDeath() {
        //Respawn
        playerIsDead = true;
        playerAnimator.enabled = false;
        yield return new WaitForSeconds(1f);
        playerHealth.value = 100;
        transform.position = originalSpawnPos;
        yield return new WaitForSeconds(0.1f);
        playerIsDead = false;
        playerAnimator.enabled = true;
    }

    IEnumerator UnShowHPBar(ShowHPBar obj) {
        unshowingHPBar = true;
        yield return new WaitForSeconds(1.5f);
        obj.ShowHPBarFunction();
        unshowingHPBar = false;
    }
}

This is the Player.cs script.


Solution

  • I'm pretty sure that OnPointerEnter detects only UI.

    No.

    It works "out of the box" on UI because by default every Canvas contains a GraphicsRaycaster component which is then used and handled by the EventSystem.

    For non-UI 3D objects you have to make sure

    • your objects have 3D Colliders
    • the Colliders are on the same object as the IPointerXY interfaces
    • on your Camera there is a PhysicsRaycaster component

    For non-UI 2D objects quite similar

    • your objects have a 2D Collider
    • the colliders are on the same object as the IPointerXY interfaces
    • the Camera has a Physics2DRaycaster component

    For both UI and 3D/2D objects you have to have the EventSystem in your scene.


    Note that it is possible that any other collider or in general raycast blocking object is between your input and the target object which would also prevent the OnPointerXY to be triggered on your objects.

    The CharacterController

    is simply a capsule shaped Collider which can be told to move in some direction from a script.

    which is probably blocking the input.


    Now with your Player code:

    You do

    Cursor.lockState = CursorLockMode.Locked;
    

    in Awake so even if you turn it off afterwards this already took effect.