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.
Related
Using unity to create a procedural map I'm getting the Error "Cs0161: not all code paths return a value" , I'm still pretty new to coding and I may have made some mistake.
I've tried google but the answers don't even make sense to me
yet
MapData GenerateMapData()
{
float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, lacunarity, persistance, offset);
Color[] colourMap = new Color[mapChunkSize * mapChunkSize];
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
return new MapData(noiseMap, colourMap);
}
}
Error CS0161 'MapGenerator.GenerateMapData()': not all code paths return a value Assembly-CSharp
After the for loops you should also return a value. See the code below and the comment at the bottom of the method:
MapData GenerateMapData()
{
float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, lacunarity, persistance, offset);
Color[] colourMap = new Color[mapChunkSize * mapChunkSize];
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
return new MapData(noiseMap, colourMap);
}
// You should also return something here
}
It is theoretically possible that mapChunkSize can have a value of 0 (zero) and it will not enter the first for loop at all. That's why you need a return statement at the bottom as well.
The reason why you are receiving that error is because your return statement is inside the for loops. More specifically, it can happen that neither of the loops execute (e.g. mapChunkSize == 0. Then 0 isn't less than 0 and the program doesn't enter the for loops) and that thus nothing gets returned. So either move the return outside the loops or add another return outside the loops.
MapData GenerateMapData()
{
float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, lacunarity, persistance, offset);
Color[] colourMap = new Color[mapChunkSize * mapChunkSize];
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
/*return new MapData(noiseMap, colourMap); //uncommenting this might still leave you with the functionality you want */
}
return new MapData(noiseMap, colourMap);
}
you are not returning any thing outside the first loop, try to evaluate values inside the loops and then return from outside the loop.
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
return new MapData(noiseMap, colourMap);
}
add return statement here
This program works with Median filter. I need help with fixing the code. It applies to the line
if ((diffX > -1) && (diffY > -1)...
...in the MedianOfArea method. It would be better if the pixels, which are included in the loop's bounds, would be determined initially. This solution helps to avoid checking every time. Can you help me with fixing it?
namespace Recognizer
{
internal static class MedianFilterTask
{
public static double[,] MedianFilter(double[,] original)
{
var filter = new double[original.GetLength(0), original.GetLength(1)];
var lengthX = original.GetLength(0);
var lengthY = original.GetLength(1);
for (var x = 0; x < lengthX; x++)
for (var y = 0; y < lengthY; y++)
filter[x, y] = MedianOfArea(x, y, original, lengthX, lengthY);
return filter;
}
public static double MedianCount(ref double median, List<double> pixelsFields)
{
pixelsFields.Sort();
var countPixels = pixelsFields.Count;
if (countPixels % 2 == 0)
median = (pixelsFields[countPixels / 2 - 1] + pixelsFields[countPixels / 2]) / 2;
else
median = pixelsFields[countPixels / 2];
return median;
}
public static double MedianOfArea(int x, int y, double[,] original, int lengthX, int lengthY)
{
var pixelsFields = new List<double>();
double median = 0;
for (int areasX = -1; areasX < 2; areasX++)
for (int areasY = -1; areasY < 2; areasY++)
{
var diffX = x + areasX;
var diffY = y + areasY;
if ((diffX > -1) && (diffY > -1) && (diffX < lengthX) && (diffY < lengthY))
pixelsFields.Add(original[diffX, diffY]);
}
MedianCount(ref median, pixelsFields);
return median;
}
}
}
You could iterate directly on diffX (and diffY) and use Min and Max to set the ranges to loop over:
using System;
int startX = Math.Max(0, x-1);
int endX = Math.Min(lengthX, x+2);
int startY = Math.Max(0, y-1);
int endY = Math.Min(lengthY, y+2);
for (int diffX = startX; diffX < endX; diffX++)
for (int diffY = startY; diffY < endY; diffY++)
pixelsFields.Add(original[diffX, diffY]);
I don't know if key is the right word for this but I implemented a version of the Diamond-Square algorithm and I was wondering if there is any way to "save" the current output and recreate it using a simple (6 to 10 characters) key. The only way I tought so far is to save it as a single number/string containing the output value of each cell but for a 17X17 grid I would have at least a 289 characters long sequence (assuming the output is a single digit per cell).
Since the algorithm is random I can't think of a better way to do it. If it is impossible can you tell me about better algorithm to use. Thank you :)
PS: I'm using unity 3D and C#
the following class is from the unity tutorial for procedural cave generation, but ive included it because it includes the core elements you need. it uses a seed to generate a map, the same seed will regenerate the same map.... you could use a seed in your generation process, even if it is random, then you could always reuse that seed to return to that map.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class MapGenerator : MonoBehaviour {
public int width;
public int height;
public string seed;
public bool useRandomSeed;
[Range(0,100)]
public int randomFillPercent;
int[,] map;
private void Start()
{
GenerateMap();
}
void GenerateMap()
{
map = new int[width, height];
RandomFillMap();
for (int i = 0; i < 5; i++)
{
SmoothMap();
}
}
void RandomFillMap()
{
if (useRandomSeed)
{
seed = Time.time.ToString();
}
System.Random prng = new System.Random(seed.GetHashCode());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
{
map[x, y] = 1;
}
else
{
map[x, y] = (prng.Next(0, 100) < randomFillPercent) ? 1 : 0;
}
}
}
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
GenerateMap();
}
}
void SmoothMap()
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int neighborWallTiles = GetSurroundingWallCount(x, y);
if (neighborWallTiles > 4)
{
map[x, y] = 1;
}
else if (neighborWallTiles<4)
{
map[x, y] = 0;
}
}
}
}
int GetSurroundingWallCount(int gridx, int gridy)
{
int wallcount = 0;
for(int neighborx=gridx-1; neighborx<=gridx + 1; neighborx++)
{
for (int neighbory = gridy - 1; neighbory <= gridy + 1; neighbory++)
{
if (neighborx >= 0 && neighborx < width && neighbory >= 0 && neighbory < height)
{
if (neighborx != gridx || neighbory != gridy)
{
wallcount += map[neighborx, neighbory];
}
}
else
{
wallcount++;
}
}
}
return wallcount;
}
void OnDrawGizmos()
{
if (map != null)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Gizmos.color = (map[x, y] == 1) ? Color.black : Color.white;
Vector3 pos = new Vector3(-width / 2 + x + .5f, 0, -height / 2 + y + .5f);
Gizmos.DrawCube(pos, Vector3.one);
}
}
}
}
}
I did like the comments said I used Random.InitState provided with unity and everything is working fine. Thanks Again!
UnityEngine.Random.InitState(seed);
I can control the seed as I wish and it gives me the same map for the same seed.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to create a dungeon generator for a project I've been working on based off of this algorithm. I've gotten everything down, but my array (Fig. 1) doesn't seem to be holding giving the map data for some reason. I'm using three types of data to determine if a cell in the map is either empty (0), a space a character can be on (1), a hallway (2), or a wall (3).
I've gotten a bit stuck on this portion so any help is appreciated!
EDIT: The problem is the map object isn't storing the data in the loop shown in Fig. 1. Sorry for being so vague.
(Fig. 1)
for (int i = 0; i < roomList.Count; i++)
{
for (int x = roomList[i].X; x < (roomList[i].X + roomList[i].W); x++)
{
for (int y = roomList[i].Y; y < (roomList[i].Y + roomList[i].H); y++)
{
map[x, y] = 1;
}
}
}
(All of my relevant code)
namespace Project
{
}
public class Room
{
int xValue, yValue, widthValue, heightValue;
public int X
{
get { return xValue; }
set { xValue = value; }
}
public int Y
{
get { return yValue; }
set { yValue = value; }
}
public int W
{
get { return widthValue; }
set { widthValue = value; }
}
public int H
{
get { return heightValue; }
set { heightValue = value; }
}
}
public class DungeonGenerate
{
public int baseWidth = 513;
public int baseHeight = 513;
public int width = 64;
public int height = 64;
Color[,] arrayColor;
Random rand = new Random();
Room room = new Room();
Rectangle[,] rectMap;
public void Generate()
{
rectMap = new Rectangle[baseWidth, baseHeight];
//Creates a 2-D Array/Grid for the Dungeon
int[,] map = new int[baseWidth, baseHeight];
//Determines all the cells to be empty until otherwise stated
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
map[x, y] = 0;
}
}
//Determines the amount of rooms in the dungeon
int minRooms = (width * height) / 300;
int maxRooms = (width * height) / 150;
int amountOfRooms = rand.Next(minRooms, maxRooms);
//Room dimensions
int widthRoot = Convert.ToInt32(Math.Round(Math.Sqrt(width * 2)));
int heightRoot = Convert.ToInt32(Math.Round(Math.Sqrt(height * 2)));
int minWidth = Convert.ToInt32(Math.Round((width * .5) / widthRoot));
int maxWidth = Convert.ToInt32((width * 2) / widthRoot);
int minHeight = Convert.ToInt32(Math.Round(height * .5) / heightRoot);
int maxHeight = Convert.ToInt32((height * 2) / heightRoot);
//Creates the rooms
List<Room> roomList = new List<Room>(amountOfRooms);
for (int i = 0; i < amountOfRooms; i++)
{
bool ok = false;
do
{
room.X = rand.Next(width);
room.Y = rand.Next(height);
room.W = (rand.Next(maxWidth)) + minWidth;
room.H = (rand.Next(maxHeight)) + minHeight;
if (room.X + room.W >= width && room.Y + room.H >= height)
{
continue;
}
for (int q = 0; q < roomList.Count; q++)
{
if (room.X > roomList[q].X && room.X < roomList[q].X + room.W && room.Y > roomList[q].Y && room.Y < roomList[q].Y + room.H)
{
ok = false;
break;
}
}
ok = true;
roomList.Add(room);
} while (!ok);
}
//This will create hallways that lead to and from the rooms
int connectionCount = roomList.Count;
List<Point> connectedCells = new List<Point>((width * height));
for (int i = 0; i < connectionCount; i++)
{
Room roomA = roomList[i];
int roomNum = i;
while (roomNum == i)
{
roomNum = rand.Next(roomList.Count);
}
Room roomB = roomList[roomNum];
//Increasing this will make the hallway more straight, decreasing it will make the hallway more skewed
int sidestepChance = 10;
Point pointA = new Point(x: (rand.Next(roomA.W)) + roomA.X, y: (rand.Next(roomA.H)) + roomA.Y);
Point pointB = new Point(x: (rand.Next(roomB.W)) + roomB.X, y: (rand.Next(roomB.H)) + roomB.Y);
while (pointA != pointB)
{
int num = rand.Next() * 100;
if (num < sidestepChance)
{
if (pointB.X != pointA.X)
{
if (pointB.X > pointA.X)
{
pointB.X--;
}
else
{
pointB.X++;
}
}
}
else if(pointB.Y != pointA.Y)
{
if (pointB.Y > pointA.Y)
{
pointB.Y--;
}
else
{
pointB.Y++;
}
}
}
if (pointB.X < width && pointB.Y < height)
{
connectedCells.Add(pointB);
}
}
//Fills the room with data
for (int i = 0; i < roomList.Count; i++)
{
for (int x = roomList[i].X; x < (roomList[i].X + roomList[i].W); x++)
{
for (int y = roomList[i].Y; y < (roomList[i].Y + roomList[i].H); y++)
{
map[x, y] = 1;
}
}
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (map[x, y] == 0)
{
bool wall = false;
for (int yy = y - 2; yy < y + 2; yy++)
{
for (int xx = x - 2; xx < x + 2; xx++)
{
if (xx > 0 && yy > 0 && xx < width && yy < height)
{
if (map[xx, yy] == 1 || map[xx, yy] == 2)
{
map[x, y] = 3;
wall = true;
}
}
}
if (wall)
{
break;
}
}
}
}
}
//Rendering the Map and giving it some Color (Sort of)!
int scaler = baseWidth / width;
for (int x = 0; x < baseWidth; x++)
{
for (int y = 0; y < baseHeight; y++)
{
rectMap[x, y] = new Rectangle(x, y, 1, 1);
arrayColor = new Color[baseWidth, baseHeight];
switch (map[x, y])
{
case 0:
arrayColor[x, y] = new Color(0,0,0);
break;
case 1:
arrayColor[x, y] = new Color(0,0,0);
break;
case 2:
arrayColor[x, y] = new Color(0,0,0);
break;
case 3:
arrayColor[x, y] = new Color (0,0,0);
break;
}
}
}
}
public Rectangle[,] GetMap()
{
return rectMap;
}
public Color[,] GetColors()
{
return arrayColor;
}
}
In the for-loop where you're populating roomList, you're not instantiating a new Room each time. You're simply manipulating the same Room object and re-adding it to the list, so roomList will just contain many references to the same Room object. Try removing the room field from your DungeonGenerate class and use a local variable instead:
for (int i = 0; i < amountOfRooms; i++)
{
bool ok = false;
do
{
var room = new Room();
...
roomList.Add(room);
} while (!ok);
}
I have a 3d-array double[,,] numbers = new double[x,y,z]; and now if one imagines the 3d-array to look like a cube with numbers I need to find the smallest and biggest value of every slice along all three directions.
It is of course easy to do by simply looping over it, but does C# have any functions to find the smallest and biggest value in a slice?
To explain it a bit further, maybe this "unreal" code will help:
int i;
double[] xmin = new double[x];
double[] xmax = new double[x];
double[] ymin = new double[y];
double[] ymax = new double[y];
double[] zmin = new double[z];
double[] zmax = new double[z];
for(i = 0; i < x; i++)
{
MinOf(numbers[i, y, z]) = xmin[i];
MaxOf(numbers[i, y, z]) = xmax[i];
}
for(i = 0; i < y; i++)
{
MinOf(numbers[x, i, z]) = ymin[i];
MaxOf(numbers[x, i, z]) = ymax[i];
}
for(i = 0; i < z; i++)
{
MinOf(numbers[x, y, i]) = zmin[i];
MaxOf(numbers[x, y, i]) = zmax[i];
}
Hopefully someone can help me with that.
Cheers, Phil13131
You can make methods for enumerating the slices. This is for one dimension, you would need another two, but I think you can manage that:
public static IEnumerable<T> SliceX<T>(T[,,] data, int x) {
for (int y = 0; y < data.GetLength(1); y++) {
for (int z = 0; z < data.GetLength(2); z++) {
yield return data[x, y, z];
}
}
}
Then you can just use the Min and Max methods, but that will of course loop through the data twice:
double min = SliceX(numbers, x).Min();
double max = SliceX(numbers, x).Max();
You can make an extension method that gets both min and max in one iteration:
public static class IEnumerableExtensions {
public static void GetMinMax<T>(this IEnumerable<T> data, out T min, out T max) where T : IComparable<T> {
bool first = true;
min = max = default(T);
foreach (T value in data) {
if (first) {
min = max = value;
first = false;
} else {
if (value.CompareTo(min) < 0) min = value;
if (value.CompareTo(max) > 0) max = value;
}
}
}
}
Usage:
double min, max;
SliceX(numbers, 0).GetMinMax(out min, out max);
Are you looking for something like this?
double[, ,] numbers = new double[2, 2, 2];
numbers[0, 0, 0] = 0;
numbers[0, 0, 1] = 1;
numbers[0, 1, 0] = 2;
numbers[0, 1, 1] = 3;
numbers[1, 0, 0] = 4;
numbers[1, 0, 1] = 5;
numbers[1, 1, 0] = 6;
numbers[1, 1, 1] = 7;
double[] xmax = new double[numbers.GetLength(0)];
double[] ymax = new double[numbers.GetLength(1)];
double[] zmax = new double[numbers.GetLength(2)];
for (int x = 0; x < xmax.Length; x++) xmax[x] = int.MinValue;
for (int y = 0; y < ymax.Length; y++) ymax[y] = int.MinValue;
for (int z = 0; z < zmax.Length; z++) zmax[z] = int.MinValue;
for (int x = 0; x < xmax.Length; x++)
for (int y = 0; y < ymax.Length; y++)
for (int z = 0; z < zmax.Length; z++)
{
xmax[x] = Math.Max(xmax[x], numbers[x, y, z]);
ymax[y] = Math.Max(ymax[y], numbers[x, y, z]);
zmax[z] = Math.Max(zmax[z], numbers[x, y, z]);
}
// xmax == { 3, 7 }
// ymax == { 5, 7 }
// zmax == { 6, 7 }