Как заставить игровые объекты следовать по пути в Unity по кривой Безье - PullRequest
1 голос
/ 09 июля 2019

У меня есть один объект, движущийся по кривой Безье, но у меня есть несколько объектов, которым необходимо следовать по этому пути, но все они следуют одновременно. Этот объект является врагом типа змеи в космическом шутере.

До сих пор я пытался сделать все объекты дочерними, но при этом они остаются по прямой линии к родителю, следуя кривой Безье.Я также сделал все объекты отдельными и прикрепил к ним скрипт Безье, чтобы они все следовали по одному и тому же маршруту, и это работает, но только они следуют по одному и тому же пути одновременно.

public class BezierFollow : MonoBehaviour{

[SerializeField]
private Transform[] routes;

private int routeToGo;

private float tParam;

private Vector2 enemyPosition;

[SerializeField]
public float speedModifier = 0.5f;

private bool coroutineAloud;


// Start is called before the first frame update
void Start()
{
    routeToGo = 0;
    tParam = 0f;
    //speedModifier = 0.5f;
    coroutineAloud = true;
}

// Update is called once per frame
void Update()
{
    if (coroutineAloud)
    {
        StartCoroutine(GoByTheRouteRoutine(routeToGo));
    }
}

private IEnumerator GoByTheRouteRoutine(int routeNumber)
{
    coroutineAloud = false;

    Vector2 p0 = routes[routeNumber].GetChild(0).position;
    Vector2 p1 = routes[routeNumber].GetChild(1).position;
    Vector2 p2 = routes[routeNumber].GetChild(2).position;
    Vector2 p3 = routes[routeNumber].GetChild(3).position;

    while(tParam < 1)
    {
        tParam += Time.deltaTime * speedModifier;

        enemyPosition = Mathf.Pow(1 - tParam, 3) * p0 +
            3 * Mathf.Pow(1 - tParam, 2) * tParam * p1 +
            3 * (1 - tParam) * Mathf.Pow(tParam, 2) * p2 +
            Mathf.Pow(tParam, 3) * p3;

        transform.position = enemyPosition;
        yield return new WaitForEndOfFrame();
    }

    tParam = 0f;

    routeToGo += 1;

    if(routeToGo > routes.Length - 1)
        routeToGo = 0;

    coroutineAloud = true;

}}

Вот скрипт маршрутовя не думаю, что вам понадобится, но я включу это

public class Route : MonoBehaviour{
[SerializeField]
private Transform[] controlPoints;

private Vector2 gizmosPos;

private void OnDrawGizmos()
{
    for(float t = 0; t <= 1; t += 0.05f)
    {
        gizmosPos = Mathf.Pow(1 - t, 3) * controlPoints[0].position +
            3 * Mathf.Pow(1 - t, 2) * t * controlPoints[1].position +
            3 * (1 - t) * Mathf.Pow(t, 2) * controlPoints[2].position +
            Mathf.Pow(t, 3) * controlPoints[3].position;

        Gizmos.DrawSphere(gizmosPos, 0.25f);
    }

    Gizmos.DrawLine(new Vector2(controlPoints[0].position.x, controlPoints[0].position.y),
        new Vector2(controlPoints[1].position.x, controlPoints[1].position.y));

    Gizmos.DrawLine(new Vector2(controlPoints[2].position.x, controlPoints[2].position.y),
       new Vector2(controlPoints[3].position.x, controlPoints[3].position.y));
}}

Я думаю, что мне нужно сделать, чтобы каждый объект не был дочерним объектом, и у всех был прикреплен скрипт для следования по маршруту, но такжеотсроченное время, прежде чем он пойдет по пути, но не уверен, как это сделать.Я подумал, что это может быть необходимо сделать в отдельном скрипте, потому что в скрипте кривой Безье он установлен так, что объект начинается снова в начале маршрута, как только достигает конца

Ответы [ 2 ]

1 голос
/ 15 июля 2019

Я знаю, чего вы пытаетесь достичь, и я верю, что вы можете сделать это с помощью дополнительного сценария без изменения текущего кода.

Здесь я создал новый сценарий под названием EnemyBehavior.

