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

如何让敌人以波次攻击炮塔?Unity脚本问题与需求实现

问题修复与功能实现方案

编辑器参数中文翻译

  • 敌人预制体(enemyPrefab):已指定敌人预制体
  • 生成半径(spawnRadius):50
  • 移动速度(moveSpeed):10
  • 攻击触发概率(attackChance):0.1
  • 攻击高度阈值(attackHeightThreshold):10
  • 攻击波动周期(attackWaveDuration):1
  • 攻击波动幅度(attackWaveAmplitude):5

原脚本问题分析

  1. 攻击协程频繁触发:每帧都执行Random.value < targetAttackChance判断,10%的概率导致敌人几乎时刻处于攻击晃动状态,完全打断移动逻辑。
  2. 移动与攻击逻辑冲突:攻击协程直接覆盖敌人position,完全忽略移动指令,敌人无法向炮塔靠近。
  3. 无绕圈飞行逻辑:仅实现直线移动,未满足绕炮塔圆周飞行的需求。
  4. 无波次生成机制:当前为单个随机生成,无法实现群体波次攻击。

修复后完整脚本

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

public class TurretEnemyManager : MonoBehaviour
{
    public GameObject enemyPrefab;
    public float spawnRadius = 50f;
    public float moveSpeed = 10f;
    public float attackChance = 0.01f; // 降低概率避免频繁触发攻击
    public float attackHeightThreshold = 10f;
    public float attackWaveDuration = 1f;
    public float attackWaveAmplitude = 5f;
    public float circleOrbitSpeed = 2f; // 绕圈角速度
    public float minCircleRadius = 15f; // 最小绕圈半径(靠近炮塔的距离)
    public float waveSpawnInterval = 8f; // 波次生成间隔
    public int waveEnemyCount = 5; // 每波敌人数量

    private List<EnemyController> enemies = new List<EnemyController>();

    void Start()
    {
        StartCoroutine(SpawnSingleEnemies());
        StartCoroutine(SpawnEnemyWaves());
    }

    // 单个随机生成敌人
    IEnumerator SpawnSingleEnemies()
    {
        while (true)
        {
            SpawnEnemy();
            yield return new WaitForSeconds(Random.Range(2f, 5f));
        }
    }

    // 群体波次生成敌人
    IEnumerator SpawnEnemyWaves()
    {
        while (true)
        {
            yield return new WaitForSeconds(waveSpawnInterval);
            for (int i = 0; i < waveEnemyCount; i++)
            {
                SpawnEnemy();
                yield return new WaitForSeconds(0.3f); // 波次内敌人间隔生成
            }
        }
    }

    // 生成单个敌人并初始化状态
    void SpawnEnemy()
    {
        Vector2 randomCircle = Random.insideUnitCircle.normalized * spawnRadius;
        Vector3 spawnPos = new Vector3(randomCircle.x, Random.Range(2f, 5f), randomCircle.y);
        GameObject enemyObj = Instantiate(enemyPrefab, spawnPos, Quaternion.identity);
        EnemyController enemy = enemyObj.AddComponent<EnemyController>();
        enemy.Init(this, transform.position, moveSpeed, circleOrbitSpeed, minCircleRadius);
        enemies.Add(enemy);
    }

    void Update()
    {
        // 清理已销毁的敌人
        enemies.RemoveAll(enemy => enemy == null);

        foreach (EnemyController enemy in enemies)
        {
            if (!enemy.IsAttacking && Random.value < attackChance)
            {
                StartCoroutine(EnemyAttack(enemy));
            }
        }
    }

    // 敌人攻击逻辑:上下晃动+向炮塔靠近的复合运动
    IEnumerator EnemyAttack(EnemyController enemy)
    {
        enemy.IsAttacking = true;
        float timeElapsed = 0f;
        Vector3 startPos = enemy.transform.position;
        Vector3 directionToTurret = (transform.position - startPos).normalized;

        while (timeElapsed < attackWaveDuration)
        {
            timeElapsed += Time.deltaTime;
            float waveOffset = Mathf.Sin(timeElapsed * Mathf.PI * 2f / attackWaveDuration) * attackWaveAmplitude;
            Vector3 newPos = startPos + directionToTurret * moveSpeed * timeElapsed + Vector3.up * waveOffset;
            enemy.transform.position = newPos;
            yield return null;
        }
        enemy.IsAttacking = false;
    }
}

// 敌人状态控制器:独立处理移动、绕圈逻辑
public class EnemyController : MonoBehaviour
{
    private TurretEnemyManager manager;
    private Vector3 turretPos;
    private float moveSpeed;
    private float orbitSpeed;
    private float minOrbitRadius;
    private bool isOrbiting = false;
    private float currentOrbitAngle;

    public bool IsAttacking { get; set; }

    public void Init(TurretEnemyManager manager, Vector3 turretPos, float moveSpeed, float orbitSpeed, float minOrbitRadius)
    {
        this.manager = manager;
        this.turretPos = turretPos;
        this.moveSpeed = moveSpeed;
        this.orbitSpeed = orbitSpeed;
        this.minOrbitRadius = minOrbitRadius;
        IsAttacking = false;
    }

    void Update()
    {
        if (IsAttacking) return;

        float distanceToTurret = Vector3.Distance(transform.position, turretPos);

        if (distanceToTurret > minOrbitRadius && !isOrbiting)
        {
            // 向炮塔直线移动
            Vector3 direction = (turretPos - transform.position).normalized;
            transform.Translate(direction * moveSpeed * Time.deltaTime, Space.World);
        }
        else if (!isOrbiting)
        {
            // 到达最小半径,切换为绕圈模式
            isOrbiting = true;
            Vector3 directionToTurret = (turretPos - transform.position).normalized;
            currentOrbitAngle = Mathf.Atan2(directionToTurret.z, directionToTurret.x) * Mathf.Rad2Deg;
        }

        if (isOrbiting)
        {
            // 绕炮塔圆周运动
            currentOrbitAngle += orbitSpeed * Time.deltaTime;
            float rad = currentOrbitAngle * Mathf.Deg2Rad;
            Vector3 orbitPos = turretPos + new Vector3(Mathf.Cos(rad), transform.position.y, Mathf.Sin(rad)) * minOrbitRadius;
            transform.position = orbitPos;
            // 始终看向炮塔
            transform.LookAt(turretPos);
        }
    }
}

关键修改说明

  1. 拆分逻辑模块:新增EnemyController脚本单独处理敌人的移动、绕圈状态,避免主脚本逻辑混乱。
  2. 修复攻击触发逻辑:仅在敌人未处于攻击状态时判断是否触发攻击,同时降低attackChance避免频繁打断移动。
  3. 实现绕圈飞行:当敌人到达设定的最小绕圈半径时,自动切换为绕炮塔圆周运动模式。
  4. 添加波次生成功能:新增SpawnEnemyWaves协程,按固定间隔生成批量敌人,实现群体波次攻击。
  5. 优化攻击运动:攻击时不再原地晃动,改为结合向炮塔移动+上下晃动的复合运动,更符合攻击行为逻辑。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 08:27:10