objects using their own unique waypoints array

2019-09-19 23:27发布

问题:

update...

First Class

using UnityEngine;
using System.Collections;

[System.Serializable]
public class Wave
{
    public GameObject enemyPrefab;
    public float spawnInterval = 2;
    public int maxEnemies = 20;
}

public class SpawnEnemy : MonoBehaviour
{

    public GameObject[] waypoints;
    public GameObject testEnemyPrefab;

    public Wave[] waves;
    public int timeBetweenWaves = 5;

    private GameManagerBehavior gameManager;

    private float lastSpawnTime;
    private int enemiesSpawned = 0;

    // Use this for initialization
    void Start()
    {
        lastSpawnTime = Time.time;
        gameManager =
            GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
    }

    // Update is called once per frame
    void Update()
    {
        // 1 Get the index of the current wave, and check if it’s the last one.
        int currentWave = gameManager.Wave;
        if (currentWave < waves.Length)
        {
            // 2 If so, calculate how much time passed since the last enemy spawn and whether it’s time to spawn an enemy. Here you consider two cases.
            // If it’s the first enemy in the wave, you check whether timeInterval is bigger than timeBetweenWaves.
            // Otherwise, you check whether timeInterval is bigger than this wave’s spawnInterval. In either case, you make sure you haven’t spawned all the enemies for this wave.
            float timeInterval = Time.time - lastSpawnTime;
            float spawnInterval = waves[currentWave].spawnInterval;
            if (((enemiesSpawned == 0 && timeInterval > timeBetweenWaves) ||
                 timeInterval > spawnInterval) &&
                enemiesSpawned < waves[currentWave].maxEnemies)
            {
                // 3 If necessary, spawn an enemy by instantiating a copy of enemyPrefab. You also increase the enemiesSpawned count.
                lastSpawnTime = Time.time;
                GameObject newEnemy = (GameObject)
                    Instantiate(waves[currentWave].enemyPrefab);
                newEnemy.GetComponent<MoveEnemy>().waypoints = waypoints;
                newEnemy.GetComponent<MoveEnemy>().JiggleWaypoints();
                enemiesSpawned++;
            }
            // 4 You check the number of enemies on screen. If there are none and it was the last enemy in the wave you spawn the next wave.
            // You also give the player 10 percent of all gold left at the end of the wave.
            if (enemiesSpawned == waves[currentWave].maxEnemies &&
                GameObject.FindGameObjectWithTag("Enemy") == null)
            {
                gameManager.Wave++;
                gameManager.Gold = Mathf.RoundToInt(gameManager.Gold * 1.1f);
                enemiesSpawned = 0;
                lastSpawnTime = Time.time;
            }
            // 5 Upon beating the last wave this runs the game won animation.
        }
        else {
            gameManager.gameOver = true;
            GameObject gameOverText = GameObject.FindGameObjectWithTag("GameWon");
            gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
        }
    }
}

Second Class

using UnityEngine;
using System.Collections;

public class MoveEnemy : MonoBehaviour
{

    [System.NonSerialized]
    public GameObject[] waypoints;
    private int currentWaypoint = 0;
    private float lastWaypointSwitchTime;
    public float speed = 1.0f;

    // Use this for initialization
    void Start()
    {
        lastWaypointSwitchTime = Time.time;
    }

    // Update is called once per frame
    void Update()
    {
        // 1 
        Vector3 startPosition = waypoints[currentWaypoint].transform.position;
        Vector3 endPosition = waypoints[currentWaypoint + 1].transform.position;
        // 2 
        float pathLength = Vector3.Distance(startPosition, endPosition);
        float totalTimeForPath = pathLength / speed;
        float currentTimeOnPath = Time.time - lastWaypointSwitchTime;
        gameObject.transform.position = Vector3.Lerp(startPosition, endPosition, currentTimeOnPath / totalTimeForPath);
        // 3 
        if (gameObject.transform.position.Equals(endPosition))
        {
            if (currentWaypoint < waypoints.Length - 2)
            {
                // 3.a 
                currentWaypoint++;
                lastWaypointSwitchTime = Time.time;
                RotateIntoMoveDirection();
            }
            else {
                // 3.b 
                Destroy(gameObject);

                AudioSource audioSource = gameObject.GetComponent<AudioSource>();
                AudioSource.PlayClipAtPoint(audioSource.clip, transform.position);
                //<< deduct health
                GameManagerBehavior gameManager =
    GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
                gameManager.Health -= 1;
                //>>
            }
        }
    }

