Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Having trouble with a tile system

Discussion in 'Scripting' started by Khosan, Mar 31, 2020.

  1. Khosan

    Khosan

    Joined:
    Jan 13, 2013
    Posts:
    3
    So, first things first, I had a version of a tile system that did work, but I did some reworks to make it faster and prettier to look at code-wise, but I've somehow broken it.

    The system itself has two basic components: tiles and edges. I didn't like having tile sized walls, so I added edges between each individual tile so I could also vary the movement weight between tiles and more easily alter those values.

    Originally, it worked with me placing the tiles manually in Unity. Deleted ones I didn't want and then I let some code handle connecting them. It grabbed all of the tiles in the map, stuffed them into a list, then painstakingly went through the list many, many times over to check for adjacency based on their positions. When two tiles were adjacent, it instantiated an Edge prefab between them and ran some functions in the tile and edge classes to connect them to one another. It worked, but it wasn't very fast and it was awful to look at. At least half of my issue with this is that I had a foreach inside another foreach because FindObjectsOfType<CTile> returns an unordered list and I couldn't easily sort it.

    So I built a new way to do it. I added some extra code to the first version that generated a .png file containing the map data I needed, storing information in the color data. Each 2x2 square in the .png file represents a tile and two of its edges, so by reading in that information I can recreate everything I had before.

    Here's my issue: Somehow, the updated method isn't actually connecting tiles to edges and vice versa, in spite of running on the same functions. When I select the created tiles and edges in the scene editor, they have zero connections, no references to the edge/tile they should be connected to. I added debug log calls to those specific functions and they're being called, the tiles/edges being passed to them aren't null, they're just not doing what they should be.

    Old calls:
    Code (CSharp):
    1. if (tile1.AdjacentTo(tile2))
    2. {
    3.     CTile_Edge edge = Instantiate(prefab_Edge) as CTile_Edge;
    4.  
    5.     edge.Setup(tile1, tile2, 1);
    6.  
    7.     tile1.ConnectTo(edge);
    8.     tile2.ConnectTo(edge);
    9.  
    10.     list_TileEdges.Add(edge);
    11. }
    I'm not going to post the entire function, I don't think it's strictly necessary. This is just for the sake of comparison to parts of the new function. The calls are being made in the same way, in the same order.

    New function:
    Code (CSharp):
    1. void MapCreate_Fast()
    2. {
    3.     byte[] mapData = System.IO.File.ReadAllBytes(Application.dataPath + "/../Assets/Maps/TestMap.png");
    4.     Texture 2D Map = new Texture2D(2, 2);
    5.     Map.LoadImage(mapData);
    6.  
    7.     float minX, minZ;
    8.  
    9.     Color32 data = Map.GetPixel(0, 0);
    10.  
    11.     minX = (float)data.r + (((float)data.g + 1) / 256);
    12.     minZ = (float)data.b + (((float)data.a + 1) / 256);
    13.  
    14.     array_Tiles = new CTile[Map.width / 2, Map.height / 2];
    15.  
    16.     //Tile creation
    17.     for (int x = 0; x < Map.width / 2; x++)
    18.     {
    19.         for (int z = 0; z < Map.height / 2; z++)
    20.         {
    21.             int px = x * 2;
    22.             int pz = z * 2 + 1;
    23.  
    24.             if (Map.GetPixel(px, pz) == Color.green)
    25.             {
    26.                 CTile tile = Instantiate(prefab_Tile) as CTile;
    27.                 array_Tiles[x, z] = tile;
    28.                 Vector3 position = new Vector3(x + minX, 0, z + minZ);
    29.                 tile.transform.position = position;
    30.  
    31.                 if (x > 0 && Map.GetPixel(px - 1, pz) == Color.white)
    32.                 {
    33.                     [B]CTile_Edge edge = Instantiate(prefab_Edge) as CTile_Edge;
    34.                     edge.transform.position = position - new Vector3(0.5f, 0, 0);
    35.  
    36.                     edge.Setup(tile, array_Tiles[(x - 1), z], 1);
    37.                     tile.ConnectTo(edge);
    38.                     array_Tiles[(x - 1), z].ConnectTo(edge);[/B]
    39.                 }
    40.                 if (z > 0 && Map.GetPixel(px, pz - 1) == Color.white)
    41.                 {
    42.                     [B]CTile_Edge edge = Instantiate(prefab_Edge) as CTile_Edge;
    43.                     edge.transform.position = position - new Vector3(0, 0, 0.5f);
    44.  
    45.                     edge.Setup(tile, array_Tiles[x, (z - 1)], 1);
    46.                     tile.ConnectTo(edge);
    47.                     array_Tiles[x, (z - 1)].ConnectTo(edge);[/B]
    48.                 }
    49.             }
    50.         }
    51.     }
    52. }
    When it specifically comes down to where the calls are made, the two are basically identical.

    Tile_Edge's Setup function;
    Code (CSharp):
    1. public void Setup(CTile tile1, CTile tile2, float weight)
    2. {
    3.     Connected_Tiles.Add(tile1);
    4.     Connected_Tiles.Add(tile2);
    5.  
    6.     Movement_Weight = weight;
    7. }
    Tile's ConnectTo function:
    Code (CSharp):
    1. public void ConnectTo(CTile_Edge edge)
    2. {
    3.     Connected_Edges.Add(edge);
    4. }
    I cannot figure out why it's not adding the connections as it should. The functions that basically do that and only that are being called and called almost identically to the way they were called before. The biggest change is the tiles aren't already included in the scene, they're created within the code block. Included some extra snippets of the problem in the scene, with a working version from the old one.

    Maybe I'm missing something simple, but any insight would be welcome.
     

    Attached Files:

    • New.PNG
      New.PNG
      File size:
      39.9 KB
      Views:
      305
    • Old.PNG
      Old.PNG
      File size:
      46.8 KB
      Views:
      305
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,971
    These sorts of data bugs can be a bit tricky to track down without the entire project right in hand, so let me offer you a few tips on it.

    First, cut your map down to the smallest possible size that can demo the transitions you need, as well as manifest the problems you're seeing. This might just be 3x1 tiles, with known tiles on the left/right, and just have it choose the middle. Or it might have to be 2x2, or 3x3, whatever your case is that can be the simplest.

    This way you can reason about each step of the way.

    Now go sprinkle in strategic Debug.Log() statements in the above code and go use it. By "strategic" I mean print out names of prefabs chosen, coordinates, input values to conditions, etc. On a regular size map such a torrent of data would be inscrutable, but hopefully with a super-simplified map you can get some insight as to which step is not doing what you think it should do.

    Good luck, I know you will find it, or at least come back with some questions about "how come this part misbehaves?!"
     
  3. Khosan

    Khosan

    Joined:
    Jan 13, 2013
    Posts:
    3
    OKAY, I figured it out, and I kind of hate the answer.

    So in the Start() function for both tiles and edges, I was instantiating the lists that would contain their connected edges/tiles respectively. I was also calling the Clear() function, as they don't start empty (or at least, past-me had left a comment stating as much and I took myself at my word). Apparently, the Start() function was being called well after they were instantiated and I did all the work of filling in their data.

    I'd like to say, on the one hand, I am very dumb and on the other hand, how does that even work? This is absolutely my inexperience talking, but when does Start() get called? It was my assumption that Start() would be called as the object was created, like how I remember init() functions worked in those C++ classes I took back in High School..
     
    Last edited: Mar 31, 2020
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,971
    This is an EXCELLENT question. I wish more people would ask this because it's important to understand Unity's timing and event execution order. Fortunately there's a great graphic here:

    https://docs.unity3d.com/Manual/ExecutionOrder.html

    Let me offer you this:

    Remember the six stages of debugging:

    1. That can’t happen.
    2. That doesn’t happen on my machine.
    3. That shouldn’t happen.
    4. Why does that happen?
    5. Oh, I see.
    6. How did that ever work?