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. Dismiss Notice

Question How to add to a 2d sprite maze box coolider's 2d to each tile(wall) automaticlly?

Discussion in 'Scripting' started by Chocolade, Jul 23, 2023.

  1. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    916
    I didn't create the maze.

    The maze is type of sprite(2d and ui)



    This is how the maze should looks like with all the colliders:




    there should be 49 colliders attached to the maze gameobject.

    but instead adding each collider individual and it will take a lot of time I want to make a script that will add the colliders automatically.

    I'm trying to use this script attached to the maze gameobject and no matter how many checks I added to find and avoid duplicated or overlapping colliders it keep adding more then 100 colliders instead 49.

    it looks like it's adding the colliders at the right position and the right alignment but also adding too many colliders. how can I fix the sript?

    Code (csharp):
    1.  
    2. using System.Collections.Generic;
    3. using Unity.VisualScripting;
    4. using UnityEngine;
    5. using UnityEngine.Tilemaps;
    6.  
    7. public class AddBoxCollidersToWalls : MonoBehaviour
    8. {
    9.     public Sprite wallSprite;
    10.     public float colliderSize = 1.5f; // The size for the BoxCollider2D
    11.     private int wallCollidersLayer;
    12.  
    13.     private void Awake()
    14.     {
    15.         wallCollidersLayer = LayerMask.NameToLayer("WallColliders");
    16.     }
    17.  
    18.     void Start()
    19.     {
    20.         if (wallSprite == null)
    21.         {
    22.             Debug.LogError("WallSprite reference not set! Please assign the WallSprite in the Inspector.");
    23.             return;
    24.         }
    25.  
    26.         AddCollidersToWallTiles();
    27.     }
    28.  
    29.     private void AddCollidersToWallTiles()
    30.     {
    31.         List<List<Vector2Int>> wallTileRegions = FindWallTileRegions();
    32.  
    33.         foreach (List<Vector2Int> region in wallTileRegions)
    34.         {
    35.             Vector3 colliderCenter = GetColliderCenter(region);
    36.             Vector2 colliderSize = GetColliderSize(region);
    37.  
    38.             if (!TileHasCollider(colliderCenter))
    39.             {
    40.                 CreateColliderComponent(colliderCenter, colliderSize);
    41.             }
    42.         }
    43.     }
    44.  
    45.     private bool TileHasCollider(Vector3 center)
    46.     {
    47.         Collider2D[] colliders = Physics2D.OverlapPointAll(new Vector2(center.x, center.y), 1 << wallCollidersLayer);
    48.         return colliders.Length > 0;
    49.     }
    50.  
    51.     private List<List<Vector2Int>> FindWallTileRegions()
    52.     {
    53.         List<List<Vector2Int>> wallTileRegions = new List<List<Vector2Int>>();
    54.         Texture2D texture = wallSprite.texture;
    55.         int width = texture.width;
    56.         int height = texture.height;
    57.         Color[] pixels = texture.GetPixels();
    58.  
    59.         bool[,] visited = new bool[width, height];
    60.  
    61.         for (int y = 0; y < height; y++)
    62.         {
    63.             for (int x = 0; x < width; x++)
    64.             {
    65.                 int pixelIndex = y * width + x;
    66.  
    67.                 if (pixels[pixelIndex].a == 0 || visited[x, y]) // Skip transparent pixels or already visited pixels
    68.                     continue;
    69.  
    70.                 List<Vector2Int> region = new List<Vector2Int>();
    71.                 FloodFill(x, y, pixels, width, height, ref visited, ref region);
    72.                 wallTileRegions.Add(region);
    73.             }
    74.         }
    75.  
    76.         return MergeOverlappingRegions(wallTileRegions);
    77.     }
    78.  
    79.     private List<List<Vector2Int>> MergeOverlappingRegions(List<List<Vector2Int>> regions)
    80.     {
    81.         List<List<Vector2Int>> mergedRegions = new List<List<Vector2Int>>();
    82.         List<Vector2Int> currentRegion = new List<Vector2Int>(regions[0]);
    83.  
    84.         for (int i = 1; i < regions.Count; i++)
    85.         {
    86.             List<Vector2Int> region = regions[i];
    87.             bool isOverlapping = false;
    88.  
    89.             foreach (Vector2Int tile in region)
    90.             {
    91.                 if (currentRegion.Contains(tile))
    92.                 {
    93.                     isOverlapping = true;
    94.                     break;
    95.                 }
    96.             }
    97.  
    98.             if (isOverlapping)
    99.             {
    100.                 currentRegion.AddRange(region);
    101.             }
    102.             else
    103.             {
    104.                 mergedRegions.Add(currentRegion);
    105.                 currentRegion = new List<Vector2Int>(region);
    106.             }
    107.         }
    108.  
    109.         mergedRegions.Add(currentRegion);
    110.         return mergedRegions;
    111.     }
    112.  
    113.     private void FloodFill(int x, int y, Color[] pixels, int width, int height, ref bool[,] visited, ref List<Vector2Int> region)
    114.     {
    115.         if (x < 0 || x >= width || y < 0 || y >= height || visited[x, y] || pixels[y * width + x].a == 0)
    116.             return;
    117.  
    118.         visited[x, y] = true;
    119.         region.Add(new Vector2Int(x, y));
    120.  
    121.         FloodFill(x + 1, y, pixels, width, height, ref visited, ref region);
    122.         FloodFill(x - 1, y, pixels, width, height, ref visited, ref region);
    123.         FloodFill(x, y + 1, pixels, width, height, ref visited, ref region);
    124.         FloodFill(x, y - 1, pixels, width, height, ref visited, ref region);
    125.     }
    126.  
    127.     private Vector3 GetColliderCenter(List<Vector2Int> region)
    128.     {
    129.         float sumX = 0f;
    130.         float sumY = 0f;
    131.  
    132.         foreach (Vector2Int tilePosition in region)
    133.         {
    134.             sumX += tilePosition.x;
    135.             sumY += tilePosition.y;
    136.         }
    137.  
    138.         float centerX = sumX / region.Count;
    139.         float centerY = sumY / region.Count;
    140.  
    141.         return new Vector3(centerX / wallSprite.pixelsPerUnit, centerY / wallSprite.pixelsPerUnit, 0f);
    142.     }
    143.  
    144.     private Vector2 GetColliderSize(List<Vector2Int> region)
    145.     {
    146.         int minX = int.MaxValue;
    147.         int minY = int.MaxValue;
    148.         int maxX = int.MinValue;
    149.         int maxY = int.MinValue;
    150.  
    151.         foreach (Vector2Int tilePosition in region)
    152.         {
    153.             minX = Mathf.Min(minX, tilePosition.x);
    154.             minY = Mathf.Min(minY, tilePosition.y);
    155.             maxX = Mathf.Max(maxX, tilePosition.x);
    156.             maxY = Mathf.Max(maxY, tilePosition.y);
    157.         }
    158.  
    159.         float tileSizeX = maxX - minX + 1;
    160.         float tileSizeY = maxY - minY + 1;
    161.  
    162.         // Calculate the gap between tiles and adjust the size accordingly
    163.         float gapX = Mathf.Max(0f, (tileSizeX - 1) * (1f - colliderSize));
    164.         float gapY = Mathf.Max(0f, (tileSizeY - 1) * (1f - colliderSize));
    165.  
    166.         return new Vector2((tileSizeX + gapX) / wallSprite.pixelsPerUnit, (tileSizeY + gapY) / wallSprite.pixelsPerUnit);
    167.     }
    168.  
    169.     private void CreateColliderComponent(Vector3 center, Vector2 size)
    170.     {
    171.         BoxCollider2D collider = gameObject.AddComponent<BoxCollider2D>();
    172.         collider.size = size * colliderSize;
    173.         collider.offset = center;
    174.         collider.gameObject.layer = wallCollidersLayer;
    175.     }
    176. }
    177.  
    The pacman gameobject is at position x = 14 y = 14 z = 0
    The pacman gameobject have Sprite Renderer , Animator , Circle Collider 2D , and Rigidbody 2D .
     
  2. coden2023

    coden2023

    Joined:
    Jan 21, 2023
    Posts:
    2
    Apologies if i'm incorrect, but couldn't you apply a polygon collider?
     
    Chocolade likes this.
  3. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    916
    I tried but some of the colliders have been added with some too many shapes so its not accurate. i'm also getting in the inspector a warning message i had to reduce the pacman size to make it move a bit but in the end it got stuck in the colliders that are diagonal this is the result after adding the polygon collider:

     
  4. Chubzdoomer

    Chubzdoomer

    Joined:
    Sep 27, 2014
    Posts:
    106
    I'm sure you could painstakingly write an algorithm to iterate over the entire image and set up colliders that way, but why not just make a tilesheet out of the different maze sprites and then use the Tilemap system to quickly and easily recreate that exact maze? Not only would it save a ton of time and effort, but it'd also be far more flexible in the long run because you could then create your very own mazes right inside of Unity--no graphics editor required.
     
    Chocolade and spiney199 like this.
  5. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    916
    can you show me example how to start doing it or some tutorial/example online? or maybe i need just to use the editor edit mode to edit the sprite and make it manual adding to each part a collider?
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,883
    Why not just google 'Unity Tilemap tutorial' yourself and get to learning?

    The first result is literally Unity's own learning site: https://learn.unity.com/tutorial/introduction-to-tilemaps
     
    Chocolade likes this.