Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

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:
    626
    Grid Framework version 2.1.0 released
     
    Tinjaw likes this.
  2. Tinjaw

    Tinjaw

    Joined:
    Jan 9, 2014
    Posts:
    518
    Thanks for v 2.1. It is exactly what I asked for. :)
     
    hiphish likes this.
  3. Batsuzai

    Batsuzai

    Joined:
    Jul 15, 2016
    Posts:
    2
    Having trouble enabling PlayMaker support. Based on documentation I'm supposed to created two files in the Assets root directory (smcs.rsp and us.rsp) of my Unity project file. Not quite sure how I go about doing that, tried searching the internet but came up empty.

    Could you please give a bit more details how to enable the support step by step?

    Thank you.
     
  4. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    There is no Unity project file, it's a directory (folder). You can open it by right-clicking any of your files in Unity's Project view and choosing "show in file explorer" or something like that (depends on your operating system). Or navigate in your file manager to where your Unity project is (I have mine in ~/Developer/Unity/ for example), there you will find the directory of your project and inside there is the Assets directory. So if I wanted to enable Playmaker in my Grid Framework project I would go to ~/Developer/Unity/grid-framework/Assets.

    Now you need to create the files. Just create them as text files and make sure they have the extension .rsp, not .rsp.txt or anything like that. There are other ways of defining preprocessor symbols in Unity, see the link to the Unity documentation in the manual. I find this one to be the most robust and universal solution though.
     
  5. Batsuzai

    Batsuzai

    Joined:
    Jul 15, 2016
    Posts:
    2
    Thanks for the quick response. I mistype regarding the Unity folder, sorry for that misunderstanding.

    I was able to create the .rsp file (I suspected it be a text file but wasn't sure) in my unity project Assets directory [...\Documents\Unity Projects\Playmaker-GridPro Test\Assets]. Unfortunately I didn't notice any changes nor was I able to find any PlayMaker Actions tied to Grid Framework.

    Is the following code snippet correct for my smcs.rsp file?
    Code (CSharp):
    1. #define GRID_FRAMEWORK_PLAYMAKER
     
    Last edited: Jul 15, 2016
  6. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    You must re-compile the project. The easiest way is to just create some new useless script in Unity and then delete it again, that should cause Unity to run the compiler again.
     
  7. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    Can you please update the tutorials, @hiphish ?
     
  8. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    I am in the process of updating the videos, but I really have problems talking into a microphone, it takes me dozens of takes to even say one sentence without botching it. I don't have a problem talking to people, I only struggle when talking to nobody. Is there a particular tutorial you have problems with?
     
  9. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    I'm struggling with changing the size manually. There doesn't seem to be an option to do this in the Rectangular Grid script. I also don't see anything about it in the documentation.
     
  10. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    The size has had been deprecated in favour of renderFrom and renderTo in version 1. In version 2 I moved all rendering-related code from the grid classes into separate renderer classes. You have to add a Parallelepiped class to your grid and then set its From and To to what you want the size to be. The old size worked as if the From was the negative of To. So if your size would have been (2, 4, 6) you have to set the From to (-2, -4, -6) and the To to (2, 4, 6).
     
  11. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    Ah, thank you for your prompt reply. I'll try doing this.

    Thanks again,

    8Development
     
  12. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    Hey @hiphish,
    I've figured out the grid setup, but again, I'm lost in figuring out how to check if the box in front is available/occupied with a GameObject. This is my logic:
    + we can first calculate the players position relative to the grid (based on the method you've said to use on your examples page)
    + then, we can calculate the size of the grid in area (e.g, 25 boxes)
    + if the players current position exceeds the area of the 25 boxes (really not sure how to check this)
    + then set the respective bool

    I've tried implementing this in code, but I'm not sure how I'd check if the current position exceeds the area (grid-framework wise).

    This is what I've come up with:

    Code (CSharp):
    1.   private bool canMove;
    2. void Pathfind ()
    3.     {
    4.         // Declare variables that are relevant to this method, and this method only.
    5.         Vector3 relativePosition = grid.WorldToGrid (m_transform.position);
    6.         int size = Mathf.FloorToInt (Mathf.Abs (gridRender.From - gridRender.To) / grid.Spacing);
    7.  
    8.         if (relativePosition > size) {   // of course we can't do this, as size is an int
    9.             canMove = false;    //  Don't move. The user has to swipe another direction (which they can see is open).
    10.         } else {
    11.             canMove = true;        //  Take the input and move on to the hopping co-routine.
    12.         }
    13.     }
    Could you kindly assist me in this? I'd really appreciate any help. I'm also open to better methods (perhaps checking if the box to the left or right is occupied by a gameobject, such as a wall).

    Regards,

    8Development
     
    Last edited: Aug 3, 2016
  13. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Don't divide the renderer's From and To by the grid's spacing, they are already in grid-coordinates. From what I understand you want to know if a point is within the visible range of the grid, right? This will do the trick:
    Code (csharp):
    1. var lower = gridRenderer.From;
    2. var upper = gridRenderer.To;
    3. var relativePosition = grid.WorldToGrid (m_transform.position);
    4.  
    5. // for every coordinate make sure the relativePosition is greater than lower and less than upper
    6. var canMove = true;
    7. for (var i = 0; i < 3; ++i) {
    8.     // EDIT: it has to be &= instead of |=
    9.     canMove &= relativePosition[i] >= lower[i] && relativePosition[i] <= upper[i];
    10. }
    If your player has a certain "width" to it you will want to add that width to lower and subtract it from upper. If you only want to clamp the position between the two limits we can make it slightly easier:
    Code (csharp):
    1. relativePosition = Vector3.Max(Vector3.Min(relativePosition, upper), lower);
    2. var absolutePosition = grid.GridToWorld(relativePosition);
     
  14. Elvess

    Elvess

    Joined:
    May 27, 2013
    Posts:
    4
    Hello.
    Currently Im looking for the grid framework and Id like Yours.
    In fact I have not enought time to study all the features of all grid frameworks on Asset Store. Would U like to be so kind to tell if Your framwrok supports the next features:
    1. Marking cells of rect grid by type: passable, unpassable, flyable etc. and get these types
    2. If object (for example building) ocupates more than one cell (three or four or more), can I put on on the grid if the position is good (there are cells of type "build" in all cells that building ocupates)

    Thank U very much,
    Paul.
     
  15. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Hello Paul, I will answer your question individually.

    1. Grid Framework does not impose any particular way of how you want to structure your game, so you will have to implement this yourself. I will give you a very simple way: keep track of your game in a 2D (or 3D) array where the index of an element corresponds to the grid-coordinates of your game logic.
    Code (csharp):
    1. // The type for the tiles/cells, you can use your own types here
    2. enum TerrainType {
    3.     passable,
    4.     unpassable,
    5.     flyable
    6. };
    7.  
    8. // Model the map of your game in this array
    9. int width, height;
    10. TerrainType[,] map = new TerrainType[width, height];
    11.  
    You don't need Grid Framework or any other framework for that, you can write your entire game logic in grid-coordinates. But you will eventually need to convert those grid-coordinates to world-coordinates so you can move your units around or display animations. This is where Grid Framework comes in.
    Code (csharp):
    1. int x, y;  // position in grid coordinates
    2. RectGrid grid;  // the grid object you want to use
    3. Vector3 worldPosition = grid.GridToWorld(new Vector3(x, y, 0));
    Grid Framework is not a "just add water" type of solution, and that's intentional. A "just add water" framework limits your game to what the framework's author envisioned you to do, whereas Grid Framework's approach is to wrap up the mathematics so you can write your game logic the way you want without dealing with the mathematical details.

    2. Again, this depends on how you want to implement your game. Let's use the example above: you first compute how many tiles your building would take up, then you find the location of a certain pivot-tile (let's say the lower-left tile) of your object and then you check your array to see if the pivot tile and all the other tiles are allowed.
    Code (csharp):
    1. // First we need to find out how many tile the building is large
    2. // Rectangular grids have a Spacing property
    3. int buildingWidth = building.scale.x / grid.Spacing.x;
    4. int buildingHeight = building.scale.y / grid.Spacing.y;
    5.  
    6. // Compute the grid-coordinates of the lower-left corner of the building
    7. Vector3 gridPosition = grid.WorldToGrid(building.position - 0.5 * building.scale);
    8. // converting to int truncates the decimals
    9. int x = gridPosition.x, y = grid.Position.y;
    10.  
    11. // First check whether the building is even within the game map
    12. if (x < 0 || y < 0 || x + buildingWidth > width || y + buildingHeight > height) {
    13.     // Handle the error
    14. }
    15.  
    16. // Now check the tiles inside the array
    17. bool legalPosition = true;
    18. for (var i = 0; i < buildingWidth; ++i) {
    19.     for (var j = 0; j < buildingHeight; ++j) {
    20.         legalPosition &= TileIsLegalToBuildOn(map[x + i, y + j]);
    21.     }
    22. }
    23.  
    24. return legalPosition;
    25.  
    As you can see we are doing the reverse of the above here: we have some information in world-coordinates, we convert it to grid-coordinates using Grid Framework, and then we make an informed decision by writing our logic in grid-coordinates. Grid Framework is the glue that makes it easy to convert from your view-space (world coordinates) to your model-space (grid-coordinates). I was assuming here that the player would drag&drop the building into the scene, that's why the building's position was in world-coordinates initially, but of course this depends on how your game works.

    I hope this answers your questions, but let me know if you want to know more.
     
  16. Elvess

    Elvess

    Joined:
    May 27, 2013
    Posts:
    4
    Thank U very much for the quick and clear answer and I thing Your Framework is the one we need.
    I suppose the base information about coding could be found in users manual after downloading the plugin.

    Thank U once more.

    Best regards,
    Paul.
     
  17. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Right, you can access the manual from Unity's menu Help -> Grid Framework Documentation. There is a user manual for understanding the concepts and a scripting reference for the actual code.
     
  18. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    Is it possible that I can downgrade? I don't need these advanced features, and I'm guessing the newer version doesn't do/need much optimization, either.
     
  19. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    I don't think the Asset Store allows purchasing a deprecated release anymore. But if it's about complexity, version 1 has a much more bloated API, which was part of the reason I wrote version 2. It was what happens when you don't fully plan ahead and add things that might come in handy once in a blue moon. The more advanced features are a side-effect of better organised code.

    What exactly is your problem? The biggest difference between version 1 and version 2 from an end-user perspective is the introduction of the renderers. Previously all visual information (size, range, colours) was stored in the grid itself. Now they are separate, the grid only contains the mathematical information and the renderers contain the visual information. A grid is infinitely large, and has been so since the very first release. The range of a renderer is just a visual thing. In version 1 when I wanted to use a finite slice of the grid I would write something like this:
    Code (csharp):
    1. // Clamp a position between two limits
    2. GFGrid grid;
    3. Vector3 position;
    4.  
    5. return Vector3.Max(Vector3.Min(position, grid.renderTo), grid.renderFrom);
    Now that the range is in its own class I first need a renderer and then I reference it:
    Code (csharp):
    1. RectGrid grid;
    2. Parallelepiped renderer;
    3. Vector3 position;
    4.  
    5. return Vector3.Max(Vector3.Min(position, renderer.To), renderer.From);
    It is possible to have multiple renderers per grid, that's why we can't do something like
    Code (csharp):
    1. grid.Renderer.From;
    And if you are wondering about the weird name, a grid can be rendered in different ways, at least in theory. Currently only hex grids have different renderers, but it would be easy to add new renderers for rectangular grids in the future. You could even write your own if you want to. The parallelepiped is simply the name of the particular shape this renderer generates.

    One more difference are the extension methods. There are some methods that don't really belong in the grid but are still general enough to be useful. Those are methods that could be written in a few lines using only the public API of the grid. They are included as extension methods and need to be imported explicitly:
    Code (csharp):
    1. using GridFramework.Extensions.Align;
    2. grid.AlignVector3(position);
    As you can see we first have to import the extensions, then we can use them just as if they were part of the class. In version 1 these methods were a fixed part of the grids, bloating the API. By putting them in a separate place they are still there, but nicely tucked away until you need them.

    If you are having trouble browsing the API click on the classes tab of the navigation bar, then click [N] Grid Framework to collapse it all and click it again to expand. Now it will only be expanded by one level instead of fully. You can ignore the namespaces Editor, Matrices, Vectors and Rendering unless you want to expand on Grid Framework. The important ones are Grids (obviously), Renderers (complementing the grids) and Extensions if you are curious. Some of the classes have nested classes (something engine in args) and events, you can ignore those unless you are interested in C# events. You can also ignore anything that is declared protected, that's just if you want to expand Grid Framework.
     
  20. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    I'd really appreciated simpler ways of doing things, like, grid.size. Even in your code, you had to think of an extra variable (but an extra variable - some simpler methods seems like a lose for me). I see what you did and why you did it, but I really am looking for the older versions - perhaps you have a backup (I can give you proof of purchase). I will probably use the current Grid Framework for other, more advanced projects, but for now, I'd like Grid Framework 1, if that's okay.
     
  21. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    If you send me a PM with your purchase verification code I can send you a version 1 build, but it really won't make your life easier. You can replicate grid.size with two extension methods:
    Code (csharp):
    1. using GridFramework.Grids;
    2. using GridFramework.Renderers.Rectangular;
    3.  
    4. public static class GridSizeExtensions {
    5.     public static Vector3 GetSize(this RectGrid grid) {
    6.         var go = grid.GameObject;
    7.         var parallelepiped = go.GetComponent<Parallelepiped>();
    8.         if (!parallelepiped) {
    9.             parallelepiped = go.AddComponent<Parallelepiped>();
    10.         }
    11.         // This assumes that To and From have the same value with opposite sign
    12.         return parallelepiped.To;
    13.     }
    14.  
    15.     public static void SetSize(this RectGrid grid, Vector3 size) {
    16.         var go = grid.GameObject;
    17.         var parallelepiped = go.GetComponent<Parallelepiped>();
    18.         if (!parallelepiped) {
    19.             parallelepiped = go.AddComponent<Parallelepiped>();
    20.         }
    21.         parallelepiped.To = size;
    22.         parallelepiped.From = -size;
    23.     }
    24. }
    25.  
    26. // Now call it like this
    27. grid.SetSize(Vector3.one);
    28. ver size = grid.GetSize();
    https://msdn.microsoft.com/en-us/library/bb383977.aspx

    This will add the methods GetSize and SetSize to your rectangular grids and take care of finding and mutating the renderer for you. If you have more than one renderer for your grid it will be undefined which renderer will be chosen, but as long as you have only one you will be fine.
     
  22. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    Hi,
    Thanks for version 1. I've had a small problem with it, however. I'm still trying to achieve my out of play area problem (to make sure the player doesn't leave the area of the grid). No matter how hard I try, I can't get my head around doing this. I'd really need some advice that applies to me.

    This is my script (including my current failed attempt):
     
    Last edited: Aug 22, 2016
  23. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    I see there are no plus signs, so I'll just assume that the paste swallowed them. If your grid does not change its size at runtime you can make the code much simpler: compute the world-coordinates of the lower-left and upper-right point of the grid and use those to clamp the player's position:
    Code (csharp):
    1. GFGrid grid;
    2. // assuming relative limits (it's the default)
    3. Vector3 from = grid.GridToWorld(grid.from), to = grid.GridToWorld(grid.to);
    4.  
    5. Vector3 ClampVector3(Vector3 vector, Vector3 from, Vector3 to) {
    6.     return Vector3.Min(Vector3.Max(vector, from), to);
    7. }
    Anyway, in your code I see line 35 is a problem:
    Code (csharp):
    1. transform.position = grid.WorldToGrid(playerCurrentPosition);
    My guess is that you want the player to stay where he is, but you are setting the player's world-position to a position in grid coordinates instead of world-coordinates.

    Let's rethink what you are trying to accomplish: first you take the player's input, then you make a decision based on that input, and finally you apply that input. The player's input is a signal used by the game to move an object, so what need to do first is receive the input (source), then transform it, filter and and finally send it to a "sink" where it will do something. The Update method is good for a source and you should only use it as a source, but you are intermixing it with other parts of the pipeline.
    Code (csharp):
    1. private void Update() {
    2.     if (m_isMoving) {
    3.         return;
    4.     }
    5.  
    6.     // Receive the signal (source)
    7.     Vector3 offset;
    8.     if (Input.GetKeyDown(KeyCode.LeftArrow)) {
    9.         offset = new Vector3(-m_moveDistance, 0, 0);
    10.     }
    11.  
    12.     if (Input.GetKeyDown(KeyCode.RightArrow)) {
    13.         offset = new Vector3(m_moveDistance, 0, 0);
    14.     }
    15.  
    16.     if (Input.GetKeyDown(KeyCode.UpArrow)) {
    17.         offset = new Vector3(0, m_moveDistance, 0);
    18.     }
    19.  
    20.     if (Input.GetKeyDown(KeyCode.DownArrow)) {
    21.         offset = new Vector3(0, -m_moveDistance, 0);
    22.     }
    23. }
    Once we have the signal from the source we pass it on to a filter. The filter will use a predicate (a condition function) to decide what to do to the signal, if anything.
    Code (csharp):
    1. Vector3 ComputeTarget(Vector3 offset, Vector3 position, Vector3 lowerLimit, Vector3 upperLimit) {
    2.     // The predicate is hard-coded in this case because it's not worth the
    3.     // hassle to pass a function. C# is not a functional programming language
    4.     for (var i = 0; i < 2; ++i) {
    5.         var total = position + offset;
    6.         if (total < lowerLimit[i] || total > upperLimit[i]) {
    7.             // return the position without offset
    8.             return position;
    9.         }
    10.     }
    11.     // If the predicate failed
    12.     return position + offset;
    13. }
    Putting it all together:
    Code (csharp):
    1. Vector3 from = grid.GridToWorld(grid.from), to = grid.GridToWorld(grid.to);
    2.  
    3. private void Update() {
    4.     if (m_isMoving) {
    5.         return;
    6.     }
    7.  
    8.     // Receive the signal (source)
    9.     Vector3 offset;
    10.     if (Input.GetKeyDown(KeyCode.LeftArrow)) {
    11.         offset = new Vector3(-m_moveDistance, 0, 0);
    12.     }
    13.  
    14.     if (Input.GetKeyDown(KeyCode.RightArrow)) {
    15.         offset = new Vector3(m_moveDistance, 0, 0);
    16.     }
    17.  
    18.     if (Input.GetKeyDown(KeyCode.UpArrow)) {
    19.         offset = new Vector3(0, m_moveDistance, 0);
    20.     }
    21.  
    22.     if (Input.GetKeyDown(KeyCode.DownArrow)) {
    23.         offset = new Vector3(0, -m_moveDistance, 0);
    24.     }
    25.  
    26.     // Apply a filter
    27.     Vector3 target = ComputeTarget(offset, transform.position, from, to);
    28.  
    29.     // Send it to the sink
    30.     Move(target);
    31. }
    32.  
    33. Vector3 ComputeTarget(Vector3 offset, Vector3 position, Vector3 lowerLimit, Vector3 upperLimit) {
    34.     // The predicate is hard-coded in this case because it's not worth the
    35.     // hassle to pass a function. C# is not a functional programming language
    36.     for (var i = 0; i < 2; ++i) {
    37.         var total = position + offset;
    38.         if (total < lowerLimit[i] || total > upperLimit[i]) {
    39.             // return the position without offset
    40.             return position;
    41.         }
    42.     }
    43.     // If the predicate failed
    44.     return position + offset;
    45. }
    The Move method can stay as it is. I have hard-coded the predicate into the filter because it's not really necessary to allow for passing different functions in this case and it's a bit of pain to do that in C#, but in a functional programming language you would make the predicate a parameter. This pattern of source -> filter -> ... -> filter -> sink is a very common pattern in signal processing. And one last bit of advice, you shouldn't use GetKeyDown for movement, use GetAxis and GetAxisRaw instead, then your code will respect the player's custom keybindings instead of being hard-coded to the arrow keys, and it saves you a number of if-blocks.
    Code (csharp):
    1. Vector3 offset;
    2. float offsetScale;  // how many units one keypress should correspond to
    3.  
    4. // Pressing up/down overrides right/left, just like in your code
    5. offset = Input.GetAxisRaw("x") * offsetScale * Vector3.right;
    6. offset = Input.GetAxisRaw("y") * offsetScale * Vector3.up;
     
  24. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    Hi hiphish,

    Thank you so much for your reply. Your support has been spectacular!
    I've understood the logic you've put behind the code, and I really appreciate that you explained it so well! However, as I did earlier, I'm getting a NullReferenceException (at line 47). In your reply, you claimed that my error was from, "setting the player's world-position to a position in grid coordinates instead of world-coordinate." My brain cannot grasp this as yet, because in these lines, I declare,
    Code (CSharp):
    1.         Vector3 from = grid.GridToWorld(grid.renderFrom);
    2.         Vector3 to = grid.GridToWorld(grid.renderTo);
    which should change these values into world co-ordinates, right? How shall I fix this, as I don't see what's wrong with the snippet just yet. Why is this happening?

    Once again, thank you very much for all your help, and sorry to bother you.

    Regards,
    8Development

    P.S:
    I've modified the script a bit, and I apologize for the earlier mishaps with my paste. I've switched to Gists to prevent further problems.
    On another note, I've not used Input.GetAxis as I plan to change everything to touch (this is just a prototype). Thanks anyways, though.

    My script: https://gist.github.com/techtide/686024409d6f8290ec4954ef7dd1c13d
     
  25. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Stupid question, but did you actually assign a grid to the object? I see your grid variable has no [SerializeField], so if you assign a grid in the editor and hit the play button the reference will be deleted. The variable has no access specifier, so it will be private by default, which means you have to assign the reference yourself. That would explain why you are getting a Null Reference error, you are trying to get a field from a grid that doesn't exist.
     
  26. techtide

    techtide

    Joined:
    Aug 22, 2015
    Posts:
    38
    Last edited: Aug 23, 2016
  27. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    I can see an error in the Move() method: you are treating the argument as an offset but we are passing in a target position. Part of the source/filter/sink pattern is that once we have filtered the signal we pass the filtered result on, not the raw signal. That's why I called the Move() method with target as an argument. You can change the method to this:
    Code (csharp):
    1. private IEnumerator Move(Vector3 target) {
    2.     // remove this line:
    3.     // Vector3 targetPos = startPos + offset;
    4.     // use the argument `target` in the place of targetPos
    5. }
    You can also rename the argument to targetPos, but it is customary to name the argument the same as the variable you are passing in. It doesn't really matter though, name it what sounds best to you.

    Other than that it looks good to me, if it still doesn't work try printing the values you are computing to the console to see where it is getting stuck. On an unrelated note, you should put
    Code (csharp):
    1. Vector3 from = grid.GridToWorld(grid.renderFrom);
    2. Vector3 to = grid.GridToWorld(grid.renderTo);
    in Awake() and store the two vectors as private members so you don't waste time re-computing the same values every frame. The vector offset on the other hand doesn't need to be stored as a private member because you will compute it once and throw it away after one method call anyway.
     
  28. NavarroFamily

    NavarroFamily

    Joined:
    Feb 15, 2016
    Posts:
    15
    What I am trying to achieve is a Grid of tiles with a point and click move system for the player.
    Using Playmaker and GridFramework



    SEE GIF IMAGES


    When the player is selected, all Valid moves are displayed

    (Valid being moves that player has left this turn, and moves that are un obstructed by a wall)



    When Tile is clicked, player moves along a valid path, until player reaches clicked tile, and stops moving.



    I own both Playmaker, and Grid FrameWork, and am stumped on where to get started.



    How would I go about making something Like this, with gird movement, wall detection, and displaying valid moves?



    Any point in the right direction would be fantastic!
     

    Attached Files:

  29. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    That's a very broad question. You need to make some more fundamental decisions first: how do you store your game map in memory (e.g. a 2D array of some custom data type), and what pathfinding algorithm are you using?

    Now assuming you have made some decision, the questions are to first find all the possible tiles your player could walk to if there are no obstacles. You can use a clockwise sweeping algorithm: Imagine if your player has a movement radius of n tiles, then you colour the first n tiles straight north of the player first and then proceed in a clockwise sweeping motion around the player. Here is a snippet of code where we draw each of the four quadrant in a loop

    Code (csharp):
    1.  
    2. int unitX, unitY;  // tile coordinates of the unit
    3.  
    4. // X
    5. // XX    The O is the position of the unit, the X are the tiles we sweep over
    6. // XXX
    7. // O
    8. for (var x = 0; x < 3; ++x) {
    9.    for (var y = 1; y <= 3 - x; ++y) {
    10.      tile_map[unitX + x, unitY + y].IsWithinRadius;  // some custom code of yours
    11.    }
    12. }
    13.  
    14. // OXXX
    15. //  XX
    16. //  X
    17. for (var y = 0; y > -3; --y) {
    18.    for (var x = 1; x <= 3 + y; ++x) {
    19.      tile_map[unitX + x, unitY + y].IsWithinRadius;  // some custom code of yours
    20.    }
    21. }
    22.  
    23. //  O
    24. // XXX
    25. //  XX
    26. //  X
    27. for (var x = 0; x > -3; --x) {
    28.    for (var y = -1; y >= -3 - x; --y) {
    29.      tile_map[unitX + x, unitY + y].IsWithinRadius;  // some custom code of yours
    30.    }
    31. }
    32.  
    33.  
    34. //  X
    35. //  XX
    36. // XXXO
    37. for (var y = 0; y < 3; ++y) {
    38.    for (var x = -1; x >= -3 + y; --x) {
    39.      tile_map[unitX + x, unitY + y].IsWithinRadius;  // some custom code of yours
    40.    }
    41. }
    42.  
    The next step is filtering out the files blocked by the obstacles. That's the task of a pathfinding algorithm, any out of the radius we have computed that has a cost larger than the number of movement points the unit has become unavailable.
     
  30. Nixlim

    Nixlim

    Joined:
    Sep 7, 2016
    Posts:
    6
    Dear HiPhish,

    First off, thank you very much for this plugin. Just got it today and have been working out how it works. Examples plus docs - I am up and running. Thank you.

    Secondly, I was wondering if you could point me in the right direction. The Snapping example is essentially 2d in that it does not utilise the Y axis - you cannot place "blocks" one on top of the other. How do I implement that functionality? I want to allow the player to choose the "level" of grid they can place their objects - eg say floor is "level 1", one unit above that - "level 2" and so on.

    Regards,

    Nixlim
     
  31. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Are you going to let the player set the level manually? In the example we use the following pattern: snap the object, then adjust the Y-position so the object is touching the floor. In your case you want to follow the same pattern, but pick a different height.
    Code (csharp):
    1. RectGrid grid;
    2. int level;
    3. // After the snapping
    4. var height = (level + 0.5f) * grid.Spacing.y + grid.position.y;
    5. block.position.y = height;
    I have added half a unit because otherwise the centre of the block would be on the edge of the grid instead of a cell. If you want the lowest level to be one you also have to subtract 1 from the level because the lowest height has coordinate 0.
    Code (csharp):
    1. // +1 - 0.5 = -0.5
    2. var height = (level - 0.5f) * grid.Spacing.y + grid.position.y;
     
  32. Nixlim

    Nixlim

    Joined:
    Sep 7, 2016
    Posts:
    6
    Thank you, HiPhish. Yeah, the idea is to allow the player to choose the level they want to place the block on. I recon I can have them press a button which would then set the height variable.
     
  33. Maxii

    Maxii

    Joined:
    May 28, 2012
    Posts:
    45
    Hi HiPhish,

    Thanks for fixing the minor VS compiler error a couple of months ago.

    I just came across another compiler error although this one is from Unity:

    get_dataPath is not allowed to be called from a ScriptableObject constructor (or instance field initializer), call it in OnEnable instead. Called from ScriptableObject 'RectGridEditor'.
    See "Script Serialization" page in the Unity Manual for further details.
    UnityEngine.Application:get_dataPath()
    GridFramework.Editor.RectGridEditor:.cctor()

    I've encountered this error in some of my own stuff during the last 6 months. It comes from using Application.dataPath in a field initializer, in your case in RectGridEditor. I'm not sure why I'm only seeing it now. Probably has something to do with my own editor for another script on the same gameObject. Anyhow, it has reared its ugly head.

    Let me know how you want to fix it and I'll manually change your code temporarily until you can get a patch out.
     
  34. Deozaan

    Deozaan

    Joined:
    Oct 27, 2010
    Posts:
    707
    I think this is a kind of new thing in Unity 5.4.x. You used to be able to do some things in the initializer and it would work without complaint. But Unity 5.4 (or maybe it was 5.3?) started complaining about these things. It's an easy enough fix. Just do what it says: Move the assignment into OnEnable (or Awake or Start, etc.) and it will stop complaining.
     
  35. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Sorry for not replying for a week, Unity has failed again to notify me about new posts (or my browser didn't register that I had read the previous post). I'll look into it over the weekend, sounds like an easy fix.

    EDIT: if you don't see me respond you can also send me a PM, I get notifications about those always.
     
    Last edited: Oct 21, 2016
  36. Maxii

    Maxii

    Joined:
    May 28, 2012
    Posts:
    45
    Thanks for getting back. Its not a big deal but thought you should know.
     
  37. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    OK, I fixed it, it was just as Deozaan said. I'll submit an update on Saturday. I don't like that I can't use Application.dataPath anymore, it was nice and clean to make the path to the documentation read-only, but the variable is private anyway, so it's not like anyone is going to mess it up.
     
  38. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    I'm considering using this for a roguelike game, where every grid coordinate is expected to contain some arbitrary collection of metadata- collision, terrain type, occupants, and so forth. Is there a relatively straightforward way I could configure one of your grids to store metadata this way, or would I be better off curating my own data in a 3d array separate from the grid?
     
  39. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Hello,

    Grids don't store any data in them, they are grids in the mathematical sense: coordinate systems that partition 3D space uniformly. In other words, a grid does the math for you of converting between grid-space and world-space (and some more math of course (like aligning), but coordinate conversion is what you will be using most of the time). In a roguelike game the model of your game world could be stored in a 3D array:
    Code (csharp):
    1. int width, height, dept;
    2. // Assuming there is a "tile" datatype
    3. Tile[,,] gameWorld = new Tile[width, height, depth];
    So far you don't need Grid Framework for that. Where Grid Framework will come in handy is when you want to convert between grid- and world coordinates:
    Code (csharp):
    1. RectGrid grid;
    2. // Assuming some "item" data type
    3. void SpawnItem(Item i, int x, int y, int z) {
    4.     // create the object in your game model
    5.     gameWorld[x, y, z].addItem(item);
    6.     // spawn the item in the world
    7.     var position = grid.GridToWorld(new Vector3(x, y, z));
    8.     // call some Unity code to instantiate the item
    9. }
    As you can see, using Grid Framework will allow you to keep the model portion of your game separate from the view portion. This can be useful in many ways, you could for example compute the waypoints for an enemy in your model and turn them into world positions:
    Code (csharp):
    1. // model space
    2. Vector3[] modelPoints;
    3. Vector3[] viewPoints = new Vector3[modelPoints.Length]
    4. for (var i = 0; i < modelPoints.Length; ++i) {
    5.     viewPoints[i] = grid.GridToWorld(modelPoints[i]);
    6. }
    You can also map from world- to grid space of course. This can be useful for translating where the player is pointing at on the screen to grid-coordinates:
    Code (csharp):
    1. Vector3 point;
    2. var localPoint = Grid.WorldToGrid(point);
    3. var x = Mathf.RoundToInt(localPoint.x);
    4. var y = Mathf.RoundToInt(localPoint.y);
    5. var z = Mathf.RoundToInt(localPoint.z);
    Depending on the type of grid you can also have more coordinate systems, with hex grids having the most of them.
     
  40. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    That is incredibly thorough and completely answers my obnoxiously vague question, thank you very much for taking the time to clarify :)
     
    hiphish likes this.
  41. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Grid Framework version 2.1.1 released

    Grid Framework version 2.1.1 has been approved by the Asset Store. This is a maintenance release which fixes an error aboutget_dataPathin the latest Unity version.
     
  42. mabello

    mabello

    Joined:
    Apr 11, 2016
    Posts:
    2
    Hi, bought the Grid Framework recently and almost everything looks straightforward, thanks.
    One question though, if you need to rotate an object on a rect grid, how would you do it ? It is fine with simple objects, but when you have more complex objects it becomes very difficult to maintain the symmetry.
     

    Attached Files:

  43. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Glad you like it. I don't understand what you mean by maintaining symmetry, can you please elaborate? Maybe a drawing or a mock-up of what you want the result to look like.

    An object in Unity has a Transform, which is a location, rotation and scale, basically it's a rectangle. When I try to fit a rectangle into a grid I take its pivot (usually the centre) and then place it either on an edge of the grid or between two edges, depending on whether the size is even (on edge) or odd (between edges). However, when the object has a different rotation than the grid your have a fundamental design question: what does it even mean to be aligned? Like if the rectangle is 30° rotated, how does it fit into the grid now? Do you even consider the entire bounding rectangle or do you break up your shape into smaller units, try fitting them into the grid and then take some sort of median of the results?
     
  44. Deleted User

    Deleted User

    Guest

    I'm struggling to get playmaker working. Below is what I have done:
    • Created the files smcs.rsp and us.rsp in the root assets folder
    • Added the line "-define:GRID_FRAMEWORK_PLAYMAKER" to each file
    • Verified the extensions in explorer
    • Run the project to get a recompile
    Now what? Am I supposed to see Grid actions in playmaker? I guess I am not 100% sure what the expected outcome is as that isn't really spelled out in the documentation. I tried exiting and restarting but still nothing.

    Thanks for any help you can provide.
     
  45. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Just running the project is not enough, Unity will not re-compile your scripts unless they have changed. The easiest way I have found is to create a new script (C# or UnityScript should both work), that should trigger the compiler. You can throw away the script afterwards.
     
  46. Deleted User

    Deleted User

    Guest

    Thanks, that was dumb of me. Added a throw away script and it compiled, now they show up. I am kind of curious why all of this is necessary, I have probably 10 other PlayMaker extensions and have never had to do anything similar to make them work. Oh well, its working.

    Thanks again!
     
  47. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    The Playmaker extensions inherit from Playmaker classes. If you don't have Playmaker installed this will cause compilation errors because those classes don't exist. That's why I have put something like this in every Playmaker extension script
    Code (csharp):
    1. #if GRID_FRAMEWORK_PLAYMAKER
    2. // the code using Playmaker
    3. #endif
    This is known as conditional compilation. Now the code between if/endif will only be compiled if the symbol GRID_FRAMEWORK_PLAYMAKER is defined, otherwise the compiler will skip it. People who don't have Playmaker installed just ignore it and those who do have Playmaker must define the symbol manually.

    I hindsight maybe it would have been better to make the Playmaker extensions a separate download, but then you would have to keep track of two plugins and update them in sync. This way there is only one plugin to keep track of, the Playmaker actions will always match the rest of Grid Framework, and you only need to define the symbol once.
     
  48. Deleted User

    Deleted User

    Guest

    That makes sense, both have their drawbacks, appreciate the insight.

    Quick question as I try to get my use case to work. Basically I am using a playmaker script on my character. I want the user to see which grid square they are targeting (based on the direction the character is looking and distance to the next grid tile).

    I set up a Raycast that aims from the characters head down to the ground and intersects with a world collider. I then use your Rect World to Grid function to convert the world coordinates to the grid. From there I would like to get the boundaries of the specific square to highlight their current target square. I can write a script to do that, but I was hopeful the framework might have a way to pass it a coordinate and have it tell me which grid tile it is located in. Does that make sense?

    Thanks again!
     
  49. hiphish

    hiphish

    Joined:
    Nov 13, 2010
    Posts:
    626
    Yes, that's the way to do it.

    In world coordinates I guess?
    Code (csharp):
    1. Vector3 tile;  // The tile from above in grid coordinates, rounded to integers
    2. var SW = tile;
    3. var NW = tile + Vector3.up;
    4. var SE = tile + Vector3.right;
    5. var NE = tile + Vector3.up + Vector3.right;
    6. // Run all four now through GridToWorld.
    You mean you pass it one world position and get four world position back like above, but all in one method? I'm afraid that's too specific to your game for inclusion, but you can write it as an extension method. Extension methods allow you to add new methods to a class without altering the class itself. There is an entry in the user manual (Help -> Grid Framework Manual) and the official C# programming guide:
    https://msdn.microsoft.com/en-us//library/bb383977.aspx

    Extension methods must be written in C#, but you can use them in UnityScript as well. In fact, there are already some official extension methods included for things that are too specific to be part of the classes, but still generally useful to include.
     
  50. Deleted User

    Deleted User

    Guest

    Thanks that looks like it will work. I was assuming I could get the tile object back in playmaker and then pull properties from it, but as you said that should be a pretty easy extension. I will post it here when I get it.

    Appreciate the help.