How to implement IEnumerator for map coordinates? - c#

I find myself using the following pattern a lot when enumerating through all tile positions on a map:
for (int y = (int) map.Rect.y; y < map.Rect.yMax; y++)
{
for (int x = (int) map.Rect.x; x < map.Rect.xMax; x++)
{
// do something with X and Y coordinates
}
}
I've been studying IEnumerator and IEnumerable but I can't figure out how to implement them to the Map.
What I'd like to achieve:
foreach (Vector3Int position in Map)
{
DoSomething(position.x, position.y);
}
And then Map can internally handle the rest of the logic, with this simpler syntax.

You can yield them:
public IEnumerable<Point> AllMapPoints()
{
for (int y = (int) map.Rect.y; y < map.Rect.yMax; y++)
{
for (int x = (int) map.Rect.x; x < map.Rect.xMax; x++)
{
yield return new Point(x, y);
}
}
}
Now you can loop them all:
foreach (var point in AllMapPoints())
{
DoSomething(point.X, point.Y);
}
or just some, for example:
foreach (var point in AllMapPoints().Take(100))
{
DoSomething(point.X, point.Y);
}

Related

<Object, Object> dict is generating garbage through simple GET

I have a grid in my game that I store in a Dict Dictionary<Coord, Cell> cells = new Dictionary<Coord, Cell>(); where Coord is:
public Coord(int x, int z)
{
this.x = x;
this.z = z;
}
And Cell simply contains some int and string variables.
To get a specific cell I use:
public Cell GetCell(Coord coord)
{
if (IsCoordWithinBorder(coord))
return cells[coord];
return null;
}
Where IsCoordWithinBorder looks like:
public bool IsCoordWithinBorder(Coord coord)
{
return coord.x >= 0 && coord.z >= 0 && coord.x < size && coord.z < size;
}
This seems to generate a ton of garbage, and I have no clue why.
One instance it creates a lot of garbage is when I try to find all surrounding cells:
public List<Cell> GetSurroundingCells(Coord coord, int distance)
{
List<Cell> matches = new List<Cell>();
for (int x = coord.x - distance; x <= coord.x + distance; x++)
{
for (int z = coord.z - distance; z <= coord.z + distance; z++)
{
Cell cell = GetCell(new Coord(x, z));
if (cell != null)
matches.Add(GetCell(new Coord(x, z)));
}
}
return matches;
}
From what I can gather from the Unity profiler, it all points to my GetCell()-method. Am I doing something wrong here?
EDIT:
for (int x = 0; x < size; x++)
{
cells[x] = new Dictionary<int, Cell>();
for (int z = 0; z < size; z++)
{
cells[x][z] = new Cell();
}
}
The issue here is most probably that you create a lot of instances of Coord.
I would probably rather use something like
Dictionary<int, Dictionary<int, Cell>> cells;
You would fill it via
cells = new Dictionary<int, Dictionary<int, Cell>>();
for (int x = 0; x < size; x++)
{
// In your use-case here this is basically equivalent to
//cells.Add(x, new Dictionary<int, Cell>());
cells[x] = new Dictionary<int, Cell>();
for (int z = 0; z < size; z++)
{
cells[x][z] = new Cell();
}
}
and then do
public bool IsCoordWithinBorder(int x, int z)
{
return x >= 0 && z >= 0 && x < size && z < size;
}
public Cell GetCell(int x, int z)
{
if (IsCoordWithinBorder(x, z))
return cells[x][z];
return null;
}
public List<Cell> GetSurroundingCells(Coord coord, int distance)
{
List<Cell> matches = new List<Cell>();
for (int x = coord.x - distance; x <= coord.x + distance; x++)
{
for (int z = coord.z - distance; z <= coord.z + distance; z++)
{
Cell cell = GetCell(x, z);
if (cell != null)
matches.Add(GetCell(x, z));
}
}
return matches;
}
Or if the indices are continues anyway maybe rather
Cell[,] cells;
You would fill it via
cells = new Cell[size, size];
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
cells[x, z] = new Cell();
}
}
and then
public bool IsCoordWithinBorder(int x, int z)
{
return x >= 0 && z >= 0 && x < size && z < size;
}
public Cell GetCell(int x, int z)
{
if (IsCoordWithinBorder(x, z))
return cells[x, z];
return null;
}
public List<Cell> GetSurroundingCells(Coord coord, int distance)
{
List<Cell> matches = new List<Cell>();
for (int x = coord.x - distance; x <= coord.x + distance; x++)
{
for (int z = coord.z - distance; z <= coord.z + distance; z++)
{
Cell cell = GetCell(x, z);
if (cell != null)
matches.Add(GetCell(x, z));
}
}
return matches;
}
Unless you have redefined comparison for the Coord type, your dictionary should search for the exact same object, not merely a Coord object with the same x and z. See documentation for Object.Equals.

