-1

我正在开发像 Hitman Go 这样的游戏,当敌人通过岩石或声音等发出警报时,我需要找到最接近我的目标的航路点(特定航路点)。

在此处输入图像描述

我为敌人设置了一些点,我的敌人在航路点( 8 -> 6 -> 1-> 2-> 3-> 4-> 5 )之间巡逻,然后尊重他的路径。所以,当我在 18 号航点扔石头时,我需要将敌人移动到这个航点,但要以最近的方式。当他得到警报时,成像敌人可以是任何这个航点(8,6,1,2,3,4,5 点)。

注 1:两点之间的距离相同。

注意 2:我的游戏是 3d 而不是 2d。

我使用这段代码一步一步地移动我的敌人(转弯基地)。

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

public class WaypointController : MonoBehaviour
{
    public List<Transform> waypoints = new List<Transform>();
    private Transform targetWaypoint;
    private int targetWaypointIndex = 0;
    private float minDistance = 0.1f;
    private int lastWaypointIndex;

    public bool reversePath;

    // what easetype to use for iTweening
    public iTween.EaseType easeType = iTween.EaseType.easeInOutExpo;

    // how fast we move
    public float moveSpeed = 1.5f;

    // time to rotate to face destination
    public float rotateTime = 0.5f;

    // delay to use before any call to iTween
    public float iTweenDelay = 0f;

    // Use this for initialization
    void Start()
    {
        lastWaypointIndex = waypoints.Count - 1;
        targetWaypoint = waypoints[targetWaypointIndex];
    }

    public void EnemyTurn()
    {
        float distance = Vector3.Distance(transform.position, targetWaypoint.position);
        CheckDistanceToWaypoint(distance);

        // move toward the destinationPos using the easeType and moveSpeed variables
        iTween.MoveTo(gameObject, iTween.Hash(
            "x", targetWaypoint.position.x,
            "y", targetWaypoint.position.y,
            "z", targetWaypoint.position.z,
            "delay", iTweenDelay,
            "easetype", easeType,
            "speed", moveSpeed
        ));
    }

    void CheckDistanceToWaypoint(float currentDistance)
    {
        if (currentDistance <= minDistance)
        {
            targetWaypointIndex++;
            UpdateTargetWaypoint();
        }
    }

    void UpdateTargetWaypoint()
    {
        if (targetWaypointIndex > lastWaypointIndex)
        {
            if (reversePath)
            {
                waypoints.Reverse();
            }
        
            targetWaypointIndex = 1;
        }
        targetWaypoint = waypoints[targetWaypointIndex];
    }
}
4

1 回答 1

1

既然您在评论中询问了非 A* 解决方案,我很无聊 :)

具有非常简单修剪的朴素DFS探路者。蛮力和非常浪费内存和 CPU 明智。

对于类似于 Hitman Go / Lara Croft Go 的游戏 - 我会使用此代码而不是 navmesh / A*。

对于 RTS / FPS 或任何 AI 密集型游戏,我绝对不会使用此解决方案。

public class GridPathfinder
{
    // Not thread safe
    private int? _currentShortestPath;

    /// <summary>
    /// Finds shortest path from cell A to B
    /// </summary>
    /// <returns>Shortest found path; null if found no path.</returns>
    public IList<Node> GetShortestPath(Node a, Node b)
    {
        _currentShortestPath = null;
        return GetShortestPathInternal(a, b, new List<Node>());
    }

    private IList<Node> GetShortestPathInternal(Node @from, Node to, List<Node> currentPath)
    {
        // Sanity
        if (currentPath.Contains(from))
        {
            return null;
        }

        // Prune
        if (_currentShortestPath.HasValue && currentPath.Count + 1 >= _currentShortestPath)
        {
            return null;
        }

        currentPath.Add(from);
        if (from == to)
        {
            return currentPath;
        }

        // Check neighbors recursively
        IList<Node> foundShortestPath = null;
        foreach (var connectedCell in from.ConnectedCells)
        {
            var cellPath = GetShortestPathInternal(connectedCell, to, new List<Node>(currentPath));
            if (cellPath == null || foundShortestPath != null && cellPath.Count >= foundShortestPath.Count)
            {
                continue;
            }

            foundShortestPath = cellPath;
        }

        // Update shortest path for future pruning
        if (foundShortestPath != null && (!_currentShortestPath.HasValue || _currentShortestPath > foundShortestPath.Count))
        {
            _currentShortestPath = foundShortestPath.Count;
        }
        
        return foundShortestPath;
    }
}

public class Node
{
    private readonly HashSet<Node> _connectedCells = new HashSet<Node>();

    public IEnumerable<Node> ConnectedCells => _connectedCells;

    /// <summary>
    /// Add a connection
    /// </summary>
    /// <param name="toAdd">Node to add</param>
    /// <param name="isTwoWay">Should add a connection from target node to this node</param>
    public void AddConnection(Node toAdd, bool isTwoWay=true)
    {
        if (toAdd == null || toAdd == this)
        {
            throw new Exception("Invalid connection attempted");
        }

        // Attempt to add
        if (!_connectedCells.Add(toAdd))
        {
            return;
        }

        if (!isTwoWay) return;
        toAdd.AddConnection(this);
    }
}
于 2021-10-10T21:50:58.850 回答