Search Unity

Grids Pro: A library for hex, tri, polar and rect grids [New Dcoumentation for Grids 2]

Discussion in 'Assets and Asset Store' started by Herman-Tulleken, Jul 10, 2013.

  1. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    I'm not sure what I'm doing wrong. I am trying to map a hex grid to a unity terrain. I DO NOT need it to map to height, just 2d xy is fine. I figured I'd make 30 cells across the terrain's width and height, but for some reason my grid isn't filling my entire terrain.

    Code (csharp):
    1.        
    2. grid = PointyHexGrid<float>.FatRectangle(30, 30);
    3.         map = new PointyHexMap(new Vector2(terrain.terrainData.heightmapWidth / 30, terrain.terrainData.heightmapHeight / 30))
    4.                     .WithWindow(new Rect(0, 0, terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight))
    5.                     .AlignMiddleCenter(grid)
    6.                     .To3DXY();
    7.  
    Below is the result. It appears to be missing maybe 5 cells on both sides of my terrain. position xy(0,0) returns like 0, -2 as the grid position, but that position does not exist in grid[]. Am I missing something?

    $gridsterrain.JPG
     
  2. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    I can't see exactly what is going on from the image.

    However, the dimensions calculation is not correct. (I assume you want regular hexes, and not stretched ones.)

    In this case, if you have more than one row, as you do, the cell width to cover terrain.terrainData.heightmapWidth is given by:

    Code (csharp):
    1. const int columnCount = 30;
    2. float cellWidth = terrain.terrainData.heightmapWidth / (columnCount + 1); //the plus 1 is because you are using a fat rectangle.
    The cell height is then given by:

    Code (csharp):
    1. float cellHeight = cellWidth / 69*80;
    (The ratio 69/80 is the width to height of a regular hexagon).

    Now this will not cover a square area if you use a 30x30 grid. If that is what you want, then all is good.

    However, if you want to cover a square area, you can calculate how many rows you will need:

    Code (csharp):
    1. int rowCount = Mathf.CeilToInt((terrain.terrainData.heightmapHeight - cellHeight / 4) / (3 * cellHeight / 4));
    This image should clarify the formula:
    $hexmath.png

    You should then get a grid of regular hexes to cover the region with the following:

    Code (csharp):
    1.        
    2. grid = PointyHexGrid<float>.FatRectangle(columnCount , rowCount );
    3. map = new PointyHexMap(new Vector2(cellWidth , cellHeight))
    4.    .WithWindow(new Rect(0, 0, terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight))
    5.    .AlignMiddleCenter(grid)
    6.    .To3DXY();
    7.  
    I hope I understood you correctly! Please let me know if not, or whether this works for you.
     
    Last edited: Jun 1, 2014
  3. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    Thanks, that helped. The fat rectangle was still off by a row on the edges, but I changed it to a thin rectangle and removed the "+1" and it appears to fill the grid nicely.

    You should post a tutorial on mapping a grid to terrain. I tried to piece together as much as I could based on the tutorials on your site, but I wouldn't have figured out the ratio stuff. I think mapping a grid to terrain would be a pretty common thing to want to do.

    I'm still using the evaluation, but I'm pretty sure there is a purchase coming in the near future. The grid system does everything I could want, and more.
     
  4. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hi delinx32,

    That's a good idea, (not sure when we will add it, there are so many tutorials to add!)
     
  5. KChou

    KChou

    Joined:
    May 5, 2014
    Posts:
    7
    Hello,

    Sorry for the delay I got busy :)

    I downloaded and imported the 1.8 package just fine. I don't know what caused the problem. Thanks for your help.

    I got a couple of questions. I am working on modifying the FlatHexTest scene in order to learn.

    - I have a bunch of images I would like to use to texture cells in the grid. Could you explain how to do that ?

    - Let's say I have a 3D model of an object (like a city, a vehicle, etc), what could be the best was to display it on top of a cell in the grid ?

    - I would like to get rid of the space (border) between each cell, would that be possible ?

    - Let's say I have points, what would be the best way to display a line between them ?
    Example:

    - Would it be possible to store data in each cell object. For example in the last question, we have cells that are empty, cells that contain a dot (orange) and cells that contain a line (red line).

    Thank you for your help :)

    - KChou
     
    Last edited: Jun 6, 2014
  6. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    If you use a SpriteCell, as that example does, then you add the textures (converted to Unity sprites) to the Sprites field. In code, you set the index of the sprite to display like this:

    Code (csharp):
    1. cell.FrameIndex = 0;// or whatever else index you want to display.
    Assuming you have your grid laid out flat, you could place them using the map:

    car.transform.position = map[gridPoint] + Vector.up*offset; //If you put your pivots at the bottom, you need not add an offset

    You could add a further offset if you don't want to have all the objects centered.

    Yup; just multiply the cell dimensions you give the map with a number slightly smaller than one. The cells have transparent borders (this reduces certain types of artifacts), so the number you should use is slightly less than the actual cell dimensions.

    There are many ways to display a line, possibly the line renderer gives the best results. For this you will need the two endpoints, which you can easily get with the map. (I am not sure how using 3D objects and lines work with sprites, you may need to use a MeshCell instead if you have rendering issues)

    Code (csharp):
    1. Vector3 lineStart = map[lineStartGridPoint];
    2. Vector3 lineEnd = map[lineEndGridPoint];
    Yup, you definitely can. One simply way is to make your own cell, and extend it from SpriteCell (or whichever cell you end up using). See here for some guidelines: http://gamelogic.co.za/grids/documentation-contents/quick-start-tutorial/making-your-own-cells/

    Then just put the information you need there, for example:

    Code (csharp):
    1. public class Cell : SpriteCell
    2. {
    3.    public bool hasDot;
    4.    public bool hasLine;
    5.    public List<GameObject> cellObjects;
    6. }
    When you construct the grid, just feed this type like this:

    Code (csharp):
    1. var grid = FlatHexGrid<MyCell>.FatRectangle(20, 20);

    Let me know if anything is unclear or you need more help!
     
  7. Sord

    Sord

    Joined:
    Jan 13, 2014
    Posts:
    10
    Documentations seems to be down: http://gamelogic.co.za/documentation/grids/_8.html

    "It seems we can’t find what you’re looking for..."

    Btw if I want generate hexmap mesh instead of just instantiating objects, what this asset can offer for me?
    Navigation? View area? What is coolest thing in this (if we are not talking about clean code / editor and stick with hexagons)?
     
  8. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Thanks for the heads-up. It seems between Doxygen and Wordpress there is something fishy. We found a workaround, so those pages should work now. (They merely list additions to the library in different versions; we are still looking for a way to have Doxygen format it a bit better). (And the new link for that particular page is http://www.gamelogic.co.za/documentation/grids/v1s8.html )

    If you generate the mesh (something with which Grids can already help you: http://gamelogic.co.za/grids/featur...mesh-generation-script-with-proper-texturing/ ), then you can still take advantage of things such as these:
    • easily get the grid point of a Unity world point (or, quickly find in which cell a unit is),
    • use some of the grid algorithms, such as path finding,
    • implement your own algorithms easily using hex points,
    • make a minimap from the main grid, but rendered in a different way and mapped to a different location.
    We do not provide specific view areas, but there is an example that shows how to render a part of a very big grid, which may be similar to what you need. http://gamelogic.co.za/grids/featur...-only-a-section-of-a-very-big-scrolling-grid/

    I think if you take away funny shapes, clean code and editor setup, the coolest thing about grids is that it allows you to make many cool things. At its core, a grid is simply a clever 2D array.The way we split mapping between world and grid makes the library very flexible: it allows you to manipulate logic and rendering more separately. Our principle design goal was to make it easy to think about grids, and build logic for it.

    (We are of course always on the lookout for interesting algorithms to add. So let us know if you are missing something cool that is more concrete!)
     
    Last edited: Jun 10, 2014
  9. zangad

    zangad

    Joined:
    Nov 17, 2012
    Posts:
    357
    Hi! I'm trying to use Grids' A-Star pathfinding feature with a hex grid, and having problems getting a straight path.

    Using the default AStar call:


    I get paths like this for unobstructed straight shots to a goal cell:


    So, taking the advice in this thread and doing some research on my own, I attempted to come up with a cost heuristics function and supply that to the A-Star algorithm in Grids. I found a website that seems to be doing nice straight paths for hexes (http://www.amagam.com/hexpath/). I looked at their code and they are doing Manhattan heuristics cost. So I tried it for Grids, here's the code I came up with:



    It works pretty well for small distances, but once you get over 10 or so hexes, it starts taking weird side paths again:


    While this isn't horrible, it's definitely weird pathing going on, and I'd like the path to be as straight as possible to the goal, even if the distance (number of hexes) is technically the same.

    I saw someone mentioned earlier in the thread to do raycasting. I think this would work for some games, but I'm wanting to have a big map. Putting colliders on each hex could easily put me into the red with performance, since 100x100 hexes would be 10,000 colliders for a relatively medium-sized map. Also, as I understand it, raycasting seems like it would work better for square grids than hex ones, because with a hex if you raycast from the center of a hex you can run parallel with borders of other hexes, and possibly get unwanted cells, right?



    So, any ideas for how to get straight paths with hex grids using the Grids asset? Thanks for reading! :)
     
  10. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    I don't have the code yet so I can't look at it, but I was wondering if there is any reason why the point classes aren't implemented as generics with references to the value stored at the point? It seems like that could clean up a bit of code.

    for example if you could do the following, it would save the cost of looking up the value once you already have the point.

    foreach (RectPoint<float> point in Grid)
    {
    if(point.Value < .5f)
    doIt();
    }

    rather than

    foreach (RectPoint point in Grid)
    {
    if(Grid[point] < .5f)
    doIt();
    }

    Or you could do single queries to return a range of points:

    var points = from p in Grid where p.Value < .5f
    select p;

    is cleaner than:

    var points = from p in Grid where Grid[p] < .5f
    select p;

    and then I'd still need to look up the value again when iterating the loop.
     
  11. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    If you think about it... what you really want is the path to be shortest by using the normal Euclidean distance... so that is exactly the distance you should use :)

    Code (csharp):
    1.     private float EuclideanDistance(PointyHexPoint p, PointyHexPoint q)
    2.     {
    3.         float distance = (Map[p] - Map[q]).magnitude;
    4.  
    5.         return distance;
    6.     }
    (Feed it for both the cost and heuristic cost, as you did before).

    hex_path.png
     
    zangad likes this.
  12. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358

    The short answer is, we designed Grids to follow the same design as arrays (and to some extent, dictionaries), so in that sense, we didn't design it the way you describe for the same reasons integers are not designed as generics with references to values in an array... put differently, we simply did not think of it :)

    Also, we want points to be immutable structs so that point arithmetic is cheap, and would not want to deal with the scenario where a point has a reference to a object that may change internally. Not to mention the fact that a point may
    inadvertently keep a reference to an object and prevent it from being garbage-collected. It's also common to design algorithms that work an more than one grid, so that defining equality in a useful way also becomes problematic.

    Another benefit of points not being generic is that it is not one more place where we can get JIT errors on iOS, which as is keeps me awake at night.

    Of course, there is a nice clean way to work with grid values on their own:

    Code (csharp):
    1. foreach(float value in Grid.Values)
    2. {
    3.    if (value < 0.5f) DoIt();
    4. }
    Code (csharp):
    1. var p = Grid.WhereCell(v => v < 0.5f);
     
    Last edited: Jun 11, 2014
  13. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    My situation is that I need to get a list of cells that match a range and then do some checks on their neighbors, so I need both the RectPoint and the Value, as well as the neighboring values from the original grid. I just worry about the performance of hitting multiple arrays in linq queries. It may not even be an issue, but my OCD alarm has a fairly low threshold.

    Anyway, nice work on the product. Another useful feature would be a GetBounds() method on the point or grid class.
     
    Last edited: Jun 11, 2014
  14. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Performance as such should in general not be a problem (for reasonable sized grids :p); GC may be a bigger issue. The diffusion example runs fairly decently on my PC with 1000 cells, and that calculates a new value based on each cell's value and the value of its neighbors each frame.

    We have been thinking of implementing a select which gives both index and cell, (similar to LINQs overload giving index and value), and also a list of point-value pairs, similar to dictionary.

    About the GetBounds method; do you mean the cell at that point's bounds in Unity space?
     
  15. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    I'm building my grid via code and I was trying to draw a gizmo to visualize the grid in the editor. I ended up using a method that takes the center point and the size, but my original intent was to draw a mesh by creating vertices. I couldn't find a good way to get the world position of the corners other than using GetCellDimensions and doing some math. Maybe there is a more elegant way to do what I was attempting.
     
  16. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Ah OK, yes, that is definitely a feature we'd like to add - it will be to the map, though, not the point, so that it will still give correct results if the map has been scaled or otherwise transformed. The map already has transformed cell dimensions, but ideally it should give you all 6 vertices (for a hex grid).
     
  17. PrisonerOfLies

    PrisonerOfLies

    Joined:
    Dec 6, 2013
    Posts:
    105
    Hi there, is Astar Algorithm for Hex grid included in this asset ?
    I can't find it in the Trial mode.
     
    Last edited: Jun 12, 2014
  18. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    For now, the editor Grids only support mouse clicks for heads-on orthographic cameras. For other situations, you need to use Raycasting with colliders, and feed the hit point to your map.

    You can use something like the following in your Update method (in your GridBehaviour, or possibly another script of your grid):

    Code (csharp):
    1. public class MyGridBehaviour : GridBehaviour<PointyHexPoint>
    2. {
    3.     public void Update()
    4.     {
    5.        RaycastHit hit;
    6.  
    7.        if(Physics.Raycast(ray, out hit))
    8.        {
    9.            Vector3 worldPosition = root.transform.InverseTransformPoint(hit.point);
    10.            PointyHexPoint gridPoint = Map[worldPosition];
    11.      
    12.            if(Grid.Contains(gridPoint))
    13.            {
    14.                if(Grid[gridPoint] != null)
    15.                {
    16.                    Grid[gridPoint].SendMessage("OnClick"); //if you want your cell to be notified
    17.              
    18.                    //otherwise, do your own stuff here
    19.                 }
    20.             }
    21.         }
    22.     }
    23. }
    Let me know if this works!
     
    Last edited: Jun 15, 2014
  19. PrisonerOfLies

    PrisonerOfLies

    Joined:
    Dec 6, 2013
    Posts:
    105
    thanks for the quick reply, how about Astar Algorithm for Hex grid ?
    is this accessible in Trial version ?
     
  20. zangad

    zangad

    Joined:
    Nov 17, 2012
    Posts:
    357
    This worked perfectly, and fixed my pathing issue. Thank you!
     
    Herman-Tulleken likes this.
  21. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Yup, the AStar algorithm works on all grids (which have neighbors configured), including PointyHexGrid.

    There is an example that I just put of how to use it:
    http://gamelogic.co.za/2014/05/25/a-simple-path-finding-example/

    Let me know if you have any trouble.

    Edit: and it does work with the trial.
     
    Last edited: Jun 13, 2014
  22. zenforhire

    zenforhire

    Joined:
    Nov 5, 2012
    Posts:
    65
    Hi,

    General questions about the hex grid product.
    o It is up to user to supply the presentation of the grid.
    So if I have 500 x 500 grid, I have to figure out how to display 250,000 meshes?

    o what data formats are supported for importing data models?
    one API needs a photoshop image and uses color of grid.
    I have been using hexographer to generate a data model.
    You want me to make one in the Unity editor?

    o how does this scale?
    Support for incrementally adding to graph for an infinite playfield.

    Cheers
     
  23. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hi zenforhire,

    It's up to the user, but there are more than one possibility. For very big grids, you will probably use a single mesh (such as in this example: http://gamelogic.co.za/grids/featur...mesh-generation-script-with-proper-texturing/ or only render the visible part of the grid, as in this example: http://gamelogic.co.za/grids/featur...-only-a-section-of-a-very-big-scrolling-grid/, or a combination thereof.

    (Also, Unity is not as bad at displaying 250 000 meshes as one would think. I did a quick test using our default prefabs, and while it takes a few seconds to generate the grid, after that it runs OK. But not in the editor :)

    250000_hexes.png

    There is also a live example with 250 000 hexes here: http://gamelogic.co.za/grids/features/examples-that-ship-with-grids/250-000-hexes/ which runs pretty smoothly in the browser. Of course, the situation is different on mobile.)

    There is no limit on what you can use inside grids. If you used Hexographer to generate the data, the best idea is to read the file and initialize the cells accordingly. I suspect you won't be using the editor grids if you use one of the special techniques for big grids, so you can read the grid data wherever you instantiate the grid.

    For an infinite playfield, you will have to work out a strategy. Grids, like arrays, cannot be altered once made. However, you should be able to implement a "dynamic" grid (similar to dynamic lists) quite easily.

    Your new grid class should implement from IGrid<MyCell, PointyHexPoint>. Internally, it will keep a dynamic list of Grids which should not intersect (the simplest way to do this would be to use grids in a parallelogram shape). Whenever a point is accessed that is not in one of the parallelogram patches, you simply add a new patch that contains that point.

    An alternative is to use a Dictionary instead of a grid, especially if your grid is sparse. You will still be able to map world and grid points, and use grid points in calculations (and of course as keys in the dictionary). You may want to wrap this Dictionary in an IGrid<MyCell, PointyHexPoint> too, and so be able to use the grid algorithms. (The only "tricky" part would be to get neighbors, but this is really easy: {point + PointyHexPoint.NorthEast, point + PointyHexPoint.East, PointyHexPoint.SouthEast, etc.}.

     
  24. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    Should creating a rect point via constructor work? For some reason, its creating a point with the wrong coordinates in some cases. For example, I'm creating a RectPoint with 2 and 0 as the x and y, but I'm getting a rect point with 1, 0 as the x and y.

    RectPoint p = gridMap[basePoint];
    Vector2 gridPoint = v + new Vector2(p.X, p.Y);

    RectPoint final = new RectPoint((int)gridPoint.x, (int)gridPoint.y);

    Debug.Log("basepoint = " + gridMap[basePoint] + ", v=" + v.ToString() + ", basepoint+v=" + gridPoint.ToString() + ", finalpoint=" + final);

    Debug output:
    basepoint = (2, 2), v=(0.0, -2.0), basepoint+v=(2.0, 0.0), finalpoint=(1, 0)

    Another log (goes from 3,0 to 2,0):
    basepoint = (2, 2), v=(1.0, -2.0), basepoint+v=(3.0, 0.0), finalpoint=(2, 0)

    from (3,4) to (3,3)
    basepoint = (2, 2), v=(1.0, 2.0), basepoint+v=(3.0, 4.0), finalpoint=(3, 3)

    from (4,0) to (3,0)
    basepoint = (2, 2), v=(2.0, -2.0), basepoint+v=(4.0, 0.0), finalpoint=(3, 0)

    I can't figure out why its doing that. Other values work perfectly fine:
    basepoint = (2, 2), v=(1.0, -1.0), basepoint+v=(3.0, 1.0), finalpoint=(3, 1)
    basepoint = (2, 2), v=(0.0, 2.0), basepoint+v=(2.0, 4.0), finalpoint=(2, 4)
    and many more
     
  25. WJ

    WJ

    Joined:
    Oct 25, 2012
    Posts:
    97
    Is it possible to use this on the server side, like a photon backend?
     
  26. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Yes, it should definitely work. You did not provide me with enough code to replicate the issue though :p (please include how your map and v are initialized).

    I tried this, and it works fine:

    Code (csharp):
    1.  
    2.         Vector2 basePoint = new Vector2(2, 2);
    3.         Vector2 v = new Vector2(0, -2);
    4.         var gridMap = new RectMap(Vector2.one);
    5.         RectPoint p = gridMap[basePoint];
    6.         Vector2 gridPoint = v + new Vector2(p.X, p.Y);
    7.  
    8.         RectPoint final = new RectPoint((int)gridPoint.x, (int)gridPoint.y);
    9.  
    10.         Debug.Log("basepoint = " + gridMap[basePoint] + ", v=" + v.ToString() + ", basepoint+v=" + gridPoint.ToString() + ", finalpoint=" + final);
    11.  
    It's most likely your cell size (the method will only work if you cell size is 1). Another possibility is a rounding error (for example, what prints as 2 might just be short of 2, so that (int) 2 give 1.

    If you post more code, I can have a more in-depth look at it.
     
  27. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    I have no idea how photon backends work... so I am not sure.

    The core library is almost independent of Unity, it only uses a few math classes such as Vector2; so as long as you can access those, it should be able to work (maybe you will need to remove the editor classes).
     
  28. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    It was indeed a rounding issue. I had forgotten that debug.log doesn't log the full precision and I use VS2013 so I wasn't stepping through and inspecting the values.
     
  29. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    How would you go about generating a grid at runtime from an undefined number of previously placed separate tiles?

    I would love to combine Grids with Aegon Games' DunGen in such a way that I set up a number of dungeon tiles and have Grids create a walking grid for movement over the generated dungeon layout afterwards, much like in your Game 16 example. Could I pre-place cells on the walkable parts of a dungeon tile and have Grids add them to a proper grid afterwards?

    Just going ahead and create a movement system which moves my player by 1 unit in whatever direction he is facing would work as well of course, but I like how Grids would help me with most of the logic like checking target tiles for occupancy etc.
     
  30. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Yes, you could. I assume (for ease) the tiles are square (it's not clear from the picture whether it is).

    So you have two problems: Determining the size of the grid, and then, to place the tiles in the grid.

    First, you will need to define a suitable cell.

    Let's say the cell look like this:

    Code (csharp):
    1. public class DungeonCell
    2. {
    3.    public GameObject dungeonTile { get; set; }
    4. }
    Next, we will use a temporary map to determine the extent of the grid. I assume you know how big a dungeon tile is.

    Code (csharp):
    1. var gridParametersMap = new RectMap(dungeonTiileDimensions).To3DXZ();
    Now, iterate through all the dungeon tiles, map each to a grid point, and determine the minX, maxX, minY and maxY grid points.

    Next, calculate the world position of the bottom left corner, i.e.:

    Code (csharp):
    1. var bottomLeftWorldPoint = gridParametersMap[new RectPoint(minX, minY);
    It's best to use a new map for the rest of the game, translated by bottomLeftWorldPoint, so that we don't have negative coordinates:

    Code (csharp):
    1. var map = new RectMap(dungeonTiileDimensions).Translate(-bottomLeftWorldPoint).To3DXZ();
    And we can make our grid:

    Code (csharp):
    1. var grid = RectGrid<DungeonCell>.Rectangle(maxX - minX + 1, maxY - minY + 1);
    And all that is left is to put the tiles in the grid:

    Code (csharp):
    1. foreach(var point in grid)
    2. {
    3.    grid[point] = new DungeonCell;
    4. }
    5.  
    6. foreach(tile in dungeonTiles)
    7. {
    8.    var point = map[tile.transform.position];
    9.    grid[point].DungeonTile = tile;
    10. }
    That is the basic gist. Let me know if it works :)
     
  31. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Unfortunately tiles can be any shape or size (though set up in a grid). For example, this would be a 3x1 floor tile with three preplaced DungeonCells highlighted in the editor (the green rectangles are for DunGen).



    Once the dungeon layout has been generated I already have all the "real world" map points in my scene. I can loop through all the DungeonCells afterwards and determine those cells' overall min/max position without problems but I fail when trying to map them to a grid.

    Code (csharp):
    1. IndexOutOfRangeException: Array index is out of range.
    2. Gamelogic.Grids.AbstractUniformGrid`2[DungeonCell,Gamelogic.Grids.RectPoint].set_Item (RectPoint point, .DungeonCell value) (at Assets/GamelogicGrids/Plugins/Gamelogic/Grids/Grids/AbstractUniformGrid.cs:69)
    3. DungeonGrid.BuildGrid () (at Assets/Scripts/DungeonGrid.cs:77)
    4. DungeonGrid.Start () (at Assets/Scripts/DungeonGrid.cs:25)
    Below is my code which I guess isn't 100% correct just yet...

    Code (DungeonGrid.cs):
    1. using Gamelogic;
    2. using Gamelogic.Grids;
    3. using UnityEngine;
    4. using System.Collections.Generic;
    5.  
    6. public class DungeonGrid : GLMonoBehaviour
    7. {
    8.     public GameObject myDungeon;
    9.     public DungeonCell myCell;
    10.  
    11.     public int minX;
    12.     public int minY;
    13.     public int maxX;
    14.     public int maxY;
    15.  
    16.     public RectGrid<DungeonCell> actualGrid;
    17.     public IMap3D<RectPoint> gridParametersMap;
    18.     public IMap3D<RectPoint> dungeonMap;
    19.  
    20.     public void Start ()
    21.     {
    22.         gridParametersMap = new RectMap (new Vector2 (4, 4)).To3DXZ ();
    23.  
    24.         if (myDungeon != null) {
    25.             BuildGrid ();
    26.         }
    27.     }
    28.  
    29.     public void Update ()
    30.     {
    31.  
    32.     }
    33.  
    34.     private void BuildGrid ()
    35.     {
    36.         Component[] myCells;
    37.  
    38.         myCells = myDungeon.GetComponentsInChildren<DungeonCell> ();
    39.  
    40.         Debug.Log (myCells.Length + " cells found...");
    41.  
    42.         // set initial min/max value for further comparison
    43.         minX = (int)myCells [0].transform.position.x;
    44.         minY = (int)myCells [0].transform.position.z;
    45.  
    46.         maxX = minX;
    47.         maxY = minY;
    48.  
    49.         // loop through all DungeonCells and find "real" min/max values
    50.         foreach (DungeonCell testCell in myCells) {
    51.             if (testCell.transform.position.x < minX)
    52.                 minX = (int)testCell.transform.position.x;
    53.             if (testCell.transform.position.x > maxX)
    54.                 maxX = (int)testCell.transform.position.x;
    55.  
    56.             if (testCell.transform.position.z < minY)
    57.                 minY = (int)testCell.transform.position.z;
    58.             if (testCell.transform.position.z < minY)
    59.                 minY = (int)testCell.transform.position.z;
    60.         }
    61.  
    62.         Debug.Log ("Cells spawn from (" + minX + "/" + minY + ") to (" + maxX + "/" + maxY + ")");
    63.  
    64.         Vector3 bottomLeftWorldPoint = gridParametersMap [new RectPoint (minX, minY)];
    65.  
    66.         dungeonMap = new RectMap (new Vector2 (4, 4)).Translate (-bottomLeftWorldPoint).To3DXZ ();
    67.         actualGrid = RectGrid<DungeonCell>.Rectangle (maxX - minX + 1, maxY - minY + 1);
    68.  
    69.         foreach (RectPoint point in actualGrid)
    70.         {
    71.             actualGrid [point] = myCell;
    72.         }
    73.  
    74.         foreach (DungeonCell testCell in myCells)
    75.         {
    76.             RectPoint point = dungeonMap [testCell.transform.position];
    77.             actualGrid [point] = testCell;
    78.         }
    79.     }
    80. }

    Don't mind that legacy code snippets - they are from my previous 3D test.
    Code (DungeonCell.cs):
    1. using System.Linq;
    2. using UnityEngine;
    3. using Gamelogic;
    4. using Gamelogic.Grids;
    5.  
    6. public class DungeonCell : GLMonoBehaviour
    7. {
    8.     private bool activated;
    9.     private Color myOriginalColor;
    10.  
    11.     public void Awake()
    12.     {
    13.         activated = false;
    14.     }
    15.  
    16.  
    17.     public void SetColor(Color color)
    18.     {
    19.         renderer.material.color = color;
    20.         myOriginalColor = color;
    21.     }
    22.  
    23.  
    24.     public void Switch()
    25.     {
    26.         renderer.material.color = activated ? myOriginalColor : Color.white;
    27.         activated = !activated;
    28.     }
    29. }
     
    Last edited: Jun 22, 2014
  32. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    I think thee problem comes in with your min and max calculations. To construct the grid from those, they should be grid points, not world points. I would change the loop like this:

    Code (csharp):
    1.         var gridPoint = gridParametersMap[myCells[0].transform.position];  
    2.  
    3.         minX = gridPoint.X;
    4.         minY = gridPoint.Y;
    5.  
    6.         maxX = minX;
    7.         maxY = minY;
    8.  
    9.         // loop through all DungeonCells and find "real" min/max values
    10.         foreach (DungeonCell testCell in myCells) {
    11.             gridPoint = gridParametersMap[testCell.transform.position];
    12.      
    13.             if (gridPoint.X < minX)
    14.                 minX = gridPoint.X;
    15.  
    16.             if (gridPoint.X > maxX)
    17.                 maxX = gridPoint.X;
    18.  
    19.             if (gridPoint.Y < minY)
    20.                 minY = gridPoint.Y;
    21.  
    22.             if (gridPoint.Y > maxY)
    23.                 minY = gridPoint.Y;
    24.         }
    25.  
    Does this fix the issue?

    About the handling of tiles over multiple cells, the Block Slider game provides a good example of how to deal with this. The basic idea is to have a datastructure "Block, with a reference to the actual tile, and put this block in every cell that the tile occupies. The only thing is to make sure to filter to remove duplicates in any lists or iterations over cells. Path finding should still work without modification.
     
    Last edited: Jun 24, 2014
  33. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Actually it gives me an Internal Compiler/Unhandled Exception Error. :eek: This happens as soon as I add the first line, and even a Debug.Log to see the value of x does not work.


    I might look into that example scene, but fortunately I don't need to keep track of the separate tiles once the grid has been successfully generated from the world map.
     
  34. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Oops! The code I gave feed floats to the map instead of Vectors... I updated the code.
     
  35. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Thanks - it's better not, but not quite there yet. ;)

    As soon as he reaches line 95 I get an IndexOutOfRangeException. If I skip this line everything seems to work fine. I added a second function which instantiates a bunch of DungeonCells to visualize the map, but that only aligns with the original dungeon layout if I apply to bottomLeftWorldPoint addition to the instantiated prefab's transform rather than the map.

    Code (csharp):
    1. using Gamelogic;
    2. using Gamelogic.Grids;
    3. using UnityEngine;
    4. using System.Collections.Generic;
    5.  
    6. public class DungeonGrid : GLMonoBehaviour
    7. {
    8.     public GameObject myDungeon;
    9.     public DungeonCell myCell;
    10.     public GameObject dungeonCellsParent;
    11.  
    12.     private int minX;
    13.     private int minY;
    14.     private int maxX;
    15.     private int maxY;
    16.  
    17.     private Vector3 bottomLeftWorldPoint;
    18.  
    19.     public RectGrid<DungeonCell> actualGrid;
    20.     public IMap3D<RectPoint> gridParametersMap;
    21.     public IMap3D<RectPoint> dungeonMap;
    22.  
    23.  
    24.     public void Start()
    25.     {
    26.         gridParametersMap = new RectMap(new Vector2(4, 4)).To3DXZ();
    27.  
    28.         if (myDungeon != null)
    29.         {
    30.             BuildGrid();
    31.             CreateDungeonCells();
    32.         }
    33.     }
    34.  
    35.  
    36.     private void BuildGrid()
    37.     {
    38.         Component[] myCells;
    39.         RectPoint gridPoint;
    40.  
    41.         myCells = myDungeon.GetComponentsInChildren<DungeonCell>();
    42.  
    43.         Debug.Log(myCells.Length + " cells found");
    44.  
    45.         // set initial min/max value for further comparison
    46.         gridPoint = gridParametersMap[myCells[0].transform.position];
    47.  
    48.  
    49.         minX = gridPoint.X;
    50.         minY = gridPoint.Y;
    51.  
    52.         maxX = minX;
    53.         maxY = minY;
    54.  
    55.  
    56.         // loop through all DungeonCells and find "real" min/max values
    57.         foreach (DungeonCell testCell in myCells)
    58.         {
    59.             gridPoint = gridParametersMap[testCell.transform.position];
    60.  
    61.             if (gridPoint.X < minX)
    62.                 minX = gridPoint.X;
    63.  
    64.             if (gridPoint.X > maxX)
    65.                 maxX = gridPoint.X;
    66.  
    67.             if (gridPoint.Y < minY)
    68.                 minY = gridPoint.Y;
    69.  
    70.             if (gridPoint.Y > maxY)
    71.                 maxY = gridPoint.Y;
    72.         }
    73.  
    74.         Debug.Log("Cells spawn from (" + minX + "/" + minY + ") to (" + maxX + "/" + maxY + ")");
    75.         Debug.Log("Needs a grid sized " + (Mathf.Abs(maxX - minX) + 1) + "/" + (Mathf.Abs(maxY - minY) + 1));
    76.        
    77.         bottomLeftWorldPoint = gridParametersMap[new RectPoint(minX, minY)];
    78.  
    79.         Debug.Log("gridParametersMap should start at " + bottomLeftWorldPoint);
    80.  
    81.         dungeonMap = new RectMap(new Vector2(4, 4))
    82.             .To3DXZ();
    83.  
    84.         actualGrid = RectGrid<DungeonCell>.Rectangle(Mathf.Abs(maxX - minX) + 1, Mathf.Abs(maxY - minY) + 1);
    85.  
    86.  
    87.         foreach (RectPoint point in actualGrid)
    88.         {
    89.             actualGrid[point] = myCell;
    90.         }
    91.  
    92.         foreach (DungeonCell testCell in myCells)
    93.         {
    94.             RectPoint point = dungeonMap[testCell.transform.position];
    95.             actualGrid[point] = testCell;
    96.         }
    97.     }
    98.  
    99.  
    100.     private void CreateDungeonCells()
    101.     {
    102.         foreach (RectPoint point in actualGrid)
    103.         {
    104.             DungeonCell newCell = Instantiate(myCell, dungeonMap[point] + bottomLeftWorldPoint, Quaternion.identity) as DungeonCell;
    105.             newCell.transform.parent = dungeonCellsParent.transform;
    106.         }
    107.     }
    108.  
    109. }
    I could throw out all external assets and upload a stripped down project zip file for you tomorrow if you'd like to take a closer look.
     
  36. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Isn't it possible to add single points to a grid after it has been created? Something like AddShape()?

    I thought I could just loop through all the DungeonCell placeholders in my layout and add one point per cell to the grid, but I don't think it's possible to place a loop between a BeginShape() and EndShape().
     
  37. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hey c-Row,

    Yup, you can definitely let me have a look at the code. (You can also email it to me at herman@gamelogic.co.za if you don't want to put it here).

    About your recent question: you could add a loop but for a large number of cells it may slow down access to cells.

    Code (csharp):
    1. //Slow, only for small grids
    2.  
    3. var gridShape = RectGrid<DungeonCell>.BeginShape(); //
    4. RectPoint gridPoint;
    5.  
    6. foreach(var cell in testCells.ButLast())
    7. {
    8.    gridPoint = map[cell.transform.position];
    9.    gridShape = gridShape.Rect(1, 1).Translate(gridPoint).Union();
    10. }
    11.  
    12. cell = testCells.Last();
    13. gridPoint = map[cell.transform.position];
    14. grid = gridShape.Rect(1, 1).Translate(gridPoint).EndShape();
    This will work, but each access will be a composition of n functions, where n is the number of cells. I give it more for interest's sake.

    Another alternative is using the raw constructors. You will still need the the grid extent, and bottom left corner, but you can feed your custom test function (that can use a HashSet and test against the actual points in the grid.

    For example:

    Code (csharp):
    1.  
    2. var dungeonPointsHashSet = new HashSet<RectPoint>();
    3.  
    4. foreach(var cell in testCells.ButLast())
    5. {
    6.    gridPoint = map[cell.transform.position];
    7.    var dungeonPointsHashSet.Add(gridPoint);
    8. }
    9.  
    10. var grid = new RectGrid<DungeonCell>(width, height, p => dungeonPointsHashSet.Contains(p), bottomLeft);
    This will be fast enough, but I would use it only for "sparse" dungeons (where the percentage of occupied cells are low compared to the size of the grid).

    I still recommend the first approach I outlined above.
     
  38. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Thanks Herman - email sent.
     
  39. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    I'd like to let everybody know that - probably through some kind of black voodoo magic - Herman solved my issue in the meantime. Darth Vader gives a thumbs-up for great customer support again.
     
  40. 99thmonkey

    99thmonkey

    Joined:
    Aug 10, 2012
    Posts:
    525
    do you have any examples that work with touch mobile controls? like slide pzzle with touch?
     
  41. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Unfortunately not. You will probably want to use a GUI package in combination with Grids (such as NGUI or Daikon Forge) that already supports drag-events.

    Edit: I forgot about this example: http://gamelogic.co.za/triangle-puzzle-game/ That shows how to drag tiles around, and may be a good starting point.
     
    Last edited: Jul 3, 2014
  42. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Hello Herman

    I tried to compile my current project but unfortunately Unity fails to do so, apparently caused by errors in GLGizmos.

    The project runs fine as expected while inside the editor.

    Currently installed version of Unity is 4.5.2f1.
     
  43. Dwel

    Dwel

    Joined:
    May 31, 2010
    Posts:
    13
    Hello,

    I'm using the PolarPointyBrick grid builder inside the Unity editor and it's been fast and simple to create the actual grid. However, I can't access the "Grid" property inside the builder in code. Unity returns an InvalidCastException whenever I try to access it.

    The problem seems to be in the actual builder script where the property is accessed:

    Code (CSharp):
    1.         public new PointyHexGrid<TileCell> Grid
    2.         {          
    3.             get { return (PointyHexGrid<TileCell>) base.Grid; }
    4.         }
    5.  
    Either this is an issue with the plugin or I'm missing something in accessing "Grid". Any idea on how I can approach resolving this?
     
  44. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Yes, you uncovered a bug.

    We will provide a fix in the next update.

    I see there are some implications for GridBehaviour too, but if you don't use that, then you can make the following change to the code you quoted, and it should work:

    Code (csharp):
    1. public new WrappedGrid<MeshTileCell, PointyHexPoint> Grid
    2. {
    3.    get { return (WrappedGrid<MeshTileCell, PointyHexPoint>)base.Grid; }
    4. }
    Thanks for bringing the issue to our attention!
     
  45. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hi c-Row,

    Are you using Grids 1.9? It should be fixed in there.
     
  46. Dwel

    Dwel

    Joined:
    May 31, 2010
    Posts:
    13
    Hello,

    Thanks for the fix for the PolarGrids, I can access the Grid property now. The GridBehaviour inconsistencies I can circumvent in a similar manner.

    I did found another issue while trying to interact with the PolarGrid during play. The test was simple enough, have the game write out the Point coordinates of the Cell I click on.

    In the following image:


    Clicking on most of the cells writes out coordinates correctly. However, it's having difficulties in certain portions.

    (4, 3) and (4, 2) don't work at all. Anywhere I click on those cells, I can't get it to recognize them
    (3, 3) and (3, 2) are only recognized if clicked anywhere below the label (southern area). The northern area (above the label) does not register a response.

    The issue is consistent for other sizes as well, affecting more cells on larger ones and fewer on smaller ones, on the same side of the Grid (North/East).

    Edit: I've made some additional logs, printing the GridPoint value of where I click before checking whether the point is contained by the grid.

    For the cells that were only registered if pressed on in the southern area, the logging showed me two GridPoint values. One value I was getting by clicking in the northern area of the cell, and the other by clicking in the southern area of the same cell. The first value wasn't contained by the Grid, but the second was.
     
    Last edited: Jul 26, 2014
  47. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hi Dwel,

    It seems you you stumbled on two (!) more bugs. *blush*

    Here is what is happening:

    screen_516.png
    As you can see, there is a horizontal offset. As it turns out, this is because the polar grid dimensions are not calculated correctly.

    Fixing this, we get the following:
    screen_517.png

    It's better, but you will notice the cells on the x-axis are sometimes split into two when they shouldn't, and some coordinates of the forward and inverse mapping do not correspond as they should (that is why certain cells are grayish - the cells in the two mappings have different coordinates and hence different colors).

    This turned out to be a modular operation that was omitted in the mapping calculation. After fixing this, the mapping is correct:

    screen_518.png
    I have attached the two files you have to change; if you replace them, all should be good. (The same must also be plaguing the other polar grid maps o.0).

    I am sorry for the inconvenience. If you have any trouble, please let me know.
     

    Attached Files:

  48. c-Row

    c-Row

    Joined:
    Nov 10, 2009
    Posts:
    853
    Ah, 1.9 solved the issue - thanks!
     
    Herman-Tulleken likes this.
  49. SnipersCode

    SnipersCode

    Joined:
    Jul 28, 2014
    Posts:
    3
    Hi! I'm by no means a newbie at programming, but I've just recently spent a couple of hours learning unity. I just recently became interested in developing a 2d rpg based on a grid system, and your library has caught my attention.

    I'm pretty much sold on buying your product after going through all the examples and using the trial for a little bit, but I had a few questions. I was planning on using a hex grid if it matters.

    - Is there a way in the library to implement "Line of Sight"? This would include cases where it would not necessarily be aligned to an axis, but would check whether tiles directly in a straight line in between two points have the "blocking" property. I would want to change the color of a tile within a certain distance based on whether it has line of sight from an origin point.
    - What would be the best way to implement an animation for a tile? If I wanted to play an animation depending on its type, could I add an animator component, call GetComponent in the tile initialization, and change the variable that would control the state machine? Is there a better way performance-wise?

    Thanks.
    P.S.: Your PathFindingHex example does not build the grid on import or play. I was confused as to why clicking in some hexes in certain places clicked its neighbor instead, but as soon as I changed the update type to editor auto, I found out the cell spacing was initially wrong.
     
    Last edited: Jul 28, 2014
  50. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hi SnipersCode!

    (Thanks for pointing out the issue with the PathFinding example; we'll look into it).

    To answer your questions:

    Question 1
    From Amit Patel's Hex Grids page, the line algorithm is extremely simple. (I have not tested it, but will a bit later today):

    Code (csharp):
    1. List<PointyHexPoint> GetLine(PointyHexPoint p1, PointyHexPoint p2)
    2. {
    3.    int distance = p1.DistanceFrom(p2);
    4.  
    5.    var line = new List<PointyHexPoint>();
    6.  
    7.    for(int i = 0; i <= distance; i++)
    8.    {
    9.       var t = i / (float) distance;
    10.       var point = map[map[p1] * (1 - t) + map[p2] * t];  //It's almost surely possible to optimize this line if necessary
    11.       list.Add(point);
    12.    }
    13.  
    14.    return line;
    15. }
    You can from this easily work out a line of sight (iterate through the points, and chop off when you encounter a blocked cell or the end of the line).

    Question 2
    What you describe sounds like it should work well. Whether there is a better method depends on some details. Will the animation play continuously? If not, can it be triggered by events other than initialization? Will it change? Does the animation need to block user input? Do cells animate from one grid position to another?

    For example, if animations are triggered by state changes, you will probably have a state property that automatically plays an animation when set. You could trigger these for the first time in your Grid Behaviour by setting the property for each cell as you iterate through the grid.

    If you could provide more detail, I can give some more info on a good approach.
     
    Last edited: Jul 28, 2014