How do I add an already instantiated gameobject to a grid? - c#

I am following multiple tutorials and I don't understand all of the code, but basically, I'm instantiating a series of game objects along an L-System. Then I'm creating a Grid like this:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is Point)
{
Point p = obj as Point;
return this.X == p.X && this.Y == p.Y;
}
return false;
}
public override int GetHashCode()
{
unchecked
{
int hash = 6949;
hash = hash * 7907 + X.GetHashCode();
hash = hash * 7907 + Y.GetHashCode();
return hash;
}
}
public override string ToString()
{
return "P(" + this.X + ", " + this.Y + ")";
}
}
public enum CellType
{
Empty,
Road,
Structure,
SpecialStructure,
None
}
public class Grid
{
private CellType[,] _grid;
private int _width;
public int Width { get { return _width; } }
private int _height;
public int Height { get { return _height; } }
private List<Point> _roadList = new List<Point>();
private List<Point> _specialStructure = new List<Point>();
private List<Point> _houseStructure = new List<Point>();
public Grid(int width, int height)
{
_width = width;
_height = height;
_grid = new CellType[width, height];
}
// Adding index operator to our Grid class so that we can use grid[][] to access specific cell from our grid.
public CellType this[int i, int j]
{
get
{
return _grid[i, j];
}
set
{
if (value == CellType.Road)
{
_roadList.Add(new Point(i, j));
}
if (value == CellType.SpecialStructure)
{
_specialStructure.Add(new Point(i, j));
}
if (value == CellType.Structure)
{
_houseStructure.Add(new Point(i, j));
}
_grid[i, j] = value;
}
}
After the game objects are instantiated, I want to add each instantiated gameobject into into the grid and assign each of them a type. I have a script on a my gameobjects which basically stores the type as a string, and I know how to pass it as an enum. But how do I set each game object into the grid with the type, and point?

Related

Cast changing generic type

I have the following classes:
public class Grid<T>
{
public int Width { get; }
public int Height { get; }
public float CellSize { get; }
public Vector2 GridOffset { get; }
public T[,] GridObjects { get; }
public Grid(int width, int height, float cellSize, Vector3 gridOffset, Func<Grid<T>, int, int, T> createGridObjectMethod)
{
this.Width = width;
this.Height = height;
this.CellSize = cellSize;
this.GridOffset = gridOffset;
GridObjects = new T[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
GridObjects[x, y] = createGridObjectMethod(this, x, y);
}
}
}
}
public interface IPathNode
{
PathNode GetPathNode();
}
public class PathNode : IPathNode
{
private readonly Grid<IPathNode> grid;
public int X { get; }
public int Y { get; }
public PathNode PreviousNode;
public PathNode(Grid<IPathNode> grid, int x, int y)
{
this.grid = grid;
this.X = x;
this.Y = y;
}
}
Now, I'm trying to create a Grid instance with the following line:
grid = new Grid<PathNode>(10, 10, 1, new Vector3(-5, -5, 0), (g, x, y) =>
{
Grid<IPathNode> test = (Grid<IPathNode>)g;
return new PathNode(g, x, y);
});
The problem is that PathNode asks for a Grid and my grid is made of PathNodes (or any other type that implements that interface).
How can I cast it, or instantiate it?
Tried casting the grid object, but it will fail to compile.
Also adding generics to the PathNode class but I don't like that
I ended up adding a restriction to the PathNode and the interface (and adding generics to it). I had to edit some of the code but it works. Don't really like this option since I only use the generics for it to work but yeah.
public class PathNode<T> : IPathNode<T> where T : IPathNode<T>
public interface IPathNode<T> where T : IPathNode<T>

How to distinct for a list of list using LINQ?

