0
\$\begingroup\$

I have 100 cubes which makes a plane, and I have a script that moves those cubes in random patterns according to random chosen methods.

For example, in the method LeftPartFalls, only the cubes on the left part of the plane change materials, then fall down, then change materials again, then rise back up.

In the other methods I do the same actions but in the different patterns like:

  • only the right part falls
  • only the first, second, third, fourth and fifth lines fall

This all works fine when I test it in the Unity editor.

But when I build it for Android, it does the changing color and falling down things but not in the pattern that I wanted. Instead I get a kind of mosaic pattern, and I noticed it does that it does this in 2 random-looking mosaic patterns. For example, the first might look like this:

Example of random mosaic pattern

And after the cubes that are low in that screenshot rise back up, the pattern reverses, with the cubes that were high taking their turn to fall. And then the cycle repeats.

Why is this behaviour different on Android than in editor, and how can I fix it?


This is one of my methods to make a pattern of cubes change colour, fall, and rise:

void LeftPartFalls()
{
    if (LeftPartMat)
    {
        for (int i = 0; i < Mathf.Min(5, FirstLine.Length); i++)        // COLOR CHANGE
        {
            FirstLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            SecondLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            ThirdLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            FourthLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            FiftLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            SixthLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            SeventhLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            EighthLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            NinthLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
            TenthLine[i].gameObject.GetComponent<MeshRenderer>().material = greyMAT;
        }
    }
    if (!LeftPartMat)
    {
        for (int i = 0; i < Mathf.Min(5, FirstLine.Length); i++)        // COLOR CHANGE 2
        {
            FirstLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            SecondLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            ThirdLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            FourthLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            FiftLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            SixthLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            SeventhLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            EighthLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            NinthLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
            TenthLine[i].gameObject.GetComponent<MeshRenderer>().material = whiteMAT;
        }
    }

    if (LeftPartFall)       // FALL DOWN
    {
        for (int i = 0; i < Mathf.Min(5, FirstLine.Length); i++)
        {
            FirstLine[i].gameObject.transform.position = Vector3.Lerp(FirstLine[i].transform.position, new Vector3(FirstLine[i].transform.position.x, -4f, FirstLine[i].transform.position.z), t);
            SecondLine[i].gameObject.transform.position = Vector3.Lerp(SecondLine[i].transform.position, new Vector3(SecondLine[i].transform.position.x, -4f, SecondLine[i].transform.position.z), t);
            ThirdLine[i].gameObject.transform.position = Vector3.Lerp(ThirdLine[i].transform.position, new Vector3(ThirdLine[i].transform.position.x, -4f, ThirdLine[i].transform.position.z), t);
            FourthLine[i].gameObject.transform.position = Vector3.Lerp(FourthLine[i].transform.position, new Vector3(FourthLine[i].transform.position.x, -4f, FourthLine[i].transform.position.z), t);
            FiftLine[i].gameObject.transform.position = Vector3.Lerp(FiftLine[i].transform.position, new Vector3(FiftLine[i].transform.position.x, -4f, FiftLine[i].transform.position.z), t);
            SixthLine[i].gameObject.transform.position = Vector3.Lerp(SixthLine[i].transform.position, new Vector3(SixthLine[i].transform.position.x, -4f, SixthLine[i].transform.position.z), t);
            SeventhLine[i].gameObject.transform.position = Vector3.Lerp(SeventhLine[i].transform.position, new Vector3(SeventhLine[i].transform.position.x, -4f, SeventhLine[i].transform.position.z), t);
            EighthLine[i].gameObject.transform.position = Vector3.Lerp(EighthLine[i].transform.position, new Vector3(EighthLine[i].transform.position.x, -4f, EighthLine[i].transform.position.z), t);
            NinthLine[i].gameObject.transform.position = Vector3.Lerp(NinthLine[i].transform.position, new Vector3(NinthLine[i].transform.position.x, -4f, NinthLine[i].transform.position.z), t);
            TenthLine[i].gameObject.transform.position = Vector3.Lerp(TenthLine[i].transform.position, new Vector3(TenthLine[i].transform.position.x, -4f, TenthLine[i].transform.position.z), t);
        }
    }
    if (!LeftPartFall)      // RISE UP
    {
        for (int i = 0; i < Mathf.Min(5, FirstLine.Length); i++)
        {
            FirstLine[i].gameObject.transform.position = Vector3.Lerp(FirstLine[i].transform.position, new Vector3(FirstLine[i].transform.position.x, 0f, FirstLine[i].transform.position.z), t);
            SecondLine[i].gameObject.transform.position = Vector3.Lerp(SecondLine[i].transform.position, new Vector3(SecondLine[i].transform.position.x, 0f, SecondLine[i].transform.position.z), t);
            ThirdLine[i].gameObject.transform.position = Vector3.Lerp(ThirdLine[i].transform.position, new Vector3(ThirdLine[i].transform.position.x, 0f, ThirdLine[i].transform.position.z), t);
            FourthLine[i].gameObject.transform.position = Vector3.Lerp(FourthLine[i].transform.position, new Vector3(FourthLine[i].transform.position.x, 0f, FourthLine[i].transform.position.z), t);
            FiftLine[i].gameObject.transform.position = Vector3.Lerp(FiftLine[i].transform.position, new Vector3(FiftLine[i].transform.position.x, 0f, FiftLine[i].transform.position.z), t);
            SixthLine[i].gameObject.transform.position = Vector3.Lerp(SixthLine[i].transform.position, new Vector3(SixthLine[i].transform.position.x, 0f, SixthLine[i].transform.position.z), t);
            SeventhLine[i].gameObject.transform.position = Vector3.Lerp(SeventhLine[i].transform.position, new Vector3(SeventhLine[i].transform.position.x, 0f, SeventhLine[i].transform.position.z), t);
            EighthLine[i].gameObject.transform.position = Vector3.Lerp(EighthLine[i].transform.position, new Vector3(EighthLine[i].transform.position.x, 0f, EighthLine[i].transform.position.z), t);
            NinthLine[i].gameObject.transform.position = Vector3.Lerp(NinthLine[i].transform.position, new Vector3(NinthLine[i].transform.position.x, 0f, NinthLine[i].transform.position.z), t);
            TenthLine[i].gameObject.transform.position = Vector3.Lerp(TenthLine[i].transform.position, new Vector3(TenthLine[i].transform.position.x, 0f, TenthLine[i].transform.position.z), t);
        }
    }
}

