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

How can I clamp distance in isometric environment ?

Discussion in '2D' started by laveurdegoudron, Feb 17, 2021.

  1. laveurdegoudron

    laveurdegoudron

    Joined:
    Oct 10, 2017
    Posts:
    14
    Hello all !
    I am currently working on a 2D isometric game where the player will be able to control different units, and interact through them.

    I created a Scriptable Object called UnitType. Thanks to this system I can define an action range and a moving range, that is the maximum distance in cells that the player can move to or interact with.

    The problem is I don't know how to implement this through code. This is what I want to achieve, using an action range of 2 cells for the demonstration.


    With a friend of mine, we thought about calculating the linear equation of those 4 lines to check if the raycast hit was within them, but it's not working right.


    What would be the best practice ?

    Thank you very much for your time and attention,
     
  2. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,319
    Hi there's a pretty straightforward way to go about implementing this.

    The key is to think in terms of "worldspace", and "screenspace", and "cellspace". Worldspace is often in 2D is usually (x,y,z) where x is left and right movement, and y is up and down, and z is unused (or used for sorting). This is what you receive when you call transform.position.

    Screenspace is the position on the screen in pixels. Input.mousePosition returns the screenspace position of the mouse, for example.

    Cellspace is the one you want. Its the space defined by the Grid the Tilemap uses. Unless you have an unusual swizzle, the frog moving up-right is +1x. Straight down is -1x, -1y. If you want to clamp movement, you can clamp using cellspace.

    How do we get cellspace? Here's a simple, useful example: getting the current cell under the mouse:
    Code (CSharp):
    1. var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    2. var noZ = new Vector3(pos.x, pos.y);
    3. Vector3Int mouseCell = grid.WorldToCell(noZ);
    We convert between the 3 space systems: screen -> world -> cell. We also clear the z due to a quirk of ScreenToWorldPoint. We ask a reference to the Grid component to do the conversion for us. Note also: you can ask the tilemap too (it has the same method).

    For clamping, what's key is that you convert everything to the same space (cellspace) and do your comparisons, checks, clamps there. This keeps your operations naturally bounded by the grid's cells.
    Code (CSharp):
    1. Vector2Int frogCell = (Vector2Int)grid.WorldToCell(frogWorldspacePos);
    2. int frogRange = 2; // get from your Scriptable Object
    3. int sqMag = (frogCell - wantsToMoveToThisCell).sqrMagnitude;
    4. if(sqMag <= frogRange * frogRange)
    5. {
    6.     // ok! within range, do stuff
    7. }
    8. // else: exceeds range! ignore!
    Or something like that. Plenty of ways to do it. You may want to make sure you position the frog in the center or the cell e.g. (1,1) -> (1.5,1.5) in cellspace, then convert back to worldspace and apply that to the frog's transform.position. Or you may want interpolated distances (Vector2/Vector3) rather than being locked into Vector2Int/Vector3Int. Vector2Int/Vector3Int is more appropriate if it's a turn based game with characters only allowed to move to the center of the cells.

    The resource you should examine is here: https://docs.unity3d.com/ScriptReference/Grid.html

    Keep in mind LocalToCell and its variants give more options but assume that your Tilemap/Grid have (0,0,0) for the position on their transforms to be accurate.
     
    Last edited: Feb 17, 2021
  3. laveurdegoudron

    laveurdegoudron

    Joined:
    Oct 10, 2017
    Posts:
    14
    Hello and thank you for your answer. We actually sorted it out yesterday by using the 4 lines as I described in the original post.

    Here is the code :
    Code (CSharp):
    1.     private void CheckInteractionDistance()
    2.     {
    3.         if (
    4.             mouseHit.transform.localPosition.y <= (-0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2)
    5.             && mouseHit.transform.localPosition.y >= (-0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
    6.             && mouseHit.transform.localPosition.y >= (0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
    7.             && mouseHit.transform.localPosition.y <= (0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2)
    8.             )
    9.         {
    10.             mouseHit.GetComponent<SpriteRenderer>().color = new Color(0, 1, 0);
    11.             isAtGoodDistance = true;
    12.         }
    13.         else
    14.         {
    15.             mouseHit.GetComponent<SpriteRenderer>().color = new Color(1, 0, 0);
    16.             isAtGoodDistance = false;
    17.         }
    18.     }
    It's as simple as that, the problem was coming from a x written down instead of a y. Otherwise it's just working fine at any range.

    Thank you again for your help sir.
     
  4. laveurdegoudron

    laveurdegoudron

    Joined:
    Oct 10, 2017
    Posts:
    14
    Forgot to mention, mouseHit is a GameObject that is a child of the character and its movement snaps grid cells.