0
\$\begingroup\$
public IEnumerator ScaleOverSeconds(List<GameObject> objectsToScale, Vector3 scaleTo, float seconds)
    {
        if (objectsToScale != null)
        {
            for (int i = 0; i < objectsToScale.Count; i++)
            {
                float elapsedTime = 0;
                Vector3 startingScale = objectsToScale[i].transform.localScale;
                while (elapsedTime < seconds)
                {
                    if (objectsToScale == null)
                    {
                        yield return null;
                    }
                    else
                    {
                        objectsToScale[i].transform.localScale = Vector3.Lerp(startingScale, scaleTo, (elapsedTime / seconds));
                        elapsedTime += Time.deltaTime;
                        yield return new WaitForEndOfFrame();
                    }
                }

                objectsToScale[i].transform.localScale = scaleTo;
            }

            waitForScaleOver = true;
        }
    }

Now when I start the coroutine it's scaling one objectsToScale when it finish than it's moving to the next one and scale it and so on. I want to scale them all at the same time in this coroutine.

The full script :

In short : I want to be able to scale multiple objects at the same time automatic or using the R key.

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

public class GenerateWalls : MonoBehaviour
{
    public GameObject gameObjectToRaise;
    public float wallsAmount;
    public float gap;
    public float duration;
    public Vector3 raiseAmount;
    public bool go = false;
    public Color[] colors = new Color[4];
    public bool randomColors = false;
    public bool autoGenerate = false;
    public bool waitForScaleOver = false;

    private GameObject wall;
    private List<GameObject> walls = new List<GameObject>();

    private void Start()
    {
        Init();

        ColorWalls();

        // The z Axis must be minimum 1 or any value above 0 could be also 0.1f
        // but it's better to keep it minimum as 1 by default.
        if (raiseAmount.z < 1)
        {
            raiseAmount.z = 1f;
        }

        if (go)
        {
            CoroutineManager();
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            autoGenerate = false;
            CoroutineManager();
        }

        if(autoGenerate)
        {
            CoroutineManager();
        }
    }

    private void Init()
    {
        walls = new List<GameObject>();

        for (int i = 0; i < wallsAmount; i++)
        {
            wall = Instantiate(gameObjectToRaise);
            wall.transform.position = new Vector3(i + i * gap, 0, i + i * gap);
            wall.name = "Walls";

            walls.Add(wall);
        }
    }

    private void CoroutineManager()
    {
        if (waitForScaleOver)
        {
            for (int i = 0; i < walls.Count; i++)
            {
                if (raiseAmount.z < 1)
                {
                    raiseAmount.z = 1f;
                }

                Destroy(walls[i]);
            }

            Init();
            ColorWalls();

            StartCoroutine(ScaleOverSeconds(walls, new Vector3(raiseAmount.x, raiseAmount.y,
                raiseAmount.z), duration));

            waitForScaleOver = false;

        }
    }

    public IEnumerator ScaleOverSeconds(List<GameObject> objectsToScale, Vector3 scaleTo, float seconds)
    {
        // Do you really need to handle this case? 
        // You probably want to just not start the coroutine at all
        // if you have nothing to scale.
        if (objectsToScale == null) yield return null;

        Vector3[] startingScale = new Vector3[objectsToScale.Count];

        for (int i = 0; i < objectsToScale.Count; i++)
        {

            // Similar here - shouldn't you just remove null objects first?
            if (objectsToScale[i] == null) continue;

            startingScale[i] = objectsToScale[i].transform.localScale;
        }

        for (float t = 0; t < 1f; t += Time.deltaTime / seconds)
        {
            for (int i = 0; i < objectsToScale.Count; i++)
            {
                {
                    if (objectsToScale[i] == null) continue;

                    objectsToScale[i].transform.localScale = Vector3.Lerp(
                              startingScale[i],
                              scaleTo,
                              t
                    );
                }
                yield return null;
            }

            foreach (var obj in objectsToScale)
                obj.transform.localScale = scaleTo;

            waitForScaleOver = true;
        }
    }

    private void ColorWalls()
    {
        var childrenWalls = GameObject.FindGameObjectsWithTag("Wall");

        for (int i = 0; i < childrenWalls.Length; i++)
        {
            if (randomColors)
            {
                childrenWalls[i].transform.GetChild(0).GetComponent<Renderer>().material.color
                    = GetRandomColour32();
            }
            else
            {
                childrenWalls[i].transform.GetChild(0).GetComponent<Renderer>().material.color = colors[i];
            }
        }
    }

    private Color32 GetRandomColour32()
    {
        //using Color32
        return new Color32(
          (byte)UnityEngine.Random.Range(0, 255), //Red
          (byte)UnityEngine.Random.Range(0, 255), //Green
          (byte)UnityEngine.Random.Range(0, 255), //Blue
          255 //Alpha (transparency)
        );
    }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Just reverse the order of the loops, obviously. Loop over time first, then over objects in each moment/frame - instead of looping over objects and then over time for each object.

You may want to work your way through some beginner programming tutorials if you're having difficulty with the basics like this.

public IEnumerator ScaleOverSeconds 
      (List<GameObject> objectsToScale, Vector3 scaleTo, float seconds)
{
    // Do you really need to handle this case? 
    // You probably want to just not start the coroutine at all
    // if you have nothing to scale.
    if (objectsToScale == null) return;

    Vector3[] startingScale = new Vector3[objectsToScale.Count];

    for (int i = 0; i < objectsToScale.Count; i++) 
    {

        // Similar here - shouldn't you just remove null objects first?
        if (objectsToScale[i] == null) continue;

        startingScale[i] = objectsToScale[i].transform.localScale;
    }

    for (float t = 0; t < 1f; t += Time.deltaTime/seconds)
    {
        for (int i = 0; i < objectsToScale.Count; i++) {
        {
            if (objectsToScale[i] == null) continue;

            objectsToScale[i].transform.localScale = Vector3.Lerp(
                      startingScale[i],
                      scaleTo,
                      t
            );                
        }
        yield return null;
   }

   foreach (var obj in objectsToScale)
       obj.transform.localScale = scaleTo;

   waitForScaleOver = true;
}
\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .