Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Tilemap collider

Discussion in 'Project Tiny' started by MasoInar, Dec 6, 2018.

  1. MasoInar

    MasoInar

    Joined:
    Feb 20, 2014
    Posts:
    126
    Hi,

    Is it possible to add collider to tilemap that collides with hitbox2d? Or any other way to detect player collision with tilemap sprites?
     
  2. vincismurf

    vincismurf

    Joined:
    Feb 28, 2013
    Posts:
    200
    I think you need to add entities with Transform Position, BoxCollider2d and some sorta "Tag" component
     
  3. MasoInar

    MasoInar

    Joined:
    Feb 20, 2014
    Posts:
    126
    I tried to add box collider to tilemap, but it just creates box in the middle of the map. Also RectHitBox2D doesn't work as it says Sprite2DRenderer component is needed.

    What I really want is TileMapCollider like in 'normal' Unity that adds colliders all the tilemap sprites on that tilemap. Currently without collider support the tilemap seems to be quite useless..
     
  4. sebastianm_unity

    sebastianm_unity

    Unity Technologies

    Joined:
    May 3, 2018
    Posts:
    21
    Tilemaps support is still in a very early stage of development. They are not integrated with HitBox or Physics yet. It certainly is on the road map!
     
    ER_Dolleman likes this.
  5. MasoInar

    MasoInar

    Joined:
    Feb 20, 2014
    Posts:
    126
    Nice to hear. I'll played a bit with different components and systems and it's very promising! I'll be actively checking this forum and waiting for updates.
     
  6. Berzee

    Berzee

    Joined:
    Sep 3, 2013
    Posts:
    11
    Here's a dubious (but working!) addition to the sample "Movement" script that will collide with a Tilemap2D. It depends on the Tilemap having a "CollisionTiles" component which lists the width in columns, height in rows, and the index of the tileset at which the walkable tiles stop and the wall tiles begin.

    I replaced the final line of the Movement script (that set the position to the new value) with this nonsense:

    Code (JavaScript):
    1. //Tilemap Collision Check
    2. if(direction.length() > 0)
    3. {
    4.     let position = transformLocalPosition.position;
    5.     position.add(direction);
    6.  
    7.     let collided = false;
    8.  
    9.     this.world.forEach([ut.Tilemap2D.Tilemap,game.CollisionTiles],
    10.       (tilemap,collisionTiles) => {
    11.         //ASSUMPTIONS
    12.        //1. Hitbox is not larger than one tile (so we just need to check if each corner of the hitbox is touching a wall
    13.        //2. Sprite, tiles, and tilemap are all center-aligned
    14.        //3. Sprite and tiles have a pixels per unit equal to the tile size
    15.         let tileMapBottomLeft = new Vector2(tilemap.position.x - collisionTiles.cols / 2, tilemap.position.y - collisionTiles.rows / 2);
    16.  
    17.         //Get world coordinates of sprite's four corners
    18.         let corners = [];
    19.         //top left
    20.         corners[0] = new Vector2(position.x - hitbox.box.width/2, position.y + hitbox.box.height/2);
    21.         corners[0] = corners[0].sub(tileMapBottomLeft);
    22.         //top right
    23.         corners[1] = new Vector2(hitbox.box.width,0).add(corners[0]);
    24.         //bottom left
    25.         corners[2] = new Vector2(0,-hitbox.box.height).add(corners[0]);
    26.         //bottom right
    27.         corners[3] = new Vector2(hitbox.box.width,-hitbox.box.height).add(corners[0]);
    28.  
    29.         //Find the tiles corresponding the the four corners
    30.         for(let i = 0; i < corners.length; i++)
    31.         {
    32.           let corner = corners[i];
    33.  
    34.           //Tilemap array lists tiles left to right, row by row, from the bottom up
    35.           let tilemapIndex = Math.floor(corner.y) * collisionTiles.rows + Math.floor(corner.x);
    36.  
    37.           this.world.usingComponentData(tilemap.tiles[tilemapIndex].tile,[ut.Tilemap2D.Tile],
    38.             (tile) => {
    39.               //Couldn't find a way to get the collider of the tile or the index of the tile from the tileset directly,
    40.               //so am using the index suffix from the auto-generated sprite name (i.e. "tiles_0", "tiles_1")
    41.               let spriteName = this.world.getEntityName(tile.sprite);
    42.               let tokens = spriteName.split("_");
    43.               let tileIndex = Number(tokens[tokens.length-1]);
    44.               if(tileIndex >= collisionTiles.collisionIndex)
    45.               {
    46.                 collided = collided || true;
    47.               }
    48.             });
    49.         }          
    50.     });
    51.  
    52.     if(!collided)
    53.     {
    54.       transformLocalPosition.position = position;
    55.     }
    56. }
    That would be quite a bit cleaner if I'd found a way to get the tile palette index of a tile from the map, instead of just its texture name...but anyhow, it's something. =)
     
  7. Berzee

    Berzee

    Joined:
    Sep 3, 2013
    Posts:
    11
    Edit: This post is in reply to a different post which has mysteriously disappeared, weird, but I will still leave it here.

    Sorry I couldn't find a better way of sharing it, but attached is a basic example that just has one sprite and one tilemap...maybe you can improve on this nonsense. =)

    (The actual creating of the tilemap was the hardest part for me. I sort of floundered through this guide and this guide and ended up with a tileset prefab somehow. ^_^)
     

    Attached Files:

    Last edited: Dec 14, 2018
  8. Dunk86

    Dunk86

    Joined:
    May 10, 2015
    Posts:
    53
    Before someone makes the same mistake I did, tilemap.tiles is a list of all the *PLACED* tiles - so if your tilemap has gaps (as you're painting walls onto one layer, but not everywhere), gaps won't be included in the list - it's nothing like the Grid in standard Unity.

    I based my code on Berzee's example above, but that one assumed a grid filled with tiles.
    For my own purposes, I made my CollisionTiles component store a 1D boolean list of just the collision (with false for empty spaces), to make this easier - also calculating and storing column and row counts, as well as the bottom left corner position:

    Code (JavaScript):
    1. namespace game {
    2.  
    3.     /** New System */
    4.     @ut.executeBefore(game.PlayerMovementSystem)
    5.     export class TileCollisionSystem extends ut.ComponentSystem
    6.     {
    7.         OnUpdate():void
    8.         {
    9.             this.world.forEach([ut.Tilemap2D.Tilemap, game.CollisionTiles],
    10.                 (tilemap, collisionTiles) =>
    11.                 {
    12.                     if (collisionTiles.cols == 0)
    13.                     {
    14.                         let tileCount = tilemap.tiles.length;
    15.                         let minX = 1000000;
    16.                         let maxX = -100000;
    17.                         let minY = 1000000;
    18.                         let maxY = -100000;
    19.                         for (let i = 0; i < tileCount; i++)
    20.                         {
    21.                             let tilePos = tilemap.tiles[i].position;
    22.                             if (tilePos.x < minX)
    23.                             {
    24.                                 minX = tilePos.x;
    25.                             }
    26.                             if (tilePos.x > maxX)
    27.                             {
    28.                                 maxX = tilePos.x;
    29.                             }
    30.                             if (tilePos.y < minY)
    31.                             {
    32.                                 minY = tilePos.y;
    33.                             }
    34.                             if (tilePos.y > maxY)
    35.                             {
    36.                                 maxY = tilePos.y;
    37.                             }
    38.                         }
    39.                         let rows = Math.round(maxY - minY) + 1;
    40.                         let columns = Math.round(maxX - minX) + 1;
    41.  
    42.                         let totalGridSpaces = rows * columns;
    43.                         let newGrid: boolean[] = [];
    44.                         for (let i = 0; i < totalGridSpaces; i++)
    45.                         {
    46.                             newGrid[i] = false;
    47.                         }
    48.                         for (let i = 0; i < tileCount; i++)
    49.                         {
    50.                             let tilePos = tilemap.tiles[i].position;
    51.                             let gridX = Math.round(tilePos.x - minX);
    52.                             let gridY = Math.round(tilePos.y - minY);
    53.                             let gridIndex = gridX + gridY * columns;
    54.                             newGrid[gridIndex] = true;
    55.                         }
    56.                         collisionTiles.Grid = newGrid;
    57.  
    58.                         collisionTiles.rows = rows;
    59.                         collisionTiles.cols = columns;
    60.                         collisionTiles.BottomLeft = new Vector2(minX - 0.5, minY - 0.5);
    61.                     }
    62.                 });
    63.         }
    64.     }
    65. }
    66.  
     
    Last edited: Jan 16, 2019