public class EnemyBehavior : MonoBehaviour{
public Path pathToFollow;

//PATH INFO
public int currentWayPointID = 0;

//speeds
public float speed = 2;
public float reachDistance = 0.4f;
public float rotationSpeed = 5f;

float distance; //DISTANCE TO NEXT PATH POINT

public bool useBezier = false;

//STATE MACHINES
public enum EnemyStates
{
    ON_PATH,        
    IDLE
}
public EnemyStates enemyState;

public int enemyID;

void Update()
{
    switch (enemyState)
    {
        case EnemyStates.ON_PATH:
            MoveOnThePath(pathToFollow);
            break;            
        case EnemyStates.IDLE:

            break;
    }
}

void MoveToFormation()
{
    //transform.position = Vector3.MoveTowards(transform.position, formation.gridList[enemyID], speed * Time.deltaTime);
    //if(Vector3.Distance(transform.position, formation.gridList[enemyID])<= 0.001f)
    {
        //transform.SetParent(formation.gameObject.transform);
        transform.eulerAngles = Vector3.zero;
        enemyState = EnemyStates.IDLE;
    }
}

void MoveOnThePath(Path path)
{
    if (useBezier)
    {
        //MOVING THE ENEMY
        distance = Vector3.Distance(path.bezierObjList[currentWayPointID], transform.position);
        transform.position = Vector3.MoveTowards(transform.position, path.bezierObjList[currentWayPointID], speed * Time.deltaTime);
        //ROTATION OF YOUR ENEMY
        var direction = path.bezierObjList[currentWayPointID] - transform.position;

        if (direction != Vector3.zero)
        {
            direction.z = 0;
            direction = direction.normalized;
            var rotation = Quaternion.LookRotation(direction);
            transform.rotation = Quaternion.Slerp(transform.rotation, rotation, rotationSpeed * Time.deltaTime);
        }
    }
    else
    {
        distance = Vector3.Distance(path.pathObjList[currentWayPointID].position, transform.position);
        transform.position = Vector3.MoveTowards(transform.position, path.pathObjList[currentWayPointID].position, speed * Time.deltaTime);

        //ROTATION OF ENEMY
        var direction = path.pathObjList[currentWayPointID].position - transform.position;

        if (direction != Vector3.zero)
        {
            direction.y = 0;
            direction = direction.normalized;
            var rotation = Quaternion.LookRotation(direction);
            transform.rotation = Quaternion.Slerp(transform.rotation, rotation, rotationSpeed * Time.deltaTime);
        }
    }        
}}

Прикрепите этот скрипт ко всем игровым объектам, для которых вы собираетесь использовать путь, и не забудьте назначить путь к ним в инспекторе.

Возможно, вам придется настроить направление в соответствии с вашими требованиями.нужно, если это 2D-игра, но это должно работать для вас.Дайте мне знать, как вы поживаете и не стесняйтесь связаться со мной напрямую.

0 голосов
/ 09 июля 2019

Как насчет этого подхода:

  1. Присоедините BezierCurve ко всем игровым объектам, которым необходимо поведение. Не поддерживайте никаких отношений между родителями и детьми.
  2. Запрет BezierCurve , чтобы следовать / запускать автоматически. Держите bool, чтобы остановить, если вам нужно.
  3. Создать новый скрипт BezierCurveBatch , прикрепить к основному (родительскому) gameObject и иметь Список содержит ссылку на детей. В том же сценарии оставьте float , скажем delayStartCurve , чтобы управлять временем между двумя BezierCurve start.
  4. В BezierCurveBatch запустите BezierCurve из Список детей после каждого delayStartCurve .

Я предоставил код демо-сценария. Не проверено, но должно работать.

public class BezierCurve
{
    //Starts following bezier curve.
    public void StartFollow()
    {
        //some code here.
    }
}

public class BezierCurveBatch : MonoBehaviour
{
    [SerializeField]
    List<BezierCurve> m_lstChildren;

    [SerializeField]
    float m_delayStartCurve = 10;
    float m_timeLeftToStartNextChild = 0;

    bool m_isRunBatchCurve = false;

    /// <summary>
    /// Start batch follow after each interval.
    /// </summary>
    public void StartBatch()
    {
        m_isRunBatchCurve = true;
    }

    private void Update()
    {
        if (!m_isRunBatchCurve)
            return;

        m_timeLeftToStartNextChild -= Time.deltaTime;
        if (m_timeLeftToStartNextChild <= 0.0f)
        {
            if (m_lstChildren.Count > 0) //if we have children left.
            {
                BezierCurve l_bCurveToStart = m_lstChildren[0];     //Getting top object.
                m_lstChildren.RemoveAt(0);                          //removing top object.
                l_bCurveToStart.StartFollow();                      //Start follow bezier curve
                m_timeLeftToStartNextChild = m_delayStartCurve;     //resetting time.
            }

            if (m_lstChildren.Count == 0)       //After processing last object, check if need to continue for next object.
                m_isRunBatchCurve = false;
        }
    }
}
...