in my case, I have list of list of points I want to discard the lists of duplicate points using linq in a similar way to how the distinct works in List of items usually.
How could I do it?
Here it is a code snippet to understand better my issue
var points = List<List<Point>>();
public struct Point: IEquatable<Point>
{
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public int X { get; }
public int Y { get; }
}
Thanks
First, actually implement IEquatable:
public struct Point: IEquatable<Point> {
public Point(int x, int y) {
this.X = x;
this.Y = y;
}
public int X { get; }
public int Y { get; }
public bool Equals (Point other) =>
this.X == other.X && this.Y == other.Y;
}
Then create a custom equality comparer. That requires an equality logic, and a hash code generator. For the equality logic, use SequenceEqual, for the hash code generator, you'll have to play around with it, but here's an example via Jon Skeet. I used part of his logic below:
class ListPointComparer : IEqualityComparer<List<Point>> {
public bool Equals(List<Point> a, List<Point> b) => a.SequenceEqual(b);
public int GetHashCode(List<Point> list) {
int hash = 19;
foreach(var point in list)
hash = hash * 31 + point.GetHashCode();
return hash;
}
}
Now imagine points like this:
var pointsA = new List<Point> { new Point (1,1), new Point(2,2) };
var pointsB = new List<Point> { new Point (1,1), new Point(2,2) };
var pointsC = new List<Point> { new Point (3,3), new Point(4,4) };
var pointLists = new List<List<Point>> { pointsA, pointsB, pointsC };
Use your comparer class:
var results = pointLists.Distinct(new ListPointComparer());
// Outputs only 2 lists, with pointsA and pointsB combined.
You can use HashSet this class provide high-performance set operations. A set is a Collection that CONTAINS NO DUPLICATE elements, and whose elements are in no particular order
Example:
public struct Point
{
public HashSet<int> coordinateX;
public HashSet<int> coordinateY;
public Point(HashSet<int> a, HashSet<int> b)
{
coordinateX = a;
coordinateY = b;
}
}
static void Main(string[] args)
{
var set1 = new HashSet<int>() { 2, 3, 4, 6, 8 };
var set2 = new HashSet<int>() { 67, 31, 1, 3, 5 };
var points = new List<List<Point>>();
points.Add(new List<Point>() { new Point(set1, set2) });
//TODO
}
If you have code like this
public class Point : IEquatable<Point>
{
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public int X { get; }
public int Y { get; }
public bool Equals(Point other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
//Check whether the products' properties are equal.
return X.Equals(other.X) && Y.Equals(other.Y);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
int hashProductX = X == null ? 0 : X.GetHashCode();
//Get hash code for the Code field.
int hashProductY = Y == null ? 0 : Y.GetHashCode();
//Calculate the hash code for the product.
return hashProductX ^ hashProductY;
}
}
then this code would work
var distinct_points = points.Distinct();
assuming points is defined like this
List<Point> points;
you can also use
var distinct_points = points.SelectMany(x => x).Distinct();
if points is defined like this
var points = List<List<Point>>();
documentation this example was adapted from https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinct?view=netframework-4.8
Linq Distinct can use an IEqualityComparer<>
You can implement a new class implementing that interface and then generate distinct lists.
Here is an implementation of a fully coded Point implementing IEquatable<>, and implemented IEqualityComparer<> and a Test class.
The test class does a 'simple' distinct on each list. If you need an more complex distinct, such as distinct points across all lists, post your functional requirements and I can see what i can do.
public struct Point : IEquatable<Point>
{
public Point(int x, int y) : this()
{
X = x;
Y = y;
}
public int X { get; set; }
public int Y { get; set; }
public bool Equals(Point other)
{
if (other.X == X && other.Y == Y)
return true;
return false;
}
public override bool Equals(object obj)
{
if (obj != null && obj.GetType() == typeof(Point))
return Equals((Point)obj);
return base.Equals(obj);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
public int GetHashCode(Point obj)
{
return obj.GetHashCode();
}
}
public class PointComparer : IEqualityComparer<Point>
{
public bool Equals(Point x, Point y)
{
return x.Equals(y);
}
public int GetHashCode(Point obj)
{
return obj.GetHashCode();
}
}
public class Tester
{
public static List<List<Point>> Dist(List<List<Point>> points)
{
var results = new List<List<Point>>();
var comparer = new PointComparer();
foreach (var lst in points)
{
results.Add(lst.Distinct(comparer).ToList());
}
return results;
}
}

C# collection of two points doesn't return any results

Class:
public class Point : IEqualityComparer<Point>
{
public char HorizontalPosition { get; set; }
public int VerticalPosition { get; set; }
public Point(char horizontalPosition, int verticalPosition)
{
HorizontalPosition = char.ToUpper(horizontalPosition);
VerticalPosition = verticalPosition;
}
public bool Equals(Point x, Point y)
{
return (x.VerticalPosition == y.VerticalPosition && x.HorizontalPosition == y.HorizontalPosition);
}
public int GetHashCode(Point obj)
{
return (obj.HorizontalPosition.GetHashCode() + obj.VerticalPosition.GetHashCode());
}
}
I am trying to find common Points (intersection) in two collections, but the result is empty collection - two elements should be in it. Why? I've implemented IEqualityComparer. Did I did something wrong?
Example collections:
List<Point> first = new List<Point> { new Point('a', 1), new Point('b', 2) };
List<Point> second = new List<Point> { new Point('a', 1), new Point('b', 2) };
List<Point> intersection = first.Intersect(second).ToList();
Intersection is empty list, but two elements should be in it.
IEqualityComparer is an interface you can give to the Intersect method to compare items. It is not used by default to compare anything. So your code is just using the built-in Equals in Object which will return false unless the objects are the same object.
You have to either override the default Equal and GetHashCode methods in the class or tell the intersection to use your implementation of the comparer. But you shouldn't implement the comparer in a data storage class.
You should override Equals and GetHashCode from object:
public class Point
{
public char HorizontalPosition { get; set; }
public int VerticalPosition { get; set; }
public Point(char horizontalPosition, int verticalPosition)
{
HorizontalPosition = char.ToUpper(horizontalPosition);
VerticalPosition = verticalPosition;
}
public override int GetHashCode()
{
unchecked
{
return (HorizontalPosition * 397) ^ VerticalPosition;
}
}
protected bool Equals(Point other)
{
return Equals(HorizontalPosition, other.HorizontalPosition) && Equals(VerticalPosition, other.VerticalPosition);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Point)obj);
}
}
You could also implement a custom IEqualityComparer and pass it to intersect:
public class PointComparer : IEqualityComparer<Point>
{
public bool Equals(Point a, Point b)
{
return a.HorizontalPosition == b.HorizontalPosition && a.VerticalPosition == b.VerticalPosition;
}
public int GetHashCode(Point p)
{
unchecked
{
return (p.HorizontalPosition * 397) ^ p.VerticalPosition;
}
}
}
// ...
List<Point> intersection = first.Intersect(second, new PointComparer()).ToList();
As mentioned in the comments by #decPL, you should also reconsider your hash code implementation.
List<Point> first = new List<Point> { new Point('a', 1), new Point('b', 2) };
List<Point> second = new List<Point> { new Point('a', 1), new Point('b', 2) };
List<Point> intersection = first.Intersect(second, new PointComparer()).ToList();
public class Point
{
public char HorizontalPosition { get; set; }
public int VerticalPosition { get; set; }
public Point(char horizontalPosition, int verticalPosition)
{
HorizontalPosition = char.ToUpper(horizontalPosition);
VerticalPosition = verticalPosition;
}
}
public class PointComparer : IEqualityComparer<Point>
{
public bool Equals(Point x, Point y)
{
return (x.VerticalPosition == y.VerticalPosition && x.HorizontalPosition == y.HorizontalPosition);
}
public int GetHashCode(Point obj)
{
return (obj.HorizontalPosition.GetHashCode() + obj.VerticalPosition.GetHashCode());
}
}
Try above example
Unless specified, Intersect uses EqualityComparer<Point>.Default which will use the object.Equals and object.GetHashCode methods for comparison (they will just check if the reference is the same);
to make it work, pass the comparer to the method:
List<Point> first = new List<Point> { new Point('a', 1), new Point('b', 2) };
List<Point> second = new List<Point> { new Point('a', 1), new Point('b', 2) };
List<Point> intersection = first.Intersect(second, new Point('a', 0)).ToList();
Although, ideally for the SRP you shouldn't have the comparer on the Point class itself as it would look hacky as it looks above creating a Point just as logic class for comparison.
From MSDN:
EqualityComparer
Intersect
You should separate your Point and PointComparer classes.
The manual has good example:
public class ProductA
{
public string Name { get; set; }
public int Code { get; set; }
}
public class ProductComparer : IEqualityComparer<ProductA>
{
public bool Equals(ProductA x, ProductA y)
{
//Check whether the objects are the same object.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether the products' properties are equal.
return x != null && y != null && x.Code.Equals(y.Code) && x.Name.Equals(y.Name);
}
public int GetHashCode(ProductA obj)
{
//Get hash code for the Name field if it is not null.
int hashProductName = obj.Name == null ? 0 : obj.Name.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = obj.Code.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
https://msdn.microsoft.com/en-us/library/bb460136(v=vs.110).aspx
As you can find on reference https://referencesource.microsoft.com/
System\Linq\Enumerable.cs
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) {
if (first == null) throw Error.ArgumentNull("first");
if (second == null) throw Error.ArgumentNull("second");
return IntersectIterator<TSource>(first, second, null);
}
...
static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
Set<TSource> set = new Set<TSource>(comparer);
foreach (TSource element in second) set.Add(element);
foreach (TSource element in first)
if (set.Remove(element)) yield return element;
}
...
// If value is in set, remove it and return true; otherwise return false
public bool Remove(TElement value) {
int hashCode = InternalGetHashCode(value);
int bucket = hashCode % buckets.Length;
int last = -1;
for (int i = buckets[bucket] - 1; i >= 0; last = i, i = slots[i].next) {
if (slots[i].hashCode == hashCode && comparer.Equals(slots[i].value, value)) {
if (last < 0) {
buckets[bucket] = slots[i].next + 1;
}
else {
slots[last].next = slots[i].next;
}
slots[i].hashCode = -1;
slots[i].value = default(TElement);
slots[i].next = freeList;
freeList = i;
return true;
}
}
return false;
}
Your comparer actually doesn't used

