Search Unity

Grid Framework [scripting and editor plugins]

Discussion in 'Assets and Asset Store' started by hiphish, Jul 24, 2012.

  1. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    OK, then got directly for the function that computes the point: ShootRay
    Code (csharp):
    1. Vector3 ShootRay () {
    2.     RaycastHit hit;
    3.     var point = transform.position;
    4.  
    5.     gridCollider.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hit, Mathf.Infinity);
    6.     if (hit.collider) {
    7.         point = hit.point;
    8.  
    9.         var lower = _grid.GridToWorld(_grid.renderFrom) + 0.5f * transform.lossyScale;
    10.         var upper = _grid.GridToWorld(_grid.renderTo  ) - 0.5f * transform.lossyScale;
    11.  
    12.         point = Vector3.Max(Vector3.Min(point, upper), lower);
    13.     }
    14.     //this is where the player's cursor is pointing towards (if nothing was hit return the current position => no movement)
    15.     return point;
    16. }
    I tried it out, that function works. You should consider now making the collider of the grid larger than the grid, the point will be clamped and it will feel nicer to players when the mouse cursor can go a bit outside of the grid.
     
  2. Syrath88

    Syrath88

    Joined:
    Jul 9, 2014
    Posts:
    7
    Hey,
    I've been using Grid Framework for a while now and I love it!
    I'm using an altered version of the runtime snapping code.

    However I was wondering if anyone could give me some advice on where to start with trying to get my objects moving on the y axes.

    What I'm trying to achieve is moving an object snapped to the grid x & z then with a key press move said object up or down y axes while the object is selected then continue snapping x & z without dropping back to ground position.

    I've tried a few different ways however with no luck. here is the code, I've taken out all the parts I don't need such as collision with other objects and changing the objects colour based on the collision and added a click selection to move the object.

    Code (JavaScript):
    1. #pragma strict
    2.  
    3. public var grid : GFGrid; // the grid we want to snap to
    4. public var isClicked : boolean;
    5.  
    6. private var gridCollider : Collider ; // a collider attached to the grid, will be used for handling muse input
    7. private var oldPosition : Vector3; // the previous valid position
    8. private var cachedTransform : Transform; //cache the transform for performance
    9.  
    10.  
    11. function Start () {
    12.     isClicked = false;
    13. }
    14.  
    15. function Awake () {
    16.     cachedTransform = transform;
    17.  
    18.     //always make a sanity check
    19.     if(grid){
    20.         gridCollider = grid.gameObject.GetComponent.<Collider>();
    21.         if (gridCollider) {
    22.             //perform an initial align and snap the objects to the bottom
    23.             grid.AlignTransform (cachedTransform);
    24.             cachedTransform.position = CalculateOffsetY ();
    25.         }
    26.     }
    27.     //store the first safe position
    28.     oldPosition = cachedTransform.position;
    29.     // setup the rigidbody for collision and contstruct a trigger
    30.     SetupRigidbody();
    31. }
    32.  
    33. // toggle object selection
    34. function OnMouseDown(){
    35.     if (isClicked == false){
    36.         isClicked = true;
    37.     }
    38.     else if (isClicked == true)
    39.         isClicked = false; ;
    40. }
    41.  
    42. // use FixedUpdate instead of Update to allow colision detection to catch up with movement
    43.  
    44. function FixedUpdate(){
    45.     if(isClicked){
    46.         //store the position if it is valid
    47.             oldPosition = cachedTransform.position;
    48.         DragObject();
    49.     }
    50. }
    51.  
    52. //this function gets called every frame while the object (its collider) is being dragged with the mouse
    53. function DragObject(){
    54.     if(!grid || !gridCollider) // if there is no grid or no collider there is nothing more we can do
    55.         return;
    56.     //handle mouse input to convert it to world coordinates
    57.     var cursorWorldPoint : Vector3 = ShootRay();
    58.      
    59.     //change the X and Z coordinates according to the cursor (the Y coordinate stays the same after the last step)
    60.     cachedTransform.position = cursorWorldPoint;
    61.  
    62.     //now align the object and snap it to the bottom.
    63.     grid.AlignTransform(cachedTransform);
    64.     cachedTransform.position = CalculateOffsetY(); // this forces the Y-coordinate into position
    65.  
    66. }
    67.  
    68.  
    69. // makes the object snap to the bottom of the grid, respecting the grid's rotation
    70. function CalculateOffsetY(){
    71.     //first store the objects position in grid coordinates
    72.     var gridPosition : Vector3 = grid.WorldToGrid(cachedTransform.position);
    73.     //then change only the Y coordinate
    74.     gridPosition.y = 0.5f * cachedTransform.lossyScale.y;
    75.  
    76.     //convert the result back to world coordinates
    77.     return grid.GridToWorld(gridPosition);
    78. }
    79.  
    80.  
    81. // shoots a ray, which can only hit the grid plane, from the mouse cursor via the camera and returns the hit position
    82. function ShootRay() {
    83.     var hit : RaycastHit;
    84.     gridCollider.Raycast(Camera.main.ScreenPointToRay (Input.mousePosition), hit, Mathf.Infinity);
    85.     //this is where the player's cursor is pointing towards (if nothing was hit return the current position => no movement)
    86.     return hit.collider != null ? hit.point : cachedTransform.position;
    87. }
    88.  
    89.  
    90.     // set up the rigidbody component for intersection recognition
    91. private function SetupRigidbody(){
    92.     var rb : Rigidbody = GetComponent.<Rigidbody>(); //the rigidbody component of this object
    93.     if(!rb) // if there is no Rigidbody yet create a new one
    94.         rb = gameObject.AddComponent.<Rigidbody>();
    95.     // non-kinematic to allow collision detection, no gravity and all rotations and movement frozen
    96.     rb.isKinematic=false;
    97.     rb.useGravity=false;
    98.     rb.constraints = RigidbodyConstraints.FreezeAll; //prevents physics from moving the object
    99. }
     
    Last edited: May 13, 2015
  3. SimStm

    SimStm

    Joined:
    May 24, 2013
    Posts:
    44
    Nice. I've applied the code to the CursorRay() and even the effect just doesn't look exactly like I've expected, your code and your tips helped me to think in a nice solution for this case.

    Thanks man. Grid Framework is just awesome. Your support too. :)
     
  4. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    How about this: you keep track of how many steps you moved the object up or down and add that to the "floor" in the CalculateOffsetY function?
    Code (csharp):
    1. int steps;
    2.  
    3. function CalculateOffsetY(){
    4.     //first store the objects position in grid coordinates
    5.     var gridPosition : Vector3 = grid.WorldToGrid(cachedTransform.position);
    6.     //then change only the Y coordinate
    7.     gridPosition.y = 0.5f * cachedTransform.lossyScale.y + steps;
    8.  
    9.     //convert the result back to world coordinates
    10.     return grid.GridToWorld(gridPosition);
    11. }
     
  5. Syrath88

    Syrath88

    Joined:
    Jul 9, 2014
    Posts:
    7
    Thanks so much for the help Hiphish, this helps a lot and works great! I have the key press working for moving the object up and down now and it looks fantastic.

    I just have 1 last question however, currently the bottom of the object is locked to the mouse cursor for moving it around the grid. but when the object is moved above the mouse it's no longer select able. I've tried altering some of the mouse position code but not sure exactly where to update the mouse position to keep it the same y position as the object.

    Also do you take any donations? I see you giving a lot of support it would be great to give you some back!

    Thanks in advance :cool:
     
  6. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    What do you mean "above the mouse"? The mouse cursor exists in a 2D plane outside the game world. The way position detection works is by casting a ray from the camera's position in the direction of the mouse cursor, but the origin of the ray is still the camera. What I can imagine is that the object is visually above the grid so when you click it your ray misses the grid.

    If that's the case you need to rework the way position detection is done. My idea would be to cast the ray from the object orthogonally downwards (Vector3(0, 0, -1)) and use the hit point as the result. You should also draw a shadow below the object so the player can tell where it is hovering above.

    I don't take donations on a paid product, that's just one of those things you should never do. I am not knocking donations themselves, if someone is offering something for free donations are a great way to compensate them.

    Besides, customer support is something customers have a right to, not something I do out of courtesy. In most cases the questions are really not that hard to solve, I answered your questions in less than five minutes each. Then there are cases where Grid Framework actually has a bug, in which case it is my own fault that I have to go back and fix things. Of course there are also the types of people who basically want me to make their entire game for them, so I have to politely point them in another direction, but those are rare.

    If you really want to support me I would very much appreciate a review and of course if you can spread some good word. Or you could buy a second copy, I have heard Grid Framework makes a great gift for birthdays, christmas, as an engagement gift and of course for baby showers ;)
     
  7. Syrath88

    Syrath88

    Joined:
    Jul 9, 2014
    Posts:
    7
    Thanks,
    yeah guess my wording was way off lol. I was trying to describe the object visually above. it was outside the objects hitbox to register the click whenever it would raise.
    But thanks for the tip will take another look.\

    Will defiantly go write that review, you've been a lot of help thanks..
    I guess I saw my topic as request rather then a needed fix lol.. so you really have no obligation to help me out but I just wanted to let you know I appreciate your time however short or long it was :)

    Thanks again! take care
     
    Last edited: May 18, 2015
  8. fieldrequired

    fieldrequired

    Joined:
    Feb 26, 2014
    Posts:
    108
    Hey there, so I have a very simple question that I'm going to ask anyway to avoid over engineering a solution myself!

    I'm making a space game and I use the grid to add a plane to the environment. I now need to create a minimap, do you have any idea as to how I could put the grid onto the minimap? Thanks!
     
  9. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    If I understand you correctly you have grid in the world and you want a grid in the minimap as well? It depends on how you are going your GUI. You can use the old Unity GUI, the new Unity GUI or one of the many custom GUI solutions.

    GUIs are tricky in general because you are trying to map 3D world coordinates to 2D screen coordinates. The easiest way I can think of would be using grid coordinates as an intermediate result:
    Code (csharp):
    1. GRGrid worldGrid;
    2. Vector3 playerWorldPosition;
    3. Vector3 playerGridPosition = worldGrid.WorldToGrid(playerWorldPosition);
    Now it depends on your GUI solution. If your solution allows you to place GUI elements in the world you can add a grid and convert the grid coordinates the the GUI-grid's world coordinates:
    Code (csharp):
    1. GRGrid guiGrid;
    2. Vector3 playerGUIPosition = guiGrid.GridToWorld(playerGridPosition);
    3. playerGUIPosition.z = 0.0f
    If you are using a 2D solution that works in scree-space only you will have to convert coordinates yourself though. The grid coordinates are basically already there, you just have to scale them to your GUI size.
     
    fieldrequired likes this.
  10. yuewahchan

    yuewahchan

    Joined:
    Jul 2, 2012
    Posts:
    305
    I have a RectGrid using GridFramework, I rotate the camera 45 degree to make it look isometric, can I obtain the cartesian coordinate system that look like the following image ?

     
  11. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Yes, you can. If you want to rotate the camera use a grid with a spacing of and rotate your camera until it looks right. In the runtime snapping example the angle around the Y-axis is 45°, Z is 0° and X can be changed to affect the aspect ratio of the rhombs. The lower the angle, the flatter they appear.

    Or, instead of rotating your camera you could keep it orthogonal to the grid and set a spacing of (1, 0.5) and a shearing of XY = -0.5 and YX = 2. Here is the sheared grid:


    Getting the rotation right is harder than just setting the shearing. Plus, if you want to use 2D graphics shearing will not distort your images. On the other hand, for 3D graphics rotation is better because you get actual 3D depth information.
     
  12. fieldrequired

    fieldrequired

    Joined:
    Feb 26, 2014
    Posts:
    108
    Excellent thanks, this highlighted the solution.

    OK so another question, I need to highlight areas of the grid - either by changing the border colour of a range of cells (preferred) or by shading a cell. Is this something that is already coded into your magical library? :)
     
  13. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Hmm, changing the border colour could be done by drawing a new grid on top of the original grid, and the new grid would have its rendering range set to that cell. The downside is that if your colours have transparency the trick will be noticed. Another solution would be to use five grids: one central for the highlighted face and four surrounding it:
    Code (csharp):
    1.  
    2. GFGrid grids[5];
    3. // Hightlighted face and range of the grid
    4. Vector3 face, from, to;
    5.  
    6. // Drawing (0 is the highlighted face)
    7. //
    8. // 3|3|3|3|2|2|2|2
    9. // 3|3|3|3|2|2|2|2
    10. // 3|3|3|3|2|2|2|2
    11. // 3|3|3|3|2|2|2|2
    12. // 3|3|3|3|0|4|4|4
    13. // 1|1|1|1|1|4|4|4
    14. // 1|1|1|1|1|4|4|4
    15. // 1|1|1|1|1|4|4|4
    16. // 1|1|1|1|1|4|4|4
    17.  
    18. // The highlighted portion
    19. grid[0].renderFrom = face;
    20. grid[0].renderTo   = face + Vector3.one;
    21.  
    22. // Lower-left grid
    23. grid[1].renderFrom = from
    24. grid[1].renderTo   = face - Vector3.one + Vector3.right;
    25.  
    26. // Upper-right grid
    27. grid[2].renderFrom = face + Vector3.one - Vector3.right;
    28. grid[2].renderTo   = to
    29.  
    30. // Upper-left grid
    31. grid[3].renderFrom = new Vector3(from.x    , face.y, 0);
    32. grid[3].renderTo   = new Vector3(face.x - 1, to.y  , 0);
    33.  
    34. // Lower-right grid
    35. grid[4].renderFrom = new Vector3(faxe.x + 1, from.y, 0);
    36. grid[4].renderTo   = new Vector3(to.x      , faxe.y, 0);
    37.  
    Shading a face is the easiest to do with a sprite. You can generate a mesh consisting of two triangles at runtime, the vertex coordinates are half the spacing each, then you set the colour to something with transparency and place it on top of the face. I used programatically generated meshes for the rotary dial example and for the polar lights in the lights-out example.
     
  14. fieldrequired

    fieldrequired

    Joined:
    Feb 26, 2014
    Posts:
    108
    Hmmm, interesting but I'm not sure I'd like that approach for multiple ranges.

    Bingo, I think stretching a sprite (either a hollow or filled square) across the required region is the best way forward.

    Thanks again for you help.
     
  15. SuHwanK

    SuHwanK

    Joined:
    Dec 30, 2014
    Posts:
    43
    Hi,
    How can i change Grid Line's RenderQueue? like this.
    GridQueue.png

    Can i do this?

    and i modify to "spacing" value in GFRectGrid Script.
    0.1f -> 0.001f
    and i get a memory error. out of range memory.
    i want set spacing of grid Unit.(for example : mm, cm, m, km)
    how can i do this?

    Thank you!
     
    Last edited: Jul 9, 2015
  16. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Hi, I'll answer your second question first: When I originally wrote Grid Framework I needed to make sure the spacing never gets 0 or less, otherwise I would run into a mathematical singularity. The number 0.1 seemed like a good idea back then, but I discovered Mathf.Epsilon since. I just never really got around to changing it.

    Change line 23 from
    Code (csharp):
    1. SetMember<Vector3>(value, ref _spacing, restrictor: Vector3.Max, limit: 0.1f*Vector3.one);
    to
    Code (csharp):
    1. SetMember<Vector3>(value, ref _spacing, restrictor: Vector3.Max, limit: Mathf.Epsilon*Vector3.one);
    This will allow you to set a value as small as a float can get on your system (1.401298e-45 for me). I'll issue an update to replace the hard-coded 0.1 with epsilon in all other grids as well. Where are you getting that memory error? I am not getting any kind of error and I tried out all the examples.

    As for your first question, the render queue is a property of the material.
    http://docs.unity3d.com/ScriptReference/Material-renderQueue.html
    http://docs.unity3d.com/Manual/class-Material.html
    If you don't provide your own render material a default material will be generated at runtime. Either provide your own material and set its properties or change the properties of the default material programatically at runtime. See the manual entry for GFGrid.renderMaterial for reference, it also includes the source of the default shader.
     
  17. module

    module

    Joined:
    Dec 30, 2012
    Posts:
    5
    Hello. I'm a bit new to Unity and saw your grid based solution. What I was wondering.. a possibility of turnbased pathfinding vie using Unitys path-finding system. May this be possible? Sorry if this was covered somewhere in this thread already.
    Thankyou
     
  18. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Sorry, this is not possible. Unity's NavMesh class does not allow you to set mesh data, you can only bake it automatically and read it. For what you want you would have to generate a mesh form the grid and then set that mesh as your NavMesh.

    Generating a mesh is simple enough:
    Code (csharp):
    1. GFGrid grid;
    2. int horizontal = Mathf.FloorToInt(grid.renderTo).x - Mathf.CeilToInt(grid.renderFrom).x;
    3. int vertical   = Mathf.FloorToInt(grid.renderTo).y - Mathf.CeilToInt(grid.renderFrom).y;
    4.  
    5. Vector3[horizontal*vertical] vertices;
    6.  
    7. for (int y = Mathf.CeilToInt(grid.renderTo).y; y <= Mathf.FloorToInt(grid.renderTo).y); ++y) {
    8.     for (int x = Mathf.CeilToInt(grid.renderTo).y; x <= Mathf.FloorToInt(grid.renderTo).y); ++x) {
    9.         // Grid coordinates to world coordinates
    10.         vertices[x + horizontal * y] = grid.GridToWorld(x, y, 0);
    11.     }
    12. }
    13. // triangles in a similar way
    We use the range of the grid to set the bounds and then generate a mesh that looks exactly like the grid. If you were to write your own pathfinding solution that would be the way to go.
     
  19. devstudents

    devstudents

    Joined:
    Feb 18, 2014
    Posts:
    132
    Hello Hipish. Your project looks very promising. I'm trying to achieve the following:

    1. I'm making a 3d, turn based, top down game. I'm trying to find a way to have movement during combat restricted to a grid, and to have the tiles that the player can move on highlighted during combat (like in Wasteland 2).
    2. I want to carefully plan the environment so obstacles (trees, rocks, buildings) take up a precise number of grid squares, and any objects instantiated into the environment at runtime are restricted to the squares. The smallest items will take up one complete grid square.
    3. I would like to integrate the grid with Aron Granberg's pathfinding project. There must be a way that the calculated path could be rounded to the center position of the closest grid square.

    I hope my goals make sense. Please let me know what you think and whether or not your project can help me achieve them.
     
    Last edited: Jul 14, 2015
  20. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Hi, I'll answer your question one by one.

    There are many ways of highlighting a portion of the ground, I guess the simplest would be to draw a semi-transparent sprite on top of it. This is really not a Grid Framework question, but if you want to the world coordinates of a grid point you would do something like this:
    Code (csharp):
    1. GFGrid grid;
    2. Vector3 gridPoint;
    3. Vector3 worldPoint = grid.GridToWorld(gridPoint)
    I assume you want to do that programmatically? If you know the grid coordinates convert it to world coordinates and instantiate it like any other GameObject in Unity. If you want to do it using smooth mouse input take the world coordinates the player is pointing to, convert to grid coordinates, round to the nearest multiple of the object's size and convert back to world.

    I have been looking at the documentation but I can't find anything about generating a nav mesh or nav graph at runtime. I have sent the Aron Granberg an email, so I have to wait until I hear from him. In theory, if there is an API to generate navigation data at runtime, then it should be quite simple. I have an example where a terrain mesh has been generated at runtime:
    http://hiphish.github.io/grid-framework/examples/terrain/
     
    devstudents likes this.
  21. devstudents

    devstudents

    Joined:
    Feb 18, 2014
    Posts:
    132
    Thank you for your response. I just purchased it. I look forward to implementing it into my project. Let me know what Aron Granberg says. It would be great if your mesh could be somehow integrated with his pathfinding code.
     
  22. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    I got my reply. The relevant pages are here:
    http://arongranberg.com/astar/docs_dev/accessing-data.php
    http://arongranberg.com/astar/docs_dev/graph-updates.php

    I wrote a small script that generates a grid graph from a rectangular grid at runtime:
    Code (csharp):
    1. using UnityEngine;
    2. using Pathfinding;
    3.  
    4. [RequireComponent(typeof(GFRectGrid))]
    5. public class Gridder : MonoBehaviour {
    6.     private GFRectGrid grid;
    7.  
    8.     void Start () {
    9.         grid = GetComponent<GFRectGrid>();
    10.         AstarData data = AstarPath.active.astarData;
    11.         var gg = data.AddGraph(typeof(GridGraph)) as GridGraph;
    12.  
    13.         gg.width = Mathf.RoundToInt(grid.renderTo.x - grid.renderFrom.x);
    14.         gg.depth = Mathf.RoundToInt(grid.renderTo.z - grid.renderFrom.z);
    15.         gg.nodeSize = 1;
    16.         gg.aspectRatio = grid.spacing.x / grid.spacing.z;
    17.         gg.center = grid.GridToWorld(Vector3.zero);
    18.  
    19.         gg.UpdateSizeFromWidthDepth();
    20.         AstarPath.active.Scan();
    21.     }
    22. }
    Here is what it looks like at runtime:


    Is that what you wanted?
     
    devstudents likes this.
  23. devstudents

    devstudents

    Joined:
    Feb 18, 2014
    Posts:
    132
    That looks perfect! Thank you very much.
     
  24. petrucio

    petrucio

    Joined:
    Aug 30, 2011
    Posts:
    124
    Why don't we have something like
    Code (CSharp):
    1. rectGrid.NearestEdgeW(out vertexA, out vertexB)
    ?
     
  25. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Because no has ever asked for it :p There are also mathematical problems: for a rectangular 2D grid convert the edges to vertices and you have and edge graph, then all you have to do is find the nearest vertex on the edge graph and the corresponding edge is the nearest edge in the real grid. But what about a 3D grid? And what if the grid is not rectangular but oblique (using shearing)?

    Hex grids are even worse, the edge graph of a hex grid is not a hex grid, it's a hexagram pattern (overlapping six-pointed stars).

    And for polar grids I don't even know what would qualify as an edge. Does the arc count as an edge? If so, what are its vertices? The end points of the arc? The end points of the arc segments as dictated by the smoothness?

    Writing a method that covers all cases for everyone is too much because there is no One True Way. I guess you want the nearest edge of a rectangular 2D grid with no shearing. In that case it would be easier to just write an extension method just for that purpose:
    Code (csharp):
    1. public static class GridEdgeExtension {
    2.     public static void NearestEdgeG(this GFRectGrid grid, out Vector3 vertexA, out Vector3 vertexB, Vector3 point) {
    3.         if (point.y % 1.0f > point.x % 1.0f) {
    4.             //north or west
    5.  
    6.             // NW-vertex
    7.             vertexA = new Vector3(Mathf.Floor(point.x), Mathf.Ceil(point.y), Mathf.Round(point.z));
    8.             if (1.0 - (point.y % 1.0f) < point.x % 1.0f) {
    9.                 // north
    10.  
    11.                 // NE-vertex
    12.                 vertexB = new Vector3(Mathf.Ceil(point.x), Mathf.Ceil(point.y), Mathf.Round(point.z));
    13.             } else {
    14.                 // west
    15.  
    16.                 // SW-vertex
    17.                 vertexB = new Vector3(Mathf.Ceil(point.x), Mathf.Ceil(point.y), Mathf.Round(point.z));
    18.  
    19.             }
    20.         } else {
    21.             //south or east
    22.  
    23.             // SE-vertex
    24.             vertexA = new Vector3(Mathf.Ceil(point.x), Mathf.Floor(point.y), Mathf.Round(point.z));
    25.             if (1.0 - (point.y % 1.0f) < point.x % 1.0f) {
    26.                 // east
    27.            
    28.                 // NE-vertex
    29.                 vertexB = new Vector3(Mathf.Ceil(point.x), Mathf.Ceil(point.y), Mathf.Round(point.z));
    30.             } else {
    31.                 // south
    32.            
    33.                 // SW-vertex
    34.                 vertexB = new Vector3(Mathf.Floor(point.x), Mathf.Floor(point.y), Mathf.Round(point.z));
    35.             }
    36.         }
    37.  
    38.     }
    39.  
    40.     public static void NearestEdgeW(this GFRectGrid grid, out Vector3 vertexA, out Vector3 vertexB, Vector3 point) {
    41.         Vector3 gridPoint = grid.WorldToGrid(point);
    42.         NearestEdgeG(out vertexA, out vertexB, gridPoint);
    43.         vertexA = grid.GridToWorld(vertexA);
    44.         vertexB = grid.GridToWorld(vertexB);
    45.     }
    46. }
     
  26. petrucio

    petrucio

    Joined:
    Aug 30, 2011
    Posts:
    124
    I get the idea, but I'm having some issues. My grid is indeed a rectangular 2D grid. My plane is XZ so I had to change some things, but that's not the issue.

    First: on your line 17, SW would be Floor, Floor, making that line identical to line 34, and thus making the whole code duplication for VertexB not necessary.

    With that in mind, the modified code looks like this:

    Code (CSharp):
    1. //=============================================================================
    2. public static class GridEdgeExtension
    3. {
    4.     //=============================================================================
    5.     public static void NearestEdgeG(this GFRectGrid grid, out Vector3 vertexA, out Vector3 vertexB, Vector3 point)
    6.     {
    7.         if (point.z % 1.0f > point.x % 1.0f) {
    8.             // north or west: NW-vertex
    9.             vertexA = new Vector3(Mathf.Floor(point.x), Mathf.Round(point.y), Mathf.Ceil(point.z));
    10.         } else {
    11.             // south or east: SE-vertex
    12.             vertexA = new Vector3(Mathf.Ceil(point.x), Mathf.Round(point.y), Mathf.Floor(point.z));
    13.         }
    14.         if (1.0 - (point.z % 1.0f) < point.x % 1.0f) {
    15.             // north or east: NE-vertex
    16.             vertexB = new Vector3(Mathf.Ceil(point.x), Mathf.Round(point.y), Mathf.Ceil(point.z));
    17.         } else {
    18.             // west or south: SW-vertex
    19.             vertexB = new Vector3(Mathf.Floor(point.x), Mathf.Round(point.y), Mathf.Floor(point.z));
    20.          }
    21.        
    22. Debug.Log("NearestEdgeG " + point + ": " + vertexA + "-" + vertexB);
    23.     }
    24.     //=============================================================================
    25.     public static void NearestEdgeW(this GFRectGrid grid, out Vector3 vertexA, out Vector3 vertexB, Vector3 point)
    26.     {
    27.         Vector3 gridPoint = grid.WorldToGrid(point);
    28.         grid.NearestEdgeG(out vertexA, out vertexB, gridPoint);
    29.         vertexA = grid.GridToWorld(vertexA);
    30.         vertexB = grid.GridToWorld(vertexB);
    31.     }
    32. }
    33.  
    This works sometimes but fails frequently. Take for example the simple case where I try to get the nearest edge to the points (0.5, 0.0, 0.2) and (0.5, 0.0, -0.2) - both should return the same edge, the first case a south edge of one face, and the second case a north edge of the face below that. What I get instead is (1.0, 0.0, 0.0)-(0.0, 0.0, 0.0) for the first point (correct), and 1.0, 0.0, -1.0)-(0.0, 0.0, -1.0) for the second (wrong).

    My first gut reaction to add Mathf.Abs to the if checks, but that didn't solve it.
     
  27. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Ah right, negative numbers... The purpose of the modulo is to convert the grid coordinates (x, z) to "face coordinates" (u, v) where a the south-western point has coordinates (0,0) and the north-eastern point has face coordinates (1, 1). Negative numbers throw a wrench into this. Here is an updated version:
    Code (csharp):
    1. float u = point.x % 1.0f, v = point.z % 1.0f;
    2.  
    3. if (u < 0) {u = 1.0f + u;}
    4. if (v < 0) {v = 1.0f + v;}
    5.  
    6. if (v > u) {
    7.     // north or west: NW-vertex
    8.     vertexA = new Vector3(Mathf.Floor(point.x), Mathf.Round(point.y), Mathf.Ceil(point.z));
    9. } else {
    10.     // south or east: SE-vertex
    11.     vertexA = new Vector3(Mathf.Ceil(point.x), Mathf.Round(point.y), Mathf.Floor(point.z));
    12. }
    13. if (1.0f - v < u) {
    14.     // north or east: NE-vertex
    15.     vertexB = new Vector3(Mathf.Ceil(point.x), Mathf.Round(point.y), Mathf.Ceil(point.z));
    16. } else {
    17.     // west or south: SW-vertex
    18.     vertexB = new Vector3(Mathf.Floor(point.x), Mathf.Round(point.y), Mathf.Floor(point.z));
    19. }
    This way a Z-coordinate -0.2 becomes V-coordinate 0.8, which means the northern edge is returned. And good call on spotting the redundancy.
     
  28. petrucio

    petrucio

    Joined:
    Aug 30, 2011
    Posts:
    124
    Beautiful, thanks!
     
  29. actraiser3

    actraiser3

    Joined:
    Sep 11, 2014
    Posts:
    28
    Hello. I have a basic question on the align panel.

    It does not (seem to) remember the Grid-Gameobject so I have to drag and drop it every time from Hierarchy to the appropriate Align Panel-Objectfield when hitting play to work in my Runtime-LevelEditor.

    Is there a way to set that Editor Window-objectfield from script at runtime or is there another way for setting the Gameobject with the attached Grid to the Grid Align Panel without manually dragging it?

    Best
    -act
     
  30. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    That's something I have been struggling with as well. According to this it should work:
    http://blogs.unity3d.com/2012/10/25/unity-serialization/

    Grids are serializable, otherwise they would not persist at all. It appear that the issue is the fact that grids are components, which in return are tied to a GameObject in the scene and it is not possible for an editor to keep track of a scene object beyond the current session, i.e. until you hit play or change the scene. All the other fields are preserved because they are not components.
     
  31. actraiser3

    actraiser3

    Joined:
    Sep 11, 2014
    Posts:
    28
    I have come up with a simple modification which works for me. It will load a default Grid on Play() from the Scene which must be a GameObject called "Grid" with an attached GFGrid-Component. You can still assign other Grids during runtime but they will of course still not persist when stopping play.

    Code (CSharp):
    1. // in GFGridAlignPanel.cs modify GridField()-method
    2.  
    3. void GridField () {
    4.     grid = (GFGrid) EditorGUILayout.ObjectField("Grid:", grid, typeof(GFGrid), true);
    5.     if (grid == null) {
    6.         GameObject defaultGrid = GameObject.Find("Grid");
    7.         if (defaultGrid != null){
    8.             grid = defaultGrid.GetComponent<GFGrid>();
    9.         }
    10.     }
    11. }
    -act
     
    Last edited: Jul 22, 2015
  32. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Nice, but that's not something I can ship for everyone, it only works in your specific setup. I recommend making a copy of my script and modifying that instead of the original, otherwise your changes will be overwritten after each update.
     
  33. SuHwanK

    SuHwanK

    Joined:
    Dec 30, 2014
    Posts:
    43
    Hi, Hiphish.
    Can I apply snap the object apply Grid's Rotate?
    I don't speak english very well. so i upload a files. about my question.
    GridQuestion1.png
    so.. Red Line is My Grid Plane.
    It's rotated 10 to Z axis.
    but i found source for positionning object.(flag = drag)
    there is not apply rotate.
    Can i do this?
     
  34. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Hello,

    Do you mean at runtime using the API (scripting) or in the editor using the panel? The panel has a flag "Rotate to Grid", if it is on objects will be rotated.

    If you mean scripting take a look in the manual (Help -> Grid Framework Documentation) at the method GFGrid.AlignTransform:
    Code (csharp):
    1. GFGrid myGrid;
    2. Transform someObjectTransform;
    3. myGrid.AlignTransform(theTransform: someObjectTransform, rotate: true);
    If rotate is set to true the aligned object will have the same rotation as the grid. There is nothing special going on here, this is what the method looks like:
    Code (csharp):
    1. public void AlignTransform(Transform theTransform, bool rotate, BoolVector3 ignoreAxis) {
    2.     theTransform.position = AlignVector3(theTransform.position, theTransform.lossyScale, ignoreAxis);
    3.     if (rotate) {theTransform.rotation = transform.rotation;}
    4. }
    The third line is what makes the object rotate.

    Is that what you were looking for?
     
  35. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    On your website examples page the 1st two videos are exactly the same:
    Moving Along A Grid & Moving With Obstacles
     
  36. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Thanks, but I don't have a video of that one. I should re-do my videos eventually anyway in a cleaner manner, then I'll make one for that example as well.
     
  37. Pendulum_

    Pendulum_

    Joined:
    Jul 30, 2015
    Posts:
    83
    Awesome product. I purchased it this morning and have been playing with it since. I'm sure the answers to my questions are very simple, so I apologize.

    I'm using a slightly modified version of your snapping example and am having trouble Instantiating units on to it. In editor it works fine, but then when I instantiate the units they ignore the grid. I have the grid prefab selected in the unit prefab.

    Also, what is the best way to change it so that when you drag an object over another object, it wont go red and snap to an empty slot, but it will change places with it?
     
    Last edited: Aug 17, 2015
  38. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    That's the problem. A prefab is essentially a template for what the object should look like when it is instantiated, i.e. when it is put in memory. But the thing is, it does not yet exist, it is not in memory. The objects have a reference to a grid, something like this:
    Code (csharp):
    1. public GFGrid grid;
    A reference is an address in memory, but you cannot get the address of something that does not yet exist. This isn't just with grids, it's with any reference type, which is what objects in C# always are. Even if you are using UnityScript it compiles down to the same thing.

    So in short, you cannot have a reference to a prefab working out of the box. But what about a reference to a prefab we instantiate and then assign at runtime? That works, but we have a little problem:
    Code (csharp):
    1. void Awake () {
    2.     gridCollider = _grid.gameObject.GetComponent<Collider> ();
    3.     // ...
    4. }
    This is the only point where the grid collider (a private member) is assigned, so if you assign the grid reference after your object has been initialised these steps will not be called and without a grid collider we cannot do collision detection for the mouse cursor. We need a sort of refresh function:
    Code (csharp):
    1. public void Refresh () {
    2.     gridCollider = _grid.gameObject.GetComponent<Collider> ();
    3.     // Rest of Awake...
    4. }
    5.  
    6. void Awake () {
    7.     Refresh();
    8. }
    You have to call Refresh() after you have assigned the grid at runtime:
    Code (csharp):
    1. GameObject go;
    2. GFGrid grid;
    3. go.SnappingUnits._grid = grid;
    4. go.SnappingUnits.Refresh();
    5.  
     
  39. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Always remember the last snap position of the object you are dragging:
    Code (csharp):
    1. private Vector3 oldPosition;
    This already exists, so the next step is to swap positions. We need to change the IntersectionTrigger script to pass a reference to the object that was intersected:
    Code (csharp):
    1. void OnTriggerEnter(Collider other){
    2.     snappingScript.SetIntersecting(other.transform);
    3. }
    4.  
    5. void OnTriggerExit(Collider other){
    6.     snappingScript.SetIntersecting(null);
    7. }
    Now change the SetIntersecting method in the SnappingUnits script:
    Code (csharp):
    1. public void SetIntersecting(Transform t){
    2.     if(!beingDragged || !t) // ignore sitting objects, only moving ones should respond
    3.         return;
    4.  
    5.     // Move the static object to where the draged one used to be
    6.     t.position = oldPosition;
    7.     oldPosition = transform.position;
    8. }
     
  40. alphaprime

    alphaprime

    Joined:
    Jul 22, 2014
    Posts:
    4
    Hi. I am studying the examples. The Unity Console panel complained concerning the Vectrosity example until four lines were changed. The example runs great. I wish I could make a screensaver from it.

    ColorSwappingGrid.js

    12 private var colors: Color32[];
    14 private var lineColors: Color32[];
    31 colors = new Color32[7];
    33 lineColors = new Color32[grid.GetVectrosityPoints().Length / 2];
     
  41. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    The Vectrosity API changes sometimes, so the example gets out of date when that happens. The Vectrosity points methods are still valid though.
     
  42. kregenrek

    kregenrek

    Joined:
    Dec 21, 2014
    Posts:
    10
    Hi, after many months I still don't know how to resolve this problem. I tried to handle this with delegates/events and some EnemyHandler Class but I'm not sure how to implement this case the right way. Is there any example you could provide (in c# code) to help me out with this problem?

    Best Regards,
    Kevin.
     
  43. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Sorry for the late reply, it took me a while to recall what the topic was.

    When I said delegation I meant the kind of delegation that's used in Cocoa development (iOS and OS X). I'll try to explain it the way it was explained to me: take Robocop and Knight Rider, both of them need to be able to do extraordinary things. The Robocop approach is to build it directly into the agent, while the Knight Rider approach is to have another object (the delegate) that will do the task when the agent needs it. In the Robocop case you extend the agent directly (more code in your class), whereas in the Knight Rider case your agent lets another object do the task.

    The Robocop approach would be to let your agents decide themselves how to avoid collision. This means every agent has to be able to make informed decisions that take itself and the other agents into consideration, it needs to know the state of the game. The Knight Rider approach would keep the agents dumb and create a new object that only serves to coordinate their movement. An agent would tell the delegate "I want to move from here to there" and after all agents have told the delegate what they want (all agents share the same delegate), the delegate decides who gets to move and who gets to wait and tells them what to do.

    Here is an example scene
    Code (csharp):
    1. Agent_1
    2. Agent_2
    3. Agent_3
    4. Agent_4
    5. Delegate
    These are all GameObjects in your scene. The agents are what you want to move (people, cars, whatever) and the delegate is an object with just a script. For convenience the delegate could be the object that is carrying the grid.

    Here is what a part of the agent script can look like. It has a reference to the delegate object and a method to attempt to move. When it attempts to move it does the usual stuff and then it informs the delegate that it will move.
    Code (csharp):
    1. public IDelegate d; // reference to the delegate
    2.  
    3. private void AttemptMove(Vector3 from, Vector3 to) {
    4.     // some computation first
    5.     d.WillMove(this, from, to);
    6. }
    The delegate would have such a method
    Code (csharp):
    1. public void WillMove(Actor a, Vector3 from, Vector3 to) {
    2.     bool canMove;
    3.     // This is where we make the decision
    4.     //...
    5.     if (canMove) {
    6.         // Do the actual movement
    7.         a.Move(from, to);
    8.     }
    9. }
    How the delegate decides is up to you, the point is that the agents themselves are delegating the decision to an object that is more capable than them.

    This is used to keep objects simple. For instance in Cocoa, a window wants to close, but whether it is allowed to depends on the state of the application. Rather than coding the window to know all about the application we delegate that decision to an object that is better suited for the task. A window should only have to display things.

    A delegate object can also serve other purposes to other objects: If your delegate object is also the grid object you can use it for other grid-related tasks as well.

    Here is a Wikipedia link
    https://en.wikipedia.org/wiki/Delegation_pattern
    Delegation is a more advanced pattern, but once you get the hang of the idea it is pretty straight-forward.
     
  44. lighting

    lighting

    Joined:
    Dec 21, 2011
    Posts:
    42
    Hi,

    I'm considering buying your GRID asset; however I'll let myself to ask you a few questions if this package can do following:
    1. I'm interested in square grid model. Do this grid is visualized on the GameObject, e.g. plane in Run mode ?
    2. If #1 is true, so let's say, I have plane with square grid. Is it possible, that the grid will create regions that will react on mouse hover/click ? In other words, could the grid cells be interactive ?
    3. The logic related to path-finding with these grid cells - does the package support sth like that or it need to be added manually ?
    4. The final question which concludes all above; if I have two+ separate planes with grids and I'd like them to be stick together (lets compare to tetris game), would it be hard to merge the grid from different planes (e.g. for pathfinding purposes) ?

    Thanks
     
  45. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Hello,

    Let me first explain a thing: a grid is not a mesh or a collection of regions, it is a mathematical construct, a coordinate system if you will. It is defined by an origin and its basis vectors. The origin is the position of the GameObject and the basis vector are the X-, Y- and Z-unit vectors scaled by the grid's spacing. There are methods to convert coordinates from and to grid-space, to align objects, to render the grid and so on. The grid itself is infinitely large, the rendered portion is just an area you choose.

    Now on to your questions:
    Yes, you can decide whether to show or hide the grid in play- and scene mode individually. Here is a playable example where the grid is rendered on top of the plane:
    http://hiphish.github.io/grid-framework/examples/snapping/
    You should visually move the grid a bit above the plane so it doesn't loot that jagged, like 0.01 units, doesn't have to be much, just so there isn't any ambiguity for the renderer which to display first.

    As I explained above, a grid is just a coordinate system, so it doesn't have any concept of regions and clicking. But it is really simple to make your own regions:
    Code (csharp):
    1. int startX, startY;
    2. int endX, endY;
    3. GFGrid grid;
    4. for (int i = startX; i < endX; i++) {
    5.     for (int j = startY; i < endY; j++) {
    6.         MakeClickableRegion(grid.GridToWorld(new Vector3(i, j, 0)));
    7.     }
    8. }
    We loop over the grid coordinates, convert them to world coordinates and use that position to set up our colliders or whatever. If you prefer having one large collider and testing where it was clicked we can do that as well:
    Code (csharp):
    1. Vector3 clickPosition;
    2. GFGrid grid;
    3.  
    4. Vector3 gridPoint = grid.WorldToGrid(clickPosition);
    5. int clickX = Mathf.RoundToInt(gridPoint.x);
    6. int clickY = Mathf.RoundToInt(gridPoint.y);
    Having one collider is more performant than having several. The point is that Grid Framework does not lock you into a workflow I envisioned, it offers you the tools to approach the problem the way way you want.

    I'm sorry there is nothing built in. People have successfully used Grid Framework together with other pathfinding solutions where Grid Framework generates the points and the pathfinding solution takes over from there. Here is where I helped another customer to set it up with Aron Granberg's A* Pathfinding:
    http://forum.unity3d.com/threads/gr...nd-editor-plugins.144886/page-16#post-2205439
    I have wanted to write a bridge between the two plugins, but I'm currently busy with the website redesign.

    I don't understand what you mean? If you want to treat several grids as one large grids you can just use one invisible grid for your logic and use additional grid for display. An invisible grid does not waste time on rendering and the memory impact of grids is very small, so you can have many of them in your scene.
     
    lighting likes this.
  46. lighting

    lighting

    Joined:
    Dec 21, 2011
    Posts:
    42
    First of all, thank you very much for robust explanation of my questions :)

    As final effect I'd like to have different planes, that could be merged or split together, all of them covered by interactive grid and all of them serving also as dynamic path-finding (dynamic, because navmesh will change in runmode).
    With all description above, I'm convinced it's feasible with you solution :)

    Regards,
    Maciej
     
  47. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Yes, that can be done, either by really merging grids (make on of them grow to cover both areas and destroy the other) or as I said above by using one large grid. For pathfinding you will need to see how your solutions lets you add or remove nodes from a nav-graph then.
     
  48. HarryDemch

    HarryDemch

    Joined:
    Mar 9, 2015
    Posts:
    19
    Hi hiphish!

    I'm working on mobile puzzle game and I had some problems making grid-based levels.

    I would like to know if your plugin can give me what I need for my game. Can you please answer my questions?

    1. I need to build only one grid (let's say 6x6) but for each level I need to be able to activate only selected cells (so they become visible) and move objects only on those cells (see grid.gif below). Is it possible? How hard is it to make it?

    2. I need glowing grid for the game . It looks perfect on one of the screenshots on the Asset Store page. Can I make the grid look like this out of the box?

    Thanks!
     

    Attached Files:

    Last edited: Sep 27, 2015
  49. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    Hi Daggerwock.

    1) Not out of the box, by default you can only set a range from and to. If you just want to highlight one cell that is enough:
    Code (csharp):
    1. GFGrid grid;
    2. int x, y; // cell coordinate
    3.  
    4. grid.renderFrom = new Vector3(x, y, 0);
    5. grid.renderFrom = new Vector3(x + 1, y + 1, 0);
    If you want more complicated patterns like in your GIF you can either have many extra grid on top of your main grid and have them all be invisible. Then when you want to highlight cells render one cell of each cell's fake grid. Invisible grids don't waste any processing on rendering.

    The downside of that approach is that shared edges of cells will be rendered twice which can look ugly when you use transparency or blurring effects.

    A better idea would be a line rendering plugin like Vectrosity and pass it the coordinates of the edges you want to render. Here is how to get the edges of a cell:
    Code (csharp):
    1. int x, y;
    2. struct Edge {
    3.     Vector3 vertex0;
    4.     Vector3 vertex1;
    5.  
    6.     Edge(Vector3 vertex0, Vector3 vertex1) {
    7.         this.vertex0 = vertex0;
    8.         this.vertex1 = vertex1;
    9.     }
    10. }
    11.  
    12. // convert grid coordinates to world coordinates
    13. Vector3 sw = grid.GridToWorld(new Vector3(x    , y    , 0))
    14. Vector3 nw = grid.GridToWorld(new Vector3(x    , y + 1, 0))
    15. Vector3 ne = grid.GridToWorld(new Vector3(x + 1, y + 1, 0))
    16. Vector3 se = grid.GridToWorld(new Vector3(x + 1, y    , 0))
    17.  
    18. // Every edge is made of two out of four vertices
    19. Edge south = new Edge(SW, SE);
    20. Edge west  = new Edge(SW, NW);
    21. Edge north = new Edge(NW, NE);
    22. Edge east  = new Edge(NE, SE);
    2) You mean the one with the glowing laser lines?
    http://hiphish.github.io/grid-framework/examples/vectrosity/

    That one is achieved using Vectrosity and a custom shader written by the Vectrosity guy. The shader is included, but it doesn't look as good on its own. You can write your own shader if you want using that shader as a base. Grid Framework lets you specify your own shader, and if you don't the plain default shader will be applied.
     
  50. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    586
    You can go to Help -> Grid Framework -> Documentation and it will list the current version in the top left under the name. Or you can check the text file Assets/Grid Framework/changelog.txt, the latest changes will be at the top. But yes, some standardised way would be preferable.