Search Unity

Question Snap Raycast.Hit.point to a grid position

Discussion in 'Scripting' started by Draad, May 28, 2023.

  1. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    Hello,

    I do some Raycast ont the following hierarchy :
    -> EmptyGameObject
    ----> Mesh that is basically a cuboid

    That cuboid exist in a virtual 3D grid. When I do click on it, I want to find the snapped grid position of my click.
    To simplify, let's take an exemple where my cuboid only represent one cube of scale (1, 1, 1).

    So far, I'm doing it like this :
    Code (CSharp):
    1. ...
    2.  
    3.     ConvertToGridPosition(hit.point);
    4. ...
    5.  
    6.     public static Vector3 ConvertToGridPosition(Vector3 v)
    7.     {
    8.       return new Vector3(
    9.         Mathf.Floor(v.x / Settings.TileSize),
    10.         Mathf.Floor(v.y / Settings.TileSize),
    11.         Mathf.Floor(v.z / Settings.TileSize)
    12.       );
    13.     }
    14.  
    Problem, if I do click on a spot where X, Y or Z = 1, then I'm obviously snap on the adjascent position of my click...
    From all my attempts, I always fall into one edge case or another, either I do not support 0 anymore, either I do not support 1.

    I tried playing with modulo to detect if I am on a edge case situation, without success so far.

    How can I make my calculation I snap correctly into the grid ?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,695
    Usually I write these coordinate translators by steps.

    - add / subtract any constant offset
    - divide by size of tile in each axis
    - cast it to an int
    - do any final adjustments, such as guarding, clamping or offsetting
    - return the values

    That let's you see what is what.

    "Guarding" could even mean throw an exception, or log a problem.

    Then you just write something to feed numbers in and print them out until it works like you want.
     
  3. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    Hum, yeah, I guess I cannot simply rely on math for that and need to actually write some logic behind it. Thanks
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,108
    To snap to a grid of arbitrary size:
    Bring the coordinate to discrete grid space, get it back to world space. Also known as a roundtrip.

    Code (csharp):
    1. int toGrid(float wp, float gridScale) => (int)MathF.Round(wp / gridScale);
    2. float fromGrid(int lp, float gridScale) => gridScale * lp;
    Code (csharp):
    1. float snapGrid(float wp, float gridScale)
    2.   => fromGrid(toGrid(wp, gridScale), gridScale);
    You could leave it at that, or meld it together
    Code (csharp):
    1. float snapGrid(float wp, float gridScale)
    2.   => gridScale * (int)MathF.Round(wp / gridScale);
    To apply this to a vector
    Code (csharp):
    1. Vector3 vec3lambda(Func<int, float> lambda)
    2.   => new Vector3(lambda(0), lambda(1), lambda(2));
    3.  
    4. Vector3 snapGrid(Vector3 pos, Vector3 gridScale)
    5.   => vec3lambda( i => snapGrid(pos[i], gridScale[i]) );
    If your grid has an offset in world units
    Code (csharp):
    1. int toGrid(float wp, float gridScale, float offset) => (int)MathF.Round((wp - offset) / gridScale);
    2. float fromGrid(int lp, float gridScale, float offset) => offset + gridScale * lp;
    3. float snapGrid(float wp, float gridScale, float offset)
    4.   => offset + gridScale * (int)MathF.Round((wp - offset) / gridScale);
    If you want to apply additional constraints, you do that in the discrete grid space.
     
    Last edited: May 29, 2023
  5. Draad

    Draad

    Joined:
    Feb 17, 2011
    Posts:
    325
    Thanks guys,

    After messing around, I found what was preventing me from using hit.normal successfully in my calculation.
    From time to time, the normal and hit point were wrong. As if it was detecting the opposite side of the clicked one.

    Turn out that I need to call Physics.SyncTransforms(); before doing my raycast.
     
    Kurt-Dekker likes this.