I'd like to make a Cellular Automaton in C#. This is a fairly easy task, which I almost have no problem with. However I'm getting an awful performance by just drawing rectangles of size one in Windows Forms Application, so my question is:
How to draw all the pixels one by one and yet have a good performance?
EDIT:
Okay, so this is my code:
public partial class Form1 : Form
{
Task t;
const int _numberOfStates = 5;
const int _resolutionX = 1920, _resolutionY = 1200;
Color[] states = new Color[_numberOfStates] { Color.Aqua, Color.Green, Color.Orange, Color.Red, Color.Blue };
Bitmap bmp = new Bitmap(1920, 1200);
short[,] map = new short[_resolutionX, _resolutionY];
public Form1()
{
InitializeComponent();
Size = new Size(_resolutionX, _resolutionY);
Random rand = new Random();
for (int x = 0; x < _resolutionX; x++)
for (int y = 0; y < _resolutionY; y++)
map[x, y] = (short)rand.Next(_numberOfStates);
t = new Task(() => { MapToBitmap(); IterateMap(); });
t.Start();
}
protected override void OnPaint(PaintEventArgs e)
{
t.Wait();
e.Graphics.DrawImage(bmp, new PointF(0, 0));
t = new Task(() => { MapToBitmap(); IterateMap(); });
t.Start();
}
void MapToBitmap()
{
for (int x = 0; x < _resolutionX; x++)
for (int y = 0; y < _resolutionY; y++)
bmp.SetPixel(x, y, states[map[x, y]]);
}
void IterateMap()
{
for (int x = 0; x < _resolutionX; x++)
for (int y = 0; y < _resolutionY; y++)
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
if ((i != 0 || j != 0) && x + i >= 0 && x + i <= _resolutionX && y + j >= 0 && y + j <= _resolutionY)
map[x, y] = (short)((map[x, y] + 1) % _numberOfStates);
}
}
Don't bother looking at IterateMap() and MapToBitmap() functions, however my problem now is, the OnPaint function is only called once, so I get only one iteration.
Any idea why?
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
The problem is to make a program the flips a 2D (2 by 2) array either horizontally(H) or vertically(V), depending on the input the user puts. For example, if the user puts "HHHVVHVHV", it means to flip 5 times horizontally, and 4 times vertically. The code I attached below works, but I am not sure if there is a built-in method in C# to flip arrays or an easier way to do it.
using System;
using System.Linq;
namespace _2019JuniorQ4
{
class Program
{
static void Main(string[] args)
{
//Varables
int[,] grid = new int[2, 2] { { 1, 2 }, { 3, 4 } };
int[,] flippedGrid = new int[2, 2];
string input = "";
int h = 0;
int v = 0;
int flipHorizontal = 0;
int flipVertical = 0;
//Input
input = Console.ReadLine();
//Count number of times
for (int i=0; i<input.Length; i++)
{
if (input[i] == Convert.ToChar("H"))
{
h++;
}
else if(input[i] == Convert.ToChar("V"))
{
v++;
}
}
//Process
flipHorizontal = h % 2;
flipVertical = v % 2;
//Vertical flip
if (flipVertical == 1)
{
for (int y = 0; y < 2; y++)
{
for (int x = 1; x >= 0; x--)
{
flippedGrid[y, Math.Abs(x - 1)] = grid[y,x];
}
}
grid = flippedGrid.Clone() as int[,];
}
//Horizontal Flip
if (flipHorizontal == 1)
{
for (int x = 0; x < 2; x++)
{
for (int y = 1; y >= 0; y--)
{
flippedGrid[Math.Abs(y - 1), x] = grid[y,x];
}
}
grid = flippedGrid.Clone() as int[,];
}
//Output
for (int x=0; x<2; x++)
{
for (int y=0; y<2; y++)
{
Console.Write(grid[x,y]+" ");
}
Console.WriteLine();
}
}
}
}
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;
}
}
}
field = new int[input.Width][][];
for (int x = 0; x < input.Width; x++)
{
field[x] = new int[input.Height][];
for (int y = 0; y < 3; y++)
{
field[x][y] = new int[3];
}
}
For some reason the above code is giving me out of range exception but the following works fine:
field = new int[input.Width][][];
for (int x = 0; x < input.Width; x++)
{
field[x] = new int[input.Height][];
}
for (int y = 0; y < input.Height; y++)
{
for (int x = 0; x < input.Width; x++)
{
field[x][y] = new int[3];
field[x][y][0] = random.Next(0, output.Width);
field[x][y][1] = random.Next(0, output.Height);
field[x][y][2] = MaskFunctions.DSCALE;
}
}
Can anyone point out what am i doing wrong?
Also: Is there a difference between out of range and out of bound exception?
Your inner loop goes from 0 to 3, instead of 0 to input.Height. This will produce an out of range exception when input.Height < 3.
You probably meant to do this:
field = new int[input.Width][][];
for (int x = 0; x < input.Width; x++)
{
field[x] = new int[input.Height][];
for (int y = 0; y < input.Height; y++)
{
field[x][y] = new int[3];
}
}
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);
}