第6章 群体智能与战术决策系统

6.1 群体智能系统概述

在复杂的游戏环境中,单个AI角色的行为已经无法满足现代游戏对真实感的需求。群体智能系统通过模拟多个AI角色之间的协作与战术配合,能够创造出更加真实、更具挑战性的游戏体验。本章将深入探讨Unity引擎中实现群体智能与战术决策的各种技术,从基础的队形管理到高级的战术分析算法。

群体智能的核心思想是让多个AI角色像真实团队一样协作行动,而不是各自为战。这种协作可以体现在多个层面:基础层面的队形保持和移动同步,战术层面的协同攻击和防御,以及战略层面的资源分配和目标规划。在商业游戏项目中,一个优秀的群体智能系统能够显著提升游戏的可玩性和重玩价值。

Unity引擎提供了强大的AI工具集,但实现高级群体智能需要开发者结合多种技术。本章将介绍如何使用路径点系统、扩展的寻路算法、势力图分析和战斗循环设计等技术,构建一个完整的群体智能解决方案。我们将从基础概念开始,逐步深入到复杂的战术决策实现。

6.2 队形系统的设计与实现

队形系统是群体智能的基础,它决定了多个AI角色如何组织和移动。一个好的队形系统不仅需要保持队形的完整性,还要能够适应环境变化和战术需求。

6.2.1 队形系统基础理论

队形系统包含几个关键要素:队形类型、位置分配、动态调整和障碍回避。常见的队形类型包括线形、楔形、V形、方形和圆形等,每种队形都有其特定的战术优势。位置分配算法需要确保每个角色在队形中都有明确的位置,并且能够根据情况动态调整。

在实现队形系统时,需要考虑以下设计原则:

  1. 适应性:队形能够根据环境障碍和战术需求自动调整
  2. 稳定性:角色在队形中的位置变化应平滑自然
  3. 效率:队形计算不应过多消耗性能资源
  4. 可扩展性:支持不同规模和类型的队形

6.2.2 Unity中的队形系统实现

以下是一个完整的队形系统实现,展示了如何在Unity中创建和管理多种队形:

using UnityEngine;
using System.Collections.Generic;

namespace FormationSystem
{
    // 队形类型枚举
    public enum FormationType
    {
        Line,       // 线形
        Wedge,      // 楔形
        Vee,        // V形
        Square,     // 方形
        Circle,     // 圆形
        Column,     // 纵队
        Custom      // 自定义
    }
    
    // 队形位置数据
    public class FormationSlot
    {
        public Vector3 localPosition;
        public Quaternion localRotation;
        public int slotIndex;
        public GameObject occupiedBy;
        public float arrivalThreshold = 0.5f;
        
        public FormationSlot(Vector3 position, Quaternion rotation, int index)
        {
            localPosition = position;
            localRotation = rotation;
            slotIndex = index;
            occupiedBy = null;
        }
        
        public Vector3 GetWorldPosition(Transform formationAnchor)
        {
            return formationAnchor.TransformPoint(localPosition);
        }
        
        public Quaternion GetWorldRotation(Transform formationAnchor)
        {
            return formationAnchor.rotation * localRotation;
        }
        
        public bool IsOccupied()
        {
            return occupiedBy != null;
        }
        
        public void Occupy(GameObject unit)
        {
            occupiedBy = unit;
        }
        
        public void Vacate()
        {
            occupiedBy = null;
        }
    }
    
    // 队形配置数据
    [System.Serializable]
    public class FormationConfiguration
    {
        public FormationType formationType;
        public float spacing = 2.0f;           // 单位间距
        public float rowSpacing = 2.0f;        // 行间距
        public int unitsPerRow = 5;            // 每行单位数
        public float circleRadius = 5.0f;      // 圆形队形半径
        public float wedgeAngle = 45.0f;       // 楔形角度
        public bool dynamicSpacing = true;     // 动态调整间距
        public float maxSpacing = 4.0f;        // 最大间距
        public float minSpacing = 1.0f;        // 最小间距
    }
    
    // 队形管理器
    public class FormationManager : MonoBehaviour
    {
        [Header("队形配置")]
        public FormationConfiguration formationConfig;
        
        [Header("队形锚点")]
        public Transform formationAnchor;
        public bool autoCreateAnchor = true;
        
        [Header("调试设置")]
        public bool drawFormationGizmos = true;
        public Color formationColor = Color.cyan;
        
        // 队形数据
        private List<FormationSlot> formationSlots;
        private List<GameObject> registeredUnits;
        private Dictionary<GameObject, FormationSlot> unitSlotMap;
        
        // 队形状态
        private bool isFormationActive = true;
        private Vector3 formationForward;
        private Vector3 lastAnchorPosition;
        private Quaternion lastAnchorRotation;
        
        private void Awake()
        {
            InitializeFormationSystem();
        }
        
        private void InitializeFormationSystem()
        {
            // 初始化数据结构
            formationSlots = new List<FormationSlot>();
            registeredUnits = new List<GameObject>();
            unitSlotMap = new Dictionary<GameObject, FormationSlot>();
            
            // 创建或获取队形锚点
            if (formationAnchor == null && autoCreateAnchor)
            {
                GameObject anchorObject = new GameObject("FormationAnchor");
                formationAnchor = anchorObject.transform;
                formationAnchor.position = transform.position;
                formationAnchor.rotation = transform.rotation;
            }
            
            // 设置初始朝向
            formationForward = formationAnchor != null ? 
                formationAnchor.forward : Vector3.forward;
            
            // 生成初始队形
            GenerateFormationSlots();
            
            Debug.Log("队形系统初始化完成");
        }
        
        private void Update()
        {
            if (!isFormationActive || formationAnchor == null)
            {
                return;
            }
            
            // 检测锚点变化
            bool anchorMoved = formationAnchor.position != lastAnchorPosition;
            bool anchorRotated = formationAnchor.rotation != lastAnchorRotation;
            
            if (anchorMoved || anchorRotated)
            {
                UpdateFormationPositions();
                lastAnchorPosition = formationAnchor.position;
                lastAnchorRotation = formationAnchor.rotation;
            }
            
            // 更新单位位置
            UpdateUnitsInFormation();
        }
        
        private void GenerateFormationSlots()
        {
            formationSlots.Clear();
            
            // 根据队形类型生成位置
            switch (formationConfig.formationType)
            {
                case FormationType.Line:
                    GenerateLineFormation();
                    break;
                    
                case FormationType.Wedge:
                    GenerateWedgeFormation();
                    break;
                    
                case FormationType.Vee:
                    GenerateVeeFormation();
                    break;
                    
                case FormationType.Square:
                    GenerateSquareFormation();
                    break;
                    
                case FormationType.Circle:
                    GenerateCircleFormation();
                    break;
                    
                case FormationType.Column:
                    GenerateColumnFormation();
                    break;
                    
                case FormationType.Custom:
                    // 自定义队形需要额外配置
                    break;
            }
            
            Debug.Log($"生成 {formationSlots.Count} 个队形位置");
        }
        
        private void GenerateLineFormation()
        {
            int unitCount = registeredUnits.Count;
            if (unitCount == 0) unitCount = formationConfig.unitsPerRow;
            
            float totalWidth = (unitCount - 1) * formationConfig.spacing;
            float startX = -totalWidth / 2f;
            
            for (int i = 0; i < unitCount; i++)
            {
                float xPos = startX + i * formationConfig.spacing;
                Vector3 localPos = new Vector3(xPos, 0, 0);
                Quaternion localRot = Quaternion.identity;
                
                FormationSlot slot = new FormationSlot(localPos, localRot, i);
                formationSlots.Add(slot);
            }
        }
        
        private void GenerateWedgeFormation()
        {
            int unitCount = registeredUnits.Count;
            if (unitCount == 0) unitCount = formationConfig.unitsPerRow * 2;
            
            // 计算楔形布局
            int rows = Mathf.CeilToInt(Mathf.Sqrt(unitCount));
            int slotIndex = 0;
            
            for (int row = 0; row < rows; row++)
            {
                int unitsInRow = row + 1;
                float rowDepth = -row * formationConfig.rowSpacing;
                float rowWidth = (unitsInRow - 1) * formationConfig.spacing;
                float startX = -rowWidth / 2f;
                
                for (int col = 0; col < unitsInRow; col++)
                {
                    if (slotIndex >= unitCount) break;
                    
                    float xPos = startX + col * formationConfig.spacing;
                    Vector3 localPos = new Vector3(xPos, 0, rowDepth);
                    
                    // 面向中心
                    Vector3 directionToCenter = -localPos.normalized;
                    Quaternion localRot = Quaternion.LookRotation(directionToCenter);
                    
                    FormationSlot slot = new FormationSlot(localPos, localRot, slotIndex);
                    formationSlots.Add(slot);
                    
                    slotIndex++;
                }
            }
        }
        
        private void GenerateVeeFormation()
        {
            int unitCount = registeredUnits.Count;
            if (unitCount == 0) unitCount = formationConfig.unitsPerRow * 2;
            
            // V形队形是楔形的镜像
            GenerateWedgeFormation();
            
            // 复制并镜像队形
            int currentCount = formationSlots.Count;
            for (int i = 0; i < currentCount; i++)
            {
                FormationSlot originalSlot = formationSlots[i];
                FormationSlot mirroredSlot = new FormationSlot(
                    new Vector3(-originalSlot.localPosition.x, 
                               originalSlot.localPosition.y, 
                               originalSlot.localPosition.z),
                    Quaternion.LookRotation(new Vector3(
                        -originalSlot.localRotation.x,
                        originalSlot.localRotation.y,
                        originalSlot.localRotation.z)),
                    formationSlots.Count
                );
                
                formationSlots.Add(mirroredSlot);
            }
        }
        
        private void GenerateSquareFormation()
        {
            int unitCount = registeredUnits.Count;
            if (unitCount == 0) unitCount = formationConfig.unitsPerRow * formationConfig.unitsPerRow;
            
            int sideLength = Mathf.CeilToInt(Mathf.Sqrt(unitCount));
            float halfSize = (sideLength - 1) * formationConfig.spacing / 2f;
            
            int slotIndex = 0;
            for (int x = 0; x < sideLength; x++)
            {
                for (int z = 0; z < sideLength; z++)
                {
                    if (slotIndex >= unitCount) break;
                    
                    float xPos = -halfSize + x * formationConfig.spacing;
                    float zPos = -halfSize + z * formationConfig.spacing;
                    
                    Vector3 localPos = new Vector3(xPos, 0, zPos);
                    Quaternion localRot = Quaternion.identity;
                    
                    FormationSlot slot = new FormationSlot(localPos, localRot, slotIndex);
                    formationSlots.Add(slot);
                    
                    slotIndex++;
                }
            }
        }
        
        private void GenerateCircleFormation()
        {
            int unitCount = registeredUnits.Count;
            if (unitCount == 0) unitCount = formationConfig.unitsPerRow;
            
            float angleStep = 360f / unitCount;
            
            for (int i = 0; i < unitCount; i++)
            {
                float angle = i * angleStep * Mathf.Deg2Rad;
                float xPos = Mathf.Cos(angle) * formationConfig.circleRadius;
                float zPos = Mathf.Sin(angle) * formationConfig.circleRadius;
                
                Vector3 localPos = new Vector3(xPos, 0, zPos);
                
                // 面向圆心
                Vector3 directionToCenter = -localPos.normalized;
                Quaternion localRot = Quaternion.LookRotation(directionToCenter);
                
                FormationSlot slot = new FormationSlot(localPos, localRot, i);
                formationSlots.Add(slot);
            }
        }
        
