You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在NavMesh上生成相机视野外敌人的代码无效问题排查

问题排查与修复方案

你的代码没效果主要有几个核心问题,逐一解决:

1. NavMesh检测方式错误

你用的NavMesh.Raycast是用来检测两点之间的NavMesh路径是否存在障碍物,不是用来验证某个点是否在NavMesh上。要找有效NavMesh位置,应该用NavMesh.SamplePosition,它能返回离目标点最近的有效NavMesh采样点。

2. 生成位置与检测位置不一致

原代码里NavMesh.Raycast的终点和Instantiate的位置是两次独立的随机计算,就算Raycast命中了,生成的位置也可能完全不在NavMesh上。必须用同一个随机位置来检测和生成。

3. 敌人数量没有递减逻辑

生成敌人后enemiesAmount只会增加,但敌人被销毁时你没处理这个数值的递减,导致第一波生成后enemiesAmount永远不为0,再也不会触发下一波生成。

修复后的完整代码

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

public class EnemySpawner : MonoBehaviour 
{
    private int waveNumber = 0;
    public int enemiesAmount = 0;
    public GameObject EnemyPrefab; // 重命名更清晰
    public Camera cam;
    public float spawnOffsetFromCamera = 10f; // 相机视野外的偏移距离,可调整
    public float spawnRange = 5f; // 生成点的随机范围,可调整

    void Start () 
    {
        if (cam == null)
            cam = Camera.main;
        enemiesAmount = 0;
    }

    void Update () 
    {
        if (enemiesAmount == 0) 
        {
            waveNumber++;
            StartCoroutine(SpawnWave()); // 用协程避免一帧生成太多敌人,可选
        }
    }

    IEnumerator SpawnWave()
    {
        for (int i = 0; i < waveNumber; i++) 
        {
            // 计算相机视野外的随机目标点
            Vector3 targetSpawnPos = GetOffCameraSpawnPos();
            
            NavMeshHit hit;
            // 检测目标点附近的有效NavMesh位置
            if(NavMesh.SamplePosition(targetSpawnPos, out hit, spawnRange, NavMesh.AllAreas))
            {
                Debug.Log("生成敌人在NavMesh上:" + hit.position);
                GameObject enemy = Instantiate(EnemyPrefab, hit.position, Quaternion.identity);
                enemiesAmount++;
                // 给敌人添加销毁时的回调
                EnemyHealth enemyHealth = enemy.GetComponent<EnemyHealth>();
                if(enemyHealth != null)
                {
                    enemyHealth.OnEnemyDeath += () => enemiesAmount--;
                }
                else
                {
                    // 如果没有EnemyHealth脚本,直接给敌人加销毁监听
                    Destroy(enemy, 10f); // 示例:10秒后销毁,实际替换成你的敌人死亡逻辑
                    StartCoroutine(DecrementEnemyCountAfterDestroy(enemy));
                }
            }
            else
            {
                Debug.LogWarning("找不到有效NavMesh位置,跳过本次生成");
            }
            yield return new WaitForSeconds(0.5f); // 每0.5秒生成一个,避免卡顿
        }
    }

    Vector3 GetOffCameraSpawnPos()
    {
        // 获取相机视野的边界
        float camHeight = cam.orthographicSize;
        float camWidth = cam.orthographicSize * cam.aspect;

        // 随机选择相机的四个方向外生成(这里选上方,你可以扩展其他方向)
        float randomX = Random.Range(cam.transform.position.x - camWidth - spawnRange, cam.transform.position.x + camWidth + spawnRange);
        // 生成在相机视野上方的外侧
        float spawnZ = cam.transform.position.z + camHeight + spawnOffsetFromCamera + Random.Range(0, spawnRange);
        
        return new Vector3(randomX, cam.transform.position.y, spawnZ);
    }

    IEnumerator DecrementEnemyCountAfterDestroy(GameObject enemy)
    {
        yield return new WaitUntil(() => enemy == null);
        enemiesAmount--;
    }
}

额外注意事项

  • 确保你的场景已经正确烘焙了NavMesh(Window > AI > NavMesh),并且敌人的Layer不在NavMesh的排除列表里。
  • 如果你的敌人有自己的死亡脚本(比如EnemyHealth),要在死亡时触发OnEnemyDeath事件,或者直接在销毁时调用enemiesAmount--。
  • 调整spawnOffsetFromCamera和spawnRange参数,适配你的游戏场景大小。

内容的提问来源于stack exchange,提问作者Rodrigo Aben-Athar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 17:42:40