I'm trying to find adjacent vertices from just one selected vertex. One condition these neighboring vertices have to fulfill is that they must be on the same height as the selected original vertex.
I have an int[] vertices array, and a Dictionary<int, float> plateauDictionary.
The vertices array holds all vertices of the entire mesh.
The Dictionary holds a collection of vertices that have a height(y-Axis) greater than 0.2f.
Im creating big flat plateaus, so finding adjacent vertices at the same height should be easy as most vertices are adjacent to each other at the same height.
Heres what I wrote so far:
Matrix4x4 localToWorld = transform.localToWorldMatrix;
Vector3[] worldVectors = new Vector3[totalVertices];
foreach (KeyValuePair<int, float> u in plateauDictionary)
worldVectors[u.Key] = localToWorld.MultiplyPoint3x4(mesh.vertices[u.Key]);
This returns an array of Vector3s consisting of all the world position of each vertex.
Now I want to pick one or a few randomly, and then get the amount of adjacent vertices at the same height, maybe in an assigned radius or maybe in a list of neighbors? How do I solve this? I'm a newb with LINQ but Im thinking that might be the way to go?
Thanks in advance!
My other code(Not really important for this question but for the sake of completeness):
plateauDictionary = new Dictionary<int, float>();
for (int j = 0; j < plateaus; j++)
{
var rndSize = plateauSizes[Random.Range(0, plateauSizes.Length)];
var rndHeight = plateauHeights[Random.Range(0, plateauHeights.Length)];
var rndVertex = ((xSize + 2) + (Random.Range(0, xSize - rndSize - 1) * (xSize + 1))) +
Random.Range(0, xSize - rndSize - 1);
plateauVertexArray = new int[rndSize * rndSize];
for (int k = 0, i = 0; k < rndSize; k++)
{
for (int l = 0; l < rndSize; l++, i++)
{
plateauVertexArray[i] = (rndVertex + ((xSize + 1) * k)) + l;
if (!plateauDictionary.ContainsKey(plateauVertexArray[i]))
plateauDictionary.Add(plateauVertexArray[i], rndHeight);
You could compare the remaining worldVectors.y value with your selected selectedWorldVector.y. I have no idea about linq but i think its a good option if you want to avoid if else spaghetti code.
Aside from that you could also instantiate an empty object at each vertex world position, add an empty box collider to each, and then use a Raycast.SphereCastAll() from your selectedWorldVector position. Set your radius in SphereCast to see each close vertex and then filter out ones that are too low/high.
Related
I have procedurally generated Islands with lakes, its basically a 3D mesh that has points above the water line and points below it, any vertex/point below the water level is water, everything above it is solid ground.
From any point on the mesh I want to know the closest distance to this water.
What I ended up doing was creating an Array of Vector2s, the array contains all the points on the mesh that are below the water level.
Next I wish to cycle through these elements and compare them all to find the closest one to my selected point. I am using Vector2.Distance for this because I only want the distance in the XZ components and not going up/down (Y Component).
The problem is that for most points I select this works absolutely fine, giving correct results, but sometimes it doesn't take the closest water point but instead one that is further away, even though this closer water point is confirmed to be in the array of water points that are being compared to find the closest one.
here is my code:
chunk.Vertices = new Vertice[totalVertices];
for (int i = 0, z = 0; z <= chunkSizeZ; z++)
{
for (int x = 0; x <= chunkSizeX; x++, i++)
{
Vertice vert = new Vertice();
vert.index = i;
vert.position = new Vector3(chunkStartPosition.x + x,
chunkStartPosition.y,
chunkStartPosition.z + z);
vert.centerPosition = new Vector3(vert.position.x + 0.5f,
vert.position.y,
vert.position.z + 0.5f);
vert.centerPos2 = new Vector2(vert.position.x + 0.5f,
vert.position.z + 0.5f);
chunk.Vertices[i] = vert;
}
}
Here we get all the water positions:
for (int i = 0; i < totalVertices; i++)
{
if (chunk.Vertices[i].position.y > heightCorrection + tileColliderMinimumY)
{
worldVectorsClean.Add(chunk.Vertices[i].position);
worldIndexClean.Add(chunk.Vertices[i].index);
}
else
{
worldVectorsWater.Add(chunk.Vertices[i].centerPos2);
}
}
Every single tile then calls this function on the generator itself, but only AFTER the whole map and all water points are added. Because the generator keeps track of ALL waterpoints across all chunks otherwise each chunk will only compare its own waterpoints which doesn't work because water from another chunk can be closer but wont be compared to if we don't do it this way;
public float CalculateDistanceToWater(Vector2 pos)
{
var distance = 9001f;
foreach (Vector2 waterVector in worldVectorsWater)
{
var thisDistance = Vector2.Distance(pos, waterVector);
if (thisDistance < distance)
distance = thisDistance;
}
return distance;
}
Finally when we call it from
IEnumerator FindWater()
{
yield return new WaitForSeconds(Random.Range(0.8f, 2.55f));
var pos = new Vector2(transform.position.x, transform.position.z);
distanceToWater = ChunkGenerator.instance.CalculateDistanceToWater(pos);
}
Looking forward to some help on this.
I have a list of vertices, of N size, and a weight gradient(which can be any length) defined as:
float[] weight_distribution = { 0f, 1f, 0f };
which says that the first and last vertices will have less weight and the middle vertices will have full. Like a black and white gradient with keys defined like the array.
This is based on the Y-axis for a plane of many segments that is to be weighted for procedural rigging based on the gradient.
The list is sorted based on the vertices' Y values, so that the lowest vertices are found at the start of the list and highest last.
I don't know how to calculate the weight for a given vertex with this kind of gradient. Any pointers would be really helpful.
I tried a few different things to find values regarding the current vertex, but I don't know how to extract the weight from the gradient for this position.
This is probably just garbage, but I'll put it here anyway in case it can help.
// find unique Ys
List<float> ys = new List<float>();
for (int i = 0; i < list.Count; i++) {
if (!ys.Contains(list[i].y)) { ys.Add(list[i].y); }
}
float min = ys[0];
float max = ys[ys.Count - 1];
int levels = (ys.Count - 1);
float levelStep = (gradient.Length * 1f / levels * 1f);
float currentY = ys[0];
// set weights here
for (int i = 0; i < list.Count; i++)
{
// find current pos/value based on gradient somehow?
if(list[i].y > currentY ) { currentY = list[i].y; yindex++; }
float pos = ((yindex * levelStep) / levels * 1f);
float lerped = Mathf.Lerp(list[i].y, max, pos);
// ... calculate weight
}
I generate a grid of cubes by 10x10. My cells are different, I choose them out of an array.
What I need is a small spacing between each cell.
This is my code so far
private void Start()
{
for (int x = 0; x < data.MapSize.x; x++)
{
for (int y = 0; y < data.MapSize.y; y++)
{
Instantiate(data.Cells[Random.Range(0, data.Cells.Length)]), new Vector3(x, 0, z), Quaternion.identity); // Create a specific cell on position (x,y)
}
}
}
So what I've tried out:
new Vector3(x + 1, 0, z + 1)
new Vector3(x * 0.1f, 0, z * 0.1f)
but obviously it won't change anything.
So I tried out this:
Before the start method I created a variable
int counter = 0;
and within the loops
counter += 0.1f;
new Vector3(x + counter, 0, z + counter)
but then there appears no grid, I get an parallelogram.
First define your spacing length, so you can easily change it later.
float spacing = 0.1f;
Then use this to get the desired result.
Instantiate(data.Cells[Random.Range(0, data.Cells.Length)]), new Vector3(x + (x*spacing), 0, y + (y*spacing)), Quaternion.identity); // Create a specific cell on position (x,y)
Notice I changed z to y.
The answer to this depends on whether or not you want your grid to remain exactly 10 units across, in world space.
If the size of the grid doesn't matter, you can just add the padding to your x and y increments (and use a floating point instead of integer). You will also have to increase data.MapSize to take into account the padding:
for (float x = 0; x < data.MapSize.x; x += 1.1f)
{
for (float y = 0; y < data.MapSize.y; y += 1.1f)
{
Alternatively, you might want to consider scaling down the objects that you're storing in data.Cells in order to create the space without affecting the grid size. You could either scale down the prefab, or do it as they're being instantiated:
GameObject newCell = (GameObject)Instantiate(data.Cells[Random.Range(0, data.Cells.Length)]), new Vector3(x, 0, y), Quaternion.identity); // Create a specific cell on position (x,y)
newCell.transform.localScale = new Vector3(0.9, 0.9, 0.9);
Edit: Lestat's solution is a cleaner version of the first option, so if the final size of the grid doesn't matter then I would go with that instead of modifying the x and y variables.
I've created a 3 dimensional grid. I have two separate objects filling the spaces of this grid. I want to have one of the objects in one row but on randomly selected columns.
Has anyone done this before or can anyone point me in the right direction?
I'm using Unity and C#. Thank you.
Vector3 towerSize = new Vector3(3, 3, 3);
//create grid tower
for (int x = 0; x < towerSize.x; x++)
{
for (int z = 0; z < towerSize.z; z++)
{
for (int y = 0; y < towerSize.y; y++)
{
//spawn tiles and space them
GameObject obj = (GameObject)Instantiate(tiles);
obj.transform.position = new Vector3(x * 1.2f, y * 1.2f, z * 1.2f);
//add them all to a List
allTiles.Add(obj);
obj.name = "tile " + allTiles.Count;
}
}
}
There is the code for the grid. I tried to have two objects in a singular List move to those tiles but the random column objects get in the same columns when I do that with this code:
for (int i = 0; i < allCubes.Count; i++)
{
allCubes[i].transform.position = Vector3.MoveTowards(
allCubes[i].transform.position,
allTiles[i].transform.position, 10 * Time.deltaTime);
}
Then thought put the two types of cubes in separate Lists themselves. Which ended up being even more messy. haha Does posting that code help?
I know this is an extremely old question of mine. It was a project that got canceled. I randomly came across it and out of curiosity I decided to try to complete this particular issue I was having such trouble doing back then. And I did.
public Vector3 towerSize = new Vector3(3, 3, 3);
public GameObject tiles;
public GameObject randomTile;
//public variables for debugging purposes.
//no real need to be seen in inspector in final. cleaner too if they're hidden
public int randomSelectedTile;
public List<GameObject> allTiles;
void Start()
{
//create grid tower
for (int x = 0; x < towerSize.x; x++)
{
for (int z = 0; z < towerSize.z; z++)
{
for (int y = 0; y < towerSize.y; y++)
{
//spawn cubes and space them
GameObject obj = (GameObject)Instantiate(tiles);
obj.transform.position = new Vector3(x * 1.2f, y * 1.2f, z * 1.2f);
//add them all to a List
allTiles.Add(obj);
obj.name = "tile " + allTiles.Count;
}
}
}
//select a random cube in the list
randomSelectedTile = Random.Range(0, allTiles.Count);
//get the cube object to delete
GameObject deleteObj = allTiles.ElementAt(randomSelectedTile);
//spawn the random cube at the position of the cube we will delete
GameObject rndObj = (GameObject)Instantiate(randomTile);
rndObj.transform.position = deleteObj.transform.position;
//remove the element at that location
allTiles.RemoveAt(randomSelectedTile);
//insert the random cube at that element's location
allTiles.Insert(randomSelectedTile, rndObj);
//destroy the unwanted cube
Destroy(deleteObj);
}
It's nice to see how you've improved over time. Just in case anyone else would benefit from the solution. Again, I apologize for bringing it back up.
I'd like to ask whether there is code out there or if you can give me some help in writing some (C#, but I guess the maths is the same everywhere).
I'd like to specify a center point from which an equilateral triangle mesh is created and get the vertex points of these triangles. The center point should not be a face center, but a vertex itself.
A further input would be the size of the triangles (i.e side length) and a radius to which triangle vertices are generated.
The reason behind it is that I want to create a mesh which is centered nicely on the screen/window center with as little code as possible. I just find mesh generation code, but not a "radial outward propagation" example.
In the end, I'd like to have the subsequently farther away vertices being displaced in a logarithmic fashion, but I guess that's just an easy addition once the mesh code is there.
Can anybody help me with that? Thanks!
You need to specify two things, a radius and the direction that the first triangle points.
The radius will be the distance from the initial point to the vertices of the first triangle. All triangles will have the same radius.
The direction is some specification in radians. I will assume that 0 means pointing to the right (PI would be point to the left).
Finding the vertices of the first triangle can be done like this (pseudo-code, not language specific):
float theta = 0; // The direction, 0 means pointing to the right
float thetaInc = TWO_PI/3; // 3 because you want a triangle
for (int i = 0; i < 3; i++) {
vertX[i] = initialPointX+cos(theta)*radius;
vertY[i] = initialPointY+sin(theta)*radius;
theta += thetaInc;
}
There are many ways to find the center points of the neighboring triangles. One way would be to use the same code but initialize theta = TWO_PI/6, replace radius with foo (see math below), assign new center points of neighboring triangles in the for loop, and then use the same code with an appropriately rotated direction (theta += PI) to find the vertices of those triangles.
Distance from one triangle center to another only knowing radius:
hypotenuse = sqrt(sq(radius)+sq(radius));
halfHypotenuse = hypotenuse/2.0;
Pythagorean theorem to find distance from center of triangle to center of an edge: foo = sqrt(sq(radius)-sq(halfHypotenuse));
Final distance = foo*2.0;
Code to find the center points of the neighboring triangles:
float[] nx = new float[3];
float[] ny = new float[3];
float theta = TWO_PI/6;
float hyp = sqrt(sq(radius)+sq(radius));
float halfHyp = hyp/2.0;
float foo = sqrt((sq(radius)-sq(halfHyp)))*2.0;
for (int i = 0; i < 3; i++) {
nx[i] = initialPointX+cos(theta)*foo;
ny[i] = initialPointY+sin(theta)*foo;
theta += thetaInc;
}
Thank you very much for your answer. I will play around with your code - the propagation part will come handy for sure.
In the meantime I have played around with hexagons instead of triangles and this codes works fairly alright for the same purpose.:
//populate array from the centre hex, going outwards the desired number of hex rings
for (int i = 0; i < numberOfHexagonRings; i++)
{
for (double j = -i; j <= i; j++)
for (double k = -i; k <= i; k++)
for (double l = -i; l <= i; l++)
if ((Math.Abs(j) + Math.Abs(k) + Math.Abs(l) == i * 2) && (j + k + l == 0))
{
positionX = (int)(screenCenterX + ((double)sideLength * (l / 2 + j)));
positionY = (int)(screenCenterY + (3/2 * ((double)sideLength / Math.Sqrt(3)) * l));