        private void GenerateColumnFormation()
        {
            int unitCount = registeredUnits.Count;
            if (unitCount == 0) unitCount = formationConfig.unitsPerRow * 3;
            
            int columns = Mathf.Min(formationConfig.unitsPerRow, unitCount);
            int rows = Mathf.CeilToInt((float)unitCount / columns);
            
            float columnWidth = (columns - 1) * formationConfig.spacing;
            float columnStartX = -columnWidth / 2f;
            
            int slotIndex = 0;
            for (int col = 0; col < columns; col++)
            {
                for (int row = 0; row < rows; row++)
                {
                    if (slotIndex >= unitCount) break;
                    
                    float xPos = columnStartX + col * formationConfig.spacing;
                    float zPos = -row * formationConfig.rowSpacing;
                    
                    Vector3 localPos = new Vector3(xPos, 0, zPos);
                    Quaternion localRot = Quaternion.identity;
                    
                    FormationSlot slot = new FormationSlot(localPos, localRot, slotIndex);
                    formationSlots.Add(slot);
                    
                    slotIndex++;
                }
            }
        }
        
        private void UpdateFormationPositions()
        {
            // 根据动态间距调整队形
            if (formationConfig.dynamicSpacing)
            {
                AdjustDynamicSpacing();
            }
        }
        
        private void AdjustDynamicSpacing()
        {
            // 根据单位数量动态调整间距
            int unitCount = registeredUnits.Count;
            if (unitCount <= 1) return;
            
            // 计算理想间距
            float idealSpacing = Mathf.Lerp(
                formationConfig.minSpacing,
                formationConfig.maxSpacing,
                Mathf.InverseLerp(1, 20, unitCount)
            );
            
            // 平滑过渡到新间距
            formationConfig.spacing = Mathf.Lerp(
                formationConfig.spacing,
                idealSpacing,
                Time.deltaTime * 2f
            );
            
            // 重新生成队形
            GenerateFormationSlots();
            ReassignUnitsToSlots();
        }
        
        public void RegisterUnit(GameObject unit)
        {
            if (registeredUnits.Contains(unit))
            {
                return;
            }
            
            registeredUnits.Add(unit);
            
            // 分配队形位置
            AssignUnitToSlot(unit);
            
            // 重新生成队形以适应新单位
            GenerateFormationSlots();
            ReassignUnitsToSlots();
            
            Debug.Log($"单位 {unit.name} 已注册到队形系统");
        }
        
        public void UnregisterUnit(GameObject unit)
        {
            if (!registeredUnits.Contains(unit))
            {
                return;
            }
            
            // 释放占用的位置
            if (unitSlotMap.ContainsKey(unit))
            {
                FormationSlot slot = unitSlotMap[unit];
                slot.Vacate();
                unitSlotMap.Remove(unit);
            }
            
            registeredUnits.Remove(unit);
            
            // 重新生成队形
            GenerateFormationSlots();
            ReassignUnitsToSlots();
            
            Debug.Log($"单位 {unit.name} 已从队形系统注销");
        }
        
        private void AssignUnitToSlot(GameObject unit)
        {
            // 寻找最近的空位置
            FormationSlot nearestSlot = null;
            float nearestDistance = float.MaxValue;
            
            Vector3 unitPosition = unit.transform.position;
            
            foreach (FormationSlot slot in formationSlots)
            {
                if (slot.IsOccupied())
                {
                    continue;
                }
                
                Vector3 slotWorldPos = slot.GetWorldPosition(formationAnchor);
                float distance = Vector3.Distance(unitPosition, slotWorldPos);
                
                if (distance < nearestDistance)
                {
                    nearestDistance = distance;
                    nearestSlot = slot;
                }
            }
            
            if (nearestSlot != null)
            {
                nearestSlot.Occupy(unit);
                unitSlotMap[unit] = nearestSlot;
            }
        }
        
        private void ReassignUnitsToSlots()
        {
            // 清空所有位置
            foreach (FormationSlot slot in formationSlots)
            {
                slot.Vacate();
            }
            unitSlotMap.Clear();
            
            // 重新分配所有单位
            foreach (GameObject unit in registeredUnits)
            {
                AssignUnitToSlot(unit);
            }
        }
        
        private void UpdateUnitsInFormation()
        {
            foreach (KeyValuePair<GameObject, FormationSlot> entry in unitSlotMap)
            {
                GameObject unit = entry.Key;
                FormationSlot slot = entry.Value;
                
                if (unit == null)
                {
                    continue;
                }
                
                // 获取目标位置和旋转
                Vector3 targetPosition = slot.GetWorldPosition(formationAnchor);
                Quaternion targetRotation = slot.GetWorldRotation(formationAnchor);
                
                // 计算移动方向
                Vector3 moveDirection = targetPosition - unit.transform.position;
                float distance = moveDirection.magnitude;
                
                // 如果距离较远,则移动单位
                if (distance > slot.arrivalThreshold)
                {
                    // 获取单位的移动组件(这里简化处理)
                    // 实际项目中可能需要根据具体的移动系统调整
                    if (unit.TryGetComponent<UnityEngine.AI.NavMeshAgent>(out var navAgent))
                    {
                        navAgent.SetDestination(targetPosition);
                        navAgent.speed = CalculateFormationSpeed(distance);
                    }
                    else
                    {
                        // 简单的位置插值
                        unit.transform.position = Vector3.MoveTowards(
                            unit.transform.position,
                            targetPosition,
                            Time.deltaTime * 5f
                        );
                    }
                    
                    // 平滑旋转
                    unit.transform.rotation = Quaternion.Slerp(
                        unit.transform.rotation,
                        targetRotation,
                        Time.deltaTime * 3f
                    );
                }
            }
        }
        
        private float CalculateFormationSpeed(float distanceToTarget)
        {
            // 根据距离调整速度,确保队形保持
            if (distanceToTarget > 10f)
            {
                return 8f; // 快速接近
            }
            else if (distanceToTarget > 3f)
            {
                return 4f; // 中等速度
            }
            else
            {
                return 2f; // 慢速微调
            }
        }
        
        public void ChangeFormationType(FormationType newType)
        {
            formationConfig.formationType = newType;
            GenerateFormationSlots();
            ReassignUnitsToSlots();
            
            Debug.Log($"队形已切换为: {newType}");
        }
        
        public void SetFormationAnchor(Transform newAnchor)
        {
            formationAnchor = newAnchor;
            UpdateFormationPositions();
        }
        
        public void SetFormationActive(bool active)
        {
            isFormationActive = active;
        }
        
        public Vector3 GetSlotPositionForUnit(GameObject unit)
        {
            if (unitSlotMap.ContainsKey(unit))
            {
                return unitSlotMap[unit].GetWorldPosition(formationAnchor);
            }
            
            return unit.transform.position;
        }
        
        private void OnDrawGizmos()
        {
            if (!drawFormationGizmos || formationSlots == null)
            {
                return;
            }
            
            // 绘制队形位置
            Gizmos.color = formationColor;
            
            foreach (FormationSlot slot in formationSlots)
            {
                Vector3 worldPos = formationAnchor != null ? 
                    slot.GetWorldPosition(formationAnchor) : 
                    transform.TransformPoint(slot.localPosition);
                
                // 绘制位置点
                Gizmos.DrawSphere(worldPos, 0.2f);
                
                // 绘制朝向
                Quaternion worldRot = formationAnchor != null ?
                    slot.GetWorldRotation(formationAnchor) :
                    transform.rotation * slot.localRotation;
                    
                Vector3 forward = worldRot * Vector3.forward;
                Gizmos.DrawLine(worldPos, worldPos + forward * 0.5f);
                
                // 绘制编号
                #if UNITY_EDITOR
                UnityEditor.Handles.Label(worldPos + Vector3.up * 0.3f, 
                    slot.slotIndex.ToString());
                #endif
            }
            
            // 绘制队形锚点
            if (formationAnchor != null)
            {
                Gizmos.color = Color.yellow;
                Gizmos.DrawSphere(formationAnchor.position, 0.3f);
                Gizmos.DrawLine(formationAnchor.position, 
                    formationAnchor.position + formationAnchor.forward);
            }
        }
    }
    
    // 队形单位控制器
    public class FormationUnitController : MonoBehaviour
    {
        [Header("队形设置")]
        public FormationManager formationManager;
        public bool autoRegister = true;
        
        [Header("移动设置")]
        public float moveSpeed = 5f;
        public float rotationSpeed = 120f;
        public float formationTolerance = 1f;
        
        // 状态变量
        private bool isInFormation = false;
        private Vector3 formationTarget;
        private Quaternion formationRotation;
        
        private void Start()
        {
            if (autoRegister && formationManager != null)
            {
                formationManager.RegisterUnit(gameObject);
                isInFormation = true;
            }
        }
        
        private void Update()
        {
            if (!isInFormation || formationManager == null)
            {
                return;
            }
            
            // 获取队形目标位置
            formationTarget = formationManager.GetSlotPositionForUnit(gameObject);
            
            // 计算移动方向
            Vector3 toTarget = formationTarget - transform.position;
            float distance = toTarget.magnitude;
            
            // 如果在容差范围内,则停止移动
            if (distance <= formationTolerance)
            {
                return;
            }
            
            // 移动和旋转
            MoveToFormationPosition(toTarget, distance);
        }
        
        private void MoveToFormationPosition(Vector3 direction, float distance)
        {
            // 标准化方向
            direction.Normalize();
            
            // 计算移动量
            float moveDistance = moveSpeed * Time.deltaTime;
            if (moveDistance > distance)
            {
                moveDistance = distance;
            }
            
            // 应用移动
            transform.position += direction * moveDistance;
            
            // 平滑旋转到目标方向
            if (direction != Vector3.zero)
            {
                Quaternion targetRotation = Quaternion.LookRotation(direction);
                transform.rotation = Quaternion.Slerp(
                    transform.rotation,
                    targetRotation,
                    rotationSpeed * Time.deltaTime
                );
            }
        }
        
        public void JoinFormation(FormationManager manager)
        {
            formationManager = manager;
            formationManager.RegisterUnit(gameObject);
            isInFormation = true;
        }
        
        public void LeaveFormation()
        {
            if (formationManager != null)
            {
                formationManager.UnregisterUnit(gameObject);
            }
            isInFormation = false;
        }
        
        private void OnDestroy()
        {
            if (formationManager != null)
            {
                formationManager.UnregisterUnit(gameObject);
            }
        }
    }
}

6.2.3 队形系统的优化与扩展

在商业项目中,队形系统需要进一步优化以适应复杂场景。以下是一些优化策略的实现:

// 高级队形管理器
public class AdvancedFormationManager : FormationManager
{
    [System.Serializable]
    public class AdvancedFormationSettings
    {
        public bool useObstacleAvoidance = true;
        public float obstacleDetectionRange = 5f;
        public LayerMask obstacleMask = -1;
        public bool useLaneSystem = true;
        public int maxLanes = 3;
        public bool adaptiveFormation = true;
        public float adaptationSpeed = 2f;
    }
    
    public AdvancedFormationSettings advancedSettings;
    
    private List<Vector3> formationLanes;
    private Dictionary<GameObject, int> unitLaneMap;
    
    protected override void InitializeFormationSystem()
    {
        base.InitializeFormationSystem();
        
        // 初始化高级系统
        formationLanes = new List<Vector3>();
        unitLaneMap = new Dictionary<GameObject, int>();
        
        // 生成车道系统
        if (advancedSettings.useLaneSystem)
        {
            GenerateFormationLanes();
        }
    }
    
    private void GenerateFormationLanes()
    {
        formationLanes.Clear();
        
        // 根据队形类型生成车道
        switch (formationConfig.formationType)
        {
            case FormationType.Line:
            case FormationType.Column:
                GenerateStraightLanes();
                break;
                
            case FormationType.Wedge:
            case FormationType.Vee:
                GenerateAngledLanes();
                break;
                
            default:
                // 其他队形不使用车道系统
                break;
        }
    }
    
    private void GenerateStraightLanes()
    {
        int laneCount = Mathf.Min(advancedSettings.maxLanes, registeredUnits.Count);
        float laneSpacing = formationConfig.spacing;
        
        for (int i = 0; i < laneCount; i++)
        {
            float laneOffset = (i - (laneCount - 1) / 2f) * laneSpacing;
            Vector3 laneDirection = formationAnchor.forward;
            formationLanes.Add(laneDirection * laneOffset);
        }
    }
    
