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

    krissebesta

    Joined:
    Oct 20, 2010
    Posts:
    16
    Thanks for the reply and the suggestions. Please let me know if/when you implement the third approach as it sounds like the easiest and most integrated. I'd rather wait for a cleaner solution (as "workarounds" usually have unforeseen limitations that you later run into and can be quite cumbersome). I'd like to easily be able to reference layers above and below the current layer if possible (ex: Which pieces are directly above and below square X7 and how many pieces are three squares away (in any direction, above, below, diagonally, etc) . Cheers! - Kris

     
    Last edited: Feb 1, 2014
  2. plcuist

    plcuist

    Joined:
    Aug 29, 2013
    Posts:
    15
    Hi,

    I receive the following exception on IOS :

    Code (csharp):
    1. ExecutionEngineException: Attempting to JIT compile method 'Gamelogic.Grids.GridExtensions:<GetNeighbors`2>m__1AE<BlockGridCell, Gamelogic.Grids.RectPoint> (Gamelogic.Grids.RectPoint)' while running with --aot-only.
    2.  
    3.   at Gamelogic.Grids.GridExtensions+<GetNeighbors>c__AnonStoreyEC`2[BlockGridCell,Gamelogic.Grids.RectPoint].<>m__1AB (RectPoint neighbor) [0x00000] in <filename unknown>:0
    4.   at System.Linq.Enumerable+<CreateWhereIterator>c__Iterator1D`1[Gamelogic.Grids.RectPoint].MoveNext () [0x00000] in <filename unknown>:0
    5.   at BlockGridLightOut.ToggleCellAt (RectPoint gridPoint) [0x00000] in <filename unknown>:0
    6.   at BlockGridLightOut.ProcessMouse () [0x00000] in <filename unknown>:0
    7.   at BlockGridLightOut.Update () [0x00000] in <filename unknown>:0

    Any idea ?

    Thanks.
     
  3. JimArtificer

    JimArtificer

    Joined:
    Feb 2, 2014
    Posts:
    8
  4. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hi J1M.

    We use what that article calls axial coordinates augmented with a Z coordinate so that X + Y + Z = 0, but with the difference that the axes are in "closed" position rather than "open". (This means our Z-coordinate corresponds with the original system's Y coordinate).

    (I did a write-up here: http://devmag.org.za/2013/08/31/geometry-with-hex-coordinates/ )

    The system is very similar to the cube coordinate system, except that the X and Z axes are swapped, and there is a sign change. It is easy to add an extension to make a cube point if you needed to:

    Code (csharp):
    1. public static class PointExtensions
    2. {
    3.     public static CubePoint AsCubePoint(this PointyHexPoint point)
    4.     {
    5.         return new CubePoint(X, Z, -Y);
    6.     }
    7. }
    Of course, you'd have to define your cube point. Of course, you can also re-use PointyHexPoint instead, but then some algorithms (such as distance) will give the wrong results.

    Me recommendation would be that if you want to implement an algorithm expressed in cube points, to just make the change in the algorithm itself, and stick with PointyHexPoints (allowing you to take advantage of overloaded operators and many math functions defined for it).

    And of course, if there is an algorithm that you feel should be part of the library proper, let us know so that we can investigate it. We are always on the lookout for new useful things to add.
     
  5. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    @plcuist

    Hmm it looks like you may have stumbled on another AOT compiler limitation.

    What is hapenning is that the static compiler cannot figure out it needs to generate that method, and when it is called on device, (because the JIT compiler does not work on iOS), it crashes.

    The general way out of this is to give the compiler an explicit hint that it needs to generate the method. It is usually generics with value types that confuse it. In this case, you you make an explicit call to

    Code (csharp):
    1. var neighbors = grid.GetNeighbors<BlockGridCell, RectPoint>(somePoint); //DO specify the type parameters
    It should work. I will replicate it on my side, and just confirm.
     
  6. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    We are looking into this. It's seems it would be fairly rough to do, so it won't happen too soon, but we will let you know of our progress if we go this route.
     
  7. JimArtificer

    JimArtificer

    Joined:
    Feb 2, 2014
    Posts:
    8
    Thanks for the quick response. :)

    What about different grid elevations? Does Grids 1.7 support projecting any grid in an isometric manner (or just diamonds) with varied heights for each cell?

    ie. http://www.artfromcode.com/wp-content/uploads/2008/08/isometric_waves_01.png
     
  8. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    No problem. We will also be adding a roadmap to our site soon too, as we have quite a bit of features coming.

    One thing that I want to mention (not to disagree with your position) is that we have designed grids with the very idea that users can define their own structures. One of the reasons that we can whip up a new grid or grid map is that it is easy to integrate it into the generic system. We should definitely add some tutorials in this direction.

    Diamond grids were originally called Isometric grids, until we realised that there are so many ways different grids can be used for isometric projections (including tri, rhombille, hex). So indeed, you can use any of those grid types.

    For the image you linked to, I would just use an ordinary RectGrid, which is placed in 3D space, and use 3D blocks.

    If you *do* want to use varried elevations with 2D tiles, you can do this too; perhaps something like this

    http://www.lostgarden.com/2013/10/prototyping-challenge-3d-modeling-tool.html

    or this

    http://www.lostgarden.com/2006/12/sprawl-worlds-first-public-wpfe-game.html

    There are may ways to approach this with grids. The way I would tackle it

    • Make my own cell, that contains a height. The cell, given a "base point location", can work out how to place the tile based on the current cell height (and switch to the right image too, if you want the cell to automatically handle that).
    • It would probably be a good idea to to make your own IMap3D (use Map3DXY to base it from, and just calculate a suitable Z) to make sure tiles are drawn in the correct order. (You could of course also let the cell handle that if you want to use layers instead of Z coordinates).
    • The last bit may be a bit tricky, and that is how to handle clicks on elevated cells. One way would be to use colliders instead of the map to determine which cell has been clicked on. An alternative is to give the map full access to the grid (so that it can get height information from cells), and implement a (tricky, I would imagine) algorithm for figuring out the right cell (something like checking from the bottom up whether the cell could extend to the click point).


    -----

    @plcuist, It looks like there is another issue. I am investigating it at the moment (I have two projects, it works in the one and not in the other, I just need to track down what is different). Will let you know soon.
     
    Last edited: Feb 2, 2014
  9. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    We found the issue, it looks like a bug that were introduced when we added several GetNeighbor functions. We will add the fix in the next update, in the meantime, you can get it up and running by making the following small change:

    In the file GridExtensions.cs, find the GetNeighbors function on line 98 that looks like this:

    Code (csharp):
    1.  
    2.         /**
    3.             Only return neighbors of the point that are inside the grid, as defined by IsInside.
    4.         */
    5.         public static IEnumerable<TPoint> GetNeighbors<TCell, TPoint>(
    6.             this IGrid<TCell, TPoint> grid,
    7.             TPoint point)
    8.  
    9.  
    10.             where TPoint : IGridPoint<TPoint>
    11.         {
    12.             return grid.GetNeighbors(point, (TPoint p) => true);
    13.         }
    14.  
    and replace it with this:
    Code (csharp):
    1.  
    2.         /**
    3.             Only return neighbors of the point that are inside the grid, as defined by Contains.
    4.         */
    5.         public static IEnumerable<TPoint> GetNeighbors<TCell, TPoint>(
    6.             this IGrid<TCell, TPoint> grid,
    7.             TPoint point)
    8.  
    9.  
    10.             where TPoint : IGridPoint<TPoint>
    11.         {
    12.             //return grid.GetNeighbors(point, (TPoint p) => true);
    13.             return
    14.                 from neighbor in grid.GetAllNeighbors(point)
    15.                 where grid.Contains(neighbor)
    16.                 select neighbor;
    17.         }
    18.  
     
  10. plcuist

    plcuist

    Joined:
    Aug 29, 2013
    Posts:
    15
    Thanks a lot. This is working great.
     
  11. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    That is good to hear. Playmaker is very popular and quite powerful. I am sure you would gain a whole new group of buyers if you support it.

    When you get around to it, please consider using me as a beta tester. I have professional experience beta testing software.

    New question:

    I am trying to get through the samples. It is a bit tough since I am learning C# as I go along. However, one of the things that is confusing me is that the samples use NGUI and I am not familiar with NGUI. I use Daikon Forge and am unable to determine how to "port" the examples from NGUI to Daikon Forge.

    Is it possible for you to take one or more of the samples and create versions that use Daikon Forge? Or at least tell me what I need to do to get your samples working with Daikon Forge.

    Thank you.
     
  12. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Thanks, that would be great!

    I am not familiar with Daikon Forge's setup, but I can have a look at it.

    In the meantime, I can give you some general suggestions if you want to give it a go:

    (It's possibly easiest to test it with RectTest, so I will explain in terms of that.)

    You basically need to make four changes:

    1. Find a suitable replacement for a sprite. Take the RectCell prefab, and replace the UISprite's (they are children) with the appropriate component. Now modify the Cell script to work with this new component instead of UISprite. It should be fairly straightforward.
    2. In the RectTest script, replace ExampleUtils.ScreenToWorld_NGUI with just ExampleUtils.ScreenToWorld. NGUI uses it's own camera, and a weird scaling in some of the root transforms. If Daikon Forge also uses it's own camera, you can create a function similar to the NGUI one.
    3. In the scene, remove all the UI Root (2D) game object, and use either a simple empty GameObject (placed at the origin, scaled to (1, 1, 1)), or whatever parent Daikon Forge sprites require. Add a camera as too.
    4. On the RectTest object, put the object you created above as the GridRoot.

    I'd imagine any change to be along those lines, but I will confirm this and get back to you.
     
  13. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    I am unsure what to use for MarkAsChanged().

    Here is my code so far: http://tinja.ws/1kO8LGP

    Here is the doc for dfSprite: http://tinja.ws/1kO854j

    BTW If you don't own a copy of Daikon Forge, get it now while it is on sale for 65% off: http://tinja.ws/Lwa8Nl

    update:: I am also having difficulty duplicating your prefabs that have UISprites as children as I cannot directly substitute a dfSprite as it requires a GUI Manager as a parent.
     
    Last edited: Feb 4, 2014
  14. murteas

    murteas

    Joined:
    Feb 14, 2012
    Posts:
    62
    Another vote for Playmaker. I'm very interested in Grids and would love to get it, but I think playmaker custom actions would be a great addition to the package.
     
  15. jmatthews

    jmatthews

    Joined:
    Jul 27, 2011
    Posts:
    199
    Is there an easy way(without doing a deep copy) of moving the contents of one cell to another?

    I've checked the API but given what you have to work with I'm not seeing any type of function that will accomplish it. I can grab a cell and do a deep copy and then swap it with the other.


    wow this is frustrating, when you move a cell locally the neighbor system seems to break. I'm also having issues telling the grid about the move.

    sent an email and Herman fixed the issue in seconds. An embarrassing error on my part but kudos to him.
     
    Last edited: Feb 5, 2014
  16. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,462
    Like many people, I also switched from NGUI to Daikon because its a really well designed GUI framework.
    Please add support for Daikon in a future update.

    Cheers.
     
    Last edited: Feb 5, 2014
  17. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    @Tinjaw, @rocki

    Grids seems to work quite well with Daikon Forge.

    I did a quick example. Please note, this may not be idiomatic Daikon Forge GUI. I am also not sure whether this is the best way to do things. This is a bare-bones example (it uses sprites straight, and does not use Cells with additional data). We will put a fuller example on our site later this week, but for now, this will get you going:

    Code (csharp):
    1.  
    2. using Gamelogic.Grids;
    3. using UnityEngine;
    4.  
    5.  
    6. public class DFGridTest : GLMonoBehaviour
    7. {
    8.    public dfPanel spritePanel;
    9.  
    10.  
    11.    public void Start()
    12.    {
    13.       var spriteSize = new Vector2(128, 128);
    14.       var grid = PointyHexGrid<dfSprite>.Hexagon(3);
    15.  
    16.  
    17.       var map = new PointyHexMap(spriteSize)
    18.          .AnchorCellBottomLeft()
    19.          .WithWindow(new Rect(0, 0, spritePanel.Width, spritePanel.Height)) //the parent panel width
    20.          .AlignMiddleCenter(grid)
    21.          .To3DXY();
    22.  
    23.  
    24.       foreach (var point in grid)
    25.       {
    26.          var cell = spritePanel.AddControl<dfSprite>();
    27.  
    28.  
    29.          cell.SpriteName = "placeholder-text";
    30.          cell.Size = spriteSize;
    31.          cell.RelativePosition = map[point];
    32.       }
    33.    }
    34. }
    35.  
    $DaikonForgeAndGrids.png

    As you can see, we simply add dfSprites to the dfSpritePanel, using the map that centers the grid in the panel.

    If it is not possible to make controls that encapsulate sprites, you can always store additional data in another grid. (The method CloneStructure defined for grids is precisely intended for this use case).

    Let me know if this is a good start. You may need to play with sizes a bit for actual hex sprites, and I am also not 100% sure how the anchoring and layout works. (Also let me know if I made a grievous error in how Daikon Forge should be used!)

    (I simply used the basic sprite example that ships with Daikon Forge. I added this script on an empty GameObject, linked in the sprite panel, and deleted all other controls on that panel).

    ----

    If you are porting Grids' Cell component, do not worry about MarkAsChanged - that is an NGUI thing. As far as I can tell, Daikon Forge does not have (or need) an equivalent.
     
    Last edited: Feb 5, 2014
  18. Posix337

    Posix337

    Joined:
    Dec 20, 2013
    Posts:
    2
    Just wanted to drop a note of thanks for the Daikon Forge Example. I will be looking forward to the expanded sample as well.

    I own Grids, DF-GUI, and Playmaker (+1 more vote for this direction, where applicable, I see potential good value in State Machine/Grid mash-ups).

    I have been working on integrating Grids into my "Workbench", with initial success (i.e. I can display my grid), and have the entire grid in a prefab and can dynamically load a "Game Map" based on the grid. I can also re-assign the sprite dynamically as to be Pool Friendly™ (at least in my head).

    I decoupled from NGUI when I brought the sample code in as a start. For the "cell" I went the route of just using a regular Unity Sprite. I have a DF-GUI camera for UI and a game camera which is the "main camera" for the scene. This allows the UI and Grid to be separate.

    I am not sure if I'm going to need any UI elements (except maybe for debugging?) for the individual cells yet. If I do, it would be on a limited (per cell pop-up info) basis. The remainder of the UI will be static, or dialog.

    I'm wondering if I might be making things more difficult by this choice. Am I loosing anything significant by going this route of UI / Grid separation?

    Thanks for the cool framework, and for any thoughts!
     
  19. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    @Posix337

    My thoughts are that no, you are not loosing anything; the extra separation may actually be beneficial. Having game data in a grid, without any references to UI, makes it for instance easier to save levels (or indeed, serialize data). It also gives you more flexibility in adapting your display - perhaps having multiple views for the same data, or changing your GUI to another framework altogether.

    The (very simple) Cell component we ship with grids was really meant to be a throwaway type, so it was not engineered with these possibilities. I always thought people will aggressively design and implement their own Cells to suit their needs; it took me by surprise when I realized it was actually used in many projects.
     
  20. barjed

    barjed

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

    I've bought your Grids during the Store Sale and I find it a really wonderful tool. I was hoping for a small tip from a pro that would be really helpful with my current project. The idea is that you have a scene with a plane and you can place various objects on it that snap on the grid - think tycoon games. The plane is supposed to be around 150 x 150 rectangular tiles large. I read a lot of stuff in the internet on the subject and it seems the general recommendation is to use a texture instead of tiles and snap position by doing some math. The whole grid logic does seem rather simplistic here, because it's only snapping and checking for valid positions. Would you recommend doing this the more complex way with your Grids or sticking to the simple stuff?

    But just to be sure - for my given example, which has a 3D plane and the grid should be a 2D one that's applied over it I would have to create my own cell type that gets a plane prefab with a texture set and then creates a grid out of it in the XZ axes? This should be somewhat similar to the 3D RectWorld example but without the height and with planes instead of cubes. I am a bit worried about performance here.

    Also, I can't wait for the 1.8 :).

    Cheers!
     
    Last edited: Feb 9, 2014
  21. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    I would definitely do it with Grids :) Given that, you have two choices: use a single mesh / texture, or sprites. For big grids, I would also recommend going the single mesh / texture route; it makes rendering very fast. I would also limit what gets rendered in the first place.

    We have two examples that solves these problems very neatly for you:

    A Quick Hex Mesh Generation Script (with Proper Texturing)

    This shows how to generate a textured mesh with a texture from a grid. The example uses a hex grid, but should be trivial to modify for a rectangular grid: replace the arrays with ones suitable for making quads instead of hexes, replaces PointyHexGrid with RectGrid, and replace PointyHexMap with RectMap.

    The second example shows how to render big grids efficiently using two grids: one big grid with all the data, and a smaller grid just for rendering.

    How to Render Only a Section of a Very Big Scrolling Grid

    Again, modifying it for RectGrid is very simple.

    To use the two ideas simultaneously, you need to make your own cell that will change the UVs based on the tile type.

    ----

    For snapping, you can define an extension method on IMap3D:

    Code (csharp):
    1. public static Vector3 Snap(this IMap3D map, Vector3 point)
    2. {
    3.    return map[map[point]];
    4. }
    ----

    Grids have many ways that it will save you time, even with rectangular grids. For me, the advantages are that you can process grids using LINQ (grid.Values.Where(cell => cell.HasWater)), and that you can do point calculations (point + 5*RectPoint.North).

    And once you get into the Griddy way of doing things, you will, I think, find it hard to imagine not using it. It just opens up the door to so much :) For example, if I made a game like yours, I'm thinking of how I would use grids at different resolutions to generate interesting terrain and AI behaviours; or how to use multiple maps and blend textures together; or how to make very attractive minimaps using the partial rendering technique...

    ----

    We are excited to get it out too :)
     
  22. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    Thanks, a very nice reply. I'll be sure to remember the quality of support when you release your next amazing Unity products ;)

    I just made a quick test - I whipped a texture in Photoshop, applied it to a Quad, made a Prefab out of it and created a 100 x 100 grid in my test scene. The number of draw calls only increases by one which makes me wonder if the single mesh is indeed needed for this. To be clear - my terrain is a separate object, the grid is only a simple overlay. What do you think?

    Also is there a easy way to center the grid around 0,0,0? Right now it gets anchored into 0,0,0 of the root object, so the only thing that comes to my mind is an offset prefabWidth * numberOfXCells + 0.5, which looks clunky.

    Thanks!

    edit: Aaand now it's taking a nosedive in the actual scene :(
     
    Last edited: Feb 9, 2014
  23. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    Okay, I tinkered some more, and here's what I've got:

    $7O6RieX.png

    Right now there's 7500 cells in the scene, each one a quad with a texture. When a player presses a button, the grid is rendered (foreach through all cells to enable their rendered, that's the best idea I could come up with). Draw call is increased by one, so it's fine. If there's performance issues I'll go with your second example (smaller grid for rendering only).

    However now I started to wonder about the shape generation. Obviously the shape of the grid should roughly map the available terrain (without the mountains and the sea) Do I understand correctly that, in order to make a "diagonal" grid edge, I have to keep unioning smaller rectangles?
     
  24. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    You can easily center your grid around a point using a trick. We should really add a proper method for that, but for now you can do the following:

    Code (csharp):
    1. var map = new RectMap(cellDimensions)
    2.    .WithWindow(new Rect(0, 0, 0, 0)) //use new Rect(x, y, 0, 0) to center around a point (x, y)
    3.    .AlignMiddleCenter(grid)
    4.    .To3DXZ();
    ----
    A single mesh is sometimes better; but if separate quads work for you that is good. (I think moving cells, and indeed creating cells can sometimes be prohibitive. Also, NGUI sometimes start to chug when you have too many sprites; so if you are not using NGUI that should be OK).

    Edit: I reread your post, and now I think much of that below (I marked it now with ####) is not the best method to solve your issue. I leave it there for I think it can be useful in other cases.

    For your example, I would say to use a rectangular grid, and either only fill cells that you require(or have a boolean to switch them on and off).

    When you generate the grid,

    Code (csharp):
    1. foreach (var point in grid)
    2. {
    3.    if(IsThisPartOfTheGrid(point))
    4.    {
    5.       var cell = Instantiate(...);
    6.       //other setup
    7.       grid[point] = cell;
    8.    }
    9. }
    When you work with your grid, you can use the following:

    Code (csharp):
    1. grid.Where(point => grid[point] != null) //gives all points that have non-null cells
    Or

    Code (csharp):
    1. grid.Values.Where(cell=> cell != null) //gives all cells that are not null
    You can of course save one or both of these to a List or array at the beginning if you want to.

    ####
    (Notes on rolling your own shapes).

    There are three shape generation methods;

    • the default static functions,
    • using the shape building syntax (where you can combine shapes using set operators), and finally,
    • rolling your own shape function.

    (You can also construct a new grid with your own test function, but the last method does that, and makes it work with the shape builder tech so you can also use combine it with other shapes using set operators if you wanted to).

    I think in this case, you'd probably use the last one of these. It's a little tricky, but fortunately rectangular grids are the easiest to do this for. There are two steps:

    First, define a function that takes a grid point and returns whether this grid point is inside the shape or not. Here is the function used to test for a Rectangle:

    Code (csharp):
    1. public static bool IsInsideRect(RectPoint point, int width, int height){
    2.     return point.X >= 0  point.X < width  point.Y >= 0  point.Y < height;
    3. }
    You can make a version that will define your own shape, let's say

    Code (csharp):
    1. public static bool IsInsideMyCoolShape(RectPoint point, /* any parameters you need*/){
    2.    return ...//true if the point is inside my cool shape.
    3. }
    The second step is to define an extension method for RectOp that constructs the info object:

    Code (csharp):
    1.  
    2. public static class RectOpExtensions
    3. {   [ShapeMethod]
    4.    public RectShapeInfo<TCell> MyCoolShape<TCell>(this RectOp<TCell> op, /* extra parms if needed*/)
    5.    {
    6.       int storageWidth = ...;
    7.       int sotrageHeight = ...;
    8.       RectPoint storageBottomLeft = ...;
    9.  
    10.  
    11.       return op.Shape(storageWidth, storageHeight, x=> IsInsideMyCoolShape(x, /* extra parms if needed*/, storageBottomLeft);
    12.    }
    13.  
    14. }
    15.  
    Calculating the storage parameters is the tricky bit. The width and height must be such that your shape can fit into it if a grid with those dimensions were generated.

    Say for example your shape is the five points (2, 2) and its four neighbors. Then the storage width and height will be 3.

    The storageBottomLeft is the bottom left point of the smallest rectangle that contains your shape (it's used internally to make sure your grid gets fitted tightly to an array; you don't need to worry about that though). If the shape is the five points above, then the bottom left will be the point (1, 1).

    Once you have done this, you can then use the folowing to construct your grid:

    Code (csharp):
    1. var grid = RectGrid<MyCell>.BeginShape().MyCoolShape(/* parms */).EndShape();
    Voilla :)
     
    Last edited: Feb 9, 2014
  25. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    So, if I understand this correctly, I should create a simple rectangular grid and then somehow determine if a cell is in a valid place? I am thinking of creating a primitive mesh outlining and then casting a ray from each cell position, see if it hits said mesh and if that's true, fill the cell. Right?
     
  26. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Yup; that should work.
     
  27. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    Fantastic, thank you for all your support :)
     
  28. redmondurbino

    redmondurbino

    Joined:
    Feb 15, 2013
    Posts:
    2
    Anyone have ideas on how to get around an aot error on iOS?
    My Unity version is at 4.3.4f1
    My Xcode version is at Version 5.0.2 (5A3005)
    My phone is an iPhone 4s
    And the Phone's iOS version is 7.0.4 (11B554a)
    I tried putting the FlatTriTest scene on an iPhone, and I got this error on startup:

    ExecutionEngineException: Attempting to JIT compile method 'Gamelogic.Grids.AbstractSplicedShapeInfo`5<Gamelogic.Grids.FlatTriShapeInfo`1<SplicedCell>, Gamelogic.Grids.FlatTriGrid`1<SplicedCell>, Gamelogic.Grids.FlatTriPoint, Gamelogic.Grids.PointyHexPoint, Gamelogic.Grids.FlatTriOp`1<SplicedCell>>:.ctor (Gamelogic.Grids.ShapeStorageInfo`1<Gamelogic.Grids.FlatTriPoint>)' while running with --aot-only.

    at Gamelogic.Grids.FlatTriShapeInfo`1[SplicedCell]..ctor (Gamelogic.Grids.ShapeStorageInfo`1 info) [0x00000] in <filename unknown>:0
    at Gamelogic.Grids.FlatTriOp`1[SplicedCell].Shape (Int32 width, Int32 height, System.Func`2 isInside, PointyHexPoint bottomLeftCorner) [0x00000] in <filename unknown>:0
    at Gamelogic.Grids.FlatTriOp`1[SplicedCell].ParallelogramXZ (Int32 width, Int32 height) [0x00000] in <filename unknown>:0
    at Gamelogic.Grids.FlatTriOp`1[SplicedCell].Hexagon (Int32 side) [0x00000] in <filename unknown>:0
    at FlatTriTest.BuildGrid () [0x00000] in /Users/urbir003/gamedev/Grids/Assets/GamelogicGrids/Examples/Scripts/BasicGridSetup/FlatTriTest.cs:43
    at FlatTriTest.Start () [0x00000] in /Users/urbir003/gamedev/Grids/Assets/GamelogicGrids/Examples/Scripts/BasicGridSetup/FlatTriTest.cs:23

    (Filename: /Users/urbir003/gamedev/Grids/Assets/GamelogicGrids/Examples/Scripts/BasicGridSetup/FlatTriTest.cs Line: 43)
     
  29. barjed

    barjed

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

    I've had some great success with your Asset - I even managed to implement a couple of my own PlayMaker actions, including snapping to the grid.
    However I noticed that the objects are snapped to the corners of cells instead of the centers. A reference:

    $K4xv8jQ.png

    The object snapped are Unity's Cube primitives (the blue ones, the character in the middle is controlled independently) - their origin is at (0,0,0). Also, I noticed that the object keeps snapping even if I move it away from the grid. Is there any way to snap objects to the middle and limit the snap to the edges of the actual grid?

    Thanks!
     
  30. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    @redmondurbino I sent you an email just now. For anyone else struggling with similar issues, there is an explanation (and solution) here:

    http://gamelogic.co.za/grids/docume...r-generates-code-to-instantiate-grids-on-ios/

    @barjed You should be able to solve that using proper anchoring for your cells.

    Code (csharp):
    1. var map = new RectMap(cellDimensions)
    2.   .AnchorCellTopLeft() //(orMiddleCenter, etc., depending on your setup)
    3.   //other methods in the chain
    4.   .To3DXZ();
    If you have trouble with this, post your entire map construction chain (sometimes, the order makes a difference).

    Edit: (sorry, forgot to answer your second question!)

    I'm not 100% sure how you want your snapping to work. If you mean you only want objects to snap once they are "close" to the middle, you can do that. For example:

    Code (csharp):
    1. var closestSnapPosition = map.Snap(obj.transform.position); //or localPosition as the case may be
    2.  
    3. if((closestSnapPosition - obj.transform.position).magnitude < snapRadius)
    4.    obj.transform.position = snapPosition;
    The trick is then of course to call this method only when needed (so not necessarily while dragging, but only after).

    If you want to snap to the edges (the lines) of the grids, it's slightly trickier.

    In this case, I would define methods that take two grid points, and return the edge world point between them.

    Code (csharp):
    1. Vector3 GetEdgeWorldPoint(RectPoint point1, RectPoint point2)
    2. {
    3.    var worldPoint1 = map[point1]; //center of grid cell at point 1
    4.    var worldPoint2 = map[point2]; //center of grid cell at point 2
    5.  
    6.    var edge = (worldPoint1 + worldPoint2) / 2; //Only works if the edges are adjacent
    7.    return edge;
    8. }
    Before calling this method, you have to figure out what snapping must happen in the first place. Let's assume you only want to snap when the object is close to an edge. There are four cases:


    • The object is far from any edge: no snapping.
    • The object is close to a vertical edge: snap horizontal position only
    • The object is close to a horizontal edge: snap vertical position only
    • The object is close to the intersection of two edges: snap to intersection

    To determine which case you are in, you could compare the object's distance with all four surrounding edges:

    Code (csharp):
    1. var objectGridPoint = map[obj.transform.position];
    2. var edgeNorth = GetEdgeWorldPoint(objectGridPoint, objectGridPoint + RectPoint.North);
    3. // and the same for the other three directions
    Now calculate the four edge distances:
    Code (csharp):
    1. var edgeNorthDistance = (edgeNorth - obj.transform.postion).magnitude;
    2. //and the same for the other three directions
    3.  
    Now you need to write a rather ugly if then statement to sort out the details. (Perhaps you can neaten it up if you repeat a bit of calculations):

    Code (csharp):
    1. bool IsCloseToIntersection(float edgeDistance1, float edgeDistance2)
    2. {
    3.    return
    4.       Mathf.Abs(edgeDistance1) < snapDistance
    5.       Mathf.Abs(edgeDistance2) < snapDistance ;
    6. }
    7.  
    8. bool IsCloseToEdge(float edgeDistance)
    9. {
    10.    return
    11.       Mathf.Abs(edgeDistance1) < snapDistance
    12. }
    Code (csharp):
    1. var snapPosition = obj.transform.position; //default
    2.  
    3. ////CASE 4
    4.  
    5. if(IsCloseToIntersection(edgeNorthDistance, edgeEastDistance))
    6.    snapPosition = new Vector3(eastEdge.x, snapPosition.y, northEdge.z);
    7.  
    8. //and the same for the other three intersections
    9.  
    10. ////CASE 2 and 3
    11. if(IsCloseToEdge(edgeNorthDistance))
    12.    snapPosition = new Vector3(snapPosition.x, edgeNorth.y, snapPosition.z);
    13.  
    14. //and the same for the other three directions
    15.  
    16. ////CASE 1 is the default
     
    Last edited: Feb 10, 2014
  31. barjed

    barjed

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

    I've tried every single Anchor method available and unfortunately objects still snap like in the picture I've attached above (they land on the intersection of 4 cells). Because they follow the mouse cursor around I can see different behaviors with different anchors but the object itself never snaps to the cell's center.

    As for the second question - what I meant is that if I have 10 x 10 grid, like in the example and I move my mouse cursor over the black area (out of the grid), the object still follows and snaps to cells that don't exist. So if I keep moving my cursor out of the northern border of the grid, the object should stop when it reaches the last row of cells in that direction.
     
  32. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    @barjed

    Oh, I see.

    I'm not sure what is going on with the anchoring; when I try it on my side it seems to work as expected. Can you post code of how you generate all your grids and maps?

    For the second question, all you have to do is check whether the grid point is in the grid, and only snap if it is:

    Code (csharp):
    1.  
    2. var gridPoint = map[obj.transform.localPosition];
    3.  
    4. if(grid.Contains(gridPoint))
    5. {
    6.    snapPoint = map[gridPoint];
    7.    obj.transform.localPosition = snapPoint;
    8. }
    9.  
    This means the cube will be left behind the last snapped grid position.
     
  33. barjed

    barjed

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

    the generation code is almost 1:1 to the RectWorld 3D example

    Code (csharp):
    1.  
    2.     private void BuildGrid()
    3.     {
    4.         grid = RectGrid<QuadCell>
    5.                  .Rectangle(10,10);
    6.        
    7.         map = new RectMap(new Vector2(1f, 1f))
    8.             .AnchorCellMiddleCenter()
    9.                 .To3DXZ();
    10.        
    11.         foreach(var point in grid)
    12.         {
    13.             var block = Instantiate(CellPrefab);
    14.             block.transform.parent = GridRoot.transform;
    15.            
    16.             block.transform.localPosition = map[point];
    17.             block.renderer.enabled = false;
    18.            
    19.             grid[point] = block;
    20.         }
    21.     }
    22.  
    The snapping script:

    Code (csharp):
    1.  
    2.     public void MoveSnap()
    3.     {
    4.         // transform mouse coordinates to world space and normalize the vector
    5.         Vector3 mouseWorldPosition = MainCamera.camera.ScreenToWorldPoint(
    6.             new Vector3(Input.mousePosition.x,Input.mousePosition.y,10));
    7.         // mouseWorldPosition.Normalize();
    8.  
    9.         // snap to the grid
    10.         mouseWorldPosition = GridManager.Snap(mouseWorldPosition);
    11.  
    12.         // place the object at a given height from the ground
    13.         mouseWorldPosition.y = BuildManager.BuildPlaceHeight;
    14.  
    15.         // move the object to the mouse
    16.         transform.position = mouseWorldPosition;
    17.     }
    18.  
    Cheers :)
     
  34. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Is the grid you have in the background generated or is it simply an image?
     
  35. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    It's generated. I took the Block from the example and swapped it for a Quad primitive with a texture.
     
  36. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    Okay, I found the culprit.

    The grid is 10 x 10, each cell is 1,1 in size. I centered the grid manually by moving the Grid Root to -4.5,-4.5 (0.5 to offset the cell's quad origin, which is 0,0,0). When I changed to Grid Root position to -5,-5 everything is fine, but now the grid is 0.5,0.5 offset from the floor (because of the cell's origin).

    Hmmm...

    edit: Okay, I brought the Root back to 0,0, applied the centering trick you mentioned earlier and now everything works fine. Thank you very much for all your help :)
     
    Last edited: Feb 12, 2014
  37. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Cool, glad you got it sorted :)
     
  38. miskomare

    miskomare

    Joined:
    Jan 14, 2014
    Posts:
    5
    Hi Herman,

    I'm also on of those that bought Grids on the recent sale:)

    I'm thinking about creating a turned based strategy on an isometric hex grid (using C#). I saw that you did all those games in Grids, so I would appreciate pointers on how to use it in my case. For example, what did you wrote your own or used existing level editor? Also, I was thinking to save levels in XML, hence any tips on serialization levels/grids and parsing XML?

    thanks!
     
  39. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hey miskomare,

    All our games used procedural generated levels (except for the odd one that had a hard-coded level).

    Whether a level editor will benefit your game depends a lot on your game. It's relatively easy to roll out your own very simple level editor using Grids (and it will be a lot easier with Grids 1.8. It becomes more difficult as you make it more and more sophisticated.

    I would say start of with something very crude; perhaps something that allows you to change and save your levels at runtime, and see how far this gets you, and build it systematically. (I would not spend too much time on level building tech before you have the main gameplay in). If you can get by with procedural levels to begin with, that is even better.

    The easiest way to save grid data is to simply save (serialise to XML or use another method) the array of values of the grid, like this:

    Code (csharp):
    1. var arrayToSave = grid.Values.ToArray();
    2. Save(arrayToSave);
    you can then simply read that into your grid to load:

    Code (csharp):
    1. var valuesArray = Load();
    2. var pointsArray = grid.ToArray(); //array of points
    3.  
    4. for(int i = 0; i++; i < pointsArray.Count())
    5.     grid[pointsArray[i]] = valuesArray[i];
    6.  
    7.  
    (The reason grids are not directly serializable at this stage is because of the complex shapes building technology.)

    Another option to investigate is ScriptableObjects - these can save data as part of your project. We did a few level-edit prototypes with that, and I like it.

    Otherwise, XML or JSON will work well. Whatever you do, don't write your own parser :p C# has XML parsing built-in, and there are at least one free JSON package you can use.

    Let me know if you have more questions!
     
    Last edited: Feb 13, 2014
  40. barjed

    barjed

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

    sorry to bring this back but here is the issue: the objects that needed to be snapped are spawned directly under mouse cursor, after a gui button is clicked - often outside of bounds of the grid. Is it possible to snap the object to the nearest edge of the grid instead of limiting it's movement from the inside?

    Thanks!
     
  41. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    No problem barjed :)

    It is possible :)

    I will give two methods; one is slightly optimized. Depending on the size of the grid, you may need even a faster version. (For about 10x10 the slow method will be fine).

    Method 1

    Code (csharp):
    1.  
    2. var worldPoints = grid.Select(point => map[point]);
    3. var closestPoint = worldPoints
    4.    .OrderBy(point => (point - clickedWorldPoint).magnitude))
    5.    .First();
    6.  
    Method 2
    Code (csharp):
    1. //in Start();
    2. cachedBorderWorldPoints = grid
    3.    .Where(point.GetNeighbors().Count() < 4); //works only for rect grids!
    4.    .Select(point => map[point]);
    5.  
    6. //somewhere else
    7. var closestPoint = cachedBorderWorldPoints
    8.    .OrderBy(point => (point - clickedWorldPoint).magnitude))
    9.    .First();
    10.  
    ----
    Both methods sort the list first, which may be a bit slow. If it is, you can get a speedup by just using a MinBy extension:

    Code (csharp):
    1. /**    Finds the minimum element in the element as scored by the given function.
    2. */
    3. public static T MinBy<T>(this IEnumerable<T> collection, Func<T, IComparable> score)
    4. {
    5.     return collection.Aggregate((x, y) => score(x).CompareTo(score(y)) < 0 ? x : y);
    6. }
    7.  
    Using this, you can use:
    Code (csharp):
    1. //somewhere else
    2. var closestPoint = cachedBorderWorldPoints.MinBy(point => (point - clickedWorldPoint).magnitude));
    3.  
     
    Last edited: Feb 16, 2014
  42. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,462
    Been having lots of fun with Grids,

    I'm mainly on Mobile, IOS and once I started to publish to the device I am running lots of very Tedious Linq AOT issues.

    Been reading all of the tips that you have suggested to follow. Have to be honest that having to worry the AOT Linq problems is quite cumbersome.

    Feature Request:

    Would you consider a way to add functionality for a Grids version on IOS to help bring back the joy of using Grids.

    Cheers.
     
  43. barjed

    barjed

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

    you must be getting bored of me and my terrible skills by now but I have yet another question.

    I am comfortable with C# but not so much with LINQ and Lambda, so I am having a little problem with your cachedBorderWorldPoints query. I fixed an arror in the first Where to this:

    Code (csharp):
    1. var cachedBorderWorldPoints = grid.Where(point => point.GetNeighbors().Count() < 4).Select(point => map[point]);
    but now I am getting "No overload for method `GetNeighbors' takes `1' arguments" compiler error :(

    Thanks!
     
  44. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    It is indeed tedious, especially since it's difficult to know up front how things can go wrong.

    We are indeed looking into it; we are also discovering more principles that we are applying to our own library. We will also be soon in the position to run all our unit tests on iOS, which will help us eliminate some of the issues.

    There are two other additional things we have discovered (we have not published this yet, I still have to check the extent to which these are true).

    1. If a generic method calls another generic method, the second one is likely to be missed by the compiler. (We have already started ridding the library of these), but it may still byte you in your own code.
    2. Some LINQ chains that do not work can be made to work by converting intermediate results to a PointList. So, for example:
      Code (csharp):
      1.  
      2. var transformedPoints = listOfPoints.LINQMethod1(...).LINQMethod2(...);
      3.  
      Code (csharp):
      1.  
      2. var transformedPoints = listOfPoints.LINQMethod1(...).ToPointList().LINQMethod2(...);
      3.  
    The reason the first one sometimes fails is often a comparison issue (the same one that makes some List methods fail). PointList solves these, and lets the second method work correctly.

    ----
    Whoops it's my mistake; it should be grid.GetNeighbors(point) and not point.GetNeighbors
     
  45. barjed

    barjed

    Joined:
    May 9, 2013
    Posts:
    70
    Thanks, works like a charm :)
     
  46. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    We finally completed the fuller Daikon Forge GUI example. (It took a bit longer than expected - figuring out how to get the right coordinates for mouse input turned out to be a bit tricky).

    http://gamelogic.co.za/lights-out-with-daikon-forge/

    You can download the source, and there are a few notes on what to watch out for when using Daikon Forge with Grids.
     
  47. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,462
    Nice Daikon GUI example update. Downloading and will check it out to give feedback.Great work. Cheers.
     
  48. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,462
    "We are indeed looking into it; we are also discovering more principles that we are applying to our own library. We will also be soon in the position to run all our unit tests on iOS, which will help us eliminate some of the issues."

    No pressure, just need to know due to IOS project planning.

    Can you give an ETA on when the AOT LinQ issues in the core Grids code can be unit tested.
     
  49. Herman-Tulleken

    Herman-Tulleken

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

    We plan to release Grids 1.8 by end of March, which we will have tested with the new unit tests.

    It's a major undertaking: We had to switch test platforms, and have to rewrite all our test framework code (the tests themselves also need some modification). Some of our test code is generated with text templates, which complicates matters further. And then we have to find good solutions for the problems AOT presents without mutilating our code-base in an unmanageable mess :)
     
  50. robertwahler

    robertwahler

    Joined:
    Mar 6, 2013
    Posts:
    43
    I hope Grids 1.8 will include the tests so we can all run the unit tests ourselves. Looking forward to the release.