Unity AI需要将GameObject移向另一个给定距离

时间:2018-08-08 12:31:56

标签: unity3d artificial-intelligence

我目前正在尝试为自己的游戏开发一个AI,该AI需要将每个小兵向敌方小兵移动特定距离,我目前正在计算所有敌方小兵之间的距离,然后我会计算当前的奴才和最接近的奴才之间的角度,然后我将应用公式:

x = minion.x + cos(angle) * distance;


z = minion.z + sin(angle) * distance;

问题在于移动似乎是随机的,它并不总是朝着敌人移动,而是随机地移动 我将在下面提供代码

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

public class EnemyAI : MonoBehaviour {

    private float tolerance = 1.2f;

    public NavMeshSurface navMesh;

    private List<GameObject> board;
    private List<Vector3> minionDesiredLocations;

    private int currentMinionIndex = 0;

    private enum State {
        Draw,
        Place,
        GetBoard,
        Move,
        MinionMoving
    }

    private State state = State.Draw;

    public GameObject PlaceCard(List<GameObject> hand)
    {
        //Generate a random integer to see what card to play
        int randomIndex = (int) UnityEngine.Random.Range(0, hand.Count - 1);

        Debug.Log("AI Generated Index: " + randomIndex);

        return hand[randomIndex];
    }