    private void GenerateAngledLanes()
    {
        // 楔形和V形队形的车道生成逻辑
        int laneCount = advancedSettings.maxLanes;
        float angleStep = formationConfig.wedgeAngle / (laneCount - 1);
        
        for (int i = 0; i < laneCount; i++)
        {
            float angle = (i - (laneCount - 1) / 2f) * angleStep;
            Quaternion rotation = Quaternion.Euler(0, angle, 0);
            Vector3 laneDirection = rotation * formationAnchor.forward;
            formationLanes.Add(laneDirection);
        }
    }
    
    protected override void UpdateUnitsInFormation()
    {
        if (advancedSettings.useObstacleAvoidance)
        {
            UpdateFormationWithObstacleAvoidance();
        }
        else
        {
            base.UpdateUnitsInFormation();
        }
    }
    
    private void UpdateFormationWithObstacleAvoidance()
    {
        foreach (KeyValuePair<GameObject, FormationSlot> entry in unitSlotMap)
        {
            GameObject unit = entry.Key;
            FormationSlot slot = entry.Value;
            
            if (unit == null)
            {
                continue;
            }
            
            Vector3 targetPosition = slot.GetWorldPosition(formationAnchor);
            
            // 检测障碍物
            Vector3 avoidanceOffset = CalculateObstacleAvoidance(unit, targetPosition);
            Vector3 adjustedTarget = targetPosition + avoidanceOffset;
            
            // 更新单位位置
            UpdateUnitPosition(unit, adjustedTarget);
        }
    }
    
    private Vector3 CalculateObstacleAvoidance(GameObject unit, Vector3 targetPosition)
    {
        Vector3 avoidanceOffset = Vector3.zero;
        
        // 射线检测前方障碍物
        Vector3 direction = (targetPosition - unit.transform.position).normalized;
        float detectionRange = advancedSettings.obstacleDetectionRange;
        
        RaycastHit hit;
        if (Physics.Raycast(unit.transform.position, direction, out hit, 
            detectionRange, advancedSettings.obstacleMask))
        {
            // 计算避障偏移
            Vector3 hitNormal = hit.normal;
            avoidanceOffset = hitNormal * detectionRange * 0.5f;
            
            // 根据队形类型调整避障策略
            if (advancedSettings.useLaneSystem && unitLaneMap.ContainsKey(unit))
            {
                avoidanceOffset = AdjustAvoidanceForLane(unit, avoidanceOffset);
            }
        }
        
        return avoidanceOffset;
    }
    
    private Vector3 AdjustAvoidanceForLane(GameObject unit, Vector3 avoidanceOffset)
    {
        int laneIndex = unitLaneMap[unit];
        
        // 尝试保持在同一车道上
        Vector3 laneDirection = formationLanes[laneIndex];
        Vector3 projectedOffset = Vector3.Project(avoidanceOffset, laneDirection);
        
        // 限制偏移量,避免偏离车道太远
        float maxLaneDeviation = formationConfig.spacing * 0.5f;
        if (projectedOffset.magnitude > maxLaneDeviation)
        {
            projectedOffset = projectedOffset.normalized * maxLaneDeviation;
        }
        
        return projectedOffset;
    }
    
    public void AssignUnitToLane(GameObject unit, int laneIndex)
    {
        if (laneIndex >= 0 && laneIndex < formationLanes.Count)
        {
            unitLaneMap[unit] = laneIndex;
        }
    }
    
    public void AutoAssignLanes()
    {
        if (!advancedSettings.useLaneSystem || formationLanes.Count == 0)
        {
            return;
        }
        
        // 根据单位位置自动分配车道
        int unitsPerLane = Mathf.CeilToInt((float)registeredUnits.Count / formationLanes.Count);
        int currentLane = 0;
        int unitsInCurrentLane = 0;
        
        foreach (GameObject unit in registeredUnits)
        {
            AssignUnitToLane(unit, currentLane);
            unitsInCurrentLane++;
            
            if (unitsInCurrentLane >= unitsPerLane)
            {
                currentLane++;
                unitsInCurrentLane = 0;
                
                if (currentLane >= formationLanes.Count)
                {
                    currentLane = formationLanes.Count - 1;
                }
            }
        }
    }
    
    private void UpdateUnitPosition(GameObject unit, Vector3 targetPosition)
    {
        // 简化的位置更新
        // 实际项目中应使用单位的移动系统
        float moveSpeed = 5f;
        float arrivalThreshold = 0.5f;
        
        Vector3 toTarget = targetPosition - unit.transform.position;
        float distance = toTarget.magnitude;
        
        if (distance > arrivalThreshold)
        {
            unit.transform.position = Vector3.MoveTowards(
                unit.transform.position,
                targetPosition,
                moveSpeed * Time.deltaTime
            );
        }
    }
}

6.3 协作寻路算法:扩展A*实现

传统的A算法适用于单个单位的路径规划,但在群体智能中,我们需要考虑多个单位的协作和战术需求。扩展的A算法能够为群体提供更加智能的路径选择。

6.3.1 A*算法基础与扩展

A*算法通过评估每个节点的代价函数(f(n) = g(n) + h(n))来寻找最优路径,其中g(n)是从起点到当前节点的实际代价,h(n)是从当前节点到目标的估计代价。在协作寻路中,我们需要扩展这个代价函数以考虑群体因素:

  1. 群体协调代价:考虑其他单位的位置和路径
  2. 战术优势代价:考虑路径的隐蔽性和安全性
  3. 队形保持代价:确保群体能够保持队形移动
  4. 资源消耗代价:考虑能量、弹药等资源消耗

6.3.2 Unity中的协作A*实现

以下是一个扩展的A*算法实现,专门为群体协作设计:

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

namespace CooperativePathfinding
{
    // 协作路径节点
    public class CooperativePathNode
    {
        public Vector3 position;
        public float gCost;           // 从起点到当前节点的实际代价
        public float hCost;           // 从当前节点到目标的估计代价
        public float fCost { get { return gCost + hCost; } } // 总代价
        public CooperativePathNode parent;
        public bool isWalkable = true;
        
        // 协作扩展字段
        public float groupPenalty;    // 群体惩罚(避免拥挤)
        public float tacticalValue;   // 战术价值(隐蔽性等)
        public float formationCost;   // 队形保持代价
        public int occupancyCount;    // 当前占用单位数
        
        public CooperativePathNode(Vector3 pos)
        {
            position = pos;
            gCost = float.MaxValue;
            hCost = 0;
            groupPenalty = 0;
            tacticalValue = 1.0f; // 默认值
            formationCost = 0;
            occupancyCount = 0;
        }
        
        public float GetAdjustedFCost(float groupWeight, float tacticalWeight)
        {
            // 调整后的代价函数
            float baseCost = gCost + hCost;
            float adjustedCost = baseCost * 
                (1 + groupPenalty * groupWeight) * 
                tacticalValue * tacticalWeight;
            
            return adjustedCost + formationCost;
        }
    }
    
    // 协作A*路径规划器
    public class CooperativeAStarPlanner : MonoBehaviour
    {
        [System.Serializable]
        public class PlanningSettings
        {
            public float nodeSpacing = 1.0f;
            public float maxSlopeAngle = 45.0f;
            public LayerMask walkableMask = -1;
            public LayerMask obstacleMask = -1;
            
            // 代价权重
            public float groupAvoidanceWeight = 0.5f;
            public float tacticalAdvantageWeight = 0.3f;
            public float formationWeight = 0.2f;
            public float resourceWeight = 0.1f;
            
            // 性能设置
            public int maxIterations = 1000;
            public bool useMultiThreading = false;
            public float planningTimeout = 5.0f;
        }
        
        [Header("规划设置")]
        public PlanningSettings settings;
        
        [Header("调试")]
        public bool drawGridGizmos = false;
        public bool drawPathGizmos = true;
        public Color walkableColor = Color.green;
        public Color obstacleColor = Color.red;
        
        // 网格数据
        private Dictionary<Vector3Int, CooperativePathNode> gridNodes;
        private Bounds gridBounds;
        private float gridCellSize;
        
        // 群体数据
        private List<GameObject> groupMembers;
        private Dictionary<GameObject, CooperativePathNode> memberPositions;
        
        // 路径缓存
        private Dictionary<string, List<Vector3>> pathCache;
        
        private void Awake()
        {
            InitializePathfindingSystem();
        }
        
        private void InitializePathfindingSystem()
        {
            gridNodes = new Dictionary<Vector3Int, CooperativePathNode>();
            groupMembers = new List<GameObject>();
            memberPositions = new Dictionary<GameObject, CooperativePathNode>();
            pathCache = new Dictionary<string, List<Vector3>>();
            
            gridCellSize = settings.nodeSpacing;
            
            Debug.Log("协作A*路径规划系统初始化完成");
        }
        
        public void RegisterGroupMember(GameObject member)
        {
            if (!groupMembers.Contains(member))
            {
                groupMembers.Add(member);
                UpdateMemberPosition(member);
            }
        }
        
        public void UnregisterGroupMember(GameObject member)
        {
            if (groupMembers.Contains(member))
            {
                groupMembers.Remove(member);
                if (memberPositions.ContainsKey(member))
                {
                    memberPositions.Remove(member);
                }
            }
        }
        
        private void UpdateMemberPosition(GameObject member)
        {
            Vector3 memberPos = member.transform.position;
            CooperativePathNode node = GetOrCreateNodeAtPosition(memberPos);
            
            if (node != null)
            {
                memberPositions[member] = node;
                node.occupancyCount++;
            }
        }
        
        private void Update()
        {
            // 更新所有成员位置
            foreach (GameObject member in groupMembers)
            {
                UpdateMemberPosition(member);
            }
            
            // 更新网格节点代价
            UpdateGridCosts();
        }
        
        private void UpdateGridCosts()
        {
            // 更新每个节点的群体惩罚
            foreach (CooperativePathNode node in gridNodes.Values)
            {
                UpdateNodeGroupPenalty(node);
                UpdateNodeTacticalValue(node);
                UpdateNodeFormationCost(node);
            }
        }
        
        private void UpdateNodeGroupPenalty(CooperativePathNode node)
        {
            // 根据附近单位的数量计算群体惩罚
            int nearbyUnits = 0;
            float maxRadius = settings.nodeSpacing * 3f;
            
            foreach (GameObject member in groupMembers)
            {
                if (memberPositions.ContainsKey(member))
                {
                    CooperativePathNode memberNode = memberPositions[member];
                    float distance = Vector3.Distance(node.position, memberNode.position);
                    
                    if (distance <= maxRadius)
                    {
                        nearbyUnits++;
                    }
                }
            }
            
            // 惩罚公式:指数增长避免过度拥挤
            node.groupPenalty = Mathf.Pow(1.5f, nearbyUnits) - 1.0f;
        }
        
        private void UpdateNodeTacticalValue(CooperativePathNode node)
        {
            // 计算战术价值(隐蔽性、安全性等)
            float visibility = CalculateVisibility(node.position);
            float coverValue = CalculateCoverValue(node.position);
            float elevationValue = CalculateElevationValue(node.position);
            
            // 综合战术价值(值越低越好)
            node.tacticalValue = 1.0f + 
                visibility * 0.5f - 
                coverValue * 0.3f + 
                elevationValue * 0.2f;
        }
        
        private float CalculateVisibility(Vector3 position)
        {
            // 简化的可见性计算
            // 实际项目中需要更复杂的视线检测
            int rayCount = 8;
            float maxDistance = 20f;
            float visibleCount = 0;
            
            for (int i = 0; i < rayCount; i++)
            {
                float angle = i * (360f / rayCount) * Mathf.Deg2Rad;
                Vector3 direction = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle));
                
