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. apgames

    apgames

    Joined:
    Nov 19, 2013
    Posts:
    6
    Thanks for the extensive reply, will see what I can do :) he he and yes it's a great ( ;) ) idea for an example... If you make an example, holes inside the border with border around would be great....
     
    Last edited: Mar 19, 2015
  2. rosskinsella0

    rosskinsella0

    Joined:
    Jun 6, 2014
    Posts:
    8
    Hi Herman,

    I bought the Lite package a few weeks back and I am happy with the product so far.

    I was wondering if you could suggest how I would go about implementing Algorithms.AStar in a system where I have multiple Grids which represent 'stories' or 'vertical levels' in the terrain or a building
    - Characters can transition between these stories and thus I need AStar to function across multiple grids.

    The peach coloured grid(Grid 1) is a level above the pink grid(Grid 2) which I want to be reachable via the golden cube.
    top.png


    bot.png

    Apologies if this is an issue which has been risen before.

    Thanks,
    Ross.
     
  3. lend

    lend

    Joined:
    Aug 7, 2014
    Posts:
    13
  4. cygnusprojects

    cygnusprojects

    Joined:
    Mar 13, 2011
    Posts:
    767
    twobob likes this.
  5. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Maybe it was just down temporarily... if you keep having problems please let me know.
     
  6. Herman-Tulleken

    Herman-Tulleken

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

    I bought the Lite package a few weeks back and I am happy with the product so far.

    I was wondering if you could suggest how I would go about implementing Algorithms.AStar in a system where I have multiple Grids which represent 'stories' or 'vertical levels' in the terrain or a building
    - Characters can transition between these stories and thus I need AStar to function across multiple grids.

    The peach coloured grid(Grid 1) is a level above the pink grid(Grid 2) which I want to be reachable via the golden cube.
    View attachment 131341


    View attachment 131342

    Apologies if this is an issue which has been risen before.

    Thanks,
    Ross.[/QUOTE]

    Hi Ross,

    For Grids Pro we provide Layered Grids, which is exactly multiple grids that represent stories, basically a 3D grid (which you can represent in proper 3D, or the "level-per-level" 3D your images suggest. Here is an example for the system: http://gamelogic.co.za/2014/05/24/a-new-look-at-layered-grids-setting-up-neighbors/ ). If you want to upgrade, you can do it from Grids Lite for $80 (just send us an email).

    If you do not want to upgrade, you can try implementing the basic functionality yourself. Make a class that implements the IGrid interface, and internally have multiple grids (in an array, one for each level), make your own point type for 3D rect points, and you should be able to use it with our AStar algorithm. It is some work, but not rocket science. If you go this route I can help you.

    layered_grid1.png
     
    Last edited: Mar 20, 2015
  7. rosskinsella0

    rosskinsella0

    Joined:
    Jun 6, 2014
    Posts:
    8
    Thanks for the quick reply Herman, I'll check out this suggestion!
    - I'd upgrade but I'm a cheap student ;)

    Also, I intend on generating levels from a CSV
    - Have you published some best practices for creating grids and setting their individual cells' variables at run-time?

    Again, sorry if this has been brought up in the past, I find this thread difficult to navigate

    EDIT:
    This is my solution at present:

    Objects which are created by parsing the CSV
    Screen Shot 2015-03-20 at 16.15.52.png

    The parsing of the csv
    Screen Shot 2015-03-20 at 16.22.55.png

    The creation of the grids and setting of their cells
    Screen Shot 2015-03-20 at 16.14.42.png

    This solution works but I feel its a little messy and you may have a better solution.

    Cheers,
    Ross.
     

    Attached Files:

    Last edited: Mar 20, 2015
  8. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Can you paste the code instead of images; that way it's easier for me to make modifications to suggest improvements.
     
  9. rosskinsella0

    rosskinsella0

    Joined:
    Jun 6, 2014
    Posts:
    8
    Sorry about that Herman.

    1. I define objects to hold parameters which are created when parsing the CSV
    Code (CSharp):
    1. public class GridConstructionHelper
    2. {
    3.         public int level; // The height of this grid, 0 being ground.
    4.         public int size; // Maybe should be outside?
    5.         public CellConstructionHelper[] cells;
    6. }
    7.  
    8. // Defaults to -1 to represent null
    9. public class CellConstructionHelper
    10. {
    11.         public bool exists; // A cell may not always be present at this location
    12.         public DiamondPoint position;
    13.         public int tile_asset_id = -1; // Unique identifier for the art asset
    14.         public int obsticle_asset_id = -1; // Unique identifier for the obsticle
    15.  
    16.         public int border_NE_id = -1;
    17.         public int border_SE_id = -1;
    18.         public int border_SW_id = -1;
    19.         public int border_NW_id = -1;
    20.  
    21.         // Still to be implemented but the API will stay.
    22.         public GameObject GetPrefab ()
    23.         {
    24.                 return BattleGridManager.baseCell;
    25.         }
    26.  
    27.  
    28. }
    2. I parse the csv and create the objects mentioned above

    Code (CSharp):
    1.                 List<GridConstructionHelper> gridConstructionHelpers = new List<GridConstructionHelper> ();
    2.                 foreach (var grid in csv) {
    3.                         List<CellConstructionHelper> cellConstructionHelpers = new List<CellConstructionHelper> ();
    4.    
    5.                         foreach (var cell in grid) {
    6.                                 cellConstructionHelpers.Add (new CellConstructionHelper () { exists = cell.exists, position = new DiamondPoint(cell.x, cell.y), tile_asset_id = cell.tile_asset_id, obsticle_asset_id = cell.obsticle_asset_id, border_NE_id = cell.border_NE_id, border_SE_id = cell.border_SE_id, border_SW_id = cell.border_SW_id, border_NW_id = cell.border_SW_id });
    7.  
    8.                         }
    9.                         gridConstructionHelpers.Add (new GridConstructionHelper (){ level = grid.level, size = grid.size, cells = cellConstructionHelpers.ToArray()});
    10.                 }
    3. I Create the grids and set their cell values


    Code (CSharp):
    1.                 foreach (GridConstructionHelper gridOptions in gridConstructionHelpers)
    2.                 {
    3.                     GameObject newGrid = Instantiate (gridPrefab, transform.position, transform.rotation) as GameObject;                
    4.                     DiamondTileGridBuilder gridBuilder = newGrid.GetComponent<DiamondTileGridBuilder> ();
    5.                     gridBuilder.Size = gridOptions.size;
    6.                     gridBuilder.Start ();
    7.                     BattleGridBehaviour gridBehavior = newGrid.GetComponent<BattleGridBehaviour> ();
    8.  
    9.                     foreach (CellConstructionHelper cellHelper in gridOptions.cellOptions) {
    10.                             BattleGridCell cell = (BattleGridCell)gridBehavior.Grid [cellHelper.position];
    11.                             cell.level = cellHelper.tile_asset_id;
    12.                             cell.FrameIndex = cellHelper.tile_asset_id;
    13.                             // .
    14.                             // .
    15.                             // .
    16.                     }
    17.                 }
    ps
    - I may actually use json as oppposed to a CSV, as so:
    Code (CSharp):
    1. {
    2.     "grids" : {
    3.         "sizes" : 2,
    4.         "grid" : {
    5.             "level" : 0,
    6.             "cells" : {
    7.                 "cell" : {
    8.                     "x" : 0,
    9.                     "y" : 0,
    10.                     "tile_asset_id" : 1
    11.                 },
    12.                 "cell" : {
    13.                     "x" : 1,
    14.                     "y" : 0,
    15.                     "tile_asset_id" : 1
    16.                 },
    17.                 "cell" : {
    18.                     "x" : 0,
    19.                     "y" : 1,
    20.                     "tile_asset_id" : 1
    21.                 },
    22.                 "cell" : {
    23.                     "x" : 1,
    24.                     "y" : 1,
    25.                     "tile_asset_id" : 1
    26.                 }
    27.             }
    28.         },
    29.         "grid" : {
    30.             "level" : 1,
    31.             "cells" : {
    32.                 "cell" : {
    33.                     "x" : 0,
    34.                     "y" : 0,
    35.                     "tile_asset_id" : 2
    36.                 },
    37.                 "cell" : {
    38.                     "x" : 1,
    39.                     "y" : 0,
    40.                     "tile_asset_id" : 2
    41.                 },
    42.                 "cell" : {
    43.                     "x" : 0,
    44.                     "y" : 1,
    45.                     "tile_asset_id" : 2
    46.                 },
    47.                 "cell" : {
    48.                     "x" : 1,
    49.                     "y" : 1,
    50.                     "tile_asset_id" : 2
    51.                 }
    52.             }
    53.         }
    54.     }
    55. }
     
    Last edited: Mar 20, 2015
  10. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Your method seems good to me overall.

    I would suggest a few changes. As it stands, you have a class that annotates cells with information you want to store. The main difference between that and the actual cell is that it contain position information. This is redundant if you can determine the grid size / shape of the grid correctly. Points are iterated in a reliable order, so the same data will always go to the same cell. If this is the case for you (it looks like it is from your code), you can simplify your system a bit.

    I would put all the serializable cell data as a separate class inside your own cell.

    Code (csharp):
    1. class MyCell
    2. {
    3.    public MyCellData cellData; //contains everything to be serialized.
    4.    //I made this public for shorter code; of course making it private with a public get-property would be better.
    5. }
    Then you can write:

    Code (csharp):
    1. var dataReadyForSerialization = Grid.Values.Select(cell => cell.cellData).ToArray();
    In this format, you can use a lib for doing the final serialization to JSon, possibly using a helper class GridData that has the level, size (?), and dataReadyForSerialization you created here.

    To construct your grid, you can then get the array using standard tools:

    Code (csharp):
    1. CellData[] dataFromDeserialization = //from lib;
    2.  
    3. var grid = //create an empty grid or set the grid builder properties.
    4.  
    5. int i = 0;
    6.  
    7. foreach(var point in grid)
    8. {
    9.    grid[point] = MakeCell(dataFromDeserialization[i]);
    10.  
    11.    i++;
    12. }
    13.  
    MakeCell is simply a method that instantiates a cell from a prefab, and then assign the parameter (which is of type CellData) to the cells cellData (or potentially a copy thereof).

    So the main points are:
    1. You only need to serialize points if you cannot build your grid with some other means. (If you store enough info, you can always do this).
    2. Instead of a constructor helper, just make the serializable data into a new class that is a field of each cell.
    Does this make sense?
     
  11. Korigoth

    Korigoth

    Joined:
    Jul 21, 2014
    Posts:
    105
    Thx for the example i was not in the good example ^^

    My hero is now moving from 1 cell to another but not with any algorithm (A*).

    I'm now trying to Implement the IPointerClickHandler on the Cell i use but it never got called...

    here is my code

    Code (CSharp):
    1.     public class TilePoint : GLMonoBehaviour, IPointerClickHandler
    2. {
    3.         // data
    4.  
    5.         public void OnPointerClick(PointerEventData eventData)
    6.         {
    7.             Debug.Log("Click");
    8.             Debug.Log(eventData);
    9.         }
    10. }
    then i will be looking at A* to move to the clicked TilePoint
     
  12. lend

    lend

    Joined:
    Aug 7, 2014
    Posts:
    13
    I can access by my phone(3G).Wi-Fi can not.
    It seems my ONU has problem.
    Or your site htaccess setting.

    Maybe my problem.
     
  13. rosskinsella0

    rosskinsella0

    Joined:
    Jun 6, 2014
    Posts:
    8
    All clear Herman, thank you for your time!

    I'll let you know how I get on with my custom 3D Grid once I get around it!
     
  14. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    Hi Herman,

    I have two questions regarding some grid calculations. Both apply to DiamondPoint grids.

    - let's say I have a shape selected out of X points (square, line, etc.). How can rotate the selection around one of the points (clockwise, counterclockwise)?

    - is there an easy way of calculating said shapes in grid? I already utilize the diamond shape thanks to your AreaFill example, but what about
    • lines of N legth
    • crosses of N size
    Thanks!
     
  15. OnePxl

    OnePxl

    Joined:
    Aug 6, 2012
    Posts:
    307
    Try using a different DNS.
     
  16. lend

    lend

    Joined:
    Aug 7, 2014
    Posts:
    13
    I tried to use VPN , and it works well.:)
    Thanks.

    By the way , I want to use Rect Tile (and other tiles) in this example(Mapping Images to Grids).
    What would be the best way to do?
     
  17. sacha

    sacha

    Joined:
    Oct 29, 2012
    Posts:
    22
    Still doesn't get it..

    So i have a terrain centered at 0,0,0 with 500m on both XY axis.
    I created and parented an empty game object as grid root to the terrain and reset it to 0,0,0.
    I arbitrary define i needed a 20x20 grids and adapted a demo scripts but all i got is an unexpected result: all the cells are aligns in one raw spreads along X axis.
    I want the cell starting from the lower left side of the terrain.

    What am i doing wrong ?
    Thanks
    Code (CSharp):
    1.  
    2. public void DoIt() {
    3.             float cellWidth = terrain.terrainData.heightmapWidth/(ColumnCount + 1);
    4.             float cellHeight = cellWidth ;
    5.            Debug.Log(string.Format("widht= {0} height={1}", cellWidth,cellHeight));
    6.             Debug.Log(string.Format("Col= {0} raw={1}", ColumnCount,rowCount));
    7.            
    8.              grid = PointyHexGrid<SpriteCell>.FatRectangle(ColumnCount, rowCount);
    9.  
    10.              map = new PointyHexMap(new Vector2(1, 1))
    11.                         .AnchorCellBottomLeft()
    12.                         .WithWindow(new Rect(0, 0, terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight))
    13.                      
    14.                            .AlignMiddleCenter(grid)
    15.                         .To3DXZ();
    16.  
    17.             foreach ( PointyHexPoint point in grid) {
    18.                 SpriteCell cell = Instantiate(cellPrefab);
    19.                 Vector3 worldpoint = map[point];
    20.                 cell.transform.parent = root.transform;
    21.                 cell.transform.localScale =new Vector3(cellWidth,1,cellHeight);
    22.                 cell.transform.localPosition = worldpoint;
    23.                // cell.transform.position = worldpoint;
    24.                 cell.name = point.ToString();
    25.                 cell.Color = ExampleUtils.Colors[point.GetColor2_4()]; //Sets the color of the cell
    26.                 Debug.Log(cell.Dimensions.ToString());
    27.  
    28.                 grid[point] = cell;
    29.             }
    30.  
    31.         }
     
  18. Chancey

    Chancey

    Joined:
    Mar 23, 2015
    Posts:
    3
    Hi Herman!

    Recently bought the Grids Basic package for a hexgrid project. I was wondering if you guys had a premade example for pathfinding using a pointy hex grid, the tutorial on your website seems simple but it would be a ton more comprehensive with a simple working example in Unity! I've parsed through all of the forum posts and I haven't found an example with pathfinding.

    Just a very basic example of Astar Pathfinding on a hex grid would make my day!

    Thanks in advance,

    - Chancey
     
  19. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    For your first question:
    There are two parts - calculating the new diamond points of the selection, and then, moving the cells logically in the grid, and then moving and (potentially) rotating the cells physically.

    For the first part you can use the TransformShape method in Algorithms:

    http://gamelogic.co.za/documentatio...rithms.html#afeb0c4204086e89df2ad8fe6b7276ef5

    Code (csharp):
    1. //Let selectedPoints be the selected points in an array of diamond points
    2. var rotatedPoints = Algorithms.TransformShape(selectedPoints, p => (p - pivot).Rotate90() + pivot).ToArray[];
    You can now move the cells logically in the grid like this (this snippet assume that the selected points and the rotate points are the same, just in a different order). We do the physical translation and rotation in the same step (assuming the cells' pivots are in the cells' centers)

    Code (csharp):
    1. Cell[] selectedCells = selectedPoints.Select(p => grid[p]);
    2.  
    3. for(int i = 0; i < selectedCells.Length; i++)
    4. {
    5.    var newPoint = rotatedPoints[i];
    6.  
    7.    grid[newPoint] = selectedCells[i];
    8.    grid[newPoint].tranform.localPosition = map[newPoint];
    9.    grid[newPoint].transform.RotateAroundZ(90);    
    10. }
    Similar code can be used for clockwise rotation.

    The second question:

    You can combine two diamonds to make crosses. You will have to play around to get the appropriate translation a bit, but the general idea is this:

    Code (csharp):
    1. var grid = DiamondGrid<Cell>
    2.    .BeginShape()
    3.       .Rectangle(9, 3)
    4.       .Translate(3, 0)
    5.       .Union()
    6.       .Rectangle(3, 9)
    7.    .EndShape();
    If you use grid builders to setup your grids in the editor, you can use the code above using this technique:

    http://gamelogic.co.za/grids/docume...k-start-tutorial/making-your-own-grid-shapes/

    Let me know if this is what you need.
     
  20. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    All you need to do is replace the grid builder with a rect builder, and implement the exact same grid behaviour that uses RectPoints instead of hexpoints. Finally, you need to make a new mesh cell hat is a rect instead of a hex (I think you may be able to use the built-in quads).

    Does this make sense?
     
  21. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    One thing thsat jumps out at me is that you create the map with dimensions 1x1 (Vector2(1, 1)). This should be Vector(cellWidth, cellHeight).

    That, however, cannot explain why the cells would be spread along one axis.. Could you maybe send me a screenshot so I can better see what is going on?
     
  22. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    We do have the example, but we have recently reworked all the examples and need to gt the update accepted in the store before we can upload it. If you want to give it a go, send me an email (herman@gamelogic.co.za) and I can mail you the package.
     
  23. DMeville

    DMeville

    Joined:
    May 5, 2013
    Posts:
    418
    Hi, I've bought Grids Pro a few weeks ago, and just now starting to play with it. So far it's really great!
    My first test was to make a simple "chessboard" XZ square grid, where I could click each cell and highlight it, very straightforward!

    However, I was wondering if you could shed some insight in how to do something a bit more complex ...

    Basically, I have a 'terrain mesh' (see attached) built from quads, and I'd like to create a matching grid, where each quad is a tile, and clickable in 3d space. I guess should probably be looking at the "mesh cell", and creating the cells to match the quads, right? My concern is that with a map this size (50x50) requiring colliders to access each tile via raycast might be too costly. (perhaps I can simply raycast against the mesh collider, grab the worldspace x and z coordinates and somehow convert that to grid-coordinates, will have to test that idea as I just thought of it while writing this...) Additionally, I'm not really sure how to begin with mesh cells!

    Ideally this will be setup in the editor so I can go in a further define the cells (ie, walkable, movement cost etc) for use with range finding and pathfinding.

    Does this sound like the proper way to go about this? Is there any documentation or tutorials already created that might help me with this? Or anything about converting a terrain to a rect grid?

    Thanks in advance!
     

    Attached Files:

  24. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Currently, grid builders only work for tiles (either 2D or 3D), and not for grids made from a single mesh.

    So the right way to go about it is to construct your grid from code.

    And you can indeed use a mesh collider with raycasting (some code below).

    There are a few examples that deal with these mesh grids. I suggest first familiarizing yourself with how grids work when constructed from code using the four code tutorials here:

    http://gamelogic.co.za/grids/documentation-contents/

    The last one of these also explain cells a bit more.

    Then have a look at these examples - they show various facets of mesh grids, including how to generate meshes for them:

    http://gamelogic.co.za/grids/featur...mesh-generation-script-with-proper-texturing/

    http://gamelogic.co.za/grids/features/examples-that-ship-with-grids/tiling-a-sphere-with-hexes/

    The ray-casting code is quite straightforward. Here is a vanilla version:

    Code (csharp):
    1.  
    2. public void Update()
    3. {
    4.     if (Input.GetMouseButtonDown(0))
    5.     {
    6.         var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    7.  
    8.         RaycastHit hit;
    9.  
    10.         if (Physics.Raycast(ray, out hit))
    11.         {
    12.             Vector3 worldPosition = gridRoot.transform.InverseTransformPoint(hit.point);
    13.             Debug.Log("mouse pos: " + Input.mousePosition + ", world_pos:" + worldPosition);
    14.  
    15.             var gridPoint = map[worldPosition];
    16.  
    17.             if (Grid.Contains(gridPoint))
    18.             {
    19.                 Debug.Log("Grid hit: " + gridPoint);
    20.             }
    21.         }
    22.     }
    23. }
    Let me know if it all makes sense once you have had some time to go through it all.
     
    Braza and DMeville like this.
  25. DMeville

    DMeville

    Joined:
    May 5, 2013
    Posts:
    418
    @Herman Tulleken Thanks a lot! I read through everything a couple of times and things really started to clear up, and this is the result!

    http://dylanmeville.com/Unity/FTT/TerrainGrid.html
    code: http://pastebin.com/x6cH8SNC

    It works really nicely - however I do have a few questions:
    1 - On line 49 of the code I've written, I'm not using a map as it was off by (0.5,0,0.5) - which I guess is because of my manual offset of the cells on line 34. I did this though to align the cells to the terrain. Instead I'm grabbing the raycast hit and converting it to a RectPoint manually, and then using this point to access the cell in the grid. Is this acceptable? Would it be better to fix this, if so could I go about that?

    2 - I wasn't aware of the ContextMenu thing, which is really cool. I'd like to generate my grid in the editor so I can go in and adjust some properties on specific tiles - however setting my buildOnStart bool to false and running the BuildGrid function in the editor generates the grid perfectly, but when I hit play things break. Specifically on line 51; I get a NullReference Exception when trying to access the grid - which leads me to believe the variables assigned in edit mode aren't being carried over into play mode. Is this right? Is there a way to rectify this? I figured I'd ask as you might know the answer, as it seems like something that may have come up before.

    Thanks again!
     
  26. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    1. Maps can be translated (and transformed with other operations). To get the alignment you want, you can write

    Code (csharp):
    1. var map = new RectMap(Vector(1, 1))
    2.    .Translate(0.5f, 0.5f)
    3.    .To3DXZ();
    Although your method obviously works, it is not robust against changes in your game.

    Also have a look at the following type of construct:

    Code (csharp):
    1. var map = new RectMap(Vector(1, 1))
    2.    .WithWindow(worldSize)
    3.     .AlignMiddleCenter()
    4.     .AnchorCellMiddleCenter()
    5.    .To3DXZ();
    Which can be used to align grids. This can be (ab)used to anchor a grid to a specific point as well by making the rect of the window 0 size.

    2. You are right, non-serialized fields are lost when you run the game. You can rectify this if the data is all serializable types, and you mark the fields with the SerializeField attribute.

    Code (csharp):
    1. [SerializeField]
    2. private SomeSerializableType someVriable;
    Grids are not serializable. What we do is store all the cells in an array, and just reasign them into the grid (which we recreate on start).

    Let me know if this makes sense!
     
    twobob and DMeville like this.
  27. DMeville

    DMeville

    Joined:
    May 5, 2013
    Posts:
    418
    @Herman Tulleken: Awesome, I had spent a good hour fiddling with trying to create a custom grid builder, or grid behaviour and just stumbling around - But you explaining just throwing it into an array made what I wanted to do work in 5 lines of code! You rock!

    Just to ensure this isn't problematic, and for reference (as I hate finding half answered questions on forums when I'm googling something..)
    1 - Build grid in editor (using code I posted earlier), throw all the cells into an array at the same time I'm throwing them into the grid.
    2 - On start:

    Code (CSharp):
    1.  grid = RectGrid<TerrainCell>.Rectangle(width - 1, height - 1);
    2. foreach (RectPoint p in grid) {
    3.      grid[p] = _editorCells[p.Y*grid.Width + p.X];
    4. }
     
  28. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    The only thing I would change, is the way you access the array. You are good to go if your grids are always rectangular, but the code will break for any other shape. When you save it into the array, use this:

    Code (csharp):
    1. _editorCells = grid.Values.ToArray();
    And then read it back in like this:

    Code (csharp):
    1. int i = 0;
    2.  
    3. foreach(var point in grid)
    4. {
    5.    grid[point] = _editorCells[i];
    6.    i++;
    7. }
    This is robust against changes in grid shape and internal presentation. (At design time, of course, not runtime).
     
    DMeville likes this.
  29. lend

    lend

    Joined:
    Aug 7, 2014
    Posts:
    13
    That makes sense,thanks!!
     
  30. delinx32

    delinx32

    Joined:
    Apr 20, 2012
    Posts:
    417
    I came here to ask a similar question about serialization for a custom editor that I'm writing. This answer helped me with that and I now have most of my serialization working, but I'm having a problem with RectPoint being serialized. I'm not sure if this is the right place to ask the question, but maybe it is so I'll try. I have a class that looks like this:

    Code (CSharp):
    1. [Serializable]
    2. public class MapCell
    3. {
    4. ...
    5.  
    6.     public RectPoint SectorPoint;
    7.     public RectPoint RoomPoint;
    8.  
    9.     public RectPoint SeedPoint;
    10. }
    For somereason the rectpoints are not being serialized/deserialized and I'm not sure why. Is there any trick that I need to get this to work?

    I did fix this by adding some vectors to serialize from/to, but it feels very hacky and I'd rather not do it:

    Code (CSharp):
    1.  
    2.     public void OnAfterDeserialize()
    3.     {
    4.         SectorPoint = new RectPoint((int)SectorPointV.x, (int)SectorPointV.y);
    5.         RoomPoint = new RectPoint((int)RoomPointV.x, (int)RoomPointV.y);
    6.         SeedPoint = new RectPoint((int)SeedPointV.x, (int)SeedPointV.y);
    7.     }
    8.  
    9.     public void OnBeforeSerialize()
    10.     {
    11.         SectorPointV = new Vector2(SectorPoint.X, SectorPoint.Y);
    12.         RoomPointV = new Vector2(RoomPoint.X, RoomPoint.Y);
    13.         SeedPointV = new Vector2(SeedPoint.X, SeedPoint.Y);
    14.     }
    15.  
     
    Last edited: Mar 28, 2015
  31. Korigoth

    Korigoth

    Joined:
    Jul 21, 2014
    Posts:
    105
    Just to tell you Herman that i like your product a lot :)

    the learning curve is hard to follow at start, the documentation is trying to teach to much thing in 1 example that you lose focus on what you need to learn from the example! ;) But when you start understanding the way it's done, it's easy.

    I'm doing my gamelogic prototype for the next 2 month then i will enter in the real documentation on production!

    Thx a lot :)


    Some questions about AStar algorithms that i would like to understand! :)

    1- What's the difference between AStar and AStar2 ?

    2- Do you have an example of neighborToNeighbor point (last parameters in AStar) that is different of (p, q) => 1 or any static number and tell me why it is used like this ? I'm not sure to understand really well this parameter else than telling how it cost to move from one cell to another one?

    Thx, continue the great work!
     
  32. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    RectPoints and other grid points are structs, and Unity only recently provided support for serializing structs. So we provided a serializable class that can be used exactly how you use Vector2, except that you can construct a new InspectableVectorPoint from a RectPoint, and it has a method for getting a rect point. It is also visible in the inspector.

    We will probably make all points serializable in the future.
     
  33. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    1. AStar2 is a method that was accidentally left in the code. We used it to do some performance benchmarks. It will be removed in the next update.

    2. Indeed, it is the cost. Have you seen this tutorial on AStar?

    http://gamelogic.co.za/grids/docume...-start-tutorial/path-finding-grids-for-unity/

    There we give some more examples of how to use that parameter.

    As one example, you could use (in a RectGrid)

    Code (csharp):
    1. (p, q) => Mathf.Sqrt(p.X*p.X + p.Y*p.Y)
    This is the normal Euclidean distance between points, and will give different values for diagonal neighbors and orthogonal neighbors.

    -------------

    I am glad you like the product :) Please think of rating it if you haven't ;)
     
    Last edited: Mar 31, 2015
  34. Ian_Suffix

    Ian_Suffix

    Joined:
    May 16, 2014
    Posts:
    5
    Wow, it's almost been a year since I first started using Grids! I really appreciate all the effort that's gone into making sure Grids compiles for any platform.

    Now that I've got a lot more experience, I'm starting to get into optimizations and the minutiae of C#. Recently, I tried a multi-threaded method for pathfinding that makes use of Algorithms.AStar, but sadly, the thread seems to hang up whenever AStar is called. I'm thinking it's because the use of IGrid isn't thread-safe... or something.

    Incidentally, one Robin_B asked about how easy it might be to multi-thread pathfinding with Grids, and at the time, Herman, you said that it was a game-specific problem and that the generic solution was the most easily adaptable. I don't disagree, but now that I'm working on a project that would no doubt greatly benefit from threaded pathfinding, with multiple actors on a 1000-2000 cell grid, I kind of wish you would have left a hint as to how to face this problem.

    Have any ideas as to how to accomplish this (maybe using a snapshot of the grid when the pathfinding thread is initialized)? Or is the threaded approach just not viable on Unity?
     
  35. CChancey

    CChancey

    Joined:
    Mar 31, 2015
    Posts:
    2
    Hey Herman!

    Sent you an email last week and again today, that package would really help me out! XD

    Thanks,

    - Chancey
     
  36. Herman-Tulleken

    Herman-Tulleken

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

    I sent it to you, but it must have been blocked somewhere in the mail (it happens sometimes that unity packages don't go through). You can try this download link:

    http://www.gamelogic.co.za/downloads/PathFinding.unitypackage

    Let me know if you have any problems.

    Kind Regards,

    Herman Tulleken
     
  37. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    OK, just to be clear, are you talking about creating a separate .Net thread, and then setting this thread loose to calculate the path? (And not suspending calculations over several frames using coroutines).
     
  38. CChancey

    CChancey

    Joined:
    Mar 31, 2015
    Posts:
    2
    Super! Exactly what I needed, thank you very much!
     
  39. Ian_Suffix

    Ian_Suffix

    Joined:
    May 16, 2014
    Posts:
    5
    I did it! Turns out I was having more trouble passing IGrid and a callback delegate to the thread initializer than I was with the actual AStar function.

    But for future reference: that's exactly what I'm talking about. No Coroutines! Those aren't truly threaded, apparently. I was inspired by (partially plagiarized) a basic RTS project I found on the Internet which has a GetPathThread class which inherits from a ThreadedJob abstract class to calculate a path on a separate .Net thread. The AStar method the project uses isn't particularly different from yours, so I was thinking that with a bit of tweaking, I'd be able to introduce the same functionality to my own project without having to create my own AStar method. As it turned out, things weren't that easy, but it was entirely my fault. My own knowledge of threads/delegates was deficient.

    EDIT: I forgot to mention this, but the state of the grid may change over the course of the AStar path calculation. I plan on verifying the path in the main thread once the path has been calculated.
     
    Last edited: Apr 1, 2015
    Herman-Tulleken likes this.
  40. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Hi

    I've been getting off a to a decent start with this but I've hit a snag with the pathfinding (I think I am too spoiled from Aaron Granbergs A*).

    I'm finding the documentation on the pathfinder too light.

    Could you share a code example of a unit moving across a grid in response to say a cell being clicked?

    My biggest issue is (effectively) fetching the start and end points to calculate the path off of.

    If I could see an example of how you basic pathfinding it would make a world of difference for me.

    Thanks!

    Here is my best attempt currently of a path finder. GridCell just extends SpriteCell. My biggest issue is getting the points from the cells effectively. I KNOW I'm being inefficent here. I'd love to be able to go right from a units position to its grid position and visa versa much more effectively.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using Gamelogic;
    5. using Gamelogic.Grids;
    6. using System.Collections.Generic;
    7.  
    8. public class Pathfinder: MonoBehaviour
    9. {
    10.     const float MIN_DISTANCE = 0.1f;
    11.  
    12.     private IGrid<GridCell, PointyHexPoint> _grid;
    13.     private IEnumerable<PointyHexPoint> _path;
    14.     private List<Vector2> _pathList;
    15.     private int _pathIterator;
    16.     private PointyHexPoint _startPoint;
    17.     private PointyHexPoint _endPoint;
    18.  
    19.     private float _speed = 1.0f;//refactor this mofo
    20.  
    21.     // Use this for initialization
    22.     void Start ()
    23.     {
    24.         _grid = GameGrid.GetGrid();
    25.         _pathList = new List<Vector2>();
    26.     }
    27.  
    28.     public void MoveToCell(GridCell cell)
    29.     {
    30.         foreach(var point in _grid)
    31.         {
    32.             if(Vector2.Distance(transform.position,point) < MIN_DISTANCE)
    33.             {
    34.                 _startPoint = point;
    35.             }
    36.  
    37.             if(Vector2.Distance(cell.transform.position,point) < MIN_DISTANCE)
    38.             {
    39.                 _endPoint= point;
    40.             }
    41.         }
    42.  
    43.         var path = Algorithms.AStar(
    44.             _grid,
    45.             _startPoint,
    46.             _endPoint,
    47.             (p,q) => p.DistanceFrom(q),
    48.             c => true,
    49.             (p,q) => 1
    50.             );
    51.  
    52.         foreach(var p in path)
    53.         {
    54.             _pathList.Add(new Vector2(p.X,p.Y));
    55.         }
    56.  
    57.         _pathIterator = 0;
    58.     }
    59.  
    60.     // Update is called once per frame
    61.     void Update ()
    62.     {
    63.         if(_pathList.Count == 0) return;
    64.         Vector2 lastPoint = _pathList[_pathList.Count - 1];
    65.         Vector2 currentPosition = transform.position;
    66.         if(Vector2.Distance(currentPosition,lastPoint) > MIN_DISTANCE)
    67.         {
    68.             if(Vector2.Distance(currentPosition,_pathList[_pathIterator]) > MIN_DISTANCE)
    69.             {
    70.                 _pathIterator++;
    71.             }
    72.  
    73.             if(_pathIterator > _pathList.Count) _pathIterator = _pathList.Count;
    74.         }
    75.  
    76.         Vector2 pointToMoveTo = _pathList[_pathIterator];
    77.         Vector2 direction = (currentPosition - pointToMoveTo).normalized;
    78.         Vector2 distance = direction*_speed*Time.fixedDeltaTime;
    79.         transform.position += (Vector3) distance;
    80.     }
    81. }
     
    Last edited: Apr 2, 2015
  41. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    First, some resources.

    Here is the path finding tutorial:

    http://gamelogic.co.za/grids/docume...-start-tutorial/path-finding-grids-for-unity/

    and the code that goes with it:

    http://www.gamelogic.co.za/downloads/PathFinding.unitypackage

    (It's not published on the web site because it may be slightly out of synch. We have been working hard on an update that should get everything into synch. So you may need to tweak it to compile.)

    Then: when using Grids, there is one important concept that makes it easier to think about code. Grids are like arrays or dictionaries, and points are indices or keys, and cells are the values.

    So cells generally don't know their grid points (the same way elements of an array don't know their array positions).

    All your (geometric) logic should be in terms of grid points, not cells or world points. So you would have a method MoveToPoint(PointyHexPoint point) rather than a method MoveToCell. Mouse input should be handled centrally for the whole grid, not by cells. So you will have a handler that detects a click, gets the world point of the click, and then you use a map to convert to a grid point.

    For 2D grids GridBehviours already have a OnClick method that you can override. The example in the package shows how you can use this.

    ----

    More generally, how do you get points?

    From user input. The user does something such as click on a cell. This gives you a world point of action. World points can be converted to grid points using maps.

    Code (csharp):
    1. var clickedGridPoint = Map[clickedWorldPoint];
    Form the grid. You can iterate over the grid to get all points, or sample the grid for a subset of points.

    Code (csharp):
    1.  foreach(var gridPoint in Grid) DoSomething(gridPoint);
    2.  
    3. var randomPoint = Grid.RandomItem();
    4.  
    From operations / algorithms. For example, you can find the neighbors of a point in a grid, or find the point to the "east" of a given point, or find the path between two points.

    Code (csharp):
    1. var gridPoint = anotherGridPoint + RectPoint.East;
    2.  
    3. var neighbors = Grid.GetNeighbors(gridPoint);
    ----

    I hope this all makes sense. Once you get it and start thinking this way, everything becomes neater and simpler.

    Let me know if you have more questions or if I can clear anything up.

    Edit: Rereading your post and looking at your code it makes me think that maybe you don't know about maps. Maps are what converts between grid points and world points. Each grid type has one or more maps (hex grids have two, for example. The PointyHexMap for cells that are hexes, and the PointyBrickMap for cells that are rectangles.)

    Inside a grid behaviour, you can use the Map property:

    Code (csharp):
    1.  
    2. var gridPoint = Map[worldPoint]; //worldPoint is a Vector3, gridPoint is a PointyHexPoint
    3.  
    or vice versa
    Code (csharp):
    1.  
    2. var worldPoint = Map[gridPoint];
    3.  
    Check out these links for more information:
    http://gamelogic.co.za/grids/docume...k-start-tutorial/working-with-gridbehaviours/

    http://gamelogic.co.za/grids/documentation-contents/quick-start-tutorial/working-with-maps/
     
    Last edited: Apr 2, 2015
    Aggressor and twobob like this.
  42. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Great answer thank you. Im going to digest all this and get it working tonight. EXCITING :)
     
  43. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    hi.
    I read your post above and have this system

    I note your example on working with a sub-part of a larger grid and wondered if it would be suitable for my needs


    I have a simple x y setup that are tiles

    upload_2015-4-3_3-21-11.png

    I would like to project my (33x33) 256m2 tiles "as" the grid tiles in the world. And only show a partial (3x3) set of them,
    It seems like I should be able to work this out from welding together a few of your examples

    I have no doubt that this will be a much sought after solution so I don't feel too bad asking for help.

    Many thanks.

    (The world 0,0 being central, bottom left, top right, really doesn't matter so I can avoid negative cell values, it was just an illustrative example of the 3x3's internal logic)
     
  44. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Going through your example pathfinder code I find one thing I cannot make sense of:

    Code (csharp):
    1. walkableGrid = (PointyHexGrid<WalkableCell>) Grid.CastValues<WalkableCell, PointyHexPoint>();
    How are you casting something that returns a <TCell,TPoint> to just a <TPoint> object?

    Why did you do this instead of having both the cell and point?

    How are you even able to cast like this? Very interesting but I've seen this before haha!

    P.S. I have sent emails to support at the start of the week with no reply.
     
    Last edited: Apr 4, 2015
    Herman-Tulleken likes this.
  45. TobiUll

    TobiUll

    Joined:
    Feb 22, 2015
    Posts:
    73
    I am kind of disappointed right now after several days of trying out Grids Pro.
    What I am missing is the possibility to assign a cellwidth and cellheight to a cell and the possibility to set a foreground image with scaling option (keep aspect, alignment, etc.) and a text like this:



    The blockslider demo scene is not sufficient to do this.
    I would have to be a really good programmer to achieve something like this.
    I hope that the author of Grids Pro will at some time include something like this.

    ps: Yes, I have also sent mails to the author without getting a reply.
     
  46. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    If I have an object which is not 'attached' to the grid (yet) is there a simple way to find the closest cell that the object is resting on? I.e. based on a position that is not directly on a cell, find the cell its closest to.
     
  47. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    There was some mention of out-of-grid points somewhere back in the history of this thread (or it may have been the official support forum?) , Based around figuring out where you are in screen space I suppose (guessing) you would have to necro it up I am afraid but I did see one once.

    This really could be an amazing system, wish I knew how to use it better.
     
    Last edited: Apr 5, 2015
    Herman-Tulleken likes this.
  48. TobiUll

    TobiUll

    Joined:
    Feb 22, 2015
    Posts:
    73
    @twobob Same here, although I am a programmer, I have a hard time seeing through this.
     
    Herman-Tulleken likes this.
  49. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    @twobob I agree this has a lot of potential. I've put about 2 work days into figuring out this pathfinding, the documentation definitely needs a boost!
     
    Herman-Tulleken likes this.
  50. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    I finally see how you have it setup so if I go Map[Vector3] I get a PointyHexPoint, and if I go Map[PointyHexPoint] I get a vector3.

    I've NEVER seen this type of dual accessing before.

    What design pattern is this?

    So interesting! :cool:

    My guess is this is a super large dictionary? But then clearly you've somehow custom override the accessor to check based on type (cool!). Wouldn't it make more sense to have a MapToPoints and a MapToVectors objects instead of 1 catchall map? Im certain I could be very wrong, but this 1 map connects all setup seems incredibly counter-intuitive, whats the logic behind 1 map vs 2?

    I am having trouble getting the starting point though.

    I have an object resting on the Grid and it seems to have a hard time selecting the closest grid point (even though its sitting on one, it picks the one beside it):

    As the picture shows in the debug log, it has a starting point at -7.1 (world space) and yet the box is resting at -5.3 (world space). If you look at the second printout, you'll see the 2nd path node is showing up a -5.2. So the box was almost resting on it but it picks the grid beside it.



    My code for this is as follows:
    Code (csharp):
    1.  
    2.     public void MoveToPoint(PointyHexPoint gridPoint)
    3.     {
    4.         if(_grid == null)
    5.         {
    6.             _grid = GameGrid.grid;
    7.             _map = GameGrid.map;
    8.         }
    9.  
    10.         _startPoint = _map[transform.position];
    11.         Debug.Log("Start point is " + _grid[_startPoint].transform.position + " vs the transforms position " + transform.position);
    12.  
    13.         _endPoint = gridPoint;
    14.        _path = Algorithms.AStar(
    15.             _grid,
    16.             _startPoint,
    17.             _endPoint,
    18.             (p,q) => p.DistanceFrom(q),
    19.             c => true,
    20.             (p,q) => 1
    21.             );
    22.  
    23.         if(_path == null) return;
    24.  
    25.         foreach(PointyHexPoint point in _path)
    26.         {
    27.             if(_grid[point] == null) continue; //TODO this shouldnt even need to be checked
    28.             GameObject pathNode = Instantiate(_circlePrefab) as GameObject;
    29.             pathNode.transform.parent = _grid[point].transform;
    30.             pathNode.transform.localPosition = new Vector3(0,0,0);
    31.             _circlePrefabs.Add(pathNode);
    32.         }
    33.     }
    34.  
    Im so close to having this working I can feel it!! :)
     
    Last edited: Apr 5, 2015