Having some issues with procedurally generating a map - c#

I'm building a very simple 2d tile map. It just spawns a few different grass tiles in random locations here's what it looks like:
public int tileHeight; //y
public int tileWidth; //x
int tileHeightCounter;
int tileWidthCounter;
public GameObject[] floorTiles;
void Start(){
tileHeightCounter = 0;
tileWidthCounter = 0;
while(tileHeightCounter < tileHeight){
Vector3 tileSpawnPoint = new Vector3 (tileWidthCounter, tileHeightCounter, 0);
GameObject groundTiles = (GameObject)Instantiate (floorTiles[Random.Range(0, floorTiles.Length)], tileSpawnPoint, Quaternion.identity);
groundTiles.transform.parent = transform;
if (tileWidthCounter == tileWidth) {
tileWidthCounter = 0;
tileHeightCounter++;
}
tileWidthCounter++;
}
}
I've run into two problems- say your tileHeight is 10 and your tileWidth is also 10 then the map it generates should be a 10x10 with 100 total tiles randomly distributed.
Instead two weird things are occurring the first is that if your map is 10x10 it actually generates a 10x9 meaning it stops one layer short on the y axis. The second is a grid(grass tile) is being created at 0,0 but the rest of the map is being created with at least x being 1 meaning that the map has a single tile attached to the bottom left sticking out.
I'm a bit confused as to whats going on here or how to fix it. Any help is much appreciated.

The issue is that you're using a < less than, so once it actually hits the tileHeight it exits the loop one iteration too early. Make it <=.

Related

Object spawn on procedural terrain spawns on unspecified area

I've managed to put together a procedural terrain with defined regions and I am looking to procedurally place objects within the world within these regions. The regions are defined by their height and I am trying to utilise this to correctly place certain objects in certain regions however my result seems to come out slightly odd where objects are able to spawn outside the defined region height. I am using an AnimationCurve as a mesh height curve to prevent water areas from becoming terrain like. I am unsure if this is causing the issue behind in the correct placement. Would appreciate any insight into where I might be going wrong
Defined regions:
The Rock region is defined with a height of 0.7 and I try to spawn trees on the map only at a Rock location
Spawning object (Spawn 10) at rock location
int amount = 0;
for (int y = 0; y < mapHeight; y++)
{
if(amount < 10)
{
for (int x = 0; x < mapWidth; x++)
{
float currentHeight = noiseMap[x, y];
if(currentHeight.ToString("f1") == (0.7f).ToString())
{
Debug.Log(currentHeight.ToString("f1"));
Vector3 spawnPosition = new Vector3(Random.Range((x), (mapWidth / 2)), currentHeight, Random.Range(y, (mapHeight / 2)));
var block = Instantiate(AssetsToSpawn[0].AssetPrefab, spawnPosition, Quaternion.identity);
block.transform.SetParent(this.transform);
amount++;
break;
}
}
} else
{
return;
}
Result
Some seem to spawn in the right location albeit looking slightly weird but the one on the far left is finding itself on flat land, with water and sand; an area not defined as 0.7 or Rock type.
I think the issue lies in the line
Vector3 spawnPosition = new Vector3(Random.Range((x), (mapWidth / 2)), currentHeight, Random.Range(y, (mapHeight / 2)));
you seem to already iterate your map grid using x and y so why pick random positions on your map that might be anywhere between this current position and the center of the map?
I think you would rather want a random position within the current field and do e.g.
Vector3 spawnPosition = new Vector3(x + Random.Range(-0.5f, 0.5f), currentHeight, y + Random.Range(-0.5f, 0.5f));
Besides that why go through strings in
if(currentHeight.ToString("f1") == (0.7f).ToString())
I see that it's probably for the rounding but I would still prefer to rather do e.g.
if(Mathf.Abs(currentHeight - 0.7f) <= 0.05f)
which would have about the same effect but the threshold is better to control.
However, sounds to me like rock rather would be anything between 0.49 and 0.7 actually so actually it should be
if(currentHeight > 0.49f && currentHeight <= 0.7f)
Finally, unless you store somewhere which map position you already populated with a tree your outer for loop will always over and over enter at the exact same grid position, the first one that is encountered to fulfill your inner loop's condition!
So far you where always using the exact se position for all 10 trees, only the random position caused that it didn't seem so.

Minecraft like terrain in unity3d

I'm a fan of Minecraft's old terrain generation with amazing overhangs, mountains and generally interesting worlds. My problem is that right now I'm using perlin noise, which while good for smooth terrain doesn't really give sporadic jumps that would allow mountains in a mostly flat area.
On top of that with the method I'm using gets 2d perlin noise, puts it in an array and then gets every Y value under it and sets it to a block; This stops generation of overhangs like this: Old Minecraft Terrain Image
Right now I have this:
public class GenerateIdMap : MonoBehaviour {
[Serializable] public class IBSerDict : SerializableDictionaryBase<int, byte> {};
public int size = 60;
public int worldHeight = 3;
public float perlinScale = 15f;
public int seed;
public int heightScale = 10;
public int maxHeight = 256;
public IBSerDict defaultBlocks = new IBSerDict();
void Start()
{
if (seed != 0) seed = (int)Network.time * 10;
CreateMap();
}
byte[,,] CreateMap()
{
byte[,,] map = new byte[size, maxHeight, size];
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
int y = (int)(Mathf.PerlinNoise((x + seed) / perlinScale, (z + seed) / perlinScale) * heightScale) + worldHeight;
y = Mathf.Clamp(y, 0, maxHeight-1);
while (y > 0)
{
map[x, y, z] = GetBlockType(y);
y--;
}
}
}
return map;
}
byte GetBlockType(int y)
{
SortedDictionary<int, byte> s_defaultBlocks = new SortedDictionary<int, byte>(defaultBlocks);
foreach (var item in s_defaultBlocks.OrderBy(key => key.Key))
{
if (y <= item.Key)
{
print(item.Value);
return item.Value;
}
}
return 0;
} }
The GetBlockType function is new and for getting the default block at that height, I'll fix it up later but it works for now. If you instantiate a prefab at that vector3 you would see terrain. Can someone help me figure out how to make better terrain? Thanks in advance!
Both of your problems should be tackled individually.
The first issue regarding the lack of variation in the generated values can usually be fixed in one of two ways, the first way is to modify the input into the perlin noise, i.e. the octaves and persistance and the second is to mix the output of multiple functions and even use the output of one function as the input to another. By functions, I mean Perlin/Simplex/Voronoi etc.
With the former method, as you mentioned, it can be pretty difficult to get terrain with interesting features over a large area (the generated values are homogeneous), but by playing with the coordinate range and octaves/persistance, it can be possible. The second approach is probably recommended however, because by mixing the inputs and outputs of different functions you can get some really interesting shapes (Voronoi produces circular crator-like shapes).
In order to fix the problem you are having with the overhangs, you would need to change your approach to generating the world slightly. Currently, you are just generating the height values of the terrain and assigning each of those values to give you the terrain surface only. What you ideally would want to do is, generate a pseudo-random value to use as a pass flag for each of the blocks in the 3d space (also those underground). The flag would indicate whether a block should be placed or not in the 3d world.
This is slower, but would generate caves and overhangs as you need.