                RaycastHit hit;
                if (Physics.Raycast(position, direction, out hit, maxDistance, settings.obstacleMask))
                {
                    // 有障碍物遮挡,减少可见性
                }
                else
                {
                    visibleCount++;
                }
            }
            
            return visibleCount / rayCount;
        }
        
        private float CalculateCoverValue(Vector3 position)
        {
            // 计算掩体价值
            float coverValue = 0f;
            float checkRadius = 2f;
            
            Collider[] nearbyColliders = Physics.OverlapSphere(position, checkRadius, settings.obstacleMask);
            foreach (Collider collider in nearbyColliders)
            {
                // 简单的掩体价值评估
                Vector3 toCover = collider.transform.position - position;
                float distance = toCover.magnitude;
                
                if (distance > 0)
                {
                    coverValue += 1.0f / distance;
                }
            }
            
            return Mathf.Clamp01(coverValue);
        }
        
        private float CalculateElevationValue(Vector3 position)
        {
            // 计算高度优势
            // 实际项目中需要地形高度数据
            return 0.1f; // 简化处理
        }
        
        private void UpdateNodeFormationCost(CooperativePathNode node)
        {
            // 计算队形保持代价
            if (groupMembers.Count == 0)
            {
                node.formationCost = 0;
                return;
            }
            
            // 计算到群体中心的距离
            Vector3 groupCenter = CalculateGroupCenter();
            float distanceToCenter = Vector3.Distance(node.position, groupCenter);
            float maxFormationRadius = settings.nodeSpacing * Mathf.Sqrt(groupMembers.Count);
            
            // 距离中心越远,代价越高
            node.formationCost = Mathf.Clamp01(distanceToCenter / maxFormationRadius) * 2.0f;
        }
        
        private Vector3 CalculateGroupCenter()
        {
            if (groupMembers.Count == 0)
            {
                return Vector3.zero;
            }
            
            Vector3 sum = Vector3.zero;
            foreach (GameObject member in groupMembers)
            {
                sum += member.transform.position;
            }
            
            return sum / groupMembers.Count;
        }
        
        public List<Vector3> FindCooperativePath(Vector3 start, Vector3 target, GameObject requester = null)
        {
            // 生成缓存键
            string cacheKey = GenerateCacheKey(start, target, requester);
            
            // 检查缓存
            if (pathCache.ContainsKey(cacheKey))
            {
                return new List<Vector3>(pathCache[cacheKey]);
            }
            
            // 执行协作A*搜索
            List<Vector3> path = PerformCooperativeAStar(start, target, requester);
            
            // 缓存结果
            if (path != null && path.Count > 0)
            {
                pathCache[cacheKey] = new List<Vector3>(path);
                
                // 限制缓存大小
                if (pathCache.Count > 100)
                {
                    RemoveOldestCacheEntry();
                }
            }
            
            return path;
        }
        
        private List<Vector3> PerformCooperativeAStar(Vector3 start, Vector3 target, GameObject requester)
        {
            System.Diagnostics.Stopwatch timer = System.Diagnostics.Stopwatch.StartNew();
            
            // 获取起始和目标节点
            CooperativePathNode startNode = GetOrCreateNodeAtPosition(start);
            CooperativePathNode targetNode = GetOrCreateNodeAtPosition(target);
            
            if (startNode == null || targetNode == null || !startNode.isWalkable || !targetNode.isWalkable)
            {
                Debug.LogWarning("起始点或目标点不可行走");
                return null;
            }
            
            // 初始化开放和关闭列表
            List<CooperativePathNode> openSet = new List<CooperativePathNode>();
            HashSet<CooperativePathNode> closedSet = new HashSet<CooperativePathNode>();
            
            startNode.gCost = 0;
            startNode.hCost = CalculateHeuristic(startNode, targetNode);
            openSet.Add(startNode);
            
            int iterations = 0;
            
            while (openSet.Count > 0 && iterations < settings.maxIterations)
            {
                iterations++;
                
                // 获取当前代价最低的节点
                CooperativePathNode currentNode = openSet[0];
                for (int i = 1; i < openSet.Count; i++)
                {
                    float currentFCost = currentNode.GetAdjustedFCost(
                        settings.groupAvoidanceWeight,
                        settings.tacticalAdvantageWeight
                    );
                    
                    float compareFCost = openSet[i].GetAdjustedFCost(
                        settings.groupAvoidanceWeight,
                        settings.tacticalAdvantageWeight
                    );
                    
                    if (compareFCost < currentFCost || 
                        (Mathf.Approximately(compareFCost, currentFCost) && 
                         openSet[i].hCost < currentNode.hCost))
                    {
                        currentNode = openSet[i];
                    }
                }
                
                // 检查是否到达目标
                if (currentNode == targetNode)
                {
                    timer.Stop();
                    Debug.Log($"路径找到,迭代次数: {iterations}, 耗时: {timer.ElapsedMilliseconds}ms");
                    return RetracePath(startNode, targetNode);
                }
                
                // 移动到关闭列表
                openSet.Remove(currentNode);
                closedSet.Add(currentNode);
                
                // 检查邻居节点
                List<CooperativePathNode> neighbors = GetNeighbors(currentNode);
                foreach (CooperativePathNode neighbor in neighbors)
                {
                    if (!neighbor.isWalkable || closedSet.Contains(neighbor))
                    {
                        continue;
                    }
                    
                    // 计算新的gCost
                    float movementCost = currentNode.gCost + 
                        GetDistance(currentNode, neighbor) * 
                        GetTerrainCost(neighbor);
                    
                    // 考虑请求者的特殊需求
                    if (requester != null && memberPositions.ContainsKey(requester))
                    {
                        movementCost += GetRequesterSpecificCost(requester, neighbor);
                    }
                    
                    if (movementCost < neighbor.gCost || !openSet.Contains(neighbor))
                    {
                        neighbor.gCost = movementCost;
                        neighbor.hCost = CalculateHeuristic(neighbor, targetNode);
                        neighbor.parent = currentNode;
                        
                        if (!openSet.Contains(neighbor))
                        {
                            openSet.Add(neighbor);
                        }
                    }
                }
                
                // 检查超时
                if (timer.Elapsed.TotalSeconds > settings.planningTimeout)
                {
                    Debug.LogWarning("路径规划超时");
                    break;
                }
            }
            
            timer.Stop();
            Debug.LogWarning($"路径未找到,迭代次数: {iterations}, 耗时: {timer.ElapsedMilliseconds}ms");
            return null;
        }
        
        private float GetDistance(CooperativePathNode a, CooperativePathNode b)
        {
            // 欧几里得距离
            return Vector3.Distance(a.position, b.position);
        }
        
        private float GetTerrainCost(CooperativePathNode node)
        {
            // 地形代价(坡度、类型等)
            // 简化处理:检查坡度
            float slopeCost = 1.0f;
            
            // 检查节点周围的高度变化
            Vector3[] checkDirections = {
                Vector3.forward, Vector3.back, 
                Vector3.left, Vector3.right
            };
            
            foreach (Vector3 dir in checkDirections)
            {
                Vector3 checkPos = node.position + dir * gridCellSize;
                CooperativePathNode neighbor = GetNodeAtPosition(checkPos);
                
                if (neighbor != null)
                {
                    float heightDiff = Mathf.Abs(node.position.y - neighbor.position.y);
                    float slope = Mathf.Atan(heightDiff / gridCellSize) * Mathf.Rad2Deg;
                    
                    if (slope > settings.maxSlopeAngle)
                    {
                        return float.MaxValue; // 不可通过
                    }
                    
                    slopeCost += heightDiff * 0.5f;
                }
            }
            
            return slopeCost;
        }
        
        private float GetRequesterSpecificCost(GameObject requester, CooperativePathNode node)
        {
            // 根据请求者的特殊需求调整代价
            float specificCost = 0;
            
            // 示例:如果请求者是狙击手,偏好高处
            if (requester.CompareTag("Sniper"))
            {
                // 高处有优势
                specificCost -= node.position.y * 0.1f;
            }
            // 示例:如果请求者是坦克,偏好平坦地形
            else if (requester.CompareTag("Tank"))
            {
                // 检查地形平坦度
                float flatnessPenalty = CalculateTerrainRoughness(node.position);
                specificCost += flatnessPenalty * 0.2f;
            }
            
            return specificCost;
        }
        
        private float CalculateTerrainRoughness(Vector3 position)
        {
            // 计算地形粗糙度
            // 简化处理:随机值
            return Random.Range(0f, 1f);
        }
        
        private float CalculateHeuristic(CooperativePathNode a, CooperativePathNode b)
        {
            // 使用对角线距离作为启发函数
            float dx = Mathf.Abs(a.position.x - b.position.x);
            float dz = Mathf.Abs(a.position.z - b.position.z);
            
            return (dx + dz) + (Mathf.Sqrt(2) - 2) * Mathf.Min(dx, dz);
        }
        
        private List<CooperativePathNode> GetNeighbors(CooperativePathNode node)
        {
            List<CooperativePathNode> neighbors = new List<CooperativePathNode>();
            
            // 8方向邻居
            for (int x = -1; x <= 1; x++)
            {
                for (int z = -1; z <= 1; z++)
                {
                    if (x == 0 && z == 0)
                    {
                        continue;
                    }
                    
                    Vector3 neighborPos = node.position + 
                        new Vector3(x * gridCellSize, 0, z * gridCellSize);
                    
                    CooperativePathNode neighbor = GetOrCreateNodeAtPosition(neighborPos);
                    if (neighbor != null && neighbor.isWalkable)
                    {
                        neighbors.Add(neighbor);
                    }
                }
            }
            
            return neighbors;
        }
        
        private List<Vector3> RetracePath(CooperativePathNode startNode, CooperativePathNode endNode)
        {
            List<Vector3> path = new List<Vector3>();
            CooperativePathNode currentNode = endNode;
            
            while (currentNode != startNode)
            {
                path.Add(currentNode.position);
                currentNode = currentNode.parent;
                
                if (currentNode == null)
                {
                    break;
                }
            }
            
            path.Reverse();
            
            // 简化路径(移除不必要的中间点)
            path = SimplifyPath(path);
            
            return path;
        }
        
        private List<Vector3> SimplifyPath(List<Vector3> path)
        {
            if (path.Count <= 2)
            {
                return path;
            }
            
            List<Vector3> simplified = new List<Vector3>();
            simplified.Add(path[0]);
            
            for (int i = 1; i < path.Count - 1; i++)
            {
                Vector3 prevDir = (path[i] - simplified[simplified.Count - 1]).normalized;
                Vector3 nextDir = (path[i + 1] - path[i]).normalized;
                
                // 如果方向变化超过阈值,保留该点
                float angle = Vector3.Angle(prevDir, nextDir);
                if (angle > 5f) // 5度阈值
                {
                    simplified.Add(path[i]);
                }
            }
            
            simplified.Add(path[path.Count - 1]);
            return simplified;
        }
        
        private CooperativePathNode GetOrCreateNodeAtPosition(Vector3 position)
        {
            Vector3Int gridCoord = WorldToGridCoordinate(position);
            
            if (gridNodes.ContainsKey(gridCoord))
            {
                return gridNodes[gridCoord];
            }
            
            // 创建新节点
            CooperativePathNode newNode = new CooperativePathNode(position);
            
            // 检查是否可行走
            newNode.isWalkable = IsPositionWalkable(position);
            
            gridNodes[gridCoord] = newNode;
            
            // 更新网格边界
            UpdateGridBounds(position);
            
            return newNode;
        }
        
        private CooperativePathNode GetNodeAtPosition(Vector3 position)
        {
            Vector3Int gridCoord = WorldToGridCoordinate(position);
            
            if (gridNodes.ContainsKey(gridCoord))
            {
                return gridNodes[gridCoord];
            }
            
            return null;
        }
        
        private Vector3Int WorldToGridCoordinate(Vector3 worldPosition)
        {
            int x = Mathf.RoundToInt(worldPosition.x / gridCellSize);
            int y = Mathf.RoundToInt(worldPosition.y / gridCellSize);
            int z = Mathf.RoundToInt(worldPosition.z / gridCellSize);
            
            return new Vector3Int(x, y, z);
        }
        
        private bool IsPositionWalkable(Vector3 position)
        {
            // 检查地面
            float groundCheckDistance = 1.0f;
            RaycastHit groundHit;
            bool hasGround = Physics.Raycast(position + Vector3.up * 0.5f, 
                Vector3.down, out groundHit, groundCheckDistance, settings.walkableMask);
            
            if (!hasGround)
            {
                return false;
            }
            
            // 检查障碍物
            float obstacleCheckRadius = gridCellSize * 0.5f;
            Collider[] obstacles = Physics.OverlapSphere(position, 
                obstacleCheckRadius, settings.obstacleMask);
            
            return obstacles.Length == 0;
        }
        
        private void UpdateGridBounds(Vector3 position)
        {
            if (gridNodes.Count == 1)
            {
                gridBounds = new Bounds(position, Vector3.one * gridCellSize);
            }
            else
            {
                gridBounds.Encapsulate(position);
            }
        }
        
        private string GenerateCacheKey(Vector3 start, Vector3 target, GameObject requester)
        {
            // 生成基于起点、目标和请求者的缓存键
            Vector3Int startGrid = WorldToGridCoordinate(start);
            Vector3Int targetGrid = WorldToGridCoordinate(target);
            string requesterId = requester != null ? requester.GetInstanceID().ToString() : "null";
            
            return $"{startGrid.x},{startGrid.z}|{targetGrid.x},{targetGrid.z}|{requesterId}";
        }
        
        private void RemoveOldestCacheEntry()
        {
            // 移除最旧的缓存条目(简化:移除第一个)
            if (pathCache.Count > 0)
            {
                string firstKey = pathCache.Keys.First();
                pathCache.Remove(firstKey);
            }
        }
        
        private void OnDrawGizmos()
        {
            if (!drawGridGizmos || gridNodes == null)
            {
                return;
            }
            
            // 绘制网格
            foreach (KeyValuePair<Vector3Int, CooperativePathNode> entry in gridNodes)
            {
                CooperativePathNode node = entry.Value;
                
                Gizmos.color = node.isWalkable ? walkableColor : obstacleColor;
                Gizmos.DrawWireCube(node.position, Vector3.one * gridCellSize * 0.8f);
                
                // 绘制代价信息
                #if UNITY_EDITOR
                if (node.groupPenalty > 0)
                {
                    UnityEditor.Handles.Label(node.position + Vector3.up * 0.2f, 
                        $"P: {node.groupPenalty:F2}");
                }
                #endif
            }
            
            // 绘制网格边界
            if (gridNodes.Count > 0)
            {
                Gizmos.color = Color.white;
                Gizmos.DrawWireCube(gridBounds.center, gridBounds.size);
            }
        }
        
        public void DrawPathGizmos(List<Vector3> path)
        {
            if (!drawPathGizmos || path == null || path.Count < 2)
            {
                return;
            }
            
            Gizmos.color = Color.yellow;
            for (int i = 0; i < path.Count - 1; i++)
            {
                Gizmos.DrawLine(path[i], path[i + 1]);
                Gizmos.DrawSphere(path[i], 0.2f);
            }
            
            if (path.Count > 0)
            {
                Gizmos.DrawSphere(path[path.Count - 1], 0.2f);
            }
        }
    }
    
    // 协作路径使用者
    public class CooperativePathUser : MonoBehaviour
    {
        [Header("路径设置")]
        public CooperativeAStarPlanner pathPlanner;
        public float pathUpdateInterval = 1.0f;
        public float waypointArrivalThreshold = 0.5f;
        
        [Header("移动设置")]
        public float moveSpeed = 5f;
        public float rotationSpeed = 120f;
        
        // 路径状态
        private List<Vector3> currentPath;
        private int currentWaypointIndex;
        private float pathUpdateTimer;
        private Vector3 currentTarget;
        
        private void Start()
        {
            if (pathPlanner != null)
            {
                pathPlanner.RegisterGroupMember(gameObject);
            }
            
            pathUpdateTimer = pathUpdateInterval;
            currentPath = new List<Vector3>();
        }
        
        private void Update()
        {
            if (pathPlanner == null || currentPath == null)
            {
                return;
            }
            
            // 更新路径计时器
            pathUpdateTimer -= Time.deltaTime;
            if (pathUpdateTimer <= 0)
            {
                UpdatePathToRandomTarget();
                pathUpdateTimer = pathUpdateInterval;
            }
            
            // 跟随路径
            FollowCurrentPath();
        }
        
        private void UpdatePathToRandomTarget()
        {
            // 生成随机目标
            Vector3 randomTarget = transform.position + 
                new Vector3(
                    Random.Range(-20f, 20f),
                    0,
                    Random.Range(-20f, 20f)
                );
            
            // 请求协作路径
            currentPath = pathPlanner.FindCooperativePath(transform.position, randomTarget, gameObject);
            
            if (currentPath != null && currentPath.Count > 0)
            {
                currentWaypointIndex = 0;
                currentTarget = currentPath[currentWaypointIndex];
                Debug.Log($"新路径生成,包含 {currentPath.Count} 个航点");
            }
            else
            {
                Debug.LogWarning("无法生成路径");
            }
        }
        
        private void FollowCurrentPath()
        {
            if (currentPath == null || currentPath.Count == 0 || 
                currentWaypointIndex >= currentPath.Count)
            {
                return;
            }
            
            // 检查是否到达当前航点
            Vector3 toTarget = currentTarget - transform.position;
            float distance = toTarget.magnitude;
            
            if (distance <= waypointArrivalThreshold)
            {
                // 移动到下一个航点
                currentWaypointIndex++;
                
                if (currentWaypointIndex < currentPath.Count)
                {
                    currentTarget = currentPath[currentWaypointIndex];
                }
                else
                {
                    // 到达路径终点
                    Debug.Log("到达路径终点");
                    currentPath.Clear();
                    return;
                }
            }
            
            // 移动到当前航点
            MoveToTarget(currentTarget, toTarget.normalized);
        }
        
        private void MoveToTarget(Vector3 target, Vector3 direction)
        {
            // 应用移动
            transform.position = Vector3.MoveTowards(
                transform.position,
                target,
                moveSpeed * Time.deltaTime
            );
            
            // 平滑旋转
            if (direction != Vector3.zero)
            {
                Quaternion targetRotation = Quaternion.LookRotation(direction);
                transform.rotation = Quaternion.Slerp(
                    transform.rotation,
                    targetRotation,
                    rotationSpeed * Time.deltaTime
                );
            }
        }
        
        public void SetTarget(Vector3 target)
        {
            if (pathPlanner != null)
            {
                currentPath = pathPlanner.FindCooperativePath(transform.position, target, gameObject);
                
                if (currentPath != null && currentPath.Count > 0)
                {
                    currentWaypointIndex = 0;
                    currentTarget = currentPath[currentWaypointIndex];
                }
            }
        }
        
        private void OnDestroy()
        {
            if (pathPlanner != null)
            {
                pathPlanner.UnregisterGroupMember(gameObject);
            }
        }
        
        private void OnDrawGizmosSelected()
        {
            if (currentPath != null && currentPath.Count > 0)
            {
                // 绘制当前路径
                Gizmos.color = Color.cyan;
                for (int i = 0; i < currentPath.Count - 1; i++)
                {
                    Gizmos.DrawLine(currentPath[i], currentPath[i + 1]);
                    Gizmos.DrawSphere(currentPath[i], 0.2f);
                }
                
                Gizmos.DrawSphere(currentPath[currentPath.Count - 1], 0.2f);
                
                // 绘制当前目标
                if (currentWaypointIndex < currentPath.Count)
                {
                    Gizmos.color = Color.red;
                    Gizmos.DrawSphere(currentTarget, 0.3f);
                    Gizmos.DrawLine(transform.position, currentTarget);
                }
            }
        }
    }
}

