I have hex tile game system that contains a bunch of data for each tile. The data is on separate data objects depending on their type eg navigation data[] and say prefab data[]. I have a graph class that iterates over GraphComponents . All i have to do is assemble the desired data eg navigation data and say terrain type data onto a single object to pass to my graph iteration. I was hoping for some feedback on my attempt below. I feel like im recreating the data on Node for no good reason when it all already exists, im just cherry picking data I need. Thanks in advance Code (csharp): // // Interface public interface IHexVertex { HexVert HexVetrex { get; set; } } public interface IHexTriangle { HexTri HexTriangle { get; set; } } public interface ITile<T> { void AddTile(T t, int index); } // // Components public class Tile : IHexVertex, IHexTriangle { private HexVert _hexVertex; public HexVert HexVetrex { get{ return _hexVertex;} set{ _hexVertex = value;} } private HexTri _hexTriangle; public HexTri HexTriangle { get { return _hexTriangle; } set { _hexTriangle = value; } } } // // Graph components public class GraphComponents<T> : ITile<T> where T : Tile { // // Fields protected Node[] nodeData; // // Properties public Node[] Components { get { return nodeData; } set { nodeData = value; } } // // Sub Class public class Node { private T data; public Node(T t) { data = t; } public T Data { get { return data; } set { data = value; } } } // // Interface implementation public void AddTile(T t , int index) { Node n = new Node(t); nodeData[index] = n; } }
The first thing I'd say, is there is some inherent danger in the GraphComponents interface. Line 53 accepts an array and line 79 just assumes all is well. Typically, a class should take care of its own internals. Relying on external code to know how to handle the internals of this class is inviting problems. I'm not exactly sure what you want, but could you not just have something like this? :- Code (CSharp): public class GraphComponents<T> where T : Tile { public T this[int index] { get { return nodeData[index]; } set { nodeData[index] = value; } } public class Node { public Node(T t) { data = t; } public T Data { get { return data; } set { data = value; } } T data; } Dictionary<int, T> nodeData; } Incidentally, I have changed nodeData from protected to private. There is an argument that protected parameters are merely slightly restricted global variables (can be accessed directly just by inheriting from the class).
This is much cleaner thanks so much. One last question. Is there an elegant way to populate the data in the GraphComponents<Tile> method with overloading constructor or implementing an interface for the type of data or should this be its own utill class. Basically what i need to achieve somewhere in constructors or interface or util? eg Code (csharp): public HexVert[] hexVerticies; public HexTri[] hexTriangles; .... public GraphComponents<Tile> compTiles = new GraphComponents<Tile>(); for (int i = 0; i < max; i++) { compTiles[i].HexTriangle = hexTriangles[i]; compTiles[i].HexVertex = hexVerticies[i]; }
That will very much depend on your code overall, there is no one right answer for that. From the sample that you have pasted, if you know max and the contents of both hexVerticies and hexTriangles at the point in time of instantiating an object, then having that code in the constructor should be fine. If not but the code can be abstracted (and will prove useful for other scenarios), then you could try the util / interface route. I think just go with what feels best. If things change over time, then you may find you have to revisit and rework your chosen option. But that's ok- it can't always be avoided.
One very last question, looking at implementing this as a constructor but im curious why people use properties to instantiate a class and could this be an elegant solution for my problem. .... MAYBE ? IDK Code (csharp): private static GraphComponents<T> _default; public static GraphComponents<T> Default { get { if (_default == null) _default = new GraphComponents<T>(); return _default; } } // Maybe something like this.. I have no idea never done it this way private static GraphComponents<T> _tile; public static GraphComponents<T> TILE { get { if (_tile == null) _tile= new GraphComponents<T>( HexTile th, HexTri htri); ... populate data return _tile; } } [code]
A property is really just a way to encapsulate read/ write access to class members. In OOP, this is basically good practice. Object creation (or class/ object instantiation), is the way of bringing about a new 'thing' into being that we can then call on. So, they are unconnected concepts. You can, of course, instantiate an object from within a property call. Likewise, you could access a property on an object from within a class constructor. The code you have given there is using a singleton pattern (here is another variation). This is a much used and familiar pattern; as to whether it is "elegant" or not comes down to personal preference as much as anything. Some people like its simplicity and ease of use, others feel it is little more than a 'glorified global variable'. It can also have limitations regarding polymorphic behaviour. By all means, have a play around with it and see how you get on. However, as you have a generic class, you may find that it is incompatible with your usage requirements. --- Purely out of interest, you may be interested in reading about a few more design patterns here.
OK took a holiday came back only to find this is not working anymore. Any addition eyes on this would be helpful, i can see it thanks in advanced Error Line 82 NullReferenceException: Object reference not set to an instance of an objectSystem.Collections.Generic.Dictionary`2[System.Int32,Lightbringer.HexSphereGraphSystem.Tile].get_Item (Int32 key) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:139) Code (csharp): // // Interface public interface IHexVertex { HexVert HexVetrex { get; set; } } public interface IHexTriangle { HexTri HexTriangle { get; set; } } // // Components public class Tile : IHexVertex, IHexTriangle { private HexVert _hexVertex; public HexVert HexVetrex { get{ return _hexVertex;} set{ _hexVertex = value;} } private HexTri _hexTriangle; public HexTri HexTriangle { get { return _hexTriangle; } set { _hexTriangle = value; } } } // // public class GraphComponents<T> //where T : Tile { public T this[int index] { get { return nodeData[index]; } set { nodeData[index] = value; } } public int Count { get { return nodeData.Count; } } public class Node { public Node(T t) { data = t; } public T Data { get { return data; } set { data = value; } } T data; } Dictionary<int, T> nodeData; } ..... // // TEST private HexVert[] _hexVerts; // This is set and working .... GraphComponents<Tile> tl = new GraphComponents<Tile>(); int max = 10; for (int i = 0; i<max; i++) { tl[i].HexVetrex = _hexVerts[i]; } [code/]
Seems like the problem is on line 40 Why is Code (csharp): tl[i].HexVetrex = _hexVerts[i];; callng the get in property Code (csharp): get { return nodeData[index]; } should it not be calling the set Code (csharp): set { nodeData[index] = value; }
We can break this into three parts (in order of execution) :- _hexVerts[i] : this is an index read (get) access on _hexVerts. tl[i] : this is an index read (get) access on tl. HexVetrex = : this is a write (set) access on HexVetrex.
Im a bit confused, when is Node instantiated then i run tll[0].HexVetrex =_hexVerts; // // Interface public interface IHexVertex { HexVert HexVetrex { get; set; } } public interface IHexTriangle { HexTri HexTriangle { get; set; } } // // Components public class Tile : IHexVertex, IHexTriangle { private HexVert _hexVertex; public HexVert HexVetrex { get{ return _hexVertex;} set{ _hexVertex = value;} } private HexTri _hexTriangle; public HexTri HexTriangle { get { return _hexTriangle; } set { _hexTriangle = value; } } } // // public class GraphComponents<T> //where T : Tile { public T this[int index] { get { return nodeData[index]; } set { nodeData[index] = value; } } public int Count { get { return nodeData.Count; } } public class Node { public Node(T t) { data = t; } public T Data { get { return data; } set { data = value; } } T data; } Dictionary<int, T> nodeData; } ..... // // TEST private HexVert[] _hexVerts; // This is set and working .... GraphComponents<Tile> tl = new GraphComponents<Tile>(); int max = 10; for (int i = 0; i<max; i++) { tl.HexVetrex = _hexVerts; [*] } [*] [*][code/]
Sorry, I'm not sure I understand the question, plus the code didn't paste cleanly. Can you restate the question and indicate the code line number(s) that you are referring to.
Sorry about bad post, here it is again in one file as test Output: 52.57308 UnityEngine.Debug:Log(Object) TEST:Start() (at Assets/Scripts/HexSphereGraphSystem/TEST/TEST.cs:39) NullReferenceException: Object reference not set to an instance of an object System.Collections.Generic.Dictionary`2[System.Int32,Tile].get_Item (Int32 key) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:139) GraphComponents`1[Tile].get_Item (Int32 index) (at Assets/Scripts/HexSphereGraphSystem/TEST/TEST.cs:83) TEST.Start () (at Assets/Scripts/HexSphereGraphSystem/TEST/TEST.cs:40) My question is how should Node class be instantiated (if this is in-face the reason for NullReferenceException), I dont know how this worked previously , im so confused Code (csharp): using System; using System.Collections.Generic; using UnityEngine; public class TEST : MonoBehaviour { private HexSphereBuildManager _hexSphereBuildManager; private HexSphereSystemBuild _hexSphereBuild; private HexVert[] _hexVerts; private HexTri[] _hexTris; private int _subdivision; // Use this for initialization void Start () { // // Get reference _hexSphereBuildManager = GameObject.FindGameObjectWithTag("tagHexSphereBuildManagerObj").GetComponent<HexSphereBuildManager>(); if (_hexSphereBuildManager == null) throw new InvalidOperationException("Did not find a reference"); // // Set data _hexSphereBuild = _hexSphereBuildManager.hexSphere; _hexVerts = _hexSphereBuild.hexVerticies; _hexTris = _hexSphereBuild.hexTriangles; _subdivision = _hexSphereBuildManager.subdivisionsCount; // // TEST GraphComponents<Tile> tll = new GraphComponents<Tile>(); int max = 10; for (int i = 0; i < max; i++) { Debug.Log(_hexVerts[i].positionX); tll[i].HexVetrex =_hexVerts[i]; Debug.Log(tll[i].HexVetrex); } } } // Interface public interface IHexVertex { HexVert HexVetrex { get; set; } } public interface IHexTriangle { HexTri HexTriangle { get; set; } } // Components public class Tile : IHexVertex, IHexTriangle { private HexVert _hexVertex; public HexVert HexVetrex { get { return _hexVertex; } set { _hexVertex = value; } } private HexTri _hexTriangle; public HexTri HexTriangle { get { return _hexTriangle; } set { _hexTriangle = value; } } } // Method public class GraphComponents<T> //where T : Tile { Dictionary<int, T> nodeData; public T this[int index] { get { return nodeData[index]; } set { nodeData[index] = value; } } public class Node { public Node(T t) { data = t; } public T Data { get { return data; } set { data = value; } } T data; } }
There is a missing initialisation here : Dictionary<int, T> nodeData = new Dictionary<int, T>();. Another thing I'd recommend is changing the initialisation of int max = 10; to be int max = _hexVerts.Length;. This will ensure that you do not run off the end of _hexVerts with an index out of range exception, or, under use it if it has more than 10 elements.
Thanks for the reply. Ok I fixed the nodeData initialization but im still seeing a KeyNotFoundException. KeyNotFoundException: The given key was not present in the dictionary. System.Collections.Generic.Dictionary`2[System.Int32,Tile].get_Item (Int32 key) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:150) GraphComponents`1[Tile].get_Item (Int32 index) (at Assets/Scripts/HexSphereGraphSystem/TEST/TEST.cs:83) TEST.Start () (at Assets/Scripts/HexSphereGraphSystem/TEST/TEST.cs:40) My question, is there a way to get Code (csharp): tll[i].HexVetrex =_hexVerts[i]; working without adding an Add() . I feel i had this working but maybe not
The problem is, tll is not having any values written to it prior to line 40. So the read from the dictionary inside GraphComponents is failing.
That would do it but notice that is not the same as line 40. Line 40 has an index accessor on tll. Do you have Visual Studio installed? If not I'd recommend getting it (Community edition is free). Then you can step through this code to see what is happening (just make sure VS is configured to step into properties). That is a really powerful way to follow exactly what is happening and a great way to learn as well.
To restate my previous point, I was only suggesting that val = someValue; will set val whereas val[index].member = someValue; will not be setting anything directly on val but on a member of an object derived from reading an index accessor on val. At this point, maybe it is worth re-asking a couple of previous questions : Have you digested the meaning of my post at #13? Have you tried stepping your code through line-by-line in Visual Studio? This will help illustrate the previous point. I would strongly recommend trying those. Otherwise, maybe try to reduce the complexity of what you are attempting- you have quite a lot going on here: interfaces, generics, index accessors, dictionaries. Maybe just a simple dictionary, keyed on location Id with a single value type, could work initially? Then build up slowly from there.
ok you are right Ive lost my way on this one. Ill go back and think about what I need here. I did get VS up and running on mac but I could not get the stepping working. But VS is way better that im old IDE thanks for the suggestion
All this stuff will make sense, just keep plugging away at it. I'm sure you'll devise a workable system that is pleasing and suits your needs. I use Windows, so afraid can't help you on the Mac front but if you can figure that out, you will find it really helpful and insightful to use. Have fun with it,
Ok went back and had a look at how I actually want this to work. I found that I was really trying to do is implement a composite design pattern. One thing that I need to do differently is also be able access the Dictionary<int, IComponent> _children without going through the Enumerator loop. Reason for this is I know when a specific tile need to be updated but I dont want to loop through the whole tree structure eg Code (csharp): foreach (Composite tilesType in world) { foreach (Composite tile in tilesType) { foreach (TileData data in tile) { if(data.Name == "OldBob")data.Name = "Bob" } } } but more like Code (csharp): world["TileTypeA"]["TileA0"][0].Name = "Bob"; line 60 // IF I CAN GET THIS TO WORK I WOULD BE HAPPY !!!Debug.Log( world["TileTypeA"]["TileA0"][0].Name ); // Should be TileA0Data0 Code (csharp): namespace test { public class CompositeStructure : MonoBehaviour { void Start() { Composite world = new Composite(); world.Name = "World"; Composite tilesTypeA = new Composite(); tilesTypeA.Name = "TileTypeA"; world.Add(tilesTypeA); Composite tile0 = new Composite(); tile0.Name = "TileA0"; tilesTypeA.Add(tile0); TileData tileA0Data0 = new TileData(); tileA0Data0.Name = "TileA0Data0"; tile0.Add(tileA0Data0); TileData tileA0Data1 = new TileData(); tileA0Data1.Name = "TileA0Data1"; tile0.Add(tileA0Data1); Composite tile1 = new Composite(); tile1.Name = "TileA1"; tilesTypeA.Add(tile1); TileData tileA1Data0 = new TileData(); tileA1Data0.Name = "TileA1Data0"; tile1.Add(tileA1Data0); TileData tileA1Data1 = new TileData(); tileA1Data1.Name = "TileA1Data1"; tile1.Add(tileA1Data1); world.Display(25); foreach (Composite tilesType in world) { //Debug.Log(" {0}" + tilesType.Name); foreach (Composite tile in tilesType) { //Debug.Log(" {0}" + tile.Name); foreach (TileData data in tile) { //Debug.Log(" {0}" + data.Name); } } } // // IF I CAN GET THIS TO WORK I WOULD BE HAPPY !!! Debug.Log(world["TileTypeA"]["TileA0"][0].Name); // Should be TileA0Data0 } } /// <summary> /// Component Interface. /// </summary> public interface IComponent { string Name { get; set; } void Display(int depth); } /// <summary> /// Composite. /// </summary> public class Composite : IComponent, IEnumerable<IComponent> { #region Fields private Dictionary<int, IComponent> _children = new Dictionary<int, IComponent>(); private string _name; #endregion #region Properties public IComponent this[int index] { get { return _children[index]; } set { _children[index] = value; } } //public IComponent this[string name] //{ // get // { // foreach (var component in _children) // { // if (component.Value.Name == name) return component.Value; // } // return null; // } // //set { _name = value; } //} public Dictionary<int, IComponent> this[string name] { get { ////return this._children[name]; //foreach (var component in _children) //{ // if (component.Value.Name == name) return this._children; //} foreach (KeyValuePair<int, IComponent> kvp in _children) { if (kvp.Value.Name == name) return this._children ; } return null; } //set { _name = value; } } public int Count { get { return _children.Count; } } public string Name { get { return _name; } set { _name = value; } } //public Dictionary<int, IComponent> Children //{ // get { return _children; } // set { _children = value; } //} #endregion #region Methods public void Add(IComponent component) { int numEntries = _children.Count; if (numEntries == 0) { _children.Add(0, component); } else { _children.Add(numEntries, component); } } public void RemoveSubordinate(int index) { _children.Remove(index); } public void Display(int depth) { Debug.Log(new String('-', depth) + Name); // Recursively display child nodes //foreach (ILeaf component in _children) //{ // component.Display(depth + 2); //} foreach (var component in _children) { component.Value.Display(depth + 2); } } #endregion #region IEnumerable Interface public IEnumerator<IComponent> GetEnumerator() { foreach (KeyValuePair<int, IComponent> kvp in _children) { yield return kvp.Value; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } /// <summary> /// Component /// </summary> public class TileData : IComponent { #region Fields private string _name; public HexVert _hexVertex; #endregion #region Properties public string Name { get { return _name; } set { _name = value; } } #endregion # region Methods public void Display(int depth) { Debug.Log(new String('-', depth) + Name); } #endregion } }
To give you the line you want, you could change your index accessors in Composite to : Code (CSharp): public TileData this[int index] { get { return (TileData)_children[index]; } set { _children[index] = value; } } public Composite this[string name] { get { foreach (KeyValuePair<int, IComponent> kvp in _children) { if (kvp.Value.Name == name) return (Composite)kvp.Value; } return null; } } However, note that you may experience problems with your implementation of void RemoveSubordinate(int index). Think about this sequence: Add new component to world (creates component with key '0') Add new component to world (creates component with key '1') RemoveSubordinate(0) Add new component to world --- what key is generated? So, what should happen now?