Getting Number of Cubes with Integer Coordinates with Certain Distance from Origin

I am working on a voxel system for my game that uses dynamic loading of chunks. To optimize it, I have a pool of chunks and a render distance, and what I want to do is fill the pool with a proper amount of chunks. So, I need a way to find that amount. I have tried the following but it seems very inefficient.
private void CreatePool()
{
int poolSize = 0;
for (int x = -m_RenderDistance; x <= m_RenderDistance; x++) {
for (int y = -m_RenderDistance; y <= m_RenderDistance; y++) {
for (int z = -m_RenderDistance; z <= m_RenderDistance; z++) {
if (Position3Int.DistanceFromOrigin(new Position3Int(x, y, z)) <= m_RenderDistance)
poolSize++;
}
}
}
}
More formally, the question involes finding the amount of unique cubes with integer coorindates with a certain distance from the origin.
If you think there is a better way to approach this or I am doing something fundamentally wrong, let me know.
Thanks,
Quintin
I assume its the distance check that you think is inefficient? What you've got shouldn't be too bad if you're just getting the count on Start() or Awake().
Draco18s solution is fine if you are okay with a cubed result. If you want a spherical result without a distance check, you can try some formulation of the volume of a sphere: 4/3*PI*r^3
checkout Bresenham's circle.
Here's a approximation algorithm for a filled 3d Bresenham Circle that I have. It is very similar to what you have already, just with a more effecient squared dist check and a minor adjustment to get a more attractive bresenham-looking circle):
public static List<Vector3> Get3DCircleKeys(int radius){
List<Vector3> keys = new List<Vector3>();
for(int y=-radius; y<=radius; y++){
for(int x=-radius; x<=radius; x++){
for(int z =-radius; z<=radius; z++){
// (+ radius*.08f) = minor modification to match Bresenham result
if(x*x+y*y+z*z <= radius*radius + radius*.08f){
keys.Add(new Vector3(x,y,z));
}
}
}
}
return keys;
}
This, however, will deliver a different count than the volume of sphere would give you, but with some tweaking to it or to the sphere volume calculation, it could be good enough, or at least, more efficient than instantiating a full volume of a cube, where many of the voxels will be outside of the bounds of the render distance.