This is the enumerator part, that chooses the random method:

private IEnumerator enumerator(float waitTime)
{
    while (true)
    {
        RandomInt = Random.Range(1, 6);
        if (RandomInt == 1)
        {
            LeftPartCalls = true;
            LeftPartMat = true;
        }
        if (RandomInt == 2)
        {
            RightPartCalls = true;
            RightPartMat = true;
        }
        if (RandomInt == 3)
        {
            VerticalStripeCalls = true;
            VerticalStripeMat = true;
        }
        if (RandomInt == 4)
        {
            UpLeftDownRightCalls = true;
            UpLeftDownRightMat = true;
        }
        if (RandomInt == 5)
        {
            UpRightDownLeftCalls = true;
            UpRightDownLeftMat = true;
        }
        if (RandomInt == 6)
        {
            HorizontalStripeCalls = true;
            HorizontalStripeMat = true;
        }
        if (RandomInt == 7)
        {
            CenterCalls = true;
            CenterMat = true;
        }
        if (RandomInt == 8)
        {
            EdgeCalls = true;
            EdgeMat = true;
        }
        if (RandomInt == 9)
        {
            SkewCalls = true;
            SkewMat = true;
        }
        if (RandomInt == 10)
        {
            HorizontalWayCalls = true;
            HorizontalWayMat = true;
        }


        yield return new WaitForSeconds(MoveT);
        if (RandomInt == 1)
            LeftPartFall = true;
        if (RandomInt == 2)
            RightPartFall = true;
        if (RandomInt == 3)
            VerticalStripeFall = true;
        if (RandomInt == 4)
            UpLeftDownRightFall = true;
        if (RandomInt == 5)
            UpRightDownLeftFall = true;
        if (RandomInt == 6)
            HorizontalStripeFall = true;
        if (RandomInt == 7)
            CenterFall = true;
        if (RandomInt == 8)
            EdgeFall = true;
        if (RandomInt == 9)
            SkewFall = true;
        if (RandomInt == 10)
            HorizontalWayFall= true;

        t = 0f;
        t += Time.deltaTime / 0.7f;

        yield return new WaitForSeconds(3);
        LeftPartFall = false;
        LeftPartMat = false;
        RightPartMat = false;
        RightPartFall = false;
        VerticalStripeMat = false;                                  
        VerticalStripeFall = false;
        UpLeftDownRightFall = false;
        UpLeftDownRightMat = false;
        UpRightDownLeftFall = false;
        UpRightDownLeftMat = false;
        HorizontalStripeFall = false;
        HorizontalStripeMat = false;
        CenterFall = false;
        CenterMat = false;
        EdgeFall = false;
        EdgeMat = false;
        SkewFall = false;
        SkewMat = false;
        HorizontalWayFall = false;
        HorizontalWayMat = false;
        yield return new WaitForSeconds(4);
        LeftPartCalls = false;
        RightPartCalls = false;
        VerticalStripeCalls = false;
        UpLeftDownRightCalls = false;
        UpRightDownLeftCalls = false;
        HorizontalStripeCalls = false;
        CenterCalls = false;
        EdgeCalls = false;
        SkewCalls = false;
        HorizontalWayCalls = false;

        if (MoveT >= 1.3f)
        {
            MoveT -= 0.7f;
        }
        if (MoveT <= 1.3f)
        {
            MoveT -= 0.6f;
        }
        if (MoveT <= 0.7f)
        {
            MoveT = 0.7f;
        }
        yield return new WaitForSeconds(MoveT);
    }
}
\$\endgroup\$
  • \$\begingroup\$ Is there any way that you know how to debug it? I mean if i would know how to debug an android build, maybe i can find a solution \$\endgroup\$ – AmonRa's Channel May 18 at 12:59
  • \$\begingroup\$ Presumably you searched "debug unity android" to find your answer to that question? \$\endgroup\$ – DMGregory May 18 at 13:02
  • \$\begingroup\$ I'm %99 sure that problem is the assignment of the objects. I assign them like: GameObject.FindGameObjectsWithTag("FirstLine"); But in the android build i think they are not assigning in order. Should i assign them one by one? like: FirstLine[0] = firstLineObject_0; \$\endgroup\$ – AmonRa's Channel May 18 at 15:48
  • \$\begingroup\$ If that's where you think your problem is, then that code should be included in your question. But no, in general, it is not safe to rely on Find methods to return results in a particular desired order - assume they might shuffle the results arbitrarily, and have a plan to introduce/create the order you need yourself. \$\endgroup\$ – DMGregory May 18 at 15:55