6.3.3 A*算法的战术扩展:伏击路径规划

伏击路径规划是协作A*算法的一个重要扩展,专门用于寻找适合伏击的路径:

// 伏击路径规划器
public class AmbushPathPlanner : CooperativeAStarPlanner
{
    [System.Serializable]
    public class AmbushSettings
    {
        public float ambushPreference = 0.7f; // 伏击偏好度 (0-1)
        public float visibilityPenalty = 2.0f; // 可见性惩罚
        public float coverBonus = 0.5f;        // 掩体奖励
        public float elevationBonus = 0.3f;    // 高度奖励
        public float flankingBonus = 0.8f;     // 侧翼攻击奖励
        public float surpriseBonus = 1.0f;     // 突袭奖励
    }
    
    public AmbushSettings ambushSettings;
    
    // 目标信息
    private Vector3 enemyPosition;
    private Vector3 enemyForward;
    private bool hasEnemyTarget = false;
    
    public void SetEnemyTarget(Vector3 position, Vector3 forward)
    {
        enemyPosition = position;
        enemyForward = forward.normalized;
        hasEnemyTarget = true;
    }
    
    protected override void UpdateNodeTacticalValue(CooperativePathNode node)
    {
        base.UpdateNodeTacticalValue(node);
        
        if (!hasEnemyTarget)
        {
            return;
        }
        
        // 计算伏击相关的战术价值
        float ambushValue = CalculateAmbushValue(node.position);
        
        // 调整战术价值(值越低越好)
        node.tacticalValue *= (1.0f + ambushValue * ambushSettings.ambushPreference);
    }
    
    private float CalculateAmbushValue(Vector3 position)
    {
        float totalValue = 0f;
        
        // 1. 可见性惩罚(在敌人视线内不好)
        if (IsVisibleFromEnemy(position))
        {
            totalValue += ambushSettings.visibilityPenalty;
        }
        
        // 2. 掩体奖励(有掩体好)
        float coverValue = CalculateCoverFromEnemy(position);
        totalValue -= coverValue * ambushSettings.coverBonus;
        
        // 3. 高度奖励(高处有利)
        float elevationAdvantage = CalculateElevationAdvantage(position);
        totalValue -= elevationAdvantage * ambushSettings.elevationBonus;
        
        // 4. 侧翼奖励(从侧面攻击有利)
        float flankingValue = CalculateFlankingValue(position);
        totalValue -= flankingValue * ambushSettings.flankingBonus;
        
        // 5. 突袭奖励(从背后攻击有利)
        float surpriseValue = CalculateSurpriseValue(position);
        totalValue -= surpriseValue * ambushSettings.surpriseBonus;
        
        return totalValue;
    }
    
    private bool IsVisibleFromEnemy(Vector3 position)
    {
        // 简化的可见性检查
        Vector3 toPosition = position - enemyPosition;
        float distance = toPosition.magnitude;
        
        if (distance > 50f) // 最大可见距离
        {
            return false;
        }
        
        // 检查是否在敌人视野锥形内
        float angle = Vector3.Angle(enemyForward, toPosition.normalized);
        if (angle > 90f) // 90度视野
        {
            return false;
        }
        
        // 检查视线遮挡
        RaycastHit hit;
        if (Physics.Raycast(enemyPosition, toPosition.normalized, out hit, distance))
        {
            if (hit.point != position)
            {
                return false; // 有遮挡
            }
        }
        
        return true;
    }
    
