Search code examples
c#unity-game-enginedetectionraycastingcollider

Raycast Issue with Exact Hit Point Detection in Unity 2D Game


I'm currently developing a 2D top-down shooter game in Unity where I use raycasting for shooting mechanics. My goal is to instantiate visual effects precisely at the point where the ray hits an enemy's collider. However, I've been experiencing issues with the accuracy of the hit detection, and I'm hoping to get some insights from the community.

  • Game Type: 2D top-down shooter

  • Objective: Spawn effects at the exact point where a ray hits the enemy's collider.

  • Setup:

      1. Enemies have 2D colliders.
    
      2. The player shoots rays using Physics2D.Raycast.
    
      3. Effects are spawned using an ObjectPool.
    

Current Observations:

Hit Detection Issues: The raycast doesn't register a hit in the place it should. I've checked that the enemyLayer is correctly assigned and that the enemies have appropriate 2D colliders. Effect Instantiation: The InstantiateHitEffect function places the hit effect at an incorrect position (always instantiates in the center of the enemy). The hit.point should theoretically be the exact contact point on the collider, but it seems off.

Debugging and Logs: I've added logs to check the hit.point, the direction vector, and the layer mask. The output seems consistent with expectations, yet the problem persists.

Object Pooling: The object pool setup is verified to be working, and I can confirm that the correct prefabs are being instantiated.

Potential Issues Considered:

Precision Issues: I wonder if there's a floating-point precision issue, but the distances are quite small, so this seems unlikely.

Collider Setup: Could the problem be related to how the colliders are set up on the enemies? They are standard 2D colliders, and there should be no issues with them being detected.

Layer Mask: The enemyLayer is set up to only include enemy colliders, and I've verified this setup multiple times.

Screenshots:

I've included screenshots showing the scene setup, the inspector settings for relevant game objects, and the console logs during the issue. These will provide visual context to better understand the problem.

The green line is where i'm aiming at, and the blue line is where the engine detects the hit and instatiates the particle effects.

Example of an Enemy Collider Set up

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerShooting : MonoBehaviour
{
    public GameObject hitEffectPrefab;
    public GameObject bulletEffectPrefab;
    public Transform particleSpawnPoint;
    public float shootingRange = 5f;
    public LayerMask enemyLayer;
    public float fireRate = 1f;
    public int damage = 10;

    private Transform targetEnemy;
    private float nextFireTime = 0f;
    private ObjectPool objectPool;

    private void Start()
    {
        objectPool = FindObjectOfType<ObjectPool>();
        if (objectPool == null)
        {
            Debug.LogError("ObjectPool not found in the scene. Ensure an ObjectPool component is present and active.");
        }
    }

    private void Update()
    {
        DetectEnemies();
        if (targetEnemy != null)
        {
            if (Time.time >= nextFireTime)
            {
                ShootAtTarget();
                nextFireTime = Time.time + 1f / fireRate;
            }
        }
    }

    private void DetectEnemies()
    {
        RaycastHit2D hit = Physics2D.Raycast(particleSpawnPoint.position, particleSpawnPoint.up, shootingRange, enemyLayer);
        if (hit.collider != null)
        {
            targetEnemy = hit.collider.transform;
        }
        else
        {
            targetEnemy = null;
        }
    }

    private void ShootAtTarget()
    {
        if (targetEnemy == null)
        {
            Debug.LogError("targetEnemy is null");
            return;
        }

        Vector3 direction = (targetEnemy.position - particleSpawnPoint.position).normalized;
        Debug.Log($"Shooting direction: {direction}");

        RaycastHit2D hit = Physics2D.Raycast(particleSpawnPoint.position, direction, shootingRange, enemyLayer);
        if (hit.collider != null)
        {
            BaseEnemy enemy = hit.collider.GetComponent<BaseEnemy>();
            if (enemy != null)
            {
                enemy.TakeDamage(damage);
            }

            // Debug log to check hit point
            Debug.Log($"Hit point: {hit.point}, Enemy: {hit.collider.name}");

            // Visual effect for bullet movement
            InstantiateBulletEffect("BulletEffect", particleSpawnPoint.position, hit.point);

            // Visual effect at point of impact
            InstantiateHitEffect("HitEffect", hit.point);
        }
        else
        {
            Debug.Log("Missed shot.");
        }
    }

    private void InstantiateBulletEffect(string tag, Vector3 start, Vector3 end)
    {
        GameObject bulletEffect = objectPool.GetObject(tag);
        if (bulletEffect != null)
        {
            bulletEffect.transform.position = start;

            TrailRenderer trail = bulletEffect.GetComponent<TrailRenderer>();
            if (trail != null)
            {
                trail.Clear(); // Clear the trail data to prevent old trail artifacts
            }

            bulletEffect.SetActive(true);
            StartCoroutine(MoveBulletEffect(bulletEffect, start, end));
        }
        else
        {
            Debug.LogError($"{tag} effect is null. Check ObjectPool settings and prefab assignment.");
        }
    }

    private void InstantiateHitEffect(string tag, Vector3 position)
    {
        GameObject hitEffect = objectPool.GetObject(tag);
        if (hitEffect != null)
        {
            Debug.Log($"Setting hit effect position to: {position}");

            hitEffect.transform.position = position;
            hitEffect.SetActive(true);
        }
        else
        {
            Debug.LogError($"{tag} effect is null. Check ObjectPool settings and prefab assignment.");
        }
    }

    private IEnumerator MoveBulletEffect(GameObject bulletEffect, Vector3 start, Vector3 end)
    {
        float duration = 0.1f; // Adjust this to match the speed of your bullet visual effect
        float time = 0;

        while (time < duration)
        {
            bulletEffect.transform.position = Vector3.Lerp(start, end, time / duration);
            time += Time.deltaTime;
            yield return null;
        }

        bulletEffect.transform.position = end;
        bulletEffect.SetActive(false);
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, shootingRange);

        Gizmos.color = Color.green;
        Gizmos.DrawLine(particleSpawnPoint.position, particleSpawnPoint.position + particleSpawnPoint.up * shootingRange);
    }

    public void IncreaseDamage(int amount)
    {
        damage += amount;
    }

    public void IncreaseFireRate(float amount)
    {
        fireRate += amount;
    }

    public void IncreaseBulletRange(float amount)
    {
        shootingRange += amount;
    }
}

Hit Detection Issues: The raycast doesn't register a hit in the place it should. I've checked that the enemyLayer is correctly assigned and that the enemies have appropriate 2D colliders. Effect Instantiation: The InstantiateHitEffect function places the hit effect at an incorrect position (always instantiates in the center of the enemy). The hit.point should theoretically be the exact contact point on the collider, but it seems off.

Debugging and Logs: I've added logs to check the hit.point, the direction vector, and the layer mask. The output seems consistent with expectations, yet the problem persists.

Object Pooling: The object pool setup is verified to be working, and I can confirm that the correct prefabs are being instantiated.


Solution

  • I solved my hitscan issue! The problem was that I was using different vectors for calculating the shooting direction: particleSpawnPoint.up in OnDrawGizmos(), but (targetEnemy.position - particleSpawnPoint.position).normalized in-game.

    Consistency in direction vectors fixed the problem, ensuring accurate hit detection and effect placement.