xD, ok i’ll try to explain in more detail now
this is what you have right now:
notice how the diffusion of the map is all weird, it’s strong on some diagonals and it seems to fade primarily into the lower left corner
on closer inspection i noticed how you forgot to find the neighbors for two of diagonals
your code:
// diagonals
if (x > 0 y > 0)
{
retVal.Add(new Vector2I(x-1, y-1));
}
if (x < _influences.GetLength(0)-1 y < _influences.GetLength(1)-1)
{
retVal.Add(new Vector2I(x+1, y+1));
}
so the first thing i did was to correct this by adding the missing diagonals
my change:
// diagonals
if (x > 0 y > 0)
{
retVal.Add(new Vector2I(x-1, y-1));
}
if (x < _influences.GetLength(0)-1 y < _influences.GetLength(1)-1)
{
retVal.Add(new Vector2I(x+1, y+1));
}
if (x > 0 y < _influences.GetLength(1)-1)
{
retVal.Add(new Vector2I(x-1, y+1));
}
if (x < _influences.GetLength(0)-1 y > 0)
{
retVal.Add(new Vector2I(x+1, y-1));
}
this is what you get:
now you can see the strength of the diagonals is more even out, BUT it has a weird shape doesn’t it? if you look at the center of the diffusion you can notice it better, the values still look like they are being pushed to the lower left corner… this happens because you are not double buffering the map, what i mean is that you calculate the influence for a certain cell and you change it’s value right away, so the next cell in line will now consider some of its neighbors with old values and some with new values, mixing up everything and giving odd results, influence calculation should not depend on which order do you read and write from the matrix, this is when you need a second matrix (double buffer technique) one to read from and another to write to
so i created a second matrix with the name _influenceCalc and here is the main trick:
public void Propagate()
{
UpdatePropagators();
for (int xIdx = 0; xIdx < _influences.GetLength(0); ++xIdx)
{
for (int yIdx = 0; yIdx < _influences.GetLength(1); ++yIdx)
{
//Debug.Log("at " + xIdx + ", " + yIdx);
float maxInf = 0.0f;
float minInf = 0.0f;
Vector2I[] neighbors = GetNeighbors(xIdx, yIdx);
foreach (Vector2I n in neighbors)
{
//Debug.Log(n.x + " " + n.y);
float inf = _influencesCalc[n.x, n.y] * Mathf.Exp(-Decay); //* Decay;
maxInf = Mathf.Max(inf, maxInf);
minInf = Mathf.Min(inf, minInf);
}
if (Mathf.Abs(minInf) > maxInf)
{
_influences[xIdx, yIdx] = Mathf.Lerp(_influencesCalc[xIdx, yIdx], minInf, Momentum);
}
else
{
_influences[xIdx, yIdx] = Mathf.Lerp(_influencesCalc[xIdx, yIdx], maxInf, Momentum);
}
}
}
for (int xIdx = 0; xIdx < _influences.GetLength(0); ++xIdx)
{
for (int yIdx = 0; yIdx < _influences.GetLength(1); ++yIdx)
{
_influencesCalc[xIdx, yIdx] = _influences[xIdx, yIdx];
}
}
}
see how i save the information on _influence BUT i use _influenceCalc to get the values, and obviously _influenceCalc must be updated at some point, i do it with a new cycle at the end… this technique has it’s costs obviously but look how the map looks now:
much more even on all sides :] now the map is working just fine, there is just one small problem, if you use a map like this to decide how a character should move by looking at the values of adjacent cells you’ll see it preferring walking horizontally or vertically because with this map the higher value cells is ALWAYS at perpendicular position EXCEPT when the character is sitting exactly on the diagonal of the map, where the higher value cell is ALWAYS at the diagonals… so basically the diagonals of the map function like magnets meaning the a character would always walk towards the diagonals first and only then walk to the center of the map diagonally
this happens because currently the map is considering all neighbors being at the same distance of any given cell, which is not true because diagonal cells are a bit more distant, 1.4142 times more to be more exact (squareroot of 2).
there are two ways of solving this (3 actually), the first one is to completely ignore diagonals altogether, if you comment out the code to get the diagonal neighbors now all neighbors (only 4 of them) do have the same distance and this is what you get:
BUT, this isn’t a really good solution, simply because the only thing it did was shifting the relevance back to the perpendiculars inverting the behavior of the map, this is why now it looks like a 4 point star, or a rotated cube, this is to be expected though, and should be used if you only care about perpendicular movement
another solution would be to diffuse the values instead of interpolating them, which would give us a nice sphere effect on the map (like a soft airbrush from photoshop), but this solution actually has the same problem as the previous solution, because while visually it looks better the values tell us a different story, where the higher value cell is ALWAYS diagonally adjacent to the cell
the solution i prefer is to give each neighbor cell it’s rightful distance value so when you calculate the exponential of the decay it depends on the distance, i did that but introducing a new value of your Vector2I structure that holds either 1 or 1.4142
public struct Vector2I
{
public int x;
public int y;
public float d;
public Vector2I(int nx, int ny, float nd)
{
x = nx;
y = ny;
d = nd;
}
}
float inf = _influencesCalc[n.x, n.y] * Mathf.Exp(-Decay * n.d); //* Decay;
Vector2I[] GetNeighbors(int x, int y)
{
List<Vector2I> retVal = new List<Vector2I>();
if (x > 0)
{
retVal.Add(new Vector2I(x-1, y, 1));
}
if (x < _influences.GetLength(0)-1)
{
retVal.Add(new Vector2I(x+1, y, 1));
}
if (y > 0)
{
retVal.Add(new Vector2I(x, y-1, 1));
}
if (y < _influences.GetLength(1)-1)
{
retVal.Add(new Vector2I(x, y+1, 1));
}
// diagonals
if (x > 0 y > 0)
{
retVal.Add(new Vector2I(x-1, y-1, 1.4142f));
}
if (x < _influences.GetLength(0)-1 y < _influences.GetLength(1)-1)
{
retVal.Add(new Vector2I(x+1, y+1, 1.4142f));
}
if (x > 0 y < _influences.GetLength(1)-1)
{
retVal.Add(new Vector2I(x-1, y+1, 1.4142f));
}
if (x < _influences.GetLength(0)-1 y > 0)
{
retVal.Add(new Vector2I(x+1, y-1, 1.4142f));
}
return retVal.ToArray();
}
and this is how it looks :]
notice how it looks like a 8 point start, which actually share the same problems as previous solutions, BUT since now both the diagonals AND the perpendiculars are “pulling” the values the map works just fine for small to medium size maps like this one, the bigger the map the more you notice the same problems on the outer parts of the map, which is not that bad if you plan on using it for a RTS game for instance
i hope it helped, most of the code is here, but if you wish i can share the package, i still advise you to try doing it yourself though
cheers :]