    private float CalculateCoverFromEnemy(Vector3 position)
    {
        // 计算从敌人方向看来的掩体保护
        Vector3 fromEnemy = position - enemyPosition;
        float distance = fromEnemy.magnitude;
        
        if (distance < 1f)
        {
            return 0f;
        }
        
        Vector3 direction = fromEnemy.normalized;
        float coverValue = 0f;
        
        // 检查周围的掩体
        Vector3[] checkOffsets = {
            Vector3.up * 0.5f,
            Vector3.right * 0.5f,
            Vector3.left * 0.5f,
            Vector3.forward * 0.5f,
            Vector3.back * 0.5f
        };
        
        foreach (Vector3 offset in checkOffsets)
        {
            Vector3 checkPos = position + offset;
            Vector3 toCheck = checkPos - enemyPosition;
            
            RaycastHit hit;
            if (Physics.Raycast(enemyPosition, toCheck.normalized, out hit, distance * 1.2f))
            {
                // 检查击中点是否接近检查位置
                float hitDistance = Vector3.Distance(hit.point, checkPos);
                if (hitDistance < 0.5f)
                {
                    coverValue += 1.0f;
                }
            }
        }
        
        return Mathf.Clamp01(coverValue / checkOffsets.Length);
    }
    
    private float CalculateElevationAdvantage(Vector3 position)
    {
        // 计算相对于敌人的高度优势
        float heightDifference = position.y - enemyPosition.y;
        
        if (heightDifference > 0)
        {
            return Mathf.Clamp01(heightDifference / 10f); // 10米最大优势
        }
        
        return 0f;
    }
    
    private float CalculateFlankingValue(Vector3 position)
    {
        // 计算侧翼攻击价值
        Vector3 toEnemy = enemyPosition - position;
        Vector3 enemyRight = Vector3.Cross(enemyForward, Vector3.up);
        
        // 计算位置相对于敌人侧面的角度
        float rightDot = Vector3.Dot(toEnemy.normalized, enemyRight);
        float flankAngle = Mathf.Abs(Mathf.Acos(rightDot) * Mathf.Rad2Deg - 90f);
        
        // 角度越小(接近90度),侧翼价值越高
        return Mathf.Clamp01(1.0f - flankAngle / 90f);
    }
    
    private float CalculateSurpriseValue(Vector3 position)
    {
        // 计算突袭价值(从背后攻击)
        Vector3 toEnemy = enemyPosition - position;
        float forwardDot = Vector3.Dot(toEnemy.normalized, enemyForward);
        
        // 如果在敌人后方(forwardDot < 0),则有突袭价值
        if (forwardDot < 0)
        {
            return Mathf.Clamp01(Mathf.Abs(forwardDot));
        }
        
        return 0f;
    }
    
    public List<Vector3> FindAmbushPath(Vector3 start, Vector3 enemyPos, Vector3 enemyFwd)
    {
        SetEnemyTarget(enemyPos, enemyFwd);
        
        // 在敌人周围寻找最佳伏击位置
        Vector3 ambushTarget = FindBestAmbushPosition(enemyPos);
        
        // 寻找路径
        return FindCooperativePath(start, ambushTarget);
    }
    
    private Vector3 FindBestAmbushPosition(Vector3 enemyPos)
    {
        // 在敌人周围搜索最佳伏击位置
        float searchRadius = 30f;
        int sampleCount = 20;
        
        Vector3 bestPosition = enemyPos + Vector3.back * 10f; // 默认位置
        float bestScore = float.MinValue;
        
        for (int i = 0; i < sampleCount; i++)
        {
            // 在圆周上采样
            float angle = (i * 360f / sampleCount) * Mathf.Deg2Rad;
            float distance = searchRadius * Random.Range(0.7f, 1.3f);
            
            Vector3 samplePos = enemyPos + 
                new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * distance;
            
            // 确保位置可行走
            if (!IsPositionWalkable(samplePos))
            {
                continue;
            }
            
            // 计算伏击分数
            float score = CalculateAmbushPositionScore(samplePos);
            
            if (score > bestScore)
            {
                bestScore = score;
                bestPosition = samplePos;
            }
        }
        
        return bestPosition;
    }
    
    private float CalculateAmbushPositionScore(Vector3 position)
    {
        if (!hasEnemyTarget)
        {
            return 0f;
        }
        
        float score = 0f;
        
        // 负分项:可见性
        if (IsVisibleFromEnemy(position))
        {
            score -= 10f;
        }
        
        // 正分项:掩体
        float coverValue = CalculateCoverFromEnemy(position);
        score += coverValue * 5f;
        
        // 正分项:高度优势
        float elevationAdvantage = CalculateElevationAdvantage(position);
        score += elevationAdvantage * 3f;
        
        // 正分项:侧翼位置
        float flankingValue = CalculateFlankingValue(position);
        score += flankingValue * 4f;
        
        // 正分项:突袭位置
        float surpriseValue = CalculateSurpriseValue(position);
        score += surpriseValue * 6f;
        
        // 距离惩罚(太近或太远都不好)
        float distance = Vector3.Distance(position, enemyPosition);
        float idealDistance = 15f;
        float distancePenalty = Mathf.Abs(distance - idealDistance) / idealDistance;
        score -= distancePenalty * 2f;
        
        return score;
    }
}

6.4 地形高度分析与路径点优化

地形高度对战术决策有重要影响。高处通常提供更好的视野和防御优势,而低处可能提供更好的隐蔽性。本节将实现一个高度分析系统,用于评估路径点的战术价值。

6.4.1 高度分析系统实现

// 地形高度分析器
public class TerrainHeightAnalyzer : MonoBehaviour
{
    [System.Serializable]
    public class HeightAnalysisSettings
    {
        public float analysisRadius = 50f;
        public float gridResolution = 2f;
        public LayerMask terrainMask = -1;
        public AnimationCurve heightImportanceCurve = AnimationCurve.Linear(0, 0, 1, 1);
        public float maxHeightDifference = 20f;
    }
    
    public HeightAnalysisSettings settings;
    
    // 高度网格数据
    private Dictionary<Vector2Int, float> heightGrid;
    private Dictionary<Vector2Int, float> tacticalHeightValue;
    private Bounds analysisBounds;
    
    private void Start()
    {
        InitializeHeightGrid();
        AnalyzeTerrainHeights();
    }
    
    private void InitializeHeightGrid()
    {
        heightGrid = new Dictionary<Vector2Int, float>();
        tacticalHeightValue = new Dictionary<Vector2Int, float>();
        
        Vector3 position = transform.position;
        analysisBounds = new Bounds(position, Vector3.one * settings.analysisRadius * 2);
    }
    
    public void AnalyzeTerrainHeights()
    {
        // 在分析范围内采样高度
        float halfSize = settings.analysisRadius;
        int gridSize = Mathf.CeilToInt((halfSize * 2) / settings.gridResolution);
        
        for (int x = 0; x < gridSize; x++)
        {
            for (int y = 0; y < gridSize; y++)
            {
                Vector3 worldPos = new Vector3(
                    transform.position.x - halfSize + x * settings.gridResolution,
                    100f, // 起始高度
                    transform.position.z - halfSize + y * settings.gridResolution
                );
                
                Vector2Int gridCoord = new Vector2Int(x, y);
                
                // 射线检测获取地形高度
                RaycastHit hit;
                if (Physics.Raycast(worldPos, Vector3.down, out hit, 200f, settings.terrainMask))
                {
                    heightGrid[gridCoord] = hit.point.y;
                    
                    // 计算战术高度价值
                    tacticalHeightValue[gridCoord] = CalculateTacticalHeightValue(gridCoord);
                }
                else
                {
                    heightGrid[gridCoord] = 0f;
                    tacticalHeightValue[gridCoord] = 0f;
                }
            }
        }
        
        Debug.Log($"地形高度分析完成,采样点: {heightGrid.Count}");
    }
    
    private float CalculateTacticalHeightValue(Vector2Int gridCoord)
    {
        float baseHeight = heightGrid[gridCoord];
        float relativeHeight = 0f;
        int sampleCount = 0;
        
        // 采样周围点计算相对高度
        for (int dx = -2; dx <= 2; dx++)
        {
            for (int dy = -2; dy <= 2; dy++)
            {
                if (dx == 0 && dy == 0) continue;
                
                Vector2Int neighborCoord = new Vector2Int(gridCoord.x + dx, gridCoord.y + dy);
                if (heightGrid.ContainsKey(neighborCoord))
                {
                    float neighborHeight = heightGrid[neighborCoord];
                    relativeHeight += (baseHeight - neighborHeight);
                    sampleCount++;
                }
            }
        }
        
        if (sampleCount > 0)
        {
            relativeHeight /= sampleCount;
        }
        
        // 标准化并应用重要性曲线
        float normalizedHeight = Mathf.Clamp01(relativeHeight / settings.maxHeightDifference);
        float tacticalValue = settings.heightImportanceCurve.Evaluate(normalizedHeight);
        
        return tacticalValue;
    }
    
    public float GetHeightAtPosition(Vector3 position)
    {
        Vector2Int gridCoord = WorldToGridCoordinate(position);
        
        if (heightGrid.ContainsKey(gridCoord))
        {
            return heightGrid[gridCoord];
        }
        
        return position.y;
    }
    
    public float GetTacticalHeightValue(Vector3 position)
    {
        Vector2Int gridCoord = WorldToGridCoordinate(position);
        
        if (tacticalHeightValue.ContainsKey(gridCoord))
        {
            return tacticalHeightValue[gridCoord];
        }
        
        return 0.5f; // 默认值
    }
    
    public Vector3 GetHighestPointInRadius(Vector3 center, float radius)
    {
        Vector3 highestPoint = center;
        float highestHeight = GetHeightAtPosition(center);
        
        int sampleCount = 20;
        for (int i = 0; i < sampleCount; i++)
        {
            float angle = (i * 360f / sampleCount) * Mathf.Deg2Rad;
            Vector3 samplePos = center + 
                new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius;
            
            float sampleHeight = GetHeightAtPosition(samplePos);
            if (sampleHeight > highestHeight)
            {
                highestHeight = sampleHeight;
                highestPoint = samplePos;
                highestPoint.y = sampleHeight;
            }
        }
        
        return highestPoint;
    }
    
    public Vector3 GetBestHighGround(Vector3 center, float searchRadius, float minHeightAdvantage = 5f)
    {
        List<Vector3> highPoints = new List<Vector3>();
        
        // 寻找所有足够高的点
        int sampleCount = 30;
        float centerHeight = GetHeightAtPosition(center);
        
        for (int i = 0; i < sampleCount; i++)
        {
            float angle = (i * 360f / sampleCount) * Mathf.Deg2Rad;
            float distance = searchRadius * Random.Range(0.5f, 1f);
            
            Vector3 samplePos = center + 
                new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * distance;
            
            float sampleHeight = GetHeightAtPosition(samplePos);
            if (sampleHeight - centerHeight >= minHeightAdvantage)
            {
                samplePos.y = sampleHeight;
                highPoints.Add(samplePos);
            }
        }
        
        if (highPoints.Count == 0)
        {
            return center;
        }
        
        // 选择战术价值最高的点
        Vector3 bestPoint = highPoints[0];
        float bestValue = GetTacticalHeightValue(bestPoint);
        
        for (int i = 1; i < highPoints.Count; i++)
        {
            float value = GetTacticalHeightValue(highPoints[i]);
            if (value > bestValue)
            {
                bestValue = value;
                bestPoint = highPoints[i];
            }
        }
        
        return bestPoint;
    }
    
    private Vector2Int WorldToGridCoordinate(Vector3 worldPosition)
    {
        Vector3 localPos = worldPosition - (transform.position - Vector3.one * settings.analysisRadius);
        int x = Mathf.FloorToInt(localPos.x / settings.gridResolution);
        int y = Mathf.FloorToInt(localPos.z / settings.gridResolution);
        
        return new Vector2Int(x, y);
    }
    
    private void OnDrawGizmosSelected()
    {
        if (heightGrid == null || heightGrid.Count == 0)
        {
            return;
        }
        
        // 绘制高度网格
        foreach (KeyValuePair<Vector2Int, float> entry in heightGrid)
        {
            Vector3 worldPos = GridToWorldPosition(entry.Key);
            worldPos.y = entry.Value;
            
            // 根据高度值着色
            float normalizedHeight = Mathf.Clamp01(entry.Value / 100f);
            Color heightColor = Color.Lerp(Color.blue, Color.red, normalizedHeight);
            Gizmos.color = heightColor;
            
            Gizmos.DrawCube(worldPos, Vector3.one * settings.gridResolution * 0.8f);
        }
        
        // 绘制分析边界
        Gizmos.color = Color.white;
        Gizmos.DrawWireCube(analysisBounds.center, analysisBounds.size);
    }
    
