├── images
├── grid_build.jpg
├── grid_path.jpg
└── grid_expand.jpg
├── documentation
└── docs.zip
├── CubeCoordinates
├── Runtime
│ ├── ctrlshiftmake.CubeCoordinates.Library.asmdef
│ ├── ctrlshiftmake.CubeCoordinates.Library.asmdef.meta
│ ├── Cubes.cs.meta
│ ├── Container.cs.meta
│ ├── Coordinate.cs.meta
│ ├── Coordinates.cs.meta
│ ├── MeshCreator.cs.meta
│ ├── Coordinate.cs
│ ├── Container.cs
│ ├── MeshCreator.cs
│ ├── Cubes.cs
│ └── Coordinates.cs
├── LICENSE.md.meta
├── README.md.meta
├── CHANGELOG.md.meta
├── package.json.meta
├── Runtime.meta
├── README.md
├── CHANGELOG.md
├── package.json
└── LICENSE.md
├── LICENSE.md
├── .gitignore
├── examples
├── HappyFace.cs
└── RandomPath.cs
└── README.md
/images/grid_build.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owmo-dev/cube-coordinates/HEAD/images/grid_build.jpg
--------------------------------------------------------------------------------
/images/grid_path.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owmo-dev/cube-coordinates/HEAD/images/grid_path.jpg
--------------------------------------------------------------------------------
/documentation/docs.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owmo-dev/cube-coordinates/HEAD/documentation/docs.zip
--------------------------------------------------------------------------------
/images/grid_expand.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owmo-dev/cube-coordinates/HEAD/images/grid_expand.jpg
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/ctrlshiftmake.CubeCoordinates.Library.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ctrlshiftmake.CubeCoordinates.Library"
3 | }
4 |
--------------------------------------------------------------------------------
/CubeCoordinates/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a3408f6d06b9047d094d68bfbee92b20
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/CubeCoordinates/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9c3c7bd0e161b4af8a6840d121131ef7
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/CubeCoordinates/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1a9cf3cbb6bd14bd2a5470f564e01e4e
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/CubeCoordinates/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8acd8c7e7369b4761bc6f5d330e0a666
3 | PackageManifestImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 57d941f0bbda942b09eca47241de49a3
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/ctrlshiftmake.CubeCoordinates.Library.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 31c9aef723ed24b548531b8a824ae415
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/CubeCoordinates/README.md:
--------------------------------------------------------------------------------
1 | # CubeCoordinates
2 |
3 | Unity package providing a cube coordinate system and methods for building hexagonal tile grids for interactive gameplay. See the following GitHub repository for documentation and examples.
4 |
5 | https://github.com/ctrlshiftmake/cube-coordinates
6 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Cubes.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c3d9975ae8d1549a297bb9d8e3d9e4b1
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/CubeCoordinates/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # changelog
2 |
3 | ## v1.0.0 | January 11, 2021
4 |
5 | - Complete re-write for increased performance, useability and portability
6 | - Abstracted coordinate containers from cube coordinate calculations
7 | - Added ability to specify a prefab GameObject to instantiate
8 | - Tested on Unity `2019.4 17f1`
9 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Container.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e56bc30e1b55b494196051a44c09763e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Coordinate.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 056c74a4eaccd4edf903c989d52f019c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Coordinates.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 38acb798ec01041f6b07480ed2016796
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/MeshCreator.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2488f0273666847599f7f7ec361c82e2
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/CubeCoordinates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.ctrlshiftmake.cubecoordinates",
3 | "version": "1.0.0",
4 | "displayName": "Cube Coordinates",
5 | "description": "Unity package providing a cube coordinate system and methods for building hexagonal tile grids for interactive gameplay.",
6 | "unity": "2019.4",
7 | "unityRelease": "17f1",
8 | "keywords": [
9 | "hexagonal",
10 | "tile",
11 | "grid",
12 | "coordinates"
13 | ],
14 | "author": {
15 | "name": "CtrlShiftMake Design",
16 | "email": "ctrlshiftmake@gmail.com",
17 | "url": "https://www.ctrlshiftmake.com"
18 | },
19 | "dependencies": {}
20 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 CtrlShiftMake
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/CubeCoordinates/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 CtrlShiftMake
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /[Ll]ibrary/
2 | /[Tt]emp/
3 | /[Oo]bj/
4 | /[Bb]uild/
5 | /[Bb]uilds/
6 | /[Ll]ogs/
7 | /[Uu]ser[Ss]ettings/
8 |
9 | # MemoryCaptures can get excessive in size.
10 | # They also could contain extremely sensitive data
11 | /[Mm]emoryCaptures/
12 |
13 | # Asset meta data should only be ignored when the corresponding asset is also ignored
14 | !/[Aa]ssets/**/*.meta
15 |
16 | # Uncomment this line if you wish to ignore the asset store tools plugin
17 | # /[Aa]ssets/AssetStoreTools*
18 |
19 | # Autogenerated Jetbrains Rider plugin
20 | /[Aa]ssets/Plugins/Editor/JetBrains*
21 |
22 | # Visual Studio cache directory
23 | .vs/
24 |
25 | # Gradle cache directory
26 | .gradle/
27 |
28 | # Autogenerated VS/MD/Consulo solution and project files
29 | ExportedObj/
30 | .consulo/
31 | *.csproj
32 | *.unityproj
33 | *.sln
34 | *.suo
35 | *.tmp
36 | *.user
37 | *.userprefs
38 | *.pidb
39 | *.booproj
40 | *.svd
41 | *.pdb
42 | *.mdb
43 | *.opendb
44 | *.VC.db
45 |
46 | # Unity3D generated meta files
47 | *.pidb.meta
48 | *.pdb.meta
49 | *.mdb.meta
50 |
51 | # Unity3D generated file on crash reports
52 | sysinfo.txt
53 |
54 | # Builds
55 | *.apk
56 | *.aab
57 | *.unitypackage
58 |
59 | # Crashlytics generated file
60 | crashlytics-build.properties
61 |
62 | # Packed Addressables
63 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*
64 |
65 | # Temporary auto-generated Android Assets
66 | /[Aa]ssets/[Ss]treamingAssets/aa.meta
67 | /[Aa]ssets/[Ss]treamingAssets/aa/*
68 |
69 | # OSX
70 | .DS_Store
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Coordinate.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace CubeCoordinates
4 | {
5 | ///
6 | /// Individual representation of a cube coordinate in the system
7 | ///
8 | public class Coordinate
9 | {
10 | public enum Type
11 | {
12 | None,
13 | Prefab,
14 | GenerateMesh
15 | }
16 |
17 | private Vector3 _cube = Vector3.zero;
18 |
19 | public Vector3 cube
20 | {
21 | get
22 | {
23 | return _cube;
24 | }
25 | }
26 |
27 | private Vector3 _position = Vector3.zero;
28 |
29 | public Vector3 position
30 | {
31 | get
32 | {
33 | return _position;
34 | }
35 | }
36 |
37 | public float gCost = 0.0f;
38 |
39 | public float hCost = 0.0f;
40 |
41 | public float fCost
42 | {
43 | get
44 | {
45 | return gCost + hCost;
46 | }
47 | }
48 |
49 | private GameObject _go;
50 |
51 | public GameObject go
52 | {
53 | get
54 | {
55 | return _go;
56 | }
57 | }
58 |
59 | ///
60 | /// Sets the Coordinate cube coordinate and world position
61 | ///
62 | ///
63 | ///
64 | public Coordinate(Vector3 cube, Vector3 position)
65 | {
66 | _cube = cube;
67 | _position = position;
68 | }
69 |
70 | ///
71 | /// Sets the GameObject which is associated with the Coordinate
72 | ///
73 | ///
74 | public void SetGameObject(GameObject gameObject)
75 | {
76 | _go = gameObject;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/examples/HappyFace.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using CubeCoordinates;
5 | using UnityEngine;
6 |
7 | public class HappyFace : MonoBehaviour
8 | {
9 | private Coordinates coordinates;
10 |
11 | private void Awake()
12 | {
13 | Coordinates.Instance.SetCoordinateType(Coordinate.Type.GenerateMesh);
14 | }
15 |
16 | private void Update()
17 | {
18 | if (Input.GetKeyDown(KeyCode.Space)) BuildMap();
19 | }
20 |
21 | private void BuildMap()
22 | {
23 | Coordinates.Instance.Clear();
24 |
25 | // Clear all Coordinates and GameObjects
26 | Coordinates.Instance.Clear();
27 |
28 | // Cube coordinate radius of 10 from Vector3.zero
29 | List cubes = Cubes.GetNeighbors(Vector3.zero, 10);
30 |
31 | // Cube coordinate circles for the "eyes"
32 | List eye_L = Cubes.GetNeighbors(new Vector3(-4, 5, -1), 2);
33 | List eye_R = Cubes.GetNeighbors(new Vector3(4, 1, -5), 2);
34 |
35 | // Subtract a smaller circle from a larger
36 | List mouth =
37 | Cubes
38 | .BooleanDifference(Cubes.GetNeighbors(new Vector3(0, 1, -1), 8),
39 | Cubes.GetNeighbors(new Vector3(0, 2, -2), 7));
40 |
41 | // Subtract two lines, to "fill in" the gaps in the first subtraction
42 | mouth =
43 | Cubes
44 | .BooleanDifference(mouth,
45 | Cubes.GetLine(new Vector3(8, 2, -10), new Vector3(8, -10, 2)));
46 | mouth =
47 | Cubes
48 | .BooleanDifference(mouth,
49 | Cubes
50 | .GetLine(new Vector3(-8, 10, -2), new Vector3(-8, -2, 10)));
51 |
52 | // Fille in & remove to make the mouth curved
53 | mouth.Add(new Vector3(-1, -4, 5));
54 | mouth.Add(new Vector3(1, -5, 4));
55 | mouth.Add(new Vector3(0, -5, 5));
56 | mouth.Remove(new Vector3(0, -7, 7));
57 |
58 | // Remove eyes and mouth from all cubes
59 | cubes = cubes.Except(eye_L).ToList();
60 | cubes = cubes.Except(eye_R).ToList();
61 | cubes = cubes.Except(mouth).ToList();
62 |
63 | // Create Coordinates and Build
64 | Coordinates.Instance.CreateCoordinates (cubes);
65 | Coordinates.Instance.Build();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/examples/RandomPath.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using CubeCoordinates;
5 | using UnityEngine;
6 |
7 | public class RandomPath : MonoBehaviour
8 | {
9 | private Coordinates coordinates;
10 |
11 | private void Awake()
12 | {
13 | Coordinates.Instance.SetCoordinateType(Coordinate.Type.GenerateMesh);
14 | }
15 |
16 | private void Update()
17 | {
18 | if (Input.GetKeyDown(KeyCode.Space)) BuildMap();
19 | }
20 |
21 | private void BuildMap()
22 | {
23 | // Clear all Coordinates and GameObjects
24 | Coordinates.Instance.Clear();
25 |
26 | // Cube coordinate radius of 10 from Vector3.zero
27 | List cubes = Cubes.GetNeighbors(Vector3.zero, 10);
28 |
29 | // Randomly remove some of the coordinates
30 | for (int i = cubes.Count - 1; i >= 0; i--)
31 | {
32 | // Exclude Vector3.zero, our starting point
33 | if (cubes[i] == Vector3.zero) continue;
34 |
35 | if (Random.Range(0.0f, 100.0f) < 30.0f) cubes.RemoveAt(i);
36 | }
37 |
38 | // Create the Coordinate objects, they're necessary for `GetExpand` to work
39 | Coordinates.Instance.CreateCoordinates (cubes);
40 |
41 | // Get the origin Coordinate
42 | Coordinate origin =
43 | Coordinates.Instance.GetContainer().GetCoordinate(Vector3.zero);
44 |
45 | // Use `GetExpand` to find all Coordinates connected to Vector3.zero, removing others
46 | Coordinates
47 | .Instance
48 | .GetContainer()
49 | .RemoveCoordinates(Coordinates
50 | .Instance
51 | .BooleanDifference(Coordinates
52 | .Instance
53 | .GetContainer()
54 | .GetAllCoordinates(),
55 | Coordinates.Instance.GetExpand(origin, 10)));
56 |
57 | // Build instantiates the GameObjects, connecting them to Coordinate objects
58 | Coordinates.Instance.Build();
59 |
60 | int steps = 10;
61 | bool found = false;
62 | do
63 | {
64 | if (steps <= 0) return;
65 |
66 | // Get a Ring (circle) of Coordinates at "steps" from origin, from existing Coordinates
67 | List ring = Coordinates.Instance.GetRing(origin, steps);
68 | if (ring.Count > 0)
69 | {
70 | // Get a Path of Coordinates from 'origin' to random Coordinate in Ring results
71 | List path =
72 | Coordinates
73 | .Instance
74 | .GetPath(origin, ring[Random.Range(0, ring.Count - 1)]);
75 |
76 | if (path.Count >= 2)
77 | {
78 | // Add Coordinates to a custom "path" container
79 | Coordinates
80 | .Instance
81 | .GetContainer("path")
82 | .AddCoordinates(path);
83 | found = true;
84 | }
85 | }
86 |
87 | // Reduce steps if Ring results in no Coordinates
88 | steps--;
89 | }
90 | while (!found);
91 |
92 | // Get the path Coordinates from the Container and add some color
93 | foreach (Coordinate
94 | c
95 | in
96 | Coordinates.Instance.GetContainer("path").GetAllCoordinates()
97 | )
98 | c.go.GetComponent().material.color = Color.red;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CubeCoordinates
2 |
3 | Unity package providing a cube coordinate system and methods for building hexagonal tile grids for interactive gameplay.
4 |
5 | |  |  |  |
6 | | ---------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------- |
7 |
8 | ## Install
9 |
10 | To install, copy the `CubeCoordinates` Unity Package directory into your project's `Packages` directory.
11 |
12 | ```csharp
13 | using CubeCoordinates;
14 |
15 | ...
16 |
17 | Coordinates coordinates = Coordinates.Instance;
18 | coordinates.SetCoordinateType(Coordinate.Type.GenerateMesh);
19 |
20 | List cubes = Cubes.GetNeighbors(Vector3.zero, 10);
21 | coordinates.CreateCoordinates(cubes);
22 |
23 | coordinates.Build();
24 | ```
25 |
26 | Please check the [examples](examples) folder and [documentation zip](documentation/docs.zip) to learn more.
27 |
28 | ---
29 |
30 | ## How to Use
31 |
32 | #### Coordinates
33 |
34 | Used to create and manage `Coordinate` instances, it uses an `"all"` default `Container` as the master list for it's methods.
35 |
36 | ```csharp
37 | Coordinates coordinates = Coordinates.Instance;
38 | coordinates.SetCoordinateType(Coordinate.Type.Prefab, myGameObject);
39 |
40 | coordinates.CreateCoordinates(
41 | Cubes.GetNeighbors(Vector3.zero, 10)
42 | );
43 |
44 | Coordinate origin = coordinates.GetContainer().GetCoordinate(Vector3.zero);
45 | Coordinate destination = coordinates.GetContainer().GetCoordinate(new Vector3(4,1,-5));
46 |
47 | Coordinates.Instance.Build();
48 |
49 | List diff = coordinates.BooleanDifference(
50 | coordinates.GetNeighbors(origin, 4),
51 | coordinates.GetNeighbors(origin, 2)
52 | );
53 |
54 | List path = Coordinates.Instance.GetPath(origin, destination);
55 | ```
56 |
57 | #### Coordinate
58 |
59 | Individual `Coordinate` instances represents tiles on the grid. It's purpose is to keep track of cube coordinates, transform position in world space, reference any GameObject instantiated to represent it, facilitate path finding by storing cost values and be supplied as an origin for many `Coordinate` operations.
60 |
61 | ```csharp
62 | Coordinate coordinate = Coordinates.Instance.GetContainer().GetCoordinate(new Vector3(4,1,-5));
63 | myGameObject.transform.position = coordinate.position;
64 | coordinate.SetGameObject(myGameObject);
65 | ```
66 |
67 | #### Container
68 |
69 | Any number of `Container` instances can be optionally created - except for `"all"`, which `Coordinates` uses as a master list internally - to manage your own lists of `Coordinate` instances that you can later retrieve for your own purposes.
70 |
71 | ```csharp
72 | Container moveArea = Coordinates.Instance.GetContainer("move_area");
73 | moveArea.AddCoordinates( new List{coordinateA, coordinateB});
74 |
75 | foreach(Coordinate c in moveArea.GetAllCoordinates())
76 | c.go.GetComponent().DoSomething();
77 |
78 | movement_range.RemoveAllCoordinates();
79 | ```
80 |
81 | #### Cubes
82 |
83 | Collection of cube coordinate system methods for calculating desired coordinate results. These methods _do not_ map to `Coordinate` instances; they simply calculate results from an infinite plane of coordinates as requested. Using `Cubes` directly is desirable when combining several operations together because you can calculate the results and last pull any matching `Coordinate` instances from a `Container` by supplying the results.
84 |
85 | ```csharp
86 | List attackShape = Cubes.BooleanCombine(
87 | Cubes.GetRing(Vector3.zero, 4),
88 | Cubes.GetDiagonalNeighbors(Vector3.zero, 3)
89 | );
90 |
91 | Vector3 activeCube = Cubes.ConvertWorldPositionToCube(myGameObject.transform.position);
92 |
93 | List attackShapeOnMap = new List();
94 | foreach (Vector3 cube in attackShape)
95 | attackShapeOnMap.Add(cube + activeCube);
96 |
97 | Container attackTiles = Coordinates.Instance.GetContainer("attack_tiles");
98 | List attackShapeCoordinates = attackTiles.GetCoordinates(attackShapeOnMap);
99 |
100 | foreach (Coordinate c in attackShapeCoordinates)
101 | c.go.GetComponent().DoSomething();
102 | ```
103 |
104 | #### MeshCreator
105 |
106 | Used to generate hexagonal meshes when using `Coordinate.Type.GenerateMesh` which is useful for debugging.
107 |
108 | ---
109 |
110 | ### Credit
111 |
112 | This package was developed by largely following the explanations found on this post:
113 |
114 | https://www.redblobgames.com/grids/hexagons/
115 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Container.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace CubeCoordinates
5 | {
6 | ///
7 | /// Used to manage lists of Coordinate instances for a given label
8 | ///
9 | public class Container
10 | {
11 | private string _label;
12 |
13 | public string label
14 | {
15 | get
16 | {
17 | return label;
18 | }
19 | }
20 |
21 | private Dictionary _contents;
22 |
23 | ///
24 | /// Constructor for Container which accepts a label and prepares the internal Dictionary
25 | ///
26 | /// Label string to name the Container
27 | public Container(string label)
28 | {
29 | _label = label;
30 | _contents = new Dictionary();
31 | }
32 |
33 | ///
34 | /// Add a Coordinate instance to the Container
35 | ///
36 | /// Coordinate
37 | public void AddCoordinates(Coordinate coordinate)
38 | {
39 | AddCoordinates(new List { coordinate });
40 | }
41 |
42 | ///
43 | /// Add a Coordinate List instances to the Container
44 | ///
45 | /// Coordinate List
46 | public void AddCoordinates(List coordinates)
47 | {
48 | foreach (Coordinate coordinate in coordinates)
49 | if (!_contents.ContainsKey(coordinate.cube))
50 | _contents.Add(coordinate.cube, coordinate);
51 | }
52 |
53 | ///
54 | /// Remove a Coordinate instance from the Container
55 | ///
56 | /// Coordinate
57 | public void RemoveCoordinates(Coordinate coordinate)
58 | {
59 | RemoveCoordinates(new List { coordinate });
60 | }
61 |
62 | ///
63 | /// Remove a Coordinate List from the Container
64 | ///
65 | /// Coordinate List
66 | public void RemoveCoordinates(List coordinates)
67 | {
68 | foreach (Coordinate coordinate in coordinates)
69 | if (_contents.ContainsKey(coordinate.cube))
70 | _contents.Remove(coordinate.cube);
71 | }
72 |
73 | ///
74 | /// Remove all Coordinate instances from the Container
75 | ///
76 | public void RemoveAllCoordinates()
77 | {
78 | _contents.Clear();
79 | }
80 |
81 | ///
82 | /// Gets a Coordinate matching the cube coordinate supplied, if found
83 | ///
84 | /// Vector3 cube coordinate
85 | /// Coordinate instance if found, null otherwise
86 | public Coordinate GetCoordinate(Vector3 cube)
87 | {
88 | Coordinate coordinate = null;
89 | _contents.TryGetValue(cube, out coordinate);
90 | return coordinate;
91 | }
92 |
93 | ///
94 | /// Gets a Coordinate matching the closest rounded cube coordinate to the world transform position (Coordinate may not exist at rounded cube coordinate)
95 | ///
96 | /// Vector3 transform position in world space
97 | /// Coordinate instance if found
98 | public Coordinate GetCoordinateFromWorldPosition(Vector3 position)
99 | {
100 | Vector3 cube = Cubes.ConvertWorldPositionToCube(position);
101 | return GetCoordinate(cube);
102 | }
103 |
104 | ///
105 | /// Get a Coordinate List that match any of the supplied cube coordinates
106 | ///
107 | /// Vector3 List of cube coordinates
108 | /// List of Coordinate instances found
109 | public List GetCoordinates(List cubes)
110 | {
111 | List results = new List();
112 | foreach (Vector3 cube in cubes)
113 | {
114 | Coordinate coordinate = GetCoordinate(cube);
115 | if (coordinate != null) results.Add(coordinate);
116 | }
117 | return results;
118 | }
119 |
120 | ///
121 | /// Gets a Coordinate List of all Coordinate instances in the Container
122 | ///
123 | /// List of Coordinate instances
124 | public List GetAllCoordinates()
125 | {
126 | return new List(_contents.Values);
127 | }
128 |
129 | ///
130 | /// Gets a Vector3 cube coordinate List for all Coordinate instances in the Container
131 | ///
132 | /// List of Vector3 cube coordinates
133 | public List GetAllCubes()
134 | {
135 | return new List(_contents.Keys);
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/MeshCreator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace CubeCoordinates
5 | {
6 | ///
7 | /// Generates hexagonal meshes useful for debugging and prototyping purposes
8 | ///
9 | public class MeshCreator : MonoBehaviour
10 | {
11 | private static MeshCreator _instance;
12 |
13 | private static readonly object Lock = new object();
14 |
15 | public static MeshCreator Instance
16 | {
17 | get
18 | {
19 | lock (Lock)
20 | {
21 | if (_instance != null) return _instance;
22 |
23 | GameObject obj =
24 | new GameObject("{MonoBehaviour}<{" +
25 | typeof (MeshCreator).ToString() +
26 | "}>");
27 | DontDestroyOnLoad (obj);
28 | return _instance = obj.AddComponent();
29 | }
30 | }
31 | }
32 |
33 | ///
34 | /// Generates a GameObject with hexagonal and outline meshes
35 | ///
36 | /// Radius of the tile geometry
37 | /// GameObject
38 | public GameObject CreateGameObject(float radius)
39 | {
40 | GameObject go = new GameObject("generated");
41 | go.hideFlags = HideFlags.HideInHierarchy;
42 | go.SetActive(false);
43 |
44 | AddBaseMeshToGameObject(go, radius, Color.white);
45 |
46 | GameObject go_outline = new GameObject("outline");
47 | go_outline.transform.parent = go.transform;
48 |
49 | AddOutlineMeshToGameObject(go_outline, radius, Color.black);
50 |
51 | return go;
52 | }
53 |
54 | ///
55 | /// Generates the base hexagonal mesh and adds it to the specified GameObject
56 | ///
57 | /// GameObject to add mesh to
58 | /// Radius of the tile geometry
59 | /// Color to apply to the geometry
60 | public void AddBaseMeshToGameObject(
61 | GameObject go,
62 | float radius,
63 | Color color
64 | )
65 | {
66 | Mesh mesh = MeshCreator.Instance.GetHexBase(radius);
67 | PrepareGameObject (go, mesh, color);
68 | }
69 |
70 | ///
71 | /// Generates the outline mesh and adds it to the specified GameObject
72 | ///
73 | /// GameObject to add mesh to
74 | /// Radius of the outline geometry
75 | /// Color to apply to the geometry
76 | public void AddOutlineMeshToGameObject(
77 | GameObject go,
78 | float radius,
79 | Color color
80 | )
81 | {
82 | Mesh mesh = MeshCreator.Instance.GetHexOutline(radius);
83 | PrepareGameObject (go, mesh, color);
84 | }
85 |
86 | ///
87 | /// Prepares a GameObject with MeshRenderer and MeshFilter
88 | ///
89 | /// GameObject to add Components to
90 | /// Mesh to add to GameObject
91 | /// Color to apply to MeshRenderer
92 | private void PrepareGameObject(GameObject go, Mesh mesh, Color color)
93 | {
94 | MeshRenderer meshRenderer = go.GetComponent();
95 | if (meshRenderer == null)
96 | meshRenderer = go.AddComponent();
97 | meshRenderer.shadowCastingMode =
98 | UnityEngine.Rendering.ShadowCastingMode.Off;
99 | meshRenderer.receiveShadows = false;
100 | meshRenderer.material = new Material(Shader.Find("Standard"));
101 |
102 | MeshFilter meshFilter = go.GetComponent();
103 | if (meshFilter == null) meshFilter = go.AddComponent();
104 | meshRenderer.material.color = color;
105 | meshFilter.mesh = mesh;
106 | }
107 |
108 | ///
109 | /// Generates a hexagonal base Mesh
110 | ///
111 | /// Radius of the hexagonal mesh to create
112 | /// Mesh geometry
113 | private Mesh GetHexBase(float radius)
114 | {
115 | Mesh mesh = new Mesh();
116 | mesh.name = "hex_base";
117 |
118 | Vector3[] verts = new Vector3[6];
119 |
120 | verts[0] = GetVertex(0, radius);
121 | verts[1] = GetVertex(1, radius);
122 | verts[2] = GetVertex(2, radius);
123 | verts[3] = GetVertex(3, radius);
124 | verts[4] = GetVertex(4, radius);
125 | verts[5] = GetVertex(5, radius);
126 |
127 | mesh.vertices = verts;
128 |
129 | int[] triangles = new int[12];
130 |
131 | triangles[0] = 0;
132 | triangles[1] = 5;
133 | triangles[2] = 1;
134 | triangles[3] = 1;
135 | triangles[4] = 5;
136 | triangles[5] = 2;
137 | triangles[6] = 5;
138 | triangles[7] = 4;
139 | triangles[8] = 2;
140 | triangles[9] = 4;
141 | triangles[10] = 3;
142 | triangles[11] = 2;
143 |
144 | mesh.triangles = triangles;
145 | mesh.RecalculateNormals();
146 |
147 | return mesh;
148 | }
149 |
150 | ///
151 | /// Generates a hexagonal outline Mesh
152 | ///
153 | /// Radius of the hexagonal outline mesh to create
154 | /// Mesh geometry
155 | private Mesh GetHexOutline(float radius)
156 | {
157 | Mesh mesh = new Mesh();
158 | mesh.name = "hexTileOutline";
159 |
160 | Vector3[] verts = new Vector3[6];
161 |
162 | verts[0] = GetVertex(0, radius);
163 | verts[1] = GetVertex(1, radius);
164 | verts[2] = GetVertex(2, radius);
165 | verts[3] = GetVertex(3, radius);
166 | verts[4] = GetVertex(4, radius);
167 | verts[5] = GetVertex(5, radius);
168 |
169 | Vector3[] v = new Vector3[12];
170 |
171 | float upOffset = radius * 0.005f;
172 | float innerRadius = radius * 0.8f;
173 |
174 | v[0] = verts[0] + (Vector3.up * upOffset);
175 | v[1] = verts[1] + (Vector3.up * upOffset);
176 | v[2] = verts[2] + (Vector3.up * upOffset);
177 | v[3] = verts[3] + (Vector3.up * upOffset);
178 | v[4] = verts[4] + (Vector3.up * upOffset);
179 | v[5] = verts[5] + (Vector3.up * upOffset);
180 | v[6] = (verts[0] * innerRadius) + (Vector3.up * upOffset);
181 | v[7] = (verts[1] * innerRadius) + (Vector3.up * upOffset);
182 | v[8] = (verts[2] * innerRadius) + (Vector3.up * upOffset);
183 | v[9] = (verts[3] * innerRadius) + (Vector3.up * upOffset);
184 | v[10] = (verts[4] * innerRadius) + (Vector3.up * upOffset);
185 | v[11] = (verts[5] * innerRadius) + (Vector3.up * upOffset);
186 |
187 | mesh.vertices = v;
188 |
189 | int[] triangles = new int[36];
190 |
191 | triangles[0] = 0;
192 | triangles[1] = 5;
193 | triangles[2] = 11;
194 |
195 | triangles[3] = 0;
196 | triangles[4] = 11;
197 | triangles[5] = 6;
198 |
199 | triangles[6] = 1;
200 | triangles[7] = 0;
201 | triangles[8] = 6;
202 |
203 | triangles[9] = 1;
204 | triangles[10] = 6;
205 | triangles[11] = 7;
206 |
207 | triangles[12] = 2;
208 | triangles[13] = 1;
209 | triangles[14] = 7;
210 |
211 | triangles[15] = 2;
212 | triangles[16] = 7;
213 | triangles[17] = 8;
214 |
215 | triangles[18] = 2;
216 | triangles[19] = 8;
217 | triangles[20] = 9;
218 |
219 | triangles[21] = 2;
220 | triangles[22] = 9;
221 | triangles[23] = 3;
222 |
223 | triangles[24] = 3;
224 | triangles[25] = 9;
225 | triangles[26] = 10;
226 |
227 | triangles[27] = 3;
228 | triangles[28] = 10;
229 | triangles[29] = 4;
230 |
231 | triangles[30] = 4;
232 | triangles[31] = 10;
233 | triangles[32] = 11;
234 |
235 | triangles[33] = 4;
236 | triangles[34] = 11;
237 | triangles[35] = 5;
238 |
239 | mesh.triangles = triangles;
240 | mesh.RecalculateNormals();
241 |
242 | return mesh;
243 | }
244 |
245 | ///
246 | /// Retruns a vertex for mesh generation
247 | ///
248 | /// index
249 | /// radius
250 | /// Vector3
251 | private Vector3 GetVertex(int i, float radius)
252 | {
253 | float angle_deg = 60.0f * (float) i;
254 | float angle_rad = (Mathf.PI / 180.0f) * angle_deg;
255 | return new Vector3((radius * Mathf.Cos(angle_rad)),
256 | 0.0f,
257 | (radius * Mathf.Sin(angle_rad)));
258 | }
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Cubes.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using UnityEngine;
4 |
5 | namespace CubeCoordinates
6 | {
7 | ///
8 | /// Cubes Coordinate System mathematics
9 | ///
10 | public static class Cubes
11 | {
12 | public static Vector3[]
13 | directions =
14 | {
15 | new Vector3(1.0f, -1.0f, 0.0f),
16 | new Vector3(1.0f, 0.0f, -1.0f),
17 | new Vector3(0.0f, 1.0f, -1.0f),
18 | new Vector3(-1.0f, 1.0f, 0.0f),
19 | new Vector3(-1.0f, 0.0f, 1.0f),
20 | new Vector3(0.0f, -1.0f, 1.0f)
21 | };
22 |
23 | public static Vector3[]
24 | diagonals =
25 | {
26 | new Vector3(2.0f, -1.0f, -1.0f),
27 | new Vector3(1.0f, 1.0f, -2.0f),
28 | new Vector3(-1.0f, 2.0f, -1.0f),
29 | new Vector3(-2.0f, 1.0f, 1.0f),
30 | new Vector3(-1.0f, -1.0f, 2.0f),
31 | new Vector3(1.0f, -2.0f, 1.0f)
32 | };
33 |
34 | ///
35 | /// Caculates adjacent cube coordinate for a given direction and distance
36 | ///
37 | /// Vector3 origin
38 | /// direction of neighbor (0-5 is valid)
39 | /// distance from origin (>=1 is valid)
40 | /// Vector3 cube coordinate
41 | public static Vector3
42 | GetNeighbor(Vector3 origin, int direction, int distance)
43 | {
44 | return origin + directions[direction] * (float) distance;
45 | }
46 |
47 | ///
48 | /// Calculates adjacent cube coordinates over a number of steps
49 | ///
50 | /// Vector3 origin
51 | /// steps from origin (>=1 is valid)
52 | /// List of Vector3 cube coordinates
53 | public static List GetNeighbors(Vector3 origin, int steps)
54 | {
55 | List results = new List();
56 |
57 | for (
58 | int x = (int)(origin.x - steps);
59 | x <= (int)(origin.x + steps);
60 | x++
61 | )
62 | for (
63 | int y = (int)(origin.y - steps);
64 | y <= (int)(origin.y + steps);
65 | y++
66 | )
67 | for (
68 | int z = (int)(origin.z - steps);
69 | z <= (int)(origin.z + steps);
70 | z++
71 | )
72 | if ((x + y + z) == 0) results.Add(new Vector3(x, y, z));
73 |
74 | return results;
75 | }
76 |
77 | ///
78 | /// Caculates diagonally adjacent cube coordinate for a given direction and distance
79 | ///
80 | /// Vector3 origin
81 | /// direction of neighbor (0-5 is valid)
82 | /// distance from origin (>=1 is valid)
83 | /// Vector3 cube coordinate
84 | public static Vector3
85 | GetDiagonalNeighbor(Vector3 origin, int direction, int distance)
86 | {
87 | return origin + diagonals[direction] * (float) distance;
88 | }
89 |
90 | ///
91 | /// Calculates diagonally adjacent cube coordinates at a given distance from origin
92 | ///
93 | /// Vector3 origin
94 | /// distance from origin (>=1 is valid)
95 | /// List of Vector3 cube coordinates
96 | public static List
97 | GetDiagonalNeighbors(Vector3 origin, int distance)
98 | {
99 | List results = new List();
100 | for (int i = 0; i < 6; i++)
101 | for (int s = 1; s <= distance; s++)
102 | results.Add(GetDiagonalNeighbor(origin, i, s));
103 | return results;
104 | }
105 |
106 | ///
107 | /// Calculates a line of cube coordinates between two cube coordinates
108 | ///
109 | /// Vector3 start
110 | /// Vector3 end
111 | /// List of Vector3 cube coordinates
112 | public static List GetLine(Vector3 a, Vector3 b)
113 | {
114 | List results = new List();
115 | float d = GetDistanceBetweenTwoCubes(a, b);
116 | for (int i = 0; i <= d; i++)
117 | results
118 | .Add(RoundCube(GetLerpBetweenTwoCubes(a, b, ((1.0f / d) * i))));
119 | results.Add (a);
120 | return results;
121 | }
122 |
123 | ///
124 | /// Calculates a cube coordinate on a line of cube coordinates at a specified distance from origin
125 | ///
126 | /// Vector3 start
127 | /// Vector3 end
128 | /// distance from start
129 | /// Vector3 cube coordinate
130 | public static Vector3 GetPointOnLine(Vector3 a, Vector3 b, int distance)
131 | {
132 | float d = GetDistanceBetweenTwoCubes(a, b);
133 | return RoundCube(GetLerpBetweenTwoCubes(a,
134 | b,
135 | ((1.0f / d) * distance)));
136 | }
137 |
138 | ///
139 | /// Calculates a ring (hexagonal) of cube coordinates at a specified distance from origin
140 | ///
141 | /// Vector3 origin
142 | /// distance from origin (>=1 is valid)
143 | /// List of Vector3 cube coordinates
144 | public static List GetRing(Vector3 origin, int distance)
145 | {
146 | List results = new List();
147 | Vector3 current = origin + directions[4] * (float) distance;
148 | for (int i = 0; i < 6; i++)
149 | {
150 | for (int j = 0; j < distance; j++)
151 | {
152 | results.Add (current);
153 | current += directions[i];
154 | }
155 | }
156 | return results;
157 | }
158 |
159 | ///
160 | /// Calculates an ordered list of cube coordinates in a clockwise spiral pattern over a specified number of steps away from origin
161 | ///
162 | /// Vector3 origin
163 | /// steps from origin (>=1 is valid)
164 | /// List of Vector3 cube coordinates
165 | public static List GetSpiral(Vector3 origin, int steps)
166 | {
167 | List results = new List();
168 | results.Add (origin);
169 | for (int i = 0; i <= steps; i++)
170 | results.AddRange(GetRing(origin, i));
171 | return results;
172 | }
173 |
174 | ///
175 | /// Combines two lists of cube coordinates, without duplicates
176 | ///
177 | /// Vector3 List a
178 | /// Vector3 List b
179 | /// List of Vector3 cube coordinates
180 | public static List
181 | BooleanCombine(List a, List b)
182 | {
183 | List results = new List();
184 | results.AddRange (a);
185 | foreach (Vector3 vb in b) if (!a.Contains(vb)) results.Add(vb);
186 | return results;
187 | }
188 |
189 | ///
190 | /// Subtracts cube coordinates from a if they are present in b
191 | ///
192 | /// Vector3 List a
193 | /// Vector3 List b
194 | /// List of Vector3 cube coordinates
195 | public static List
196 | BooleanDifference(List a, List b)
197 | {
198 | List results = new List();
199 | results.AddRange (a);
200 | foreach (Vector3 vb in b) if (a.Contains(vb)) results.Remove(vb);
201 | return results;
202 | }
203 |
204 | ///
205 | /// Caculates which cube coordinates are share in both a and b
206 | ///
207 | /// Vector3 List a
208 | /// Vector3 List b
209 | /// List of Vector3 cube coordinates
210 | public static List
211 | BooleanIntersect(List a, List b)
212 | {
213 | List results = new List();
214 | foreach (Vector3 va in a)
215 | foreach (Vector3 vb in b) if (va == vb) results.Add(va);
216 | return results;
217 | }
218 |
219 | ///
220 | /// Calculates which cube coordinates are mutually exclusive in a and b
221 | ///
222 | /// Vector3 List a
223 | /// Vector3 List b
224 | /// List of Vector3 cube coordinates
225 | public static List
226 | BooleanExclude(List a, List b)
227 | {
228 | return BooleanDifference(BooleanCombine(a, b),
229 | BooleanIntersect(a, b));
230 | }
231 |
232 | ///
233 | /// Rounds a provided Vector3 to the nearest valid cube coordinate
234 | ///
235 | /// Vector3 input cube coordinate (does not have to be valid)
236 | /// Vector3 cube coordinate
237 | public static Vector3 RoundCube(Vector3 cube)
238 | {
239 | float rx = Mathf.Round(cube.x);
240 | float ry = Mathf.Round(cube.y);
241 | float rz = Mathf.Round(cube.z);
242 |
243 | float x_diff = Mathf.Abs(rx - cube.x);
244 | float y_diff = Mathf.Abs(ry - cube.y);
245 | float z_diff = Mathf.Abs(rz - cube.z);
246 |
247 | if (x_diff > y_diff && x_diff > z_diff)
248 | rx = -ry - rz;
249 | else if (y_diff > z_diff)
250 | ry = -rx - rz;
251 | else
252 | rz = -rx - ry;
253 |
254 | return new Vector3(rx, ry, rz);
255 | }
256 |
257 | ///
258 | /// Rotates the provided cube coordinate right, around Vector3.zero
259 | ///
260 | /// Vector3 cube coordinate
261 | /// Vector3 cube coordinate
262 | public static Vector3 RotateCubeCoordinatesRight(Vector3 cube)
263 | {
264 | return new Vector3(-cube.z, -cube.x, -cube.y);
265 | }
266 |
267 | ///
268 | /// Rotates the provided cube coordinate left, around Vector3.zero
269 | ///
270 | /// Vector3 cube coordinate
271 | /// Vector3 cube coordinate
272 | public static Vector3 RotateCubeCoordinatesLeft(Vector3 cube)
273 | {
274 | return new Vector3(-cube.y, -cube.z, -cube.x);
275 | }
276 |
277 | ///
278 | /// Converts a Vector3 cube coordinate to a Vector2 axial coordinate
279 | ///
280 | /// Vector3 cube coordinate
281 | /// Vector2 axial coordinate
282 | public static Vector2 ConvertCubeToAxial(Vector3 cube)
283 | {
284 | return new Vector2(cube.x, cube.z);
285 | }
286 |
287 | ///
288 | /// Converts a Vector2 axial coordinate to a Vector3 cube coordinate
289 | ///
290 | /// Vector2 axial coordinate
291 | /// Vector3 cube coordinate
292 | public static Vector3 ConvertAxialToCube(Vector2 axial)
293 | {
294 | return new Vector3(axial.x, (-axial.x - axial.y), axial.y);
295 | }
296 |
297 | ///
298 | /// Converts a Vector2 axial coordinate to a Vector3 world transform position
299 | ///
300 | /// Vector2 axial coordinte
301 | /// Vector3 world transform position
302 | public static Vector3 ConvertAxialToWorldPosition(Vector2 axial)
303 | {
304 | return new Vector3(axial.x * Coordinates.Instance.spacingX,
305 | 0.0f,
306 | -(
307 | (axial.x * Coordinates.Instance.spacingZ) +
308 | (axial.y * Coordinates.Instance.spacingZ * 2.0f)
309 | ));
310 | }
311 |
312 | ///
313 | /// Converts a Vector3 cube coordinate to a Vector3 world transform position
314 | ///
315 | /// Vector3 cube coordinate
316 | /// Vector3 world transform position
317 | public static Vector3 ConvertCubeToWorldPosition(Vector3 cube)
318 | {
319 | return new Vector3(cube.x * Coordinates.Instance.spacingX,
320 | 0.0f,
321 | -(
322 | (cube.x * Coordinates.Instance.spacingZ) +
323 | (cube.z * Coordinates.Instance.spacingZ * 2.0f)
324 | ));
325 | }
326 |
327 | ///
328 | /// Converts a Vector3 world transform position to Vector2 axial coordinate
329 | ///
330 | /// Vector3 world transform position
331 | /// Vector2 axial coordinate
332 | public static Vector2 ConvertWorldPositionToAxial(Vector3 position)
333 | {
334 | float q =
335 | (position.x * (2.0f / 3.0f)) / Coordinates.Instance.radius;
336 | float r =
337 | ((-position.x / 3.0f) + ((Mathf.Sqrt(3) / 3.0f) * position.z)) /
338 | Coordinates.Instance.radius;
339 | return RoundAxial(new Vector2(q, r));
340 | }
341 |
342 | ///
343 | /// Converts a Vector3 world transform position to Vector3 cube coordinate
344 | ///
345 | /// Vector3 world transfor position
346 | /// Vector3 cube coordinate
347 | public static Vector3 ConvertWorldPositionToCube(Vector3 position)
348 | {
349 | return ConvertAxialToCube(ConvertWorldPositionToAxial(position));
350 | }
351 |
352 | ///
353 | /// Rounds a calculated (may not be valid) axial coordinate to the nearest valid Axial coordinate
354 | ///
355 | /// Vector2 axial coordinate
356 | /// Vector2 axial coordinate
357 | public static Vector2 RoundAxial(Vector2 axial)
358 | {
359 | return RoundCube(ConvertAxialToCube(axial));
360 | }
361 |
362 | ///
363 | /// Calculates the distance betwen two Vector3 cube coordinates
364 | ///
365 | /// Vector3 cube coordinate a
366 | /// Vector3 cube coordinate b
367 | /// float distance
368 | public static float GetDistanceBetweenTwoCubes(Vector3 a, Vector3 b)
369 | {
370 | return Mathf
371 | .Max(Mathf.Abs(a.x - b.x),
372 | Mathf.Abs(a.y - b.y),
373 | Mathf.Abs(a.z - b.z));
374 | }
375 |
376 | ///
377 | /// Calcaultes a lerp between two Vector3 cube coordinates at a given distance
378 | ///
379 | /// Vector3 cube coordinate a
380 | /// Vector3 cube coordinate b
381 | /// float distance (0.0f to 1.0f)
382 | /// Vector3 cube coordinate
383 | public static Vector3
384 | GetLerpBetweenTwoCubes(Vector3 a, Vector3 b, float t)
385 | {
386 | Vector3 cube =
387 | new Vector3(a.x + (b.x - a.x) * t,
388 | a.y + (b.y - a.y) * t,
389 | a.z + (b.z - a.z) * t);
390 | return cube;
391 | }
392 | }
393 | }
394 |
--------------------------------------------------------------------------------
/CubeCoordinates/Runtime/Coordinates.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using UnityEngine;
4 |
5 | namespace CubeCoordinates
6 | {
7 | ///
8 | /// Manages Coordinate and Container instances, instantiation of GameObject tiles
9 | ///
10 | public class Coordinates : MonoBehaviour
11 | {
12 | private static Coordinates _instance;
13 |
14 | private static readonly object Lock = new object();
15 |
16 | public static Coordinates Instance
17 | {
18 | get
19 | {
20 | lock (Lock)
21 | {
22 | if (_instance != null) return _instance;
23 |
24 | GameObject obj =
25 | new GameObject("{MonoBehaviour}<{" +
26 | typeof (Coordinates).ToString() +
27 | "}>");
28 | DontDestroyOnLoad (obj);
29 | return _instance = obj.AddComponent();
30 | }
31 | }
32 | }
33 |
34 | private GameObject _coordinateGroup;
35 |
36 | private GameObject _coordinateGameObject = null;
37 |
38 | private Coordinate.Type _coordinateType = Coordinate.Type.None;
39 |
40 | private Dictionary
41 | _containers = new Dictionary();
42 |
43 | private float _scale = 1.0f;
44 |
45 | public float scale
46 | {
47 | get
48 | {
49 | return _scale;
50 | }
51 | }
52 |
53 | private float _radius = 1.0f;
54 |
55 | public float radius
56 | {
57 | get
58 | {
59 | return _radius;
60 | }
61 | }
62 |
63 | private float _spacingX = 0.0f;
64 |
65 | public float spacingX
66 | {
67 | get
68 | {
69 | return _spacingX;
70 | }
71 | }
72 |
73 | private float _spacingZ = 0.0f;
74 |
75 | public float spacingZ
76 | {
77 | get
78 | {
79 | return _spacingZ;
80 | }
81 | }
82 |
83 | private void Awake()
84 | {
85 | CalculateDimensions();
86 | _coordinateGroup = new GameObject("(Group) Coordinates");
87 | }
88 |
89 | ///
90 | /// Uses global radius and scale to calculate coordinate spacing
91 | ///
92 | private void CalculateDimensions()
93 | {
94 | _radius = _radius * _scale;
95 | _spacingX = _radius * 2 * 0.75f;
96 | _spacingZ = ((Mathf.Sqrt(3) / 2.0f) * (_radius * 2) / 2);
97 | }
98 |
99 | ///
100 | /// Destroys all Coordinate, Container and GameObject instances created by Coordinates
101 | ///
102 | public void Clear()
103 | {
104 | _containers.Clear();
105 | Destroy (_coordinateGroup);
106 | _coordinateGroup = new GameObject("(Group) Coordinates");
107 | }
108 |
109 | ///
110 | /// Sets the desired instantiation type and GameObject for the Build method
111 | ///
112 | /// Coordinate.Type enum (None, Prefab, GenerateMesh)
113 | /// Prefab GameObject to Instatiate at the Coordinate world position
114 | public void SetCoordinateType(
115 | Coordinate.Type type,
116 | GameObject gameObject = null
117 | )
118 | {
119 | _coordinateType = type;
120 |
121 | if (_coordinateGameObject != null) Destroy(_coordinateGameObject);
122 |
123 | switch (_coordinateType)
124 | {
125 | case Coordinate.Type.None:
126 | _coordinateGameObject = null;
127 | break;
128 | case Coordinate.Type.Prefab:
129 | _coordinateGameObject = gameObject;
130 | break;
131 | case Coordinate.Type.GenerateMesh:
132 | _coordinateGameObject =
133 | MeshCreator.Instance.CreateGameObject(_radius);
134 | break;
135 | }
136 | }
137 |
138 | ///
139 | /// Creates Coordinate instances from a list of valid cube coordinates
140 | ///
141 | /// List of Vector3 cube coordinates
142 | public void CreateCoordinates(List cubes)
143 | {
144 | Container container = GetContainer();
145 | List coordinates = new List();
146 |
147 | foreach (Vector3 cube in cubes)
148 | {
149 | if (container.GetCoordinate(cube) != null) continue;
150 | Coordinate coordinate =
151 | new Coordinate(cube,
152 | Cubes.ConvertCubeToWorldPosition(cube));
153 | coordinates.Add (coordinate);
154 | }
155 |
156 | container.AddCoordinates (coordinates);
157 | }
158 |
159 | ///
160 | /// Instantiates the GameObject specified in SetCoordinateType for all Coordinate instances in the "all" Container
161 | ///
162 | public void Build()
163 | {
164 | if (_coordinateType == Coordinate.Type.None) return;
165 |
166 | List coordinates = GetContainer().GetAllCoordinates();
167 | foreach (Coordinate coordinate in coordinates)
168 | {
169 | string label =
170 | "Coordinate [" +
171 | coordinate.cube.x +
172 | "," +
173 | coordinate.cube.y +
174 | "," +
175 | coordinate.cube.z +
176 | "]";
177 | GameObject go =
178 | (GameObject)
179 | Instantiate(_coordinateGameObject,
180 | coordinate.position,
181 | Quaternion.identity);
182 | go.name = label;
183 | go.transform.parent = _coordinateGroup.transform;
184 | go.SetActive(true);
185 | coordinate.SetGameObject (go);
186 | }
187 | }
188 |
189 | ///
190 | /// Returns a Container for the provided key (will create if not found)
191 | ///
192 | /// String used to identify the Container, defaults to "all"
193 | /// Container with label matching key
194 | public Container GetContainer(string key = "all")
195 | {
196 | Container container;
197 | if (!_containers.TryGetValue(key, out container))
198 | {
199 | CreateContainer (key);
200 | _containers.TryGetValue(key, out container);
201 | }
202 | return container;
203 | }
204 |
205 | ///
206 | /// Creates a new Container instance, used by GetContainer internally
207 | /// ///
208 | /// String used to identify the Container
209 | private void CreateContainer(string key)
210 | {
211 | if (!_containers.ContainsKey(key))
212 | _containers.Add(key, new Container(key));
213 | }
214 |
215 | ///
216 | /// Gets a Coordinate instance adjacent to the origin Coordinate at a given distance and direction
217 | ///
218 | /// Coordinate
219 | /// Direction to get (0 to 5 valid)
220 | /// Distance from origin (>=1 valid)
221 | /// Container to get results from, defaults to "all"
222 | /// Coordinate instance if found, otherwise null
223 | public Coordinate
224 | GetNeighbor(
225 | Coordinate origin,
226 | int direction,
227 | int distance,
228 | string container_label = "all"
229 | )
230 | {
231 | return GetContainer(container_label)
232 | .GetCoordinate(Cubes
233 | .GetNeighbor(origin.cube, direction, distance));
234 | }
235 |
236 | ///
237 | /// Gets a List of Coordinate instances adjacent to the origin, for the specified number of steps
238 | ///
239 | /// Coordinate
240 | /// Number of iterations outwards to get (>=1 is valid)
241 | /// Container to get results from, defaults to "all"
242 | /// List of Coordinate instances found
243 | public List
244 | GetNeighbors(
245 | Coordinate origin,
246 | int steps,
247 | string container_label = "all"
248 | )
249 | {
250 | return GetContainer(container_label)
251 | .GetCoordinates(Cubes.GetNeighbors(origin.cube, steps));
252 | }
253 |
254 | ///
255 | /// Gets a Coordinate instance diagonally adjacent to the Origin at a given distance and direction
256 | ///
257 | /// Coordinate
258 | /// Direction to get (0 to 5 valid)
259 | /// Distance from origin (>=1 valid)
260 | /// Container to get results from, defaults to "all"
261 | /// Coordinate instance if found, otherwise null
262 | public Coordinate
263 | GetDiagonalNeighbor(
264 | Coordinate origin,
265 | int direction,
266 | int distance,
267 | string container_label = "all"
268 | )
269 | {
270 | return GetContainer(container_label)
271 | .GetCoordinate(Cubes
272 | .GetDiagonalNeighbor(origin.cube, direction, distance));
273 | }
274 |
275 | ///
276 | /// Gets a List of Coordinate instances diagonally adjacent to the origin, for the specified number of steps
277 | ///
278 | /// Coordinate
279 | /// Distance from origin (>=1 valid)
280 | /// Container to get results from, defaults to "all"
281 | /// List of Coordinate instances found
282 | public List
283 | GetDiagonalNeighbors(
284 | Coordinate origin,
285 | int distance,
286 | string container_label = "all"
287 | )
288 | {
289 | List results = new List();
290 | Container container = GetContainer(container_label);
291 | for (int i = 0; i < 6; i++)
292 | {
293 | Coordinate coordinate =
294 | GetDiagonalNeighbor(origin, i, distance, container_label);
295 | if (coordinate != null) results.Add(coordinate);
296 | }
297 | return results;
298 | }
299 |
300 | ///
301 | /// Gets a line of Coordinate instances from Coordinate a to Coordinate b, inclusive
302 | ///
303 | /// Coordinate start
304 | /// Coordinate end
305 | /// Container to get results from, defaults to "all"
306 | /// List of Coordinate instances found
307 | public List
308 | GetLine(Coordinate a, Coordinate b, string container_label = "all")
309 | {
310 | return GetContainer(container_label)
311 | .GetCoordinates(Cubes.GetLine(a.cube, b.cube));
312 | }
313 |
314 | ///
315 | /// Gets a Coordinate on a line between Coordinate a to Coordinate b at a given distance
316 | ///
317 | /// Coordinate start
318 | /// Coordinate end
319 | /// Distance from Coordinate a to get (>=1 valid)
320 | /// Container to get results from, defaults to "all"
321 | /// List of Coordinate instances found
322 | public Coordinate
323 | GetPointOnLne(
324 | Coordinate a,
325 | Coordinate b,
326 | int distance,
327 | string container_label = "all"
328 | )
329 | {
330 | return GetContainer(container_label)
331 | .GetCoordinate(Cubes.GetPointOnLine(a.cube, b.cube, distance));
332 | }
333 |
334 | ///
335 | /// Gets a ring (hexagonal shape) of Coordinate instances at a specified distance from origin
336 | ///
337 | /// Coordinate
338 | /// Distance from origin (>=1 is valid)
339 | /// Container to get results from, defaults to "all"
340 | /// List of Coordinate instances found
341 | public List
342 | GetRing(Coordinate origin, int distance, string container_label = "all")
343 | {
344 | return GetContainer(container_label)
345 | .GetCoordinates(Cubes.GetRing(origin.cube, distance));
346 | }
347 |
348 | ///
349 | /// Gets an ordered list of Coordinates in a clockwise spiral shape for a given number of concentric steps
350 | ///
351 | /// Coordinate
352 | /// Number of concentric steps (>=1 is valid)
353 | /// Container to get results from, defaults to "all"
354 | /// List of Coordinate instances found
355 | public List
356 | GetSpiral(Coordinate origin, int steps, string container_label = "all")
357 | {
358 | return GetContainer(container_label)
359 | .GetCoordinates(Cubes.GetSpiral(origin.cube, steps));
360 | }
361 |
362 | ///
363 | /// Combines two Coordinate Lists into one, without duplicates
364 | ///
365 | /// Coordinate List a
366 | /// Coordinate List b
367 | /// Container to get results from, defaults to "all"
368 | /// List of Coordinate instances found
369 | public List
370 | BooleanCombine(
371 | List a,
372 | List b,
373 | string container_label = "all"
374 | )
375 | {
376 | return GetContainer(container_label)
377 | .GetCoordinates(Cubes
378 | .BooleanCombine(a.Select(x => x.cube).ToList(),
379 | b.Select(x => x.cube).ToList()));
380 | }
381 |
382 | ///
383 | /// Subtracts Coordinate instances from a if they are present in b
384 | ///
385 | /// Coordinate List a
386 | /// Coordinate List b
387 | /// Container to get results from, defaults to "all"
388 | /// List of Coordinate instances found
389 | public List
390 | BooleanDifference(
391 | List a,
392 | List b,
393 | string container_label = "all"
394 | )
395 | {
396 | return GetContainer(container_label)
397 | .GetCoordinates(Cubes
398 | .BooleanDifference(a.Select(x => x.cube).ToList(),
399 | b.Select(x => x.cube).ToList()));
400 | }
401 |
402 | ///
403 | /// Gets the Coordinate instances that are shared in both a and b
404 | ///
405 | /// Coordinate List a
406 | /// Coordinate List b
407 | /// Container to get results from, defaults to "all"
408 | /// List of Coordinate instances found
409 | public List
410 | BooleanIntersect(
411 | List a,
412 | List b,
413 | string container_label = "all"
414 | )
415 | {
416 | return GetContainer(container_label)
417 | .GetCoordinates(Cubes
418 | .BooleanIntersect(a.Select(x => x.cube).ToList(),
419 | b.Select(x => x.cube).ToList()));
420 | }
421 |
422 | ///
423 | /// Gets the Coordinate instances that are mutually exclusive to a and b
424 | ///
425 | /// Coordinate List a
426 | /// Coordinate List b
427 | /// Container to get results from, defaults to "all"
428 | /// List of Coordinate instances found
429 | public List
430 | BooleanExclude(
431 | List a,
432 | List b,
433 | string container_label = "all"
434 | )
435 | {
436 | return GetContainer(container_label)
437 | .GetCoordinates(Cubes
438 | .BooleanExclude(a.Select(x => x.cube).ToList(),
439 | b.Select(x => x.cube).ToList()));
440 | }
441 |
442 | ///
443 | /// Gets the Coordinate instances that are "walkable" from the origin over a number of steps
444 | ///
445 | /// Coordinate
446 | /// Steps over which to walk to get results
447 | /// Container to get results from, defaults to "all"
448 | /// List of Coordinate instances found
449 | public List
450 | GetExpand(Coordinate origin, int steps, string container_label = "all")
451 | {
452 | List results = new List();
453 | Container container = GetContainer(container_label);
454 |
455 | List visited = new List();
456 | visited.Add(origin.cube);
457 |
458 | List> fringes = new List>();
459 | fringes.Add(new List());
460 | fringes[0].Add(origin.cube);
461 |
462 | for (int i = 1; i <= steps; i++)
463 | {
464 | fringes.Add(new List());
465 | foreach (Vector3 v in fringes[i - 1])
466 | {
467 | Coordinate current = container.GetCoordinate(v);
468 | foreach (Coordinate
469 | c
470 | in
471 | GetNeighbors(current, 1, container_label)
472 | )
473 | {
474 | if (!visited.Contains(c.cube))
475 | {
476 | visited.Add(c.cube);
477 | fringes[i].Add(c.cube);
478 | }
479 | }
480 | }
481 | }
482 |
483 | foreach (Vector3 v in visited)
484 | {
485 | results.Add(container.GetCoordinate(v));
486 | }
487 |
488 | return results;
489 | }
490 |
491 | ///
492 | /// Gets an A* path between two Coordinate instances (must be connected)
493 | ///
494 | /// Coordinate start
495 | /// Coordinate end
496 | /// Container to get results from, defaults to "all"
497 | /// List of Coordinate instances found
498 | public List
499 | GetPath(
500 | Coordinate origin,
501 | Coordinate target,
502 | string container_label = "all"
503 | )
504 | {
505 | if (origin == target) new List();
506 |
507 | Container container = GetContainer(container_label);
508 | List openSet = new List();
509 | List closedSet = new List();
510 | openSet.Add(origin.cube);
511 |
512 | Dictionary cameFrom =
513 | new Dictionary();
514 | cameFrom.Add(origin.cube, Vector3.zero);
515 |
516 | Vector3 current = Vector3.zero;
517 | Coordinate coordinate = null;
518 | Coordinate currentCoordinate = null;
519 | Coordinate neighborCoordinate = null;
520 | float newCost = 0.0f;
521 |
522 | while (openSet.Count > 0)
523 | {
524 | current = openSet[0];
525 | currentCoordinate = container.GetCoordinate(current);
526 |
527 | for (int i = 1; i < openSet.Count; i++)
528 | {
529 | coordinate = container.GetCoordinate(openSet[i]);
530 | if (
531 | coordinate.fCost < currentCoordinate.fCost ||
532 | coordinate.fCost == currentCoordinate.fCost &&
533 | coordinate.hCost < currentCoordinate.hCost
534 | )
535 | {
536 | current = openSet[i];
537 | currentCoordinate = container.GetCoordinate(current);
538 | }
539 | }
540 |
541 | openSet.Remove (current);
542 | closedSet.Add (current);
543 |
544 | if (current == target.cube) break;
545 |
546 | List neighbors =
547 | GetNeighbors(container.GetCoordinate(current),
548 | 1,
549 | container_label);
550 |
551 | foreach (Vector3
552 | neighbor
553 | in
554 | neighbors.Select(x => x.cube).ToList()
555 | )
556 | {
557 | coordinate = container.GetCoordinate(neighbor);
558 | if (coordinate == null || closedSet.Contains(neighbor))
559 | continue;
560 |
561 | newCost =
562 | currentCoordinate.gCost +
563 | Cubes.GetDistanceBetweenTwoCubes(current, neighbor);
564 | neighborCoordinate = container.GetCoordinate(neighbor);
565 |
566 | if (
567 | newCost < neighborCoordinate.gCost ||
568 | !openSet.Contains(neighbor)
569 | )
570 | {
571 | neighborCoordinate.gCost = newCost;
572 | neighborCoordinate.hCost =
573 | Cubes.GetDistanceBetweenTwoCubes(current, neighbor);
574 | cameFrom.Add (neighbor, current);
575 |
576 | if (!openSet.Contains(neighbor)) openSet.Add(neighbor);
577 | }
578 | }
579 | }
580 |
581 | List path = new List();
582 |
583 | current = target.cube;
584 | path.Add(target.cube);
585 |
586 | while (current != origin.cube)
587 | {
588 | cameFrom.TryGetValue(current, out current);
589 | path.Add (current);
590 | }
591 |
592 | path.Reverse();
593 |
594 | return container.GetCoordinates(path);
595 | }
596 | }
597 | }
598 |
--------------------------------------------------------------------------------