How to stop working constructor in C#?

Here's typical class.
class Round
{
private int radius;
private bool Validate(int radius)
{
if (radius <= 0)
{
return false;
}
else
{
return true;
}
}
public int X { get; set;}
public int Y { get; set;}
public int Radius
{
get
{
return radius;
}
set
{
try
{
if(!Validate(value))
{
throw new ArgumentException();
}
radius = value;
}
catch (ArgumentException)
{
Console.WriteLine("ooops... something went wrong.");
return;
}
}
}
public Round (int X, int Y, int radius)
{
try
{
if (!Validate(radius))
{
throw new ArgumentException();
}
this.X = X;
this.Y = Y;
this.radius = radius;
}
catch (Exception)
{
Console.WriteLine("ooops... something went wrong.");
}
}
public double GetPerimeter()
{
return Math.PI * radius * 2;
}
}
class Program
{
static void Main(string[] args)
{
Round r = new Round(0,0,0);
Console.WriteLine(r.GetPerimeter());
}
}
As you can see, I send incorrect value to constructor that sends the value to validator. I can't understand what should I do to make constructor stop creating object ???.
The way to stop a constructor is to throw an exception.
You are doing that, but then you are also catching the exception. That way, the constructor will continue. Don't catch it.
if you don't want to throw exception in constructor, you could consider this method
// constructor
public Round (int X, int Y, int radius)
{
this.X = X;
this.Y = Y;
this.radius = radius;
}
// create instance
public static Round CreateInstance(int X, int Y, int radius)
{
if (!Validate(radius))
{
return null;
}
return new Round(X, Y, radius);
}
How to use:
// invalid should be null
Round invalid = Row.CreateInstance(1, 1, -1);

