I'm working on civilization game in C# and XNA. I use a two dimensional integer array, populated with a loop, to generate tiles, I've done a ton research and have been unable to find a way to generate earth like terrain. Can anyone explain how to do this or at least give me code that could do it, though I would prefer and explanation? Thank you.
I use an algorithm similar to this to make my terrain. Basicly it generates some random numbers and uses a sine wave to generate hills, when combined they give a nice hilly landscape. Note that you can add a loop and array of values if you want more than just 3 passes.
private void GenerateTerrain()
{
terrainContour = new int[Width*Height];
//Make Random Numbers
double rand1 = randomizer.NextDouble() + 1;
double rand2 = randomizer.NextDouble() + 2;
double rand3 = randomizer.NextDouble() + 3;
//Variables, Play with these for unique results!
float peakheight = 20
float flatness = 50
int offset = 30;
//Generate basic terrain sine
for (int x = 0; x < Width; x++)
{
double height = peakheight / rand1 * Math.Sin((float)x / flatness * rand1 + rand1);
height += peakheight / rand2 * Math.Sin((float)x / flatness * rand2 + rand2);
height += peakheight / rand3 * Math.Sin((float)x / flatness * rand3 + rand3);
height += offset;
terrainContour[x] = (int)height;
}
}
Then to fill the heightmap just loop through the values and check if it is above the threshold or not.
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
if (y > terrainContour[x])
tiles[x, y] = Solid Tile
else
tiles[x, y] = Blank Tile
}
}
Theres much more you can add to it, I've added more randomness and indenting some tiles by 1 up or down for better terrain. And adding more sine waves will make it more realistic.
Try using 2D Perlin Noise algorithms, and selecting certain heights to make caves and more advanced terrain, as this is now what I do, but this code here is a good start.
Related
I am trying to make a system that uses a perlin noise field to generate random positions that are then saved, or saved as the are generated, to a list. Then using that list the game will use those positions to spawn in objects within the level. I think that I might be on the right track, but I might have a few things in the wrong places.
Here is a link to an image of all the things that I think are relevant:
the blue dots should be the saved locations that are added to the list. But i am only getting one position 5 times, which is the correct number of positions but they are all the same position that is the problem.
// --- Random Generation of Objects --- //
Color[] colourMap = new Color[mapWidth * mapHeight];
for(int y = 0; y < mapHeight; y++) {
for(int x = 0; x < mapWidth; x++) {
float currentHeight = noiseMap[x, y];
for(int i = 0; i < regions.Length; i++) {
// --- Random Gen of Asteroids --- //
if(currentHeight <= regions[i].height) {
colourMap[y * mapWidth + x] = regions[i].colour;
Vector3 ping = new Vector3(x, 0, y);
asteroids.Add(ping);
Debug.Log(asteroids[i]);
break;
}
}
}
}
Anyway thank you for the help and reading this far, please let me know if you need anything more.
I am trying to create a texture (in a 3-D byte array) that is a coloured gabor patch. I am using OpenTK to map the texture. The texture mapping is working fine, but the texture that is created by my code below is not what I need.
The code I have come up with is as follows:
for (int x = 0; x < size; x++)
{
for (int y = 0; y < size; y++)
{
double sin_term = 0.5*(double)Math.Sin(10 * 3.14159 * ((double)x / (double)size));
sin_term += 0.5;
double gauss = 0.5+Math.Exp(-((Math.Pow(x,2)+Math.Pow(y,2))/(2*Math.Pow(sigma,2))));
double gabor = sin_term * gauss;
byteTexture2[j,i,0] = (byte)(((double)Colour.R * gabor));
byteTexture2[j,i,1] = (byte)(((double)Colour.G * gabor));
byteTexture2[j,i,2] = (byte)(((double)Colour.B * gabor));
}
}
My maths isn't alll that good, so I may be off track but I was trying to multiply the sine wave by the gaussian. The sine wave term seems to work OK by itself but the gaussian may be where it is having problems.
Any help would be much appreciated.
Have found MATLAB code for this problem but no c/c++/c# code
Thanks.
I recently coded up a Gabor filter kernel for use in OpenCV (using C++). Here is my code for the kernel:
/// compute Gabor filter kernels
for (int i = 0; i < h; i++){
x = i - 0.5*(h - 1);
for (int j = 0; j < h; j++) {
y = j - 0.5*(h - 1);
gaborKernelCos.at<float>(i, j) = exp((-16 / (h*h))*(x*x + y*y))*cos((2 * M_PI*w / h)*(x*cos(q) + y*sin(q))) / (h*h);
gaborKernelSin.at<float>(i, j) = exp((-16 / (h*h))*(x*x + y*y))*sin((2 * M_PI*w / h)*(x*cos(q) + y*sin(q))) / (h*h);
}
}
Where the input parameters are the kernel size h, wave number w, and filter orientation q. Note the wave number is related to the filter pixel wavelength by l = h/w. Also, my value for sigma is simply a constant multiple of h.
This shouldn't really produce anything wildly different from your code as far as I can tell. Does your value for sigma make sense? It should probably be at most sigma = 0.5*size.
I am using Unity 5 to create an isometric game. I have generated a grid of tiles and it works well. However, when I use two different tiles to fill in the grid (their image sizes are slightly different), I get gaps in between the tiles. The obvious solution would be to create the tiles so that they are all the same image size, but this would prevent me from creating anything on a tile that is larger than the size of a tile (eg. a tree).
Here are some images to demonstrate:
With only one type of tile:
With two types of tile:
This is the code I use to create the map:
private void CreateMap() {
float tileWidth;
float tileHeight;
int orderInLayer = 0;
SpriteRenderer r = floorTiles [0].GetComponent<SpriteRenderer> ();
tileWidth = r.bounds.max.x - r.bounds.min.x;
tileHeight = r.bounds.max.y - r.bounds.min.y;
for (int i = 0; i < map.GetLength(0); i++) {
orderInLayer += 1;
for (int j = 0; j < map.GetLength (1); j++) {
Vector2 position = new Vector2 ((j * tileWidth / 2) + (i * tileWidth / 2) + (tileWidth / 2), (j * tileHeight / 2) - (i * tileHeight / 2) + (tileHeight/ 2));
r = map[i,j].GetComponent<SpriteRenderer>();
r.sortingOrder = orderInLayer;
Instantiate(map[i, j], position, Quaternion.identity);
}
}
}
Any help would be greatly appreciated, I cannot seem to fix it!
You appear to be calculating a position for each of your tiles from scratch every time you create one. If you have 2 different sized tiles, then your calculation comes out different, hence the gaps in your tiles. This is because you're only using the width/height of the current tile, failing to take into account any previous tiles that may be a shorter/longer height/width.
Given you have varying heights AND widths you'll need a way to calculate the correct position for both to prevent gaps in the X and Y direction. I've mocked up something here, but it's untested. More of a concept(?) I guess.
float tileHeight = 0;
float tileWidth = 0;
Vector2 position = new Vector2(0,0);
Dictionary<int, float> HeightMap = new Dictionary<int, float>();
for (int iRow = 0; iRow < map.GetLength(0); iRow++)
{
position.x = 0;
orderInLayer += 1;
for (int jColumn = 0; jColumn < map.GetLength (1); jColumn++)
{
position.y = HeightMap[jColumn];
r = map[iRow, jColumn].GetComponent<SpriteRenderer>();
tileWidth = r.bounds.max.x - r.bounds.min.x;
tileHeight = r.bounds.max.y - r.bounds.min.y;
r.sortingOrder = orderInLayer;
position.x += tileWidth / 2;
position.y += tileHeight / 2;
Instantiate(map[iRow, jColumn], position, Quaternion.identity);
HeightMap[jColumn] = position.y;
}
}
I leave the best way of storing the height, or instantiating the contents of the HeightMap dictionary to however you see fit.
Alright, so today I decided to try to further optimize my collision detection code for my tile engine.
This is what I did:
Circle class checks if there are points within range. If there are, then check for collision between player and tile.
Code:
int tileWidth = 128;
int tileHeight = 128;
int[,] Layer3 = { 1, 1, 1, etc... };
int tileMapWidth = Layer3.GetLength(1);
int tileMapHeight = Layer3.GetLength(0);
Rectangle tile, tile2;
for (int x = 0; x < tileMapWidth; x++)
{
for (int y = 0; y < tileMapHeight; y++)
{
int wallIndex = Layer3[y, x];
if (wallIndex == 1) //Full-sized Tile Collision (128 x 128)
{
if (collisionCircle.Contains(new Vector2(x * tileWidth + (tileWidth / 2) + (int)Player.camera.Position.X,
y * tileHeight + (tileHeight / 2) + (int)Player.camera.Position.Y))) //+ tile / 2 is for centering the point
{
tile = new Rectangle(x * tileWidth + (int)Player.camera.Position.X, y * tileHeight + (int)Player.camera.Position.Y, tileWidth, tileHeight);
Collide(tile);
}
}
}
}
This would check throughout layer3 if there is a "1". If there is, assign rectangle and check for collision if point is inside collision radius.
Also, I checked this code(with a draw method), and I know it's working properly, at least the behavior.
I added in about 120,000(32 x 3888) tiles to try to make it lag, and before the code, it lagged a little bit. But after I added in the code, it lagged even more so.
I thought that since it would only check for collision between tiles(points) that are within the radius it wouldn't even remotely lag, but that's not the case...
Any help/ideas on how to optimize this would be great.
Thanks a lot,
Shyy
EDIT:
Cirlce.Contains() code:
public bool Contains(Vector2 Point)
{
return ((Point - position).Length() <= radius);
}
I used a circle because I've heard it's faster than using a rectangle.
Another possible optimization is instead of
return ((Point - position).Length() <= radius);
use
return ((Point - position).LengthSquared() <= radius * radius);
This is faster because Vector2.Length() has to perform a costly square root operation. Vector2.LengthSquared() does not have to perform that slow operation. The radius has to be multiplied by itself to account for the length from the vector being squared.
It sounds like you're trying to determine what tiles you don't need to use for collision with the player. Another optimization you could do is that if a tile at (X=5,Y=5) is above and to the left of the player, then you don't need to check a tile at (X=4,Y=4). Similarly if (X=5,Y=5) is below and to the right, (X=6,Y=6) is guaranteed to be too far as well. Try to determine when you've passed the player and no longer need to check collisions.
I suggest to loop only over visible tiles in screen to check collision using movement offset.
i will try something from my head..
for x as integer = 0 + offSetX to tilesInWidth + offSetX
for y as integer = 0 + offSetY to tilesInHeight + offSetY
if player.insideCircle(player.position, radius) '
object = layer(y,x);
if player.collideWith(object) then Collide()
end if
next
next
I am trying to take the positions of the cells of a game level and map them to a 2D array. I want to do this so I can make each ground cell (and NOT background cell) collidable with my player character.
Below is the current code that someone created for me:
int tileSize = 20;
int screenSizeInTiles = 30;
// initializing multidimensional array of points
var tilePositions = new System.Drawing.Point[screenSizeInTiles, screenSizeInTiles];
for (int x = 0; x < screenSizeInTiles; x++)
{
for (int y = 0; y < screenSizeInTiles; y++)
{
tilePositions[x, y] = new System.Drawing.Point(x * tileSize, y * tileSize);
}
}
It can be found here: How can I use a jagged array to record the x and y axes of these tiles?
along with a better description of what I'm trying to do.
So, when I run this code, I get an empty array in tilePositions. Well, the x, and y values are there, but the values are all 0. The values should be the position data for the cells.
Here is what the tilesPosition array looks like:
http://imgur.com/VYyxp
I'm still working on the collision code though... I need this to work before I can figure that part out.
Thank you all incredibly much, you have been so helpful! I am still a beginner, but am working around the clock to make myself a better programmer.
if you did
int tileSize = 20;
int screenSizeInTiles = 30;
// initializing jagged array of points
var tilePositions = new Point[screenSizeInTiles][screenSizeInTiles];
for (int x = 0; x < screenSizeInTiles; x++)
{
for (int y = 0; y < screenSizeInTiles; y++)
{
tilePositions[x][y] = new Point(x * tileSize, y * tileSize);
}
}
it would be jagged (an array of arrays.)