    private Vector3 GridToWorldPosition(Vector2Int gridCoord)
    {
        return new Vector3(
            transform.position.x - settings.analysisRadius + gridCoord.x * settings.gridResolution,
            0,
            transform.position.z - settings.analysisRadius + gridCoord.y * settings.gridResolution
        );
    }
}

6.4.2 高度感知路径点生成器

// 高度感知路径点生成器
public class HeightAwareWaypointGenerator : MonoBehaviour
{
    [System.Serializable]
    public class WaypointGenerationSettings
    {
        public float generationRadius = 100f;
        public float waypointDensity = 0.1f; // 每平方米路径点数
        public float minWaypointDistance = 2f;
        public float maxWaypointDistance = 10f;
        public LayerMask groundMask = -1;
        public LayerMask obstacleMask = -1;
        
        // 高度相关设置
        public float preferredHeightRange = 10f;
        public float heightVariation = 5f;
        public bool prioritizeHighGround = true;
        public float highGroundBonus = 2f;
    }
    
    public WaypointGenerationSettings settings;
    public TerrainHeightAnalyzer heightAnalyzer;
    
    private List<Vector3> generatedWaypoints;
    private List<float> waypointTacticalValues;
    
    private void Start()
    {
        generatedWaypoints = new List<Vector3>();
        waypointTacticalValues = new List<float>();
        
        if (heightAnalyzer == null)
        {
            heightAnalyzer = GetComponent<TerrainHeightAnalyzer>();
        }
        
        GenerateWaypoints();
    }
    
    private void GenerateWaypoints()
    {
        // 计算需要生成的路径点数量
        float area = Mathf.PI * settings.generationRadius * settings.generationRadius;
        int targetCount = Mathf.CeilToInt(area * settings.waypointDensity);
        
        int attempts = 0;
        int maxAttempts = targetCount * 10;
        
        while (generatedWaypoints.Count < targetCount && attempts < maxAttempts)
        {
            attempts++;
            
            // 生成随机位置
            Vector3 randomPos = GenerateRandomPosition();
            
            // 检查位置有效性
            if (IsValidWaypointPosition(randomPos))
            {
                // 计算战术价值
                float tacticalValue = CalculateWaypointTacticalValue(randomPos);
                
                generatedWaypoints.Add(randomPos);
                waypointTacticalValues.Add(tacticalValue);
            }
        }
        
        // 优化路径点分布
        OptimizeWaypointDistribution();
        
        Debug.Log($"生成 {generatedWaypoints.Count} 个路径点");
    }
    
    private Vector3 GenerateRandomPosition()
    {
        // 在圆形区域内生成随机位置
        float angle = Random.Range(0f, 360f) * Mathf.Deg2Rad;
        float distance = Random.Range(0f, settings.generationRadius);
        
        Vector3 position = transform.position + 
            new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * distance;
        
        // 获取地形高度
        position.y = GetGroundHeight(position);
        
        return position;
    }
    
    private float GetGroundHeight(Vector3 position)
    {
        RaycastHit hit;
        if (Physics.Raycast(position + Vector3.up * 100f, Vector3.down, out hit, 200f, settings.groundMask))
        {
            return hit.point.y;
        }
        
        return position.y;
    }
    
    private bool IsValidWaypointPosition(Vector3 position)
    {
        // 检查是否在地面上
        if (Mathf.Abs(position.y - GetGroundHeight(position)) > 0.1f)
        {
            return false;
        }
        
        // 检查障碍物
        Collider[] obstacles = Physics.OverlapSphere(position, 0.5f, settings.obstacleMask);
        if (obstacles.Length > 0)
        {
            return false;
        }
        
        // 检查与其他路径点的最小距离
        foreach (Vector3 existingWaypoint in generatedWaypoints)
        {
            if (Vector3.Distance(position, existingWaypoint) < settings.minWaypointDistance)
            {
                return false;
            }
        }
        
        // 检查坡度(简化)
        float maxSlope = 45f;
        if (CheckSlopeTooSteep(position, maxSlope))
        {
            return false;
        }
        
        return true;
    }
    
    private bool CheckSlopeTooSteep(Vector3 position, float maxSlopeDegrees)
    {
        // 简化的坡度检查
        float checkDistance = 1f;
        Vector3[] checkDirections = {
            Vector3.forward, Vector3.back, 
            Vector3.left, Vector3.right
        };
        
        foreach (Vector3 dir in checkDirections)
        {
            Vector3 checkPos = position + dir * checkDistance;
            float checkHeight = GetGroundHeight(checkPos);
            float heightDiff = Mathf.Abs(checkHeight - position.y);
            
            float slope = Mathf.Atan(heightDiff / checkDistance) * Mathf.Rad2Deg;
            if (slope > maxSlopeDegrees)
            {
                return true;
            }
        }
        
        return false;
    }
    
    private float CalculateWaypointTacticalValue(Vector3 position)
    {
        float value = 1.0f;
        
        // 高度价值
        if (heightAnalyzer != null)
        {
            float heightValue = heightAnalyzer.GetTacticalHeightValue(position);
            value *= heightValue;
            
            if (settings.prioritizeHighGround)
            {
                value += heightValue * settings.highGroundBonus;
            }
        }
        
        // 掩体价值
        float coverValue = CalculateCoverValue(position);
        value *= (1.0f + coverValue);
        
        // 可见性价值(越低越好)
        float visibility = CalculateVisibility(position);
        value /= (1.0f + visibility * 0.5f);
        
        return value;
    }
    
    private float CalculateCoverValue(Vector3 position)
    {
        // 简化的掩体价值计算
        float coverValue = 0f;
        float checkRadius = 3f;
        
        Collider[] nearbyObjects = Physics.OverlapSphere(position, checkRadius);
        foreach (Collider collider in nearbyObjects)
        {
            // 检查是否可以作为掩体
            if (collider.gameObject.CompareTag("Cover"))
            {
                float distance = Vector3.Distance(position, collider.transform.position);
                coverValue += 1.0f / (distance + 0.1f);
            }
        }
        
        return Mathf.Clamp01(coverValue);
    }
    
    private float CalculateVisibility(Vector3 position)
    {
        // 简化的可见性计算
        int rayCount = 12;
        float maxDistance = 20f;
        float visibleCount = 0;
        
        for (int i = 0; i < rayCount; i++)
        {
            float angle = i * (360f / rayCount) * Mathf.Deg2Rad;
            Vector3 direction = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle));
            
            RaycastHit hit;
            if (!Physics.Raycast(position, direction, out hit, maxDistance, settings.obstacleMask))
            {
                visibleCount++;
            }
        }
        
        return visibleCount / rayCount;
    }
    
    private void OptimizeWaypointDistribution()
    {
        // 移除过于密集的路径点
        List<int> indicesToRemove = new List<int>();
        
        for (int i = 0; i < generatedWaypoints.Count; i++)
        {
            for (int j = i + 1; j < generatedWaypoints.Count; j++)
            {
                float distance = Vector3.Distance(generatedWaypoints[i], generatedWaypoints[j]);
                
                if (distance < settings.minWaypointDistance)
                {
                    // 保留战术价值更高的路径点
                    if (waypointTacticalValues[i] < waypointTacticalValues[j])
                    {
                        indicesToRemove.Add(i);
                    }
                    else
                    {
                        indicesToRemove.Add(j);
                    }
                }
            }
        }
        
        // 移除标记的路径点
        indicesToRemove.Sort();
        indicesToRemove.Reverse();
        
        foreach (int index in indicesToRemove)
        {
            if (index < generatedWaypoints.Count)
            {
                generatedWaypoints.RemoveAt(index);
                waypointTacticalValues.RemoveAt(index);
            }
        }
    }
    
    public Vector3 GetBestWaypointNear(Vector3 position, float searchRadius)
    {
        Vector3 bestWaypoint = position;
        float bestValue = -1f;
        
        for (int i = 0; i < generatedWaypoints.Count; i++)
        {
            float distance = Vector3.Distance(position, generatedWaypoints[i]);
            
            if (distance <= searchRadius)
            {
                // 考虑距离的战术价值
                float distanceFactor = 1.0f - (distance / searchRadius);
                float combinedValue = waypointTacticalValues[i] * (1.0f + distanceFactor * 0.5f);
                
                if (combinedValue > bestValue)
                {
                    bestValue = combinedValue;
                    bestWaypoint = generatedWaypoints[i];
                }
            }
        }
        
        return bestWaypoint;
    }
    
    public List<Vector3> GetWaypointsInArea(Vector3 center, float radius)
    {
        List<Vector3> waypointsInArea = new List<Vector3>();
        
        foreach (Vector3 waypoint in generatedWaypoints)
        {
            if (Vector3.Distance(center, waypoint) <= radius)
            {
                waypointsInArea.Add(waypoint);
            }
        }
        
        return waypointsInArea;
    }
    
    public void AddDynamicWaypoint(Vector3 position)
    {
        if (IsValidWaypointPosition(position))
        {
            float tacticalValue = CalculateWaypointTacticalValue(position);
            generatedWaypoints.Add(position);
            waypointTacticalValues.Add(tacticalValue);
        }
    }
    
    public void RemoveWaypoint(Vector3 position, float tolerance = 0.5f)
    {
        for (int i = generatedWaypoints.Count - 1; i >= 0; i--)
        {
            if (Vector3.Distance(generatedWaypoints[i], position) <= tolerance)
            {
                generatedWaypoints.RemoveAt(i);
                waypointTacticalValues.RemoveAt(i);
                break;
            }
        }
    }
    
    private void OnDrawGizmos()
    {
        if (generatedWaypoints == null)
        {
            return;
        }
        
        // 绘制路径点
        Gizmos.color = Color.cyan;
        foreach (Vector3 waypoint in generatedWaypoints)
        {
            Gizmos.DrawSphere(waypoint, 0.3f);
        }
        
        // 绘制生成区域
        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(transform.position, settings.generationRadius);
    }
}

6.5 掩体与可见性分析系统

在战术决策中,掩体利用和可见性管理至关重要。一个完善的掩体分析系统能够帮助AI角色选择最佳的防御位置和攻击角度。

6.5.1 掩体分析系统实现

// 掩体分析系统
public class CoverAnalysisSystem : MonoBehaviour
{
    [System.Serializable]
    public class CoverAnalysisSettings
    {
        public float analysisRange = 50f;
        public float raycastHeight = 1f; // 角色眼睛高度
        public LayerMask coverMask = -1;
        public LayerMask obstacleMask = -1;
        public int raysPerDirection = 3;
        public float coverQualityThreshold = 0.7f;
    }
    
    public CoverAnalysisSettings settings;
    
    // 掩体数据
    private Dictionary<Vector3, CoverPoint> coverPoints;
    private Dictionary<GameObject, List<CoverPoint>> objectCoverMap;
    
    // 掩体点定义
    public class CoverPoint
    {
        public Vector3 position;
        public Vector3 normal; // 掩体法线方向
        public float quality;  // 掩体质量 (0-1)
        public CoverType type;
        public GameObject sourceObject;
        
        public CoverPoint(Vector3 pos, Vector3 norm, float qual, CoverType t, GameObject source)
        {
            position = pos;
            normal = norm;
            quality = qual;
            type = t;
            sourceObject = source;
        }
        
        public bool ProvidesCoverFrom(Vector3 threatPosition)
        {
            Vector3 toThreat = threatPosition - position;
            float angle = Vector3.Angle(normal, toThreat.normalized);
            
            // 如果威胁在掩体后方,则提供保护
            return angle < 90f;
        }
        
