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

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Also regarding the InitGrid call. Is that not an Awake call? I am having some issue's where the Grid is null in my path finders Start methods. Can I somehow make the InitGrid call occur in the Awake call?
     
  2. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    I'm noticing my maps aren't correlating properly.

    Heres two screen shots.

    It will show the starting point (where the black box is). The black box has a very basic pathfinder script which is just stating where its starting PointyHexPoint is:

    Code (csharp):
    1.  
    2.  
    3. Map[transform.position] //Im saying given then cubes world position give me the associated PointyHeyPoint right?
    4.  
    5.  
    Here the start point should be 0,0 but its offset by -1,-1


    I had to move it up this far to get it to think it was at the 0,0


    Can you think of why my points seem to be a full grid value down and to the left?
     
  3. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    Does that change if you resize the screen?
    (i.e. it is not a factored mapping of the screen space he posited as a complete guess)
     
  4. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Edit: rereading your post, I see you already saw the example. You should be able to use that as a starting point. I am not sure how your situation differs from the example (except for the "window" size). Could you please explain a bit?
     
  5. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Thanks for alerting us that our mail is not working. Nothing came trough the entire week for some reason, so we missed quite a few mails. It's been fixed now.

    To answer this question,

    PointyHexGrid<TCell> is already a IGrid<TCell, PointyHexPoint>. So the cast makes sense, since the hex grid already has the point type specified. Does that make sense?

    The reason we cast is for convenience. We can after the full cast access more specific methods on the grid and its cells.
     
    Aggressor likes this.
  6. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358

    I apologize for not replying, we had a problem with our mailbox which is now fixed.

    To answer your question, you have to make cells that are a bit more sophisticated to accomplish what you need. Here is an example showing cells with two layers of images:

    http://gamelogic.co.za/grids/exampl...using-scriptable-objects-to-manage-tile-sets/

    The process should be similar for using uGUI images (adapting the UITextImageCell). I will see if I can make an example and upload it.

    Working with cells that occupy multiple slots in the grid is tricky.

    Here is what you need to do:

    Add data to your cell that gives it a width and height (in grid units). Add methods for setting these, and updating the cells visual presentation accordingly. (The tricky bit here is to make sure the pivot is in a predictable place. The center of the top-left subcell is probably the best).

    To simplify the logic a bit, in this rare occasion we will store the top-left grid coordinate of the cell too. Add this field, and add the necessary modifiers / accessors.

    The rest is how to "put" your cells in the grid, and do queries on the grid. Normally, you would follow this pattern to "put a cell in the grid":

    Code (csharp):
    1. var cell = Instantiate(cellPrefab);
    2. grid[gridPoint] = cell;
    3. cell.transform.parent = gridRoot;
    4. cell.transform.localPosition = map[gridPoint];
    For your variable-sized cells, you need to do the following, which is slightly different.

    Code (csharp):
    1. var cell = Instantiate(cellPrefab);
    2. cell.Width = width;
    3. cell.Height = height;
    4. cell.topLeft  = gridPosition;
    5.  
    6. cell.transform.parent = gridRoot;
    7. cell.transform.localPosition = map[gridPoint];
    8.  
    9. for (int i = 0; i < width; i++)
    10. {
    11.    for(int j = 0; j < height; j++)
    12.    {
    13.        var point = gridPoint + new RectPoint(i, j);
    14.        grid[point] = point;
    15.    }
    16. }
    So we store a reference to the cell in each position that it occupies in the grid. This makes it easy to check occupancy.

    When you remove a cell, you need to be careful to use the same pattern to clear all the grid slots. This is where the topleft coordinate comes in handy, in case you have accessed the cell from some other point in the grid.

    Dos all this make sense?
     
  7. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    You can use the following:

    1. Cache the border points of your grid. Assuming a rect grid, you can use this:

    Code (csharp):
    1. borderPoints = grid.Where(p => grid.GetNeighbors(p) < 4);
    Then, when you need to find the closest cell, first check if the world point maps to a grid point in the grid: if it does, use the point as is. If it doesn't, find the closest cell in the border using brute force.

    Code (csharp):
    1. var gridPoint = map[worldPoint];
    2.  
    3. if(grid.Contains(gridPoint))
    4.    closestPoint = gridPoint;
    5. else
    6.    closestPoint = borderPoints.MinBy(p => p.DistanceFrom(gridPoint));
     
    Last edited: Apr 5, 2015
    Aggressor likes this.
  8. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Yes, the idea is that it works like a two way dictionary. The map as a single entity is really a invertible* function that maps RxR to NxN. (Of course, it is not truly invertible, many points in RxR map to the same point in NxN).

    The notation is just for convenience, and perhaps not the best choice in retrospect. There is no explicit type checking, we simply overloaded the index operator with two different method signatures. One disadvantage of this is that maps cannot map grid points to grid points, which is would have been nice to chain maps...

    The idea of using two maps is a powerful one. (I think of it as using two halfmaps), but it is one that did not occur to me before much later. The ability to mix and match different halfmaps in some circumstances is extremely useful, and indeed we use the concept for implementing some of the more advanced grids (line grids and generic splice grids). But most users do not in fact implement their own maps, so it's a feature that will benefit very few users. Yet it's something you may see in Grids at some point.

    One possibility is that you are not taking into account the fact that maps work relative to the grid root.

    When you give a world point to the map, you should already have transformed it relative to the root. So this line:

    Code (csharp):
    1. _startPoint = _map[transform.position];
    should really be
    Code (csharp):
    1. _startPoint = _map[Vector3.zero];
    In general, you can use Transform.InverseTransformPoint to convert a world point to local space.

    It is a bit suspicious that the offset is an exact hex... so this may not be the only issue. But fix this first, then we can get better information.
     
    Aggressor likes this.
  9. TobiUll

    TobiUll

    Joined:
    Feb 22, 2015
    Posts:
    73
    Wow, the author liking critics seems really promising.
    Keep up the good work.
     
  10. mfav

    mfav

    Joined:
    Aug 27, 2014
    Posts:
    5
    Hey, thanks for this asset. It has really helped my workflow, and is much better written than my prior grid implementation :) Unfortunately, I've hit a brick wall and lost a few hours on exporting to iOS.

    The error, which I've seen cited elsewhere in this thread is as follows:
    Code (CSharp):
    1. ExecutionEngineException: Attempting to JIT compile method 'Gamelogic.Grids.AbstractUniformGrid`2<Gamelogic.Grids.TileCell, Gamelogic.Grids.RectPoint>:.ctor (int,int,System.Func`2<Gamelogic.Grids.RectPoint, bool>,System.Func`2<Gamelogic.Grids.RectPoint, Gamelogic.Grids.RectPoint>,System.Func`2<Gamelogic.Grids.RectPoint, Gamelogic.Grids.RectPoint>,System.Collections.Generic.IEnumerable`1<Gamelogic.Grids.RectPoint>)' while running with --aot-only.
    2.  
    I've done all the fixes I've found elsewhere. All of my IEnumerable and Lists of RectPoint have been replaced with PointList<RectPoint>. I have a custom Cell class (named Cell) and I'm using a Rectangular Grid. I have used this hint to make sure nothing is missed by the compiler:

    Code (CSharp):
    1.     private bool __CompilerHint__()
    2.     {
    3.         var grid = new RectGrid<Cell>(1, 1);
    4.        
    5.         foreach (var point in grid) { grid[point] = cellPrefab; }
    6.        
    7.         var shapeStorageInfo = new ShapeStorageInfo<RectPoint>(new IntRect(), p => true);
    8.         var shapeInfo = new RectShapeInfo<Cell>(shapeStorageInfo);
    9.        
    10.         return grid[grid.First()] != null || shapeInfo.Translate(RectPoint.Zero) != null;
    11.     }
    This is called from my subclass of GridBehaviour<RectPoint> in it's Awake() method. I have tried placing it other places and using both the array version of the compiler hint above and the non-array version.

    Any more ideas? This is pretty frustrating as it takes a bit of time to build and run with XCode just to find out nothing has changed. Otherwise, working great in-editor :)

    Thank you very much.
     
  11. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    It seems it still needs the hint for TileCell (that is what the error complains about), so try exactly what you have with TileCell replacing Cell, and then combine the two calls of the two hints in the call:

    Code (csharp):
    1. if(!(__CompilerHint__() && __CompilerHint2__()) return;
    Edit: and if that works, then try removing the hint involving just Cell. I have not seen this type of requirement before.... Also, double check that there is not another Cell (possibly in another namespace).

    Let me know if this does the trick.
     
  12. mfav

    mfav

    Joined:
    Aug 27, 2014
    Posts:
    5
    Thanks for the really quick follow-up. I was able to fix it with your help. I had tried just TileCell earlier, but after adding a hint for SpriteCell as well and it seemed to work. I will track down exactly which combination fixes it later.

    Also had some issues with the Algorithms.GetPointsInRange, but solved them. For anyone else who encounters the same problem, adding these lines to the compiler hint worked for me:

    Code (csharp):
    1.  
    2. var clone = grid.CloneStructure<bool>();
    3.  
    4. foreach (var point in grid) { grid[point] = cellPrefab; }
    5. foreach (var point in clone) { clone[point] = true; }
    6.  
     
    Herman-Tulleken likes this.
  13. TobiUll

    TobiUll

    Joined:
    Feb 22, 2015
    Posts:
    73
    Please!!! I want to work like in a regular grid. I want to say "ThisGrid.Cells(1,1).Width = 2"
    Can't you make a wrapper to accomplish this?
    Or in other words: I would like to make cells that cover more than one cell space.
     
    Last edited: Apr 8, 2015
    Herman-Tulleken likes this.
  14. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Thanks for your help. I love to keep blundering away (and digging through your code) till I get it working. Your answers were some sweet gravvy though!.

    Great news, path finding and instantiation on the proper grid positions is now working. I am going to work on some other mechanics for my game, but I will definitely be revisiting this when its time to do target selection and range finding.

    I found a great resource on hex grid by the way, feel free to share & mention.

    http://www.redblobgames.com/grids/hexagons/

    Thanks for the help.
     
  15. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    I do have one question regarding the Grid object. I need it to exist on Awake instead of Start. Can I change its init to an Awake call?
     
  16. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    It is not so clear how to solve the problem generally for grids, which makes it non-trivial at a library level. But it is something that is required for some games, so it is definitely something we will consider for future versions.

    Thanks for the feedback!
     
  17. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    It is indeed a great resource, and in fact we have a page that maps the features described there to our own library:

    http://gamelogic.co.za/grids/docume...or-unity-and-amit-patels-guide-for-hex-grids/
     
  18. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    You could, but there may be weird consequences. You said earlier, that the grid is sometimes null. Could you mail m a test case so I can trace the exact order of events?
     
  19. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    I have a GameGrid object.
    Code (csharp):
    1.  
    2. public class GameGrid : GridBehaviour<PointyHexPoint> {
    3.  
    4.     public static GameObject SINGLETON;
    5.  
    6.     public static IGrid<GridCell, PointyHexPoint> grid;
    7.     public static IMap3D<PointyHexPoint> map;
    8.     public GameObject circlePrefab;
    9.  
    10.     // Use this for initialization
    11.     void Awake ()
    12.     {
    13.         SINGLETON = gameObject;
    14.     }
    15.    
    16.     // Update is called once per frame
    17.     void Update () {
    18.    
    19.     }
    20.  
    21.     public override void InitGrid ()
    22.     {
    23.         grid = Grid.CastValues<GridCell,PointyHexPoint>();
    24.         map = Map;
    25.         //Debug.Log("Grid Init'd to : " + grid);
    26.     }
    27. }
    Another object calls this in a start. Because InitGrid is not called till start, this debug is null. I would like to have all Grid initialization occur during OnAwake (I would think this is also very common is it not? You want to spawn enemies on the Start() call and initialize the grid in the Awake() call).

    Code (csharp):
    1.  
    2. Public Class Object:MonoBehaviour
    3. {
    4.     void Start()
    5.     {
    6.         Debug.Log(GameGrid.Singleton.grid);
    7.     }
    8.  
    9. }
     
    twobob and TobiUll like this.
  20. smelchers

    smelchers

    Joined:
    Feb 22, 2015
    Posts:
    22
    @HermanTulleken "Aggressor" might really be your most active user. :)

    I almost purchased your asset, but then I thought I might visit your forum first.
    I thought that individual cell width and height would already be included.
    I will wait with my purchase until this feature has been included.
    I totally need it and could not live without it.
     
  21. OfficialHermie

    OfficialHermie

    Joined:
    Oct 12, 2012
    Posts:
    585
    I need individual cell width/height as well. Will wait before I buy.
     
    Herman-Tulleken likes this.
  22. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    If thats a show stopper for you I question your decision. While the documentation needs a boost, the creator is very supportive and will answer your questions.

    If you are building a game with grids, I can see no reason that that missing feature would trump all the other problems this package solves (range finding and path finding). Not to mention, if you need cell width and height, you could definitely add it in (map each cell point to size struct in your own dictionary) and be on your way. You're essentially saying you're waiting to make your game for this one feature?! Don't be crazy, get grids and get coding :)
     
    Herman-Tulleken, twobob and DMeville like this.
  23. smelchers

    smelchers

    Joined:
    Feb 22, 2015
    Posts:
    22
    Hmmm, I have created a grid in a non-gaming-world that includes individual cell spacing, and I have fought like 3 years to get just everything right. Do you think you might post an example project if it is so easy for you?
    I did not spend 3 years on just the individual cell width/height feature, but it contributed a lot to it, that is why I am really anxious about something that is not officially supported.

    Edit: A sample project for this would really be absolutely great!!!
     
    Last edited: Apr 12, 2015
    Herman-Tulleken likes this.
  24. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358

    OK I understand the problem.

    The reason that it's not trivial to move the call into Awake is that it's very complicated to get the right behaviour in both the editor and at runtime. There are quite a few things happening behind the scenes. But I will look into it again, and see what is possible.

    Is it the case that you have spawners placed in the scene, and that they want to do their thing when the game starts? The best solution (for now) is probably to send a notification to such objects once the grid is initialised.

    It may surprise you that it is not at all common (although certainly not unique). The scheme we recommend and many people follow is to drive all logic from the grid. So anything spawned is spawned in the InitGrid method, or spawned as a result of something that happens there. Of course, there are cases where this is not the ideal solution.
     
    smelchers and Aggressor like this.
  25. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    First, I need to understand what you mean with cell dimensions. There are two possible meanings.

    To set the cell size and so alter the grid size. The grid automatically uses the (given) cell size to calculate its own size. Although we do not have a specific feature to set this dynamically, it is easy to write a few lines of code to readjust the grid. Like this:

    Code (csharp):
    1. map = new RectMap(newCellSizeAsVector2).AFewMethodsChainedToGetAlignmentIfDesired();
    2.  
    3. foreach(var point in Grid)
    4. {
    5.    var cell = Grid[point];
    6.    cell.transform.localPosition = map[point];
    7. }
    Of course, you have to use this map for everything onwards, so it's best to replace the existing map.

    To set the number of cells of the grid a tile occupies. This too is not supported out of the box, since the different use cases do not have enough in common to make a suitable library addition. However, it too is not difficult to add. We have in fact just made an example to show how to do this. We will upload it onto the web site soon, but in the meantime you can download it from here:

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

    screen_50.png
    ----------------
    Either way, we appreciate your feedback. We have a list of some 300 features that gets requested, and we use it extensively in deciding how to improve the library. Grids is a large library, and its abstract nature makes it relatively easy to extend it in weird and wonderful ways. We are careful now, however, to extend it without carefully thinking of the consequences. Not least of which is the cost of documenting and providing information on how to use the feature, which easily dwarfs the actual implementation cost. But we keep careful note of everything, so thanks again!
     
    Last edited: Apr 13, 2015
    Aggressor likes this.
  26. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hmmm it seems that I misunderstood your initial post.

    Could you perhaps provide a screenshot of what you mean? It may be easier than you think.
     
  27. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Great advice. Send a message from the InitGrid call. 100X better than my hack co-routine.

    If you're ever in Toronto ping me and we grab BEER!
     
  28. smelchers

    smelchers

    Joined:
    Feb 22, 2015
    Posts:
    22
    I am looking for more of less the same thing that another user posted:

    http://s3.postimg.org/7io5co1kj/apple1.jpg

    Edit: Yes, I mean "To set the number of cells of the grid a tile occupies."
    Does such an example project exist?
     
  29. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Yes, it does. The link in my post is exactly that :) Let me know if it works!
     
  30. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Glad to hear :)

    And I'll most definitely will remember to ping you if I ever land in Toronto :)
     
  31. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Hi @Herman Tulleken,

    We're making a strategy RPG on a square grid and I was thinking of trying out your plugin. I'll try out the trial in the coming weeks. I was taking a gander at the documentation, it's a lot to take in.

    I have a few questions:

    1. Is it easy to edit the existing built-in pathfinding to take into account agents that occupy more than one tile? (i.e. large characters). Such that if an agent occupies 4 tiles, their pathfinding shouldn't be able to go through corridors or doors that are 1-tile in width, for example.

    2. Likewise if that can be done for large characters that take up tiles in some odd shape (of our own choosing), like say, a tank, that occupies a 2x3 size (rectangular), and taking into account the way they rotate. Or a giant monster that occupies a circle whose radius is 3 tiles.

    3. Another thing I had to work out for large units was that it was different to check if a large character is adjacent to a small character (small meaning they only occupy 1 tile), or if a large character is adjacent to another large character. Should it be straightforward to take such changes into account?

    4. Also is there a way to check for offset in adjacency, for example, a character whose weapon can reach 2 tiles away?

    5. Are there helper functions to check which agents are inside a certain shape? Like checking which characters are affected by a grenade explosion, with the explosion being a tile-based shape. It could be a circle with a radius of 5 tiles, for example. Or a cone shape, or perhaps a custom pattern that we'd define by ourselves, etc.

    6. I'm looking into using some other plugin to draw the grid map, as well as showing movement trajectory or the perimeter of movement range as drawn lines. I'm guessing it should be possible to do such (can I feed such information to the other plugin for it to draw)? For example, this image shows movement range as a border, and the path of movement as a line, it's what we're looking into making.


    7. Should Grids Lite be sufficient for the above questions? Or should I need the Basic or Pro?

    Here's a short video of our game's alpha (it's a work-in-progress). While I do have a grid map working, it's only a prototype and currently doesn't work very well for larger sized maps, which is why I was looking into this Grids plugin as a possible alternative instead of rolling my own:



    On a side note, I'm looking into creating influence maps for the enemy AI. I saw that you had an example already for diffusion, and that's nice.
     
    Last edited: Apr 13, 2015
    Aggressor likes this.
  32. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hi! Here are my answers to your questions:

    1. Yes it is easy. The way to do this is to use different grids for different sized agents, with squares marked as inaccessible if they are not surrounded by enough adjacent open squares. Alternatively, you can maintain the data necessary in one map, but store an integer for each indicating what is the closest distance to a inaccessible square. The function you use to check accessibility will then look something like this cell => cell.DistanceToWall <= unitDiameter;

    2. It's relatively easy to handle things that occupy multiple cells. (We have a vanilla example just a few posts up). However, I am not sure what you mean with taking the way units turn into account. Could you explain?

    3. Again, I am not 100% sure what you mean with this. If I understand you correctly, with a proper occupation map (a grid), these queries are indeed easy to implement, and fast too. You will need to write a bit of code, but really just a bit. But perhaps you could clarify this point.

    4. You mean a way to check whether one multi-cell units is within shooting range of another? There is not a build-in function that works on multi-cells, but the functionality is easily implemented. One easy way to do it is to maintain a list of grid vectors that represent all displacements within shooting range. The list is easy to construct in code, and can then be used for queries.

    To give you an idea, here is the query:

    Code (csharp):
    1. var CanShootSomething(RectPoint shooterPosition, List<RectPoint> shootableOffsets)
    2. {
    3.    foreach(var offset in shootableOffsets)
    4.    {
    5.        if(grid[shooterPosition + offset].IsEnemy) return true;
    6.    }
    7.  
    8.    return false;
    9. }
    5. There are functions for working with halfplanes (which can be used to construct polygons), and circles are easy too:

    Code (csharp):
    1. bool inCircle = p.DistanceFrom(q) < radius;
    which combined with halfplanes can give you cones.

    6. I am sure it will be possible. It depends on what type of data the plugin wants, but I cannot see why anything more than a straightforward conversion would be necessary.

    7. Yup indeed. Except for more grid types, you will be missing some machinery for constructing general types of (other) grids, and some machinary to work with vertex and edge grids.
     
  33. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Hey this is Graywalkers.

    I backed this kickstarter XD
     
  34. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Thanks for the response @Herman Tulleken!

    Some very clever solutions there, I can see why this plugin is versatile.

    2. For turning of vehicles, here's an image for the explanation:


    So the pathfinding should know that the tank can't pass through this corridor. This isn't really something I strictly need, I'm just wondering how it might be taken into account with the Grids plugin.


    3. I meant that for agents that occupy a single tile, checking for adjacency is simply checking if their tiles are 1 grid coordinate away from each other. With a unit that occupies many tiles, it gets a little trickier.


    4. I see. I imagine some scenario like this: I need to make the enemy AI decide what's the best place to position itself such that it's in shooting range of a target unit that it wants to attack. So the pathfinding should take into account:
    a) an occupiable tile that's a certain distance away from the target, instead of landing as close as possible to it
    b) the tile to go to needs to give clear line-of-sight to the target
    c) some other arbitrary criteria like favoring spots that provide cover, favoring a path that doesn't make the enemy AI go through the line-of-fire of the player's units, etc.


    I like what I'm seeing so far! I should be able to tinker with this plugin in earnest soon.


    @Aggressor: Hey there! Thanks for backing us!
     
  35. Jerome-P

    Jerome-P

    Joined:
    Apr 14, 2015
    Posts:
    34
    The Blocks example is great for beginners!
    Is it possible to get an advanced example where the BlockCell
    1) has a text
    2) behaves like a ButtonCell?

    I've played around with it for many hours, but at some point I always got stuck. I was too afraid I might introduce a mistake, apart from some things just not working when I tried them. I tried to simply add a UIText to a block cell, and there were no errors, but my text was simply not shown.

    Would be really helpful if you could publish a more advanced samples for beginners like me.

    Thanks a lot!
    Jerome
     
  36. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    2. This is a tricky situation, and I am not 100% sure you can wangle it into a form solvable by our AStar, especially for multi-cell units. Even for single cell grids I have not thought of a good way to eliminate turns. The main problem is that the local path info is not enough - for each node you need to consider not only what nodes are accessible, but also what is the orientation of the agent when it lands there. The fact that some nodes can be reached from certain other nodes makes this very tricky.

    3. For multi-agent cells, I would add a list of points that the unit covers when it's placed at the origin. So to find all the points an agent cover, you just add the position point. If you then do a Minkowski add with a 3x3 rectangle, you get the points that extend by one point. Comparing this to the points covered by another unit will give you the answer. You can cache, and eliminate the center from the Minkowski sum to make things a bit faster.

    Code (csharp):
    1. //You can make this a static constant somewhere central
    2. RectGrid<bool> adjacencyGrid = RectGrid<bool>(3, 3).Translate(-1, -1); //no need to populate, we only want the points
    3.  
    4. List<RectPoint> AdjacencyBorder {get; private set;}
    5. List<RectPoint> CoverPoints {get;} //Assume list of cover points for this unit is defined or initilaised
    6.  
    7. void Start()
    8. {
    9.   var minkowskiSum = (
    10.          from coverPoint in coverPoints
    11.          from adjacencyPoint in adjacencyGrid
    12.          select coverPoint + adjacencyGrid)
    13.       .Distinct()
    14.       .ToList();
    15.  
    16.    AdjacencyBorder = minkowskiSum.Select(p => IsBorderPoint(p, minkowskiSum)).ToList();
    17. }
    18.  
    19. //Helper method
    20. bool IsBorderPoint(RectPoint point, IEnumerable<RectPoint> shape)
    21. {
    22.    foreach(var neighbor in grid.GetAllNeighbors(point))
    23.       if (shape.Contains(neighbor)) return false;
    24.  
    25.    return true;
    26. }
    27.  
    28. //And final the query
    29. bool TouchAnotherUnit(Unit other)
    30. {
    31.    return other.CoverPoints.Intersect(AdjacencyBorder).Any();
    32. }
    After writing it out it looks a bit more hectic than I imagined. I am sure it can be cleaned up some, or perhaps there is a cleaner way to find the adjacency border.

    4. In what you describe, this seems to be not the task for the path finding algorithm. What you would probably need is to run the range finding algorithm (giving you all possible reachable points, potentially with the cost of reaching each point), and select the best target to move to based on other criteria from this set of points. Or simply choose a suitable target directly (using your criteria), and then path find a route to that node.
     
    Last edited: Apr 15, 2015
    AnomalusUndrdog likes this.
  37. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    We will see if we can make an example. In case you want to give it a try in the meantime:
    1. Extend the Block script (or a duplicate thereof) from ButtonCell instead of SpriteCell.
    2. Take one of the ButtonCell prefabs, and duplicate it (on for each of your cell types). Replace the ButtonCell script with the Block script.
    3. In the scene, make sure your grid is rooted to the GUI canvas.
    That's it :)
     
  38. Jerome-P

    Jerome-P

    Joined:
    Apr 14, 2015
    Posts:
    34
    Thanks. I will try that.
    And if somebody needs to display an additional icon on the cell, do you think one should create the merged texture internally and then use the merged texture do you think we might have 2 sprites on the cell (background button + overlaid icon)? I have spent some hours on merging 2 textures, and it has been a pain so far, so I would really appreciate an example where I can use 2 images / textures on a cell.
    Jerome
     
  39. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    There is an example here:

    http://gamelogic.co.za/grids/exampl...using-scriptable-objects-to-manage-tile-sets/

    It is worth noting that you can implement your cell anyway you want - you can have a dozen layers, and anything extra that you want. As long as you have a script that implements the ITileCell interface, you can put that into any grid builder, and in of you construct code in grids you don't even need that.
     
  40. Jerome-P

    Jerome-P

    Joined:
    Apr 14, 2015
    Posts:
    34
    Trying the CustomCells.unitypackage, I get the error "Assets/CustomCells/IconTiles/IconTileSet.cs(16,33): Error CS0103: The name 'ExampleUtils' does not exist in the current context".
     
    Herman-Tulleken likes this.
  41. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    Hello again.

    Can you suggest a lightweight solution for finding the closest point to move to?

    I have an enemy unit. He can only move 3 grid spaces. The target he is after is across the board. Can you suggest a lightweight way I can have this enemy unit pick the point he wants to move to that is closest to the target he is after?

    My shotgun approach is I get all cells in range, then I compare every cell to the point cell and move there, but I'm curious if theres a better way to reduce the overhead on this call?

    Code (csharp):
    1.  
    2.     public void MoveAsCloseAsPossibleToPoint(PointyHexPoint targetPoint)
    3.     {
    4.         IEnumerable<PointyHexPoint> pointsInRange = RangeFinder.GetPointsInRange(_unit.gridPoint,_unitStats.movement);
    5.         PointyHexPoint closestPoint = pointsInRange.First();
    6.         foreach(PointyHexPoint point in pointsInRange)
    7.         {
    8.             if(point.DistanceFrom(targetPoint) < closestPoint.DistanceFrom(targetPoint))
    9.             {
    10.                 closestPoint = point;
    11.             }
    12.         }
    13.  
    14.         MoveToPoint(closestPoint);
    15.     }
    16.  
     
    Last edited: Apr 17, 2015
  42. Jerome-P

    Jerome-P

    Joined:
    Apr 14, 2015
    Posts:
    34
    My first problem is already in 1).
    When I rename
    "public class BlockCell : SpriteCell" to "public class BlockCell : ButtonCell", I get the error "The type or namespace name 'ButtonCell' could not be found. Are you missing a using directive or an assembly reference?"
    How do I fix that, please?
     
  43. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    I cannot really think of a faster method except if there is some geometry to your points in range that you can exploit. (For example, if the costs are all the same, your range will always be "circular" disks, for which you may be able to write equations directly to get the closest point - although I don't know what those are).

    You could clean up the code a bit by using the MinBy extension:

    Code (csharp):
    1.  
    2.     public void MoveAsCloseAsPossibleToPoint(PointyHexPoint targetPoint)
    3.     {
    4.         IEnumerable<PointyHexPoint> pointsInRange =          
    5.             RangeFinder.GetPointsInRange(_unit.gridPoint,_unitStats.movement);
    6.        
    7.         PointyHexPoint closestPoint = pointsInRange.MinBy(p = p.DistanceFrom(targetPoint);
    8.         MoveToPoint(closestPoint);
    9.     }
    10.  
    Edit: Although, is it true that the line between the unit and target goes through the closest point? Then it may be easiest to look at all the points on that line that is in range.
     
    Aggressor likes this.
  44. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Sorry I had the wrong class name. It is UIImageTextCell.
     
  45. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Thanks for letting us know, I will upload a fix.
     
  46. Aggressor

    Aggressor

    Joined:
    Aug 10, 2012
    Posts:
    62
    I've run into a snag with the path finding.

    I am using your Algorithms.GetPointsInRange() but that call gets incredible expensive after a range value of 4. If I set my units range to say, 15, it takes like 10 seconds for this to run. I've attached a profiler log. I cant even run the profiler above a move range of 5, because the profiler will skip the frame because theres too much to process. Can you advise what the issue is here?

    Happy to provide as much information as necessary :)

     
  47. Jerome-P

    Jerome-P

    Joined:
    Apr 14, 2015
    Posts:
    34
    @Herman Tulleken Thank you. Can you introduce a sample anyways that would allow users to easily create cell that have

    1) individual cell width / height
    2) a background image
    3) a foreground image
    4) a text?

    I think many users would need such an example.
    I know you said you would look into it.
    I just wanted to stress again that I think that many people would need it and that it would be great if such an example existed.
     
    Herman-Tulleken likes this.
  48. Jerome-P

    Jerome-P

    Joined:
    Apr 14, 2015
    Posts:
    34
    @Herman Tulleken
    Now I am getting the error "Assets/AdditionalExamples/Blocks/BlockCell.cs(16,32): error CS0120: An object reference is required to access non-static member `UnityEngine.Component.transform'"
    in this line: "SpriteRenderer.transform.localPosition = new Vector3(x, y, 0);"
     
  49. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    Hmm yes, you should look out for references to sprite, and change it to Image instead.

    (I will defn keep your suggestion for an example that in mind).
     
  50. Herman-Tulleken

    Herman-Tulleken

    Joined:
    Nov 20, 2009
    Posts:
    358
    There was a problem with an earlier version of the algorithm. Can u please verify what version of Grids you are using? It should be pretty fast. There are 4 versions of the algorithm, and I guess another possibility is that one of them still have the problem. (Could you also paste your calling code so I can see for sure?)