C# - Per Pixel Collision Detection

Here's my situation I'm making a 2D maze game(XNA 4.0). I have figured out that the best way to do collision detection is by using per-pixel detection. While searching it up on the internet I have found people explaining or showing code for two things colliding(i.e. mouse & player, player & player, two shapes). What I would like to do is have this collision detect whether the player collides with a wall or not(the background is black but the maze walls are white). Could someone explain how to do this or to give some sort of starting point with the code. Much Appreciated.
P.S. A link to a website or anything relating to my question would also be helpful
The best way to go about this CPU-intensive operation is checking for hitbox collision first, then the per-pixel collision.
Most of this code can be found in this helpful video.
static bool IntersectsPixel(Rectangle hitbox1, Texture2D texture1, Rectangle hitbox2, Texture2D texture2)
{
Color[] colorData1 = new Color[texture1.Width * texture1.Height];
texture1.GetData(colorData1);
Color[] colorData2 = new Color[texture2.Width * texture2.Height];
texture2.GetData(colorData2);
int top = Math.Max(hitbox1.Top, hitbox2.Top);
int bottom = Math.Min(hitbox1.Bottom, hitbox2.Bottom);
int right = Math.Max(hitbox1.Right, hitbox2.Right);
int left = Math.Min(hitbox1.Left, hitbox2.Left);
for(y = top; y< bottom; y++)
{
for(x = left; x < right; x++)
{
Color color1 = colorData1[(x - hitbox1.Left) + (y - hitbox1.Top) * hitbox1.Width]
Color color2 = colorData2[(x - hitbox2.Left) + (y - hitbox2.Top) * hitbox2.Width]
if (color1.A != 0 && color2.A != 0)
return true;
}
}
return false;
}
You can call this method like so:
if (IntersectsPixel(player.hitbox, player.texture, obstacle.hitbox, obstacle.texture))
{
// Action that happens upon collision goes here
}
Hope I could help you out,
- GHC
Create a matrix of bools representing your sprite and a matrix of bools representing your maze (the matrix representing your sprite needs to have the same dimensions as your maze).
then you can do something simple like iterate over all x-y coordinates and check whether or not they're both true
// as an optimization, store a bounding box to minimize the
// coordinates of what you need to check
for(int i = 0; i < width, i++) {
for(int j = 0; j < height, j++) {
if(sprite[i][j] && maze[i][j]) {
collision = true
//you might want to store the coordinates
}
}
}
If you want to be very fancy you can flatten your maze matrix and use bit operations

Spawning objects at random generated worldpoints

I'm new to unity 3d and I want to make a very simple obstacle course game. I don't want it to have multiple levels but instead there will be only one scene which will generate randomly each time someone starts the game.
This is a picture to better explain the idea:
In each highlighted section there will be a wall which will be generated every time the application starts and the player can only get through a gap which will be randomly generated in any of the areas a, b or c of each section.
I tried looking this up but there wasn't really much of this example.
If have any questions, please don't hesitate. I'm always notified for responses.
Thanks for your time!
Basic concept:
Create a prefab from your obstacle
Create a script (e.g. WallSpawner) with a couple of parameters (distance between each wall, possible positions, etc.) and attach it to an object in your scene (in your case Walls for example).
In the Start or Awake method, create copies of your prefab with Instantiate and pass in the randomly picked position.
Example Script:
public class WallSpawner : MonoBehaviour
{
// Prefab
public GameObject ObstaclePrefab;
// Origin point (first row, first obstacle)
public Vector3 Origin;
// "distance" between two rows
public Vector3 VectorPerRow;
// "distance" between two obstacles (wall segments)
public Vector3 VectorPerObstacle;
// How many rows to spawn
public int RowsToSpawn;
// How many obstacles per row (including the one we skip for the gap)
public int ObstaclesPerRow;
void Start ()
{
Random r = new Random();
// loop through all rows
for (int row = 0; row < RowsToSpawn; row++)
{
// randomly select a location for the gap
int gap = r.Next(ObstaclesPerRow);
for (int column = 0; column < ObstaclesPerRow; column++)
{
if (column == gap) continue;
// calculate position
Vector3 spawnPosition = Origin + (VectorPerRow * row) + (VectorPerObstacle * column);
// create new obstacle
GameObject newObstacle = Instantiate(ObstaclePrefab, spawnPosition, Quaternion.identity);
// attach it to the current game object
newObstacle.transform.parent = transform;
}
}
}
}
Example parameters:
Example result:

Categories