        public float GetCoverEffectiveness(Vector3 threatPosition)
        {
            if (!ProvidesCoverFrom(threatPosition))
            {
                return 0f;
            }
            
            Vector3 toThreat = threatPosition - position;
            float distance = toThreat.magnitude;
            float angle = Vector3.Angle(normal, toThreat.normalized);
            
            // 角度越小,掩体越有效
            float angleEffectiveness = 1.0f - (angle / 90f);
            
            // 距离越远,掩体越有效
            float distanceEffectiveness = Mathf.Clamp01(1.0f - (distance / 100f));
            
            return quality * angleEffectiveness * distanceEffectiveness;
        }
    }
    
    public enum CoverType
    {
        Full,       // 完全掩体
        Half,       // 半掩体
        Low,        // 低掩体
        Partial     // 部分掩体
    }
    
    private void Start()
    {
        coverPoints = new Dictionary<Vector3, CoverPoint>();
        objectCoverMap = new Dictionary<GameObject, List<CoverPoint>>();
        
        AnalyzeEnvironmentCover();
    }
    
    private void AnalyzeEnvironmentCover()
    {
        // 查找所有可能提供掩体的对象
        Collider[] potentialCoverObjects = Physics.OverlapSphere(
            transform.position, 
            settings.analysisRange, 
            settings.coverMask
        );
        
        foreach (Collider collider in potentialCoverObjects)
        {
            AnalyzeObjectForCover(collider.gameObject);
        }
        
        Debug.Log($"发现 {coverPoints.Count} 个掩体点");
    }
    
    private void AnalyzeObjectForCover(GameObject coverObject)
    {
        List<CoverPoint> objectCoverPoints = new List<CoverPoint>();
        
        // 获取对象边界
        Renderer renderer = coverObject.GetComponent<Renderer>();
        if (renderer == null)
        {
            return;
        }
        
        Bounds bounds = renderer.bounds;
        
        // 在对象周围采样点
        Vector3[] samplePoints = GenerateSamplePointsAroundBounds(bounds);
        
        foreach (Vector3 samplePoint in samplePoints)
        {
            CoverPoint coverPoint = AnalyzeCoverAtPoint(samplePoint, coverObject);
            if (coverPoint != null && coverPoint.quality >= settings.coverQualityThreshold)
            {
                // 添加到全局掩体点
                if (!coverPoints.ContainsKey(coverPoint.position))
                {
                    coverPoints[coverPoint.position] = coverPoint;
                }
                
                // 添加到对象掩体点列表
                objectCoverPoints.Add(coverPoint);
            }
        }
        
        if (objectCoverPoints.Count > 0)
        {
            objectCoverMap[coverObject] = objectCoverPoints;
        }
    }
    
    private Vector3[] GenerateSamplePointsAroundBounds(Bounds bounds)
    {
        List<Vector3> samplePoints = new List<Vector3>();
        
        // 在边界盒的每个面生成采样点
        Vector3 center = bounds.center;
        Vector3 size = bounds.size;
        
        // 前面和后面
        for (int i = 0; i <= settings.raysPerDirection; i++)
        {
            for (int j = 0; j <= settings.raysPerDirection; j++)
            {
                float x = Mathf.Lerp(-size.x / 2, size.x / 2, i / (float)settings.raysPerDirection);
                float y = Mathf.Lerp(-size.y / 2, size.y / 2, j / (float)settings.raysPerDirection);
                
                // 前面
                samplePoints.Add(center + new Vector3(x, y, size.z / 2));
                // 后面
                samplePoints.Add(center + new Vector3(x, y, -size.z / 2));
            }
        }
        
        // 左面和右面
        for (int i = 0; i <= settings.raysPerDirection; i++)
        {
            for (int j = 0; j <= settings.raysPerDirection; j++)
            {
                float z = Mathf.Lerp(-size.z / 2, size.z / 2, i / (float)settings.raysPerDirection);
                float y = Mathf.Lerp(-size.y / 2, size.y / 2, j / (float)settings.raysPerDirection);
                
                // 左面
                samplePoints.Add(center + new Vector3(-size.x / 2, y, z));
                // 右面
                samplePoints.Add(center + new Vector3(size.x / 2, y, z));
            }
        }
        
        return samplePoints.ToArray();
    }
    
    private CoverPoint AnalyzeCoverAtPoint(Vector3 point, GameObject sourceObject)
    {
        // 调整点到角色眼睛高度
        Vector3 analysisPoint = point + Vector3.up * settings.raycastHeight;
        
        // 向多个方向发射射线检查掩体质量
        int coverHits = 0;
        int totalRays = 8; // 8个方向
        
        Vector3 averageNormal = Vector3.zero;
        
        for (int i = 0; i < totalRays; i++)
        {
            float angle = i * (360f / totalRays) * Mathf.Deg2Rad;
            Vector3 direction = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle));
            
            RaycastHit hit;
            if (Physics.Raycast(analysisPoint, direction, out hit, 5f, settings.coverMask))
            {
                if (hit.collider.gameObject == sourceObject)
                {
                    coverHits++;
                    averageNormal += hit.normal;
                }
            }
        }
        
        if (coverHits == 0)
        {
            return null;
        }
        
        // 计算掩体质量
        float coverQuality = coverHits / (float)totalRays;
        averageNormal = (averageNormal / coverHits).normalized;
        
        // 确定掩体类型
        CoverType coverType = DetermineCoverType(coverQuality, analysisPoint);
        
        return new CoverPoint(point, averageNormal, coverQuality, coverType, sourceObject);
    }
    
    private CoverType DetermineCoverType(float quality, Vector3 position)
    {
        // 根据掩体质量和位置确定类型
        if (quality > 0.9f)
        {
            return CoverType.Full;
        }
        else if (quality > 0.7f)
        {
            // 检查是否为低掩体
            RaycastHit hit;
            if (Physics.Raycast(position + Vector3.up * 0.5f, Vector3.down, out hit, 2f))
            {
                if (hit.point.y < position.y + 1f)
                {
                    return CoverType.Low;
                }
            }
            return CoverType.Half;
        }
        else
        {
            return CoverType.Partial;
        }
    }
    
    public CoverPoint FindBestCoverFromThreat(Vector3 unitPosition, Vector3 threatPosition, float searchRadius)
    {
        CoverPoint bestCover = null;
        float bestScore = -1f;
        
        foreach (CoverPoint cover in coverPoints.Values)
        {
            float distance = Vector3.Distance(unitPosition, cover.position);
            
            if (distance <= searchRadius)
            {
                // 计算掩体有效性
                float effectiveness = cover.GetCoverEffectiveness(threatPosition);
                
                if (effectiveness > 0)
                {
                    // 综合评分:有效性 * 距离因子
                    float distanceFactor = 1.0f - (distance / searchRadius);
                    float score = effectiveness * (1.0f + distanceFactor * 0.5f);
                    
                    if (score > bestScore)
                    {
                        bestScore = score;
                        bestCover = cover;
                    }
                }
            }
        }
        
        return bestCover;
    }
    
    public List<CoverPoint> FindCoverInDirection(Vector3 position, Vector3 direction, float maxAngle = 45f)
    {
        List<CoverPoint> suitableCover = new List<CoverPoint>();
        
        foreach (CoverPoint cover in coverPoints.Values)
        {
            Vector3 toCover = cover.position - position;
            float angle = Vector3.Angle(direction, toCover.normalized);
            
            if (angle <= maxAngle)
            {
                suitableCover.Add(cover);
            }
        }
        
        // 按距离排序
        suitableCover.Sort((a, b) => 
            Vector3.Distance(position, a.position).CompareTo(
                Vector3.Distance(position, b.position)
            )
        );
        
        return suitableCover;
    }
    
    public bool IsPositionInCover(Vector3 position, Vector3 threatPosition)
    {
        // 检查位置是否有掩体保护
        foreach (CoverPoint cover in coverPoints.Values)
        {
            float distance = Vector3.Distance(position, cover.position);
            if (distance < 2f && cover.ProvidesCoverFrom(threatPosition))
            {
                return true;
            }
        }
        
        return false;
    }
    
    public float CalculateCoverQualityAtPosition(Vector3 position, Vector3 threatPosition)
    {
        float bestQuality = 0f;
        
        foreach (CoverPoint cover in coverPoints.Values)
        {
            float distance = Vector3.Distance(position, cover.position);
            if (distance < 3f)
            {
                float effectiveness = cover.GetCoverEffectiveness(threatPosition);
                if (effectiveness > bestQuality)
                {
                    bestQuality = effectiveness;
                }
            }
        }
        
        return bestQuality;
    }
    
    public void UpdateDynamicCover(GameObject newCoverObject)
    {
        // 动态添加新的掩体
        AnalyzeObjectForCover(newCoverObject);
    }
    
    public void RemoveCoverObject(GameObject coverObject)
    {
        // 移除掩体对象及其所有掩体点
        if (objectCoverMap.ContainsKey(coverObject))
        {
            foreach (CoverPoint coverPoint in objectCoverMap[coverObject])
            {
                coverPoints.Remove(coverPoint.position);
            }
            
            objectCoverMap.Remove(coverObject);
        }
    }
    
    private void OnDrawGizmosSelected()
    {
        if (coverPoints == null)
        {
            return;
        }
        
        // 绘制掩体点
        foreach (CoverPoint cover in coverPoints.Values)
        {
            // 根据掩体质量着色
            Color coverColor = Color.Lerp(Color.yellow, Color.green, cover.quality);
            Gizmos.color = coverColor;
            
            // 绘制掩体点
            Gizmos.DrawSphere(cover.position, 0.2f);
            
            // 绘制掩体法线
            Gizmos.DrawRay(cover.position, cover.normal);
            
            // 绘制掩体类型标识
            switch (cover.type)
            {
                case CoverType.Full:
                    Gizmos.color = Color.green;
                    break;
                case CoverType.Half:
                    Gizmos.color = Color.yellow;
                    break;
                case CoverType.Low:
                    Gizmos.color = Color.blue;
                    break;
                case CoverType.Partial:
                    Gizmos.color = Color.red;
                    break;
            }
            
            Gizmos.DrawWireCube(cover.position, Vector3.one * 0.3f);
        }
        
        // 绘制分析范围
        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(transform.position, settings.analysisRange);
    }
}

总结

本章详细探讨了Unity中群体智能与战术决策系统的实现。从基础的队形管理到高级的协作寻路算法,从地形高度分析到掩体可见性评估,我们构建了一个完整的战术AI框架。

关键要点总结:

  1. 队形系统:提供了灵活的队形管理,支持多种队形类型和动态调整,确保群体移动的协调性和战术优势。

  2. 协作寻路:扩展的A*算法不仅考虑路径长度,还整合了群体协调、战术价值和队形保持等多种因素,为群体提供智能路径规划。

  3. 伏击路径规划:专门针对战术伏击设计的路径规划系统,能够识别最佳的伏击位置和路径。

  4. 地形分析:高度感知的路径点生成和战术位置评估,帮助AI利用地形优势。

  5. 掩体系统:完整的掩体分析和利用系统,使AI能够智能地利用环境进行防御和攻击。

在实际商业项目中,这些系统可以组合使用,创建出具有高度智能的AI敌人。例如,在战术射击游戏中,AI小队可以使用队形系统保持阵型,利用协作寻路找到最佳进攻路线,通过高度分析占领制高点,最后利用掩体系统进行智能攻防。

实现这些系统时需要注意性能优化,特别是在大型开放世界游戏中。可以通过空间分区、LOD系统、异步计算和智能缓存等技术来确保系统的运行效率。

通过本章介绍的技术,开发者可以为游戏创建出具有真实战术行为的AI,显著提升游戏的挑战性和沉浸感。这些系统不仅适用于军事题材游戏,也可以经过调整应用于RTS、MOBA甚至角色扮演游戏中,为玩家提供更加丰富和智能的游戏体验。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