How to apply DRY principle to repeated C# for loops

I keep going through a two dimensional array and would like to stop repeating the same nested for loops
for (int x = 0; x < width; x++) {
for (int y =0; y < height; y++) {
// Do some stuff
}
}
Is there a way to DRY-out the nested for loops to something more along the lines of
iterateThroughMatrix ( doSomeStuff )
iterateThroughMatrix ( doSomethingElse )
iterateThroughMatric ( doSomeOtherStuff )
void iterateThroughMatrix ( doSomething ) {
for (int x = 0; x < width; x++) {
for (int y =0; y < height; y++) {
// doSomething here
}
}
}
You need something like this:
void iterateThroughMatrix(Action<int, int> doSomething)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
doSomething(x, y);
}
}
}
You can use any delegate that has two integers, but Action<int, int> is built-in and ready to go.

C# multidimensional array value changes unexpectedly

Hello i have a realy strange behavior in this piece of code:
public class IGraphics
{
public int[,] screen;
private int[,] world;
private int[,] entitys;
private int[,] buffer;
private int screenW;
private int screenH;
public IGraphics(int screenW, int screenH) {
this.screenH = screenH;
this.screenW = screenW;
screen = new int[screenW + 1, screenH];
buffer = new int[screenW + 1, screenH];
}
public void loadWorld(int[,] world) {
this.world = world;
}
public void clear() {
screen = new int[screenW + 1, screenH];
world = new int[screenW, screenH];
for (int y = 0; y < world.GetLength(1); y++) {
for (int x = 0; x < world.GetLength(0); x++) {
world[x, y] = 0;
}
}
}
private void loadScreen() {
}
private void updateEntitys()
{
entitys = new int[screenW, screenH];
List<GameObject> EntRow = Common.world.getEntitys();
for (int i = 0; i < EntRow.Count(); i++)
{
entitys[EntRow[i].x, EntRow[i].y] = EntRow[i].Icon;
}
}
public void draw() {
updateEntitys();
for (int y = 0; y < screen.GetLength(1); y++)
{
for (int x = 0; x < screen.GetLength(0) - 1; x++)
{
if (entitys[x, y] == 0)
{
screen[x, y] = world[x, y];
}
else
{
screen[x, y] = entitys[x, y];
}
}
screen[screen.GetLength(0) - 1, y] = 123;
}
if (buffer.Cast<int>().SequenceEqual(screen.Cast<int>()))
{
return;
}
Console.Clear();
buffer = screen;
for (int y = 0; y < screen.GetLength(1); y++) {
for (int x = 0; x < screen.GetLength(0); x++) {
if (screen[x, y] == 123)
{
Console.WriteLine();
}
else {
Console.Write(objectStore.getIcon(screen[x, y]));
}
}
}
}
}
the problem comes in the Draw() function where i set the value of the screen[,] array for some reason it also change the value of the buffer[,] array before the control also tried moving the buffer[,] in a seperate class but i had the same problem.
Someone as an explanation?
When you assign a reference variable to another variable, you copy the pointer to that variable, instead of copying the content, so what you end up with is two variables that point to the same array.
Try to instead copy the array using Clone or copy or something. I think its screen.CopyTo
screen.CopyTo(buffer, 0);
If you look at the body of the method called draw, you will notice this assignment:
buffer = screen;
This is might the cause of the change you noticed.
See the other answers for possible reasons for your problem. However, the following code shows that changing screen[] does not change buffer[]. This kind of effort on your part would allow you to focus your investigation elsewhere. The principle is to first simply.
int counter = 1;
public void draw()
{
for (int y = 0; y < screen.GetLength(1); y++)
{
for (int x = 0; x < screen.GetLength(0) - 1; x++)
{
screen[x, y] = counter++;
}
screen[screen.GetLength(0) - 1, y] = 123;
}
if (buffer.Cast<int>().SequenceEqual(screen.Cast<int>()))
{
MessageBox.Show("Help!");
return;
}
// check again
for (int y = 0; y < screen.GetLength(1); y++)
{
for (int x = 0; x < screen.GetLength(0) - 1; x++)
{
if (screen[x, y] == buffer[x, y])
{
MessageBox.Show("Help two!");
return;
}
}
}

Get objects at index out of List<Object>

I'm trying to make a 2D RPG game, and would like to use a List< List< Object > > to make a grid in which I store world objects of different types. Only problem is, that I don't know how to get stuff out of this multidimensional List.
The code below creates a multidimensional List and fills it with 'Dirt' Objects (though objects[i] doesnt work, which is my problem).
public List<List<Object>> objects;
this.mapWidth = 36;
this.mapHeight = 21;
this.objects = new List<List<Object>>();
for (int y = 0; y < mapHeight; y++)
{
objects.Add(new List<Object>());
}
for (var i = 0; i < mapWidth; i++)
{
for (var j = 0; j < mapHeight; j++)
{
objects[i].Add(new Dirt());
}
}
The players has a position, e.g. 18,11, which is in the middle of this map/multidimensional List. I would like to check which 'tiles' are around him so I only have to update those things on the map. List[y][x] doesnt work.
I think your initial construction code is incorrect. Looks like you're building your grid to have mapHeight rows and mapheight columns. Rewrite the construction code to be this:
for (var y = 0; y < mapHeight; y++)
{
objects.Add(new List<Object>());
for (var x = 0; x < mapWidth; x++)
{
objects[y].Add(new Dirt());
}
}
EDIT: And I think this will produce a table where your lookup can be achieved by: Object gridEntry = objects[y][x];
EDITx2: If you like, you can rewrite the creation code to this:
for (var y = 0; y < mapHeight; y++)
{
List<Object> currentRow = new List<Object>();
objects.Add(currentRow);
for (var x = 0; x < mapWidth; x++)
{
currentRow.Add(new Dirt());
}
}

Need help saving map

I need help with my map editor, I'm stuck on saving it. When I save, after I put some grass on map, it gets grass everywhere in map file.
Here's variables:
mapMaximumX: maximum of the map in X (it is set as 500)
mapMaximumY: maximum of the map in Y (it is also set as 500)
mapTiles[index]: this is a list with class, each class has ID (0 = empty, 1 = grass, 2 = water), X and Y
if (Keyboard.GetState().IsKeyDown(Keys.F1))
{
for(int y = 0; y < mapMaximumY; y++)
{
for (int x = 0; x < mapMaximumX; x++)
{
if (MapTiles[i3].X == x && MapTiles[i3].Y == y)
{
}
else
{
MapTiles.Add(new Class1(0, x * 32, y * 32));
}
if (i3 < MapTiles.Count)
{
i3++;
}
}
}
TextWriter file = new StreamWriter("map1.MAP");
for (int y = 0; y < mapMaximumY; y++)
{
for (int x = 0; x < mapMaximumX; x++)
{
file.Write(MapTiles[i2].ID + ", ");
}
file.Write(file.NewLine);
}
i2 = 0;
System.Windows.Forms.MessageBox.Show("Saved!");
file.Close();
}
Full code is here, if u need it:
http://pastebin.com/qrWbuPtb
Thanx.
file.Write(MapTiles[i2].ID + ", ");
i2 never changes within your loop, so whatever i2 is will always be what's used to write your output.
You need to be using X and Y from your loops in determining which cell to write out.

Categories

Resources