0
\$\begingroup\$

It still doesn't look like we have enough information here to identify why your game behaves differently on Android, but I'd like to recommend we start with a clean slate to hopefully make the code simpler to understand, more concise, with less room for bugs to creep in. I'll build this up in pieces so it's easier to follow:

First, let's generate your plane of cubes by script, and store it in a 2D array so we don't need to juggle ten different variables to store all the lines:

public class FallingFloor : MonoBehaviour {

    public Vector2Int size = new Vector2Int(10, 10);
    public float spacing = 1f;
    public MeshRenderer tilePrefab;
    MeshRenderer[,] tiles;

    void CreateFloor() {
        // Define our tiles array in the appropriate size.
        tiles = new MeshRenderer[size.x, size.y];

        // Spawn the floor centered around this object's position.
        Vector3 origin = transform.position + new Vector3(size.x, 0, size.y) * -0.5f * spacing; 

        // For each row, and each column, instantiate a tile.
        for(int x = 0; x < size.x; x++) {
            for(int y = 0; y < size.y; y++) {
                tiles[x, y] = Instantiate<MeshRenderer>(
                                   tilePrefab, 
                                   new Vector3(x, 0, y) * spacing + origin,
                                   Quaternion.identity
                              );
            }
        }
    }

// ...

With all the tiles in one variable, it's easy to define functions that select different patterns of tiles. Let's follow the convention that our tile selection functions will populate a list with the desired tiles:

void SelectLeftHalf(List<MeshRenderer> pattern) {
    pattern.Clear();
    for(int x = 0; x < size.x/2; x++) {
        for(int y = 0; y < size.y; y++) {
            pattern.Add(tiles[x, y]);
        }
    }
}

void SelectEverySecondRow(List<MeshRenderer> pattern) {
    pattern.Clear();
    for(int x = 0; x < size.x; x++) {
        for(int y = 0; y < size.y; y += 2) {
            pattern.Add(tiles[x, y]);
        }
    }
}

void SelectCross(List<MeshRenderer> pattern) {
    pattern.Clear();
    // Make sure we don't go out of bounds on non-square maps.
    int limit = Mathf.Min(size.x, size.y);
    for(int x = 0; x < limit; x++) {
        pattern.Add(tiles[x, x]);
        int y = size.y - 1 - x;
        // Don't double-add the middle tile in the case of odd sizes.
        if(x != y)
            pattern.Add(tiles[x, y]);
    }
}

// ...

Now we can simplify the falling and colour-changing animations to single functions each that act on a selected pattern of tiles:

void ChangeAllMaterials(List<MeshRenderer> pattern, Material material) {
    foreach(var renderer in pattern)
        renderer.sharedMaterial = material;
}

IEnumerator SlideAllBlocks(List<MeshRenderer> pattern, float startHeight, float endHeight, float duration) {
    float progress = 0f;
    while(progress < 1f) {
        progress = Mathf.Clamp01(progress + Time.deltaTime/duration);

        // Compute a height to move to, with an ease-out curve.
        float height = Mathf.Lerp(startHeight, endHeight, 1 - (1 - progress) * (1 - progress));

        // Set all blocks in the pattern to this height.
        foreach(var renderer in pattern) {
            var position = renderer.transform.position;
            position.y = height;
            renderer.transform.position = position;
        }

        // Wait one frame, then resume.
        yield return null;
    }
}

// ...

And our master loop can then just select a pattern, call these functions on it in our desired sequence, and repeat:

public Material fallingMaterial;
public Material risingMaterial;
public float fallSeconds = 3f;
public float fallHeight = -4f;
public float riseSeconds = 4f;

IEnumerator AnimationLoop(float moveSeconds) {
    // Prep our variable for tracking the pattern of tiles we're acting on.
    // Since this is a local variable, we can control exactly who gets to act on it,
    // so anything that changes the pattern should be easy to track down.
    var pattern = new List<MeshRenderer>();

    while(true) {
        // Each cycle, select a randomly-chosen pattern of blocks.
        int selection = Random.Range(1, 6);

        switch(selection) {
            case 1 : SelectLeftHalf(pattern); break;
            case 2 : SelectRightHalf(pattern); break;
            case 3 : SelectEverySecondRow(pattern); break;
            case 4 : SelectEverySecondColumn(pattern); break;
            case 5 : SelectCross(pattern); break;
        }

        // Change the material of all blocks in this pattern.
        ChangeAllMaterials(pattern, fallingMaterial);

        // Wait before we start to fall.
        yield return new WaitForSeconds(moveSeconds);

        // Chain control to our sliding method until the fall is complete.
        yield return SlideAllBlocks(pattern, 0, fallHeight, fallSeconds);

        // Done falling. Reset the materials and rise back up.
        ChangeAllMaterials(pattern, risingMaterial);

        // Chain control to our sliding method until the fall is complete.
        yield return SlideAllBlocks(pattern, fallHeight, 0f, riseSeconds);

        // Everything has risen and reset. Now adjust our moveSeconds for next cycle:
        if (moveSeconds >= 1.3f) {
            moveSeconds -= 0.7f;
        } else if (moveSeconds <= 1.3f) {
            moveSeconds = Mathf.Max(moveSeconds - 0.6f, 0.7f);
        }

        // Wait before starting the next cycle & choosing a new pattern.
        yield return new WaitForSeconds(moveSeconds);
     }
}

Now we don't need to rely on a small army of bool variables to coordinate all our actions. You should find an approach something like this makes your game behave more predictably, and makes it less laborious to add or change behaviour.

\$\endgroup\$
  • \$\begingroup\$ one last thing, can you pls increase the thickness of the pattern? I tried somethings but didn't work. \$\endgroup\$ – AmonRa's Channel May 19 at 17:30
  • \$\begingroup\$ You mean you want a double-wide X? Try drawing the pattern you want on graph paper and look at the coordinates of the squares you coloured in. Old sprite graphics used to be made this way, and it will give you practice converting between a shape and its numeric representation. \$\endgroup\$ – DMGregory May 19 at 17:32
  • \$\begingroup\$ Okay i'll try smth, if i can't do it, i'll write here \$\endgroup\$ – AmonRa's Channel May 19 at 17:35
  • \$\begingroup\$ Post it as a new question if you need. Comments are not for extended discussion. \$\endgroup\$ – DMGregory May 19 at 17:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.