Converting world to cell with custom grid

i hvae been at this for a while and I am quite tired so there might be an answer that I am not seeing, but how would I convert from world pos to cell pos, with a custom grid class.
here is what I currently have

    public Vector3Int getCellPos(Vector3 _worldPos)
    {
        _worldPos -= transform.position;
        _worldPos = _worldPos  / cellSize;
        int x = Mathf.CeilToInt(_worldPos.x);
        int y = Mathf.CeilToInt(_worldPos.y);
        int z = Mathf.CeilToInt(_worldPos.z);
        return new Vector3Int(x, y, z);
    }

I have also tried

 public Vector3Int getCellPos(Vector3 _worldPos)
 {
     _worldPos -= transform.position;
     _worldPos = _worldPos  / cellSize;
     int x = Mathf.FloorToInt(_worldPos.x);
     int y = Mathf.FloorToInt(_worldPos.y);
     int z = Mathf.FloorToInt(_worldPos.z);
     return new Vector3Int(x, y, z);
 }

and I always get weird results that are slightly offset from the world position by 1 or 2 tiles.

Is this a 3 dimensional grid? What is transform.position?

Presumably the transform.position of a game object.

OP, I would make sure your cell size is correct. But really just some thorough debugging is needed.

I’ve solved these problems in the past by using OnDrawDizmos to draw a Handle.Label to display the cell position of a game object (and other relevant data) that I can move around and see how my maths behaves.

2 Likes

Obviously its a transform.position of a game object. It helps to know what gameObject. That’s what I was asking.

Fair. Probably the component managing the grid, I would say. At least, that’s what would make the most sense.

I can also suggest OP steps through the math step by step manually to see at what point it goes wrong.

yes sorry it is the position of the grid, aka the world offset.
for some reason mathf.floortoint() was not operating as I thought, and was returning incorrect values, more debugging will have to be done to determine exactly why, but I fixed the issue by removing the floortoin, and replacing it with just an int cast

yes thanks :stuck_out_tongue: I actually took that advice and spent some time stepping through and found the issue, I also got out a paper and pen and physically wrote down what the equation was just to make sure I wasn’t making a logical error xD

What was the issue, for those perhaps coming across this thread in future? Posting the working code would be good for those future people, too.

2 Likes

Define world offset and what incorrect means. A grid cell has several potential reference points, one of the corners or the center of the cell. What is your reference origin here? If it’s not the lower corner, your math is indeed wrong. There’s not just a single way how to setup a grid. This is information we’re lacking here.

Floor literally just rounds downwards (towards the “floor”) to the next smaller integer. So 4.7 becomes 4 and -7.2 becomes -8. Ceil is the same thing but in the other direction (rounds upwards to the ceiling) so it always rounds up to the next larger integer. So the first example would become 5 and the second example would be -7. The difference between floor and ceil is almost always exactly 1, except for values that are already an integer. Though with floating point numbers that’s really hard to reach. So in the range of 0 to 1, floor would always return 0 except when the value is exactly 1 and ceil would always return 1 except when the value is exactly 0. That’s why you shouldn’t mix floor and ceil. Use one, usually floor.

If your reference is not the lower corner of the cell but instead the center of a cell, you need an additional half-cell offset or your “round” instead. Though it usually makes more sense to use floor and have the reference at a corner. So things have a clear start and only expand in one direction for each axis rather than “around” the reference point.

For geometric problems this often helps to get a better visual understanding of the problem. So kudos for taking that step. I would always recommend to have grid paper at hand. It can help with many issues :slight_smile:

4 Likes

Every grid system is probably a bit different too, so every one will probably end up with some slightly different calculation. Especially so if your grid is chunked, so you need to step from grid to chunk then to the cell/tile.

And then you don’t touch the code for ages, then go back to look at it and have no idea how it works…

Just looked at my two methods for converting from world position to Chunk and Tile and came across some amusing comments from past me:

public WorldChunk WorldPositionToChunk(Vector3 position)
{
   // because tiles are 0.5f wide, we need to double the position
   // before we derive an index from it
   // it doesn't make sense to me but works
   Vector3Int flooredPosition = Vector3Int.FloorToInt(position * 2f);

   int x = math.abs(flooredPosition.x);
   int y = math.abs(flooredPosition.y) - 1;

   int chunkX = x / 16;
   int chunkY = y / 16;

   int index = this.CoordinatesToIndex(chunkX, chunkY);
   return this[index];
}

public int WorldPositionToTileIndex(Vector3 worldPosition)
{
    // because tiles are 0.5f wide, we need to double the position
    // before deriving the index (again don't really understand why)
    int x = Mathf.FloorToInt(math.abs(worldPosition.x * 2f));
    int y = Mathf.FloorToInt(math.abs(worldPosition.y * 2f));

    int tileX = x % 16;
    int tileY = y % 16;

    return (tileY * 16) + tileX;
}

offset as in, if the grid is positioned at world position (5.5,15.5) the first cell in the grid, aka cell 0,0 would be at world position 5.5,15.5 and grid cell 0,1 would be at world position (5.5, 16.5) if each cell has a size of one world unit.

when converting from world to cell, I first subtract the offset position, so if the world position was at (5.7,18.8) its new position correcting for offset would be, (0.2, 3.3) then i / by cell size which in this case is 1 so no change, and truncate. so the cell position of world pos (5.7, 18.8) would be (0,3)

for some reason floortoint was not returning the correct result, but truncating will always return the correct result so i just did that

oof hard coding cell size is crazy lol, but yeah i would have done the same thing a couple months ago, before diving into this project

good idea

  public Vector3Int GetCellPos(Vector3 _worldPos)
  {
//transform.position is the offset of the entire grid object
//Scale is the size (in world units) of each grid cell
      _worldPos -= transform.position - new Vector3(Scale/2, Scale/2, Scale/2);
      _worldPos = _worldPos / Scale;
//cast to int to get final cell pos
      int x = (int)_worldPos.x;
      int y = (int)_worldPos.y;
      int z = (int)_worldPos.z;
      return new Vector3Int(x, y, z);
  }

this is what worked for me. As for why the floor or ceiling wasn’t returning correctly, I truly have no idea, I did a little test, and the floor seems to be returning the same as a cast, perhaps there was something off in my scaling code that was causing an issue idk.

i don’t have a test to back this up but I think using a cast when dealing with negative values will produce different results from a floortoint, so I will probably have to change that in my code