    private bool MoveMinion(GameObject minion, List<GameObject> enemyMinions)
    {
        NavMeshAgent minionAgent = minion.GetComponent<NavMeshAgent>();
        Card minionCard = minion.transform.Find("Card").GetComponent<Card>();
        Vector3 maPosition = minion.transform.position;

        //Finding the closest position of the enemy minion
        float minDistance = 100.0f;
        Vector3 closestMinionPosition = Vector3.negativeInfinity; 
        foreach (GameObject enemyMinion in enemyMinions)
        {
            //Calculating distance beetween the enemy AI minion and the enemy
            float distance = Vector3.Distance(minion.transform.position, enemyMinion.transform.position);
            if (distance <= minDistance)
            {
                minDistance = distance;
                //Attacking the minion is close to the one
                if (minDistance <= minionCard.cardRange)
                {
                    float xRange = UnityEngine.Random.Range(-tolerance, tolerance);
                    float yRange = UnityEngine.Random.Range(-tolerance, tolerance);
                    closestMinionPosition = enemyMinion.transform.position + new Vector3(xRange, 0, yRange);

                    minionDesiredLocations[currentMinionIndex] = closestMinionPosition;
                    minionAgent.SetDestination(closestMinionPosition);

                    NavMeshHit hit;
                    if (NavMesh.SamplePosition(closestMinionPosition, out hit, 1.0f, NavMesh.AllAreas))
                    {
                        minionDesiredLocations[currentMinionIndex] = hit.position;
                        minionAgent.SetDestination(hit.position);
                        return true;
                    }
                }
                else
                {
                    //Calculating angle beetween the minion and the target
                    float angle = GetAngle(minion.transform.position, enemyMinion.transform.position);

                    float randomDistance = UnityEngine.Random.Range((float)minionCard.cardRange / 2, (float)minionCard.cardRange * 2);
                    randomDistance = 1.0f;

                    float x = minion.transform.position.x + (float)Math.Cos(angle) * randomDistance;
                    float z = minion.transform.position.z + (float)Math.Sin(angle) * randomDistance;

                    closestMinionPosition = new Vector3(x, 0, z);

                    NavMeshHit hit;
                    if (NavMesh.SamplePosition(closestMinionPosition, out hit, 3.0f, NavMesh.AllAreas))
                    {
                        minionDesiredLocations[currentMinionIndex] = hit.position;
                        minionAgent.SetDestination(hit.position);
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public bool PerformTurn(GameController gameController)
    {
        switch (state)
        {
            case State.Draw:
                currentMinionIndex = 0;
                gameController.drawCard(1);
                state = State.Place;
                break;
            case State.Place:
                //Getting the hand
                List<GameObject> hand = gameController.GetHand(1);
                //Generate a random integer to see what card to play
                int randomIndex = (int)UnityEngine.Random.Range(0, hand.Count - 1);
                gameController.PlaceMinion(randomIndex);
                state = State.GetBoard;
                break;
            case State.GetBoard:
                board = gameController.GetBoard(1);
                minionDesiredLocations = GenerateActualLocations(board);
                currentMinionIndex = 0;
                state = State.Move;
                break;
            case State.Move:
                //If we have moved all minions
                if (currentMinionIndex >= board.Count)
                {
                    Debug.Log("[IA] Passing turn");
                    state = State.Draw;
                    gameController.NextPlayer();
                    return true;
                }
                else if(MoveMinion(board[currentMinionIndex], gameController.GetBoard(0)))
                {
                    gameController.SetCameraTo(board[currentMinionIndex].transform.position);
                    MoveMinion(board[currentMinionIndex], gameController.GetBoard(0));
                    state = State.MinionMoving;
                }
                break;
            case State.MinionMoving:
                Vector3 actualMinionPosition = board[currentMinionIndex].transform.position;
                Vector3 desiredMinionLocation = minionDesiredLocations[currentMinionIndex];
                actualMinionPosition = new Vector3(actualMinionPosition.x, 0, actualMinionPosition.z);
                desiredMinionLocation = new Vector3(desiredMinionLocation.x, 0, desiredMinionLocation.z);
                if (actualMinionPosition == desiredMinionLocation)
                {
                    Debug.Log("Next Minion");
                    currentMinionIndex++;
                    state = State.Move;
                }
                //Debug lines
                if (Input.GetKey(KeyCode.P))
                {
                    Debug.Log("Next Minion");
                    currentMinionIndex++;
                    state = State.Move;
                }
                break;
        }

        //Continue turn proccessing
        return false;
    }

    private List<Vector3> GenerateActualLocations(List<GameObject> board)
    {
        List<Vector3> l = new List<Vector3>();
        foreach (GameObject minion in board)
        {
            l.Add(minion.transform.position);
        }
        return l;
    }

    private float GetAngle(Vector3 minion, Vector3 target)
    {
        return Vector3.Angle(minion, target);
    }
}

1 个答案:

答案 0 :(得分:0)

感谢您的帮助,我终于找到了解决方案。 我所做的就是打电话给

agent.SetDestination(enemyMinion.transform.position)

此后,我存储了代理的起始位置,并在逻辑循环内部,请检查起始位置与实际位置之间的距离,只要该距离大于小仆可以行走的距离,我只需停止NavMeshAgent

这是更新的代码,以防万一有人需要

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

public class EnemyAI : MonoBehaviour {

private float tolerance = 1.2f;

public NavMeshSurface navMesh;

private List<GameObject> board;
private List<Vector3> minionDesiredLocations;
private Vector3 startMinionLocation;

private int currentMinionIndex = 0;

private enum State {
    Draw,
    Place,
    GetBoard,
    Move,
    MinionMoving
}

private State state = State.Draw;

public GameObject PlaceCard(List<GameObject> hand)
{
    //Generate a random integer to see what card to play
    int randomIndex = (int) UnityEngine.Random.Range(0, hand.Count - 1);

    Debug.Log("AI Generated Index: " + randomIndex);

    return hand[randomIndex];
}

private bool MoveMinion(GameObject minion, List<GameObject> enemyMinions)
{
    NavMeshAgent minionAgent = minion.GetComponent<NavMeshAgent>();
    Card minionCard = minion.transform.Find("Card").GetComponent<Card>();
    Vector3 maPosition = minion.transform.position;

    //Finding the closest position of the enemy minion
    float minDistance = 100.0f;
    Vector3 closestMinionPosition = Vector3.negativeInfinity; 
    foreach (GameObject enemyMinion in enemyMinions)
    {
        //Calculating distance beetween the enemy AI minion and the enemy
        float distance = Vector3.Distance(minion.transform.position, enemyMinion.transform.position);
        if (distance <= minDistance)
        {
            minDistance = distance;
            //Attacking the minion is close to the one
            float xRange = UnityEngine.Random.Range(-tolerance, tolerance);
            float yRange = UnityEngine.Random.Range(-tolerance, tolerance);
            closestMinionPosition = enemyMinion.transform.position + new Vector3(xRange, 0, yRange);

            minionDesiredLocations[currentMinionIndex] = closestMinionPosition;
            minionAgent.SetDestination(closestMinionPosition);

            NavMeshHit hit;
            if (NavMesh.SamplePosition(closestMinionPosition, out hit, 1.0f, NavMesh.AllAreas))
            {
                minionDesiredLocations[currentMinionIndex] = hit.position;
                minionAgent.isStopped = false;
                minionAgent.SetDestination(hit.position);
                startMinionLocation = minion.transform.position;
                return true;
            }
        }
    }

    return false;
}

public bool PerformTurn(GameController gameController)
{
    switch (state)
    {
        case State.Draw:
            currentMinionIndex = 0;
            gameController.drawCard(1);
            state = State.Place;
            break;
        case State.Place:
            //Getting the hand
            List<GameObject> hand = gameController.GetHand(1);
            //Generate a random integer to see what card to play
            int randomIndex = (int)UnityEngine.Random.Range(0, hand.Count - 1);
            gameController.PlaceMinion(randomIndex);
            state = State.GetBoard;
            break;
        case State.GetBoard:
            board = gameController.GetBoard(1);
            minionDesiredLocations = GenerateActualLocations(board);
            currentMinionIndex = 0;
            state = State.Move;
            break;
        case State.Move:
            //If we have moved all minions
            if (currentMinionIndex >= board.Count)
            {
                Debug.Log("[IA] Passing turn");
                state = State.Draw;
                gameController.NextPlayer();
                return true;
            }
            else if(MoveMinion(board[currentMinionIndex], gameController.GetBoard(0)))
            {
                gameController.SetCameraTo(board[currentMinionIndex].transform.position);
                MoveMinion(board[currentMinionIndex], gameController.GetBoard(0));
                state = State.MinionMoving;
            }
            break;
        case State.MinionMoving:
            Vector3 actualMinionPosition = board[currentMinionIndex].transform.position;

            float distance = Vector3.Distance(startMinionLocation, actualMinionPosition);
            Card card = board[currentMinionIndex].transform.Find("Card").GetComponent<Card>();
            NavMeshAgent agent = board[currentMinionIndex].GetComponent<NavMeshAgent>();

            if (distance >= card.cardRange)
            {
                agent.isStopped = true;
                currentMinionIndex++;
                state = State.Move;
            }
            //Debug lines
            if (Input.GetKey(KeyCode.P))
            {
                Debug.Log("Next Minion");
                currentMinionIndex++;
                state = State.Move;
            }
            break;
    }

    //Continue turn proccessing
    return false;
}

private List<Vector3> GenerateActualLocations(List<GameObject> board)
{
    List<Vector3> l = new List<Vector3>();
    foreach (GameObject minion in board)
    {
        l.Add(minion.transform.position);
    }
    return l;
}

private float GetAngle(Vector3 minion, Vector3 target)
{
    return Vector3.Angle(minion, target);
}
}