serializing an object in C#

I have a serializable class named DataSource:
namespace GraphLib
{
public struct cPoint
{
public float x;
public float y;
}
[Serializable]
public class DataSource
{
public delegate String OnDrawXAxisLabelEvent(DataSource src, int idx);
public delegate String OnDrawYAxisLabelEvent(DataSource src, float value);
public OnDrawXAxisLabelEvent OnRenderXAxisLabel = null;
public OnDrawYAxisLabelEvent OnRenderYAxisLabel = null;
private cPoint[] samples = null;
private int length = 0;
private String name = String.Empty;
private int downSample = 1;
private Color color = Color.Black;
public float VisibleDataRange_X = 0;
public float DY = 0;
public float YD0 = -200;
public float YD1 = 200;
public float Cur_YD0 = -200;
public float Cur_YD1 = 200;
public float grid_distance_y = 200; // grid distance in units ( draw a horizontal line every 200 units )
public float off_Y = 0;
public float grid_off_y = 0;
public bool yFlip = true;
public bool Active = true;
private bool YAutoScaleGraph = false;
private bool XAutoScaleGraph = false;
public float XAutoScaleOffset = 100;
public float CurGraphHeight = 1.0f;
public float CurGraphWidth = 1.0f;
public float InitialGraphHeight = 0;
public float InitialGraphWidth = 0;
public bool AutoScaleY
{
get
{
return YAutoScaleGraph;
}
set
{
YAutoScaleGraph = value;
}
}
public bool AutoScaleX
{
get
{
return XAutoScaleGraph;
}
set
{
XAutoScaleGraph = value;
}
}
public cPoint[] Samples
{
get
{
return samples;
}
set
{
samples = value;
length = samples.Length;
}
}
public float XMin
{
get
{
float x_min = float.MaxValue;
if (samples.Length > 0)
{
foreach (cPoint p in samples)
{
if (p.x < x_min) x_min=p.x;
}
}
return x_min;
}
}
public float XMax
{
get
{
float x_max = float.MinValue;
if (samples.Length > 0)
{
foreach (cPoint p in samples)
{
if (p.x > x_max) x_max = p.x;
}
}
return x_max;
}
}
public float YMin
{
get
{
float y_min = float.MaxValue;
if (samples.Length > 0)
{
foreach (cPoint p in samples)
{
if (p.y < y_min) y_min = p.y;
}
}
return y_min;
}
}
public float YMax
{
get
{
float y_max = float.MinValue;
if (samples.Length > 0)
{
foreach (cPoint p in samples)
{
if (p.y > y_max) y_max = p.y;
}
}
return y_max;
}
}
public void SetDisplayRangeY(float y_start, float y_end)
{
YD0 = y_start;
YD1 = y_end;
}
public void SetGridDistanceY( float grid_dist_y_units)
{
grid_distance_y = grid_dist_y_units;
}
public void SetGridOriginY( float off_y)
{
grid_off_y = off_y;
}
[Category("Properties")] // Take this out, and you will soon have problems with serialization;
[DefaultValue(typeof(string), "")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public String Name
{
get { return name; }
set { name = value; }
}
[Category("Properties")] // Take this out, and you will soon have problems with serialization;
[DefaultValue(typeof(Color), "")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color GraphColor
{
get { return color; }
set { color = value; }
}
[Category("Properties")] // Take this out, and you will soon have problems with serialization;
[DefaultValue(typeof(int), "0")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Length
{
get { return length; }
set
{
length = value;
if (length != 0)
{
samples = new cPoint[length];
}
else
{
// length is 0
if (samples != null)
{
samples = null;
}
}
}
}
[Category("Properties")] // Take this out, and you will soon have problems with serialization;
[DefaultValue(typeof(int), "1")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Downsampling
{
get { return downSample; }
set { downSample = value; }
}
}
}
and i want to serialize it in a form like this:
public partial class Form1 : Form
{
public GraphLib.PlotterDisplayEx display;
private void serialize()
{
System.IO.Stream TestFileStream = System.IO.File.Create(#"C:\Users\Public\Documents\test.txt");
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
serializer.Serialize(TestFileStream, display.DataSources[0]);
TestFileStream.Close();
}
not that DataSource class that i want to serialize in Form1, is one of the attributes in GraphLib.PlotterDisplayEx class
but when i run the program it gives me the following error:
An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll
Additional information: Type 'KTK.Form1' in Assembly 'KTK, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
UPDATE
I updated the code for DataSource class.now it's complete code guys.
You probably didn't show the complete code of the DataSource class. It directly or indirectly holds a reference to an object of type KTK.Form1.
This might be through an event to which the form is subscribed.
In this case you probably don't want to serialize it an should mark it as NonSerialized:
[field:NonSerialized]
public event ...;
Now that you updated the question. Change
public OnDrawXAxisLabelEvent OnRenderXAxisLabel = null;
public OnDrawYAxisLabelEvent OnRenderYAxisLabel = null;
to
[NonSerialized]
public OnDrawXAxisLabelEvent OnRenderXAxisLabel;
[NonSerialized]
public OnDrawYAxisLabelEvent OnRenderYAxisLabel;
The delegates may hold references to non-serializable classes.

Categories