    public void JiggleWaypoints()
    {
        for (int i = 1; i < waypoints.Length; i++)
        {
            waypoints[i].transform.position = new Vector3(waypoints[i].transform.position.x + Random.Range(-3, 3), waypoints[i].transform.position.y + Random.Range(-3, 3), 0);
        }
    }

    private void RotateIntoMoveDirection()
    {
        //1 It calculates the bug’s current movement direction by subtracting the current waypoint’s position from that of the next waypoint.
        Vector3 newStartPosition = waypoints[currentWaypoint].transform.position;
        Vector3 newEndPosition = waypoints[currentWaypoint + 1].transform.position;
        Vector3 newDirection = (newEndPosition - newStartPosition);
        //2 It uses Mathf.Atan2 to determine the angle toward which newDirection points, in radians, assuming zero points to the right.
        // Multiplying the result by 180 / Mathf.PI converts the angle to degrees.
        float x = newDirection.x;
        float y = newDirection.y;
        float rotationAngle = Mathf.Atan2(y, x) * 180 / Mathf.PI;
        //3 Finally, it retrieves the child named Sprite and rotates it rotationAngle degrees along the z-axis.
        // Note that you rotate the child instead of the parent so the health bar — you’ll add it soon — remains horizontal.
        GameObject sprite = (GameObject)
            gameObject.transform.FindChild("Sprite").gameObject;
        sprite.transform.rotation =
            Quaternion.AngleAxis(rotationAngle, Vector3.forward);
    }

    public float distanceToGoal()
    {
        float distance = 0;
        distance += Vector3.Distance(
            gameObject.transform.position,
            waypoints[currentWaypoint + 1].transform.position);
        for (int i = currentWaypoint + 1; i < waypoints.Length - 1; i++)
        {
            Vector3 startPosition = waypoints[i].transform.position;
            Vector3 endPosition = waypoints[i + 1].transform.position;
            distance += Vector3.Distance(startPosition, endPosition);
        }
        return distance;
    }
}

Code is working 100% without errors, BUT.... After each spawn all objects get the same waypoint array. This can be seen on the screen as all objects jump to new waypoint in line together each time new object is spawned. I want the object which is already spawn to live life with it's own array of only once created waypoints.

回答1:

You need to create a new array of waypoints each time you create one from the prefabricated object. You don't show your Instantiate method but I'm guessing that it has a line like this:

this.waypoints = prefab.waypoints;

This will mean that all object you create will share the same list of waypoints (as you've discovered).

What you need is something like this - assuming that the waypoints have X, Y, and Z properties):

this.waypoints = new GameObject[5];
for (int i = 0; i++ ; i < 5)
{
    this.waypoints[i].X = prefab.waypoints[i].X;
    this.waypoints[i].Y = prefab.waypoints[i].Y;
    this.waypoints[i].Z = prefab.waypoints[i].Z;
}

(If you want your points to be a variable length you might want to consider using a list).

This means that each object has a list of unique points even if they start with the same values you can change each independently.



回答2:

Based on ChrisFs' and Joe Blows' answers, do something like this in your MoveEnemy script:

private Vector3[] myWay;

public void JiggleWaypoints(GameObject[] waypoints)
{
    myWay = new Vector3[waypoints.Length];
    for(int i = 1; i < waypoints.Length; i++)
    {
        myWay[i] = new Vector3(waypoints[i].transform.position.x + Random.Range(-3, 4), waypoints[i].transform.position.y + Random.Range(-3, 4), 0);
    }
}

myWay replaces the GameObject[].

In your SpawnEnemy script you do this:

GameObject e = (GameObject)Instantiate(enemyPrefab);
e.GetComponent<MoveEnemy>().JiggleWaypoints(waypoints);