How can i find the middle of a Hexgrid? - c#

So ive created a code which will use my Standard hexagon Node from Godot and istance it for a given size into a hexgrid, now i wanted to find the exact middle of this Hexgrid to position my player there eg. move the Nodes along the X and Z axis to the standard position of the map 0,0,0.
I thought the exact middle of the Hexgrid is Axis X - Size of the X Axis / 2, Axis Z - Size of the Y Axis / 2.
But this just doesnt seem to work. Any suggestions on how to finde the middle of the Hexgrid and how to move it along the axis?
The Code for the middle looks like this:
public void GenerateGrid()
{
for (int y = 0; y < gridSize.Y; y++)
{
for (int x = 0; x < gridSize.X; x++)
{
Vector3 newPos = GetPositionFromCurrentHex(new Vector2(x, y));
Node3D generator_scene = (Node3D) ResourceLoader.Load<PackedScene>(tilePath).Instantiate();
generator_scene.Position = new Vector3(newPos.X - (gridSize.X / 2.0f), newPos.Y, newPos.Z - (gridSize.Y/ 2.0f));
if (isFlatTopped)
{
generator_scene.RotateY(Mathf.DegToRad(90));
}
AddChild(generator_scene);
}
}
}
And for the Calculation of the Hexgrid:
public Vector3 GetPositionFromCurrentHex(Vector2 coordinate)
{
float col = coordinate.X;
float row = coordinate.Y;
float width;
float height;
float xPos;
float yPos;
bool shouldOffset;
float horizontalDistance;
float verticalDistance;
float offset;
float size = outerSize;
if (!isFlatTopped)
{
shouldOffset = (row % 2) == 0;
width = Mathf.Sqrt(3) * size;
height = 2f * size;
horizontalDistance = width;
verticalDistance = height * (3f / 4f);
offset = (shouldOffset) ? width / 2 : 0;
xPos = (col * (horizontalDistance)) + offset;
yPos = (row * verticalDistance);
}
else
{
shouldOffset = (col % 2) == 0;
width = 2f * size;
height = Mathf.Sqrt(3f) * size;
horizontalDistance = width * (3f / 4f);
verticalDistance = height;
offset = (shouldOffset) ? height / 2 : 0;
xPos = (col * (horizontalDistance));
yPos = (row * verticalDistance) - offset;
}
return new Vector3(xPos, 0, yPos);
}

Answer depends on how you define center in different cases, for example
Is your hexgrid flat top or pointy top?
Is the topmost row of your hexgrid also leftmost or not (offset of rows)
Do you expect your "center" of the hexgrid to be also center of an hexagon, or it can be a point along edge of some hexagons.
If the number of rows and columns are odd, you can get away with calculating the coordinates of 4 corner points (topleft, topright, botleft, botright) and average them. If your grid is pointy-top this is also center of the middle hexagon, if your grid is flat-top it is on the edge between two hexagons.

Related

Raycast not capturing all Vector coordinates

I have a gameobject that occupies the whole screen just for testing purposes. I'm drawing a line btw. What I'm trying to achieve is if the mouse position hits a gameobject it will store the vector2 coordinates in a list. But raycast is not storing all the coordinates. Below is my code
private void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = Input.mousePosition;
Vector2 Pos = _camera.ScreenToWorldPoint(mousePos);
if(!mousePositions.Contains(Pos))
mousePositions.Add(Pos);
if (Physics.Raycast(Camera.main.ScreenPointToRay(mousePos), out RaycastHit hit))
{
Vector2 textureCoord = hit.textureCoord;
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
Vector2Int paintPixelPosition = new Vector2Int(pixelX, pixelY);
if (!linePositions.Contains(paintPixelPosition))
linePositions.Add(paintPixelPosition);
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
_templateDirtMask.SetPixel(
pixelXOffset + x,
pixelYOffset + y,
Color.black
);
}
}
}
_templateDirtMask.Apply();
}
}
}
Everytime I checked the element count mousePositions are always greater than linePositions. I don't know what's causing this
the element count mousePositions are always greater than linePosition
well it is quite simple: In
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
you are casting to int and cut off any decimals after the comma (basically like doing Mathf.FloorToInt).
So you can totally have multiple mouse positions which result in float pixel positions like e.g.
1.2, 1.2
1.4, 1.7
1.02, 1.93
...
all these will map to
Vector2Int paintPixelPosition = new Vector2Int(1, 1);
Besides, you might want to look at some better line drawing algorithms like e.g. this simple one
And then note that calling SetPixel repeatedly is quite expensive. You want to do a single SetPixels call like e.g.
var pixels = _templateDirtMask.GetPixels();
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
pixels[(pixelXOffset + x) + (pixelYOffset + y) * _templateDirtMask.width] = Color.black;
}
}
}
_templateDirtMask.SetPixels(pixels);
_templateDirtMask.Apply();
It happens because there is really could be a case, when several elements from mousePositions are associated with one elment from linePositions.
Rough example: your texture resolution is only 1x1px. In this case you linePositons will contain only one element. And this element will be associated with all elements from mosePositions.
So, relation of the number of elements in these lists depends on relation of your texture and screen resolutions.

Unity - How to create circular gradient?

I need a small help, I am trying to create an Island generator using Unity.
However I do not know how to make circular fall Off map.But instead I manage to create an island that is more of a box shaped island.
This is what I am doing:
I am generating Height map using Perlin noise
I will generate FallOff map and substract it from Perlin noise
The Island getsgenerated, but instead of having circular shape the
island hasbox-like shape
I want to create this
This is my Perlin noise function
public float[,] GenerateNoise(int mapSize,int octaves, string seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
{
if (noiseScale <= 0)
{
noiseScale = 0.0001f;
}
float halfWidth = mapSize / 2f;
float halfHeight = mapSize / 2f;
float[,] noiseMap = new float[mapSize + 1, mapSize + 1];
System.Random rand = new System.Random(seed.GetHashCode());
//Octaves offset
Vector2[] octavesOffset = new Vector2[octaves];
for (int i = 0; i < octaves; i++)
{
float offset_X = rand.Next(-100000, 100000) + offset.x;
float offset_Y = rand.Next(-100000, 100000) + offset.y;
octavesOffset[i] = new Vector2(offset_X / mapSize, offset_Y / mapSize);
}
for (int x = 0; x < mapSize; x++)
{
for (int y = 0; y < mapSize; y++)
{
float amplitude = 1;
float frequency = 1;
float noiseHeight = 0;
float superpositionCompensation = 0;
for (int i = 0; i < octaves; i++)
{
float sampleX = (x - halfWidth) / noiseScale * frequency + octavesOffset[i].x;
float sampleY = (y - halfHeight) / noiseScale * frequency + octavesOffset[i].y;
float perlinValue = Mathf.PerlinNoise(sampleX, sampleY);
noiseHeight += perlinValue * amplitude;
noiseHeight -= superpositionCompensation;
amplitude *= persistence;
frequency *= lacunarity;
superpositionCompensation = amplitude / 2;
}
noiseMap[x, y] = Mathf.Clamp01(noiseHeight);
}
}
return noiseMap;
}
And this is my FallOff map function
public float[,] GenerateFallOffMap(int mapSize)
{
float[,] fallOffMap = new float[mapSize , mapSize];
for (int x = 0; x < mapSize; x++)
{
for (int y = 0; y < mapSize; y++)
{
int index = x * mapSize + y;
float fallOff_A = x / (float)mapSize * 2 - 1;
float fallOff_B = y / (float)mapSize * 2 - 1;
float value = Mathf.Max(Mathf.Abs(fallOff_A), Mathf.Abs(fallOff_B));
fallOffMap[x,y] = Evaluate(value);
}
}
return fallOffMap;
}
static float Evaluate(float value)
{
float a = 3;
float b = 2.2f;
return Mathf.Pow(value, a) / (Mathf.Pow(value, a) + Mathf.Pow(b - b * value, a));
}
Use the distance from the center of the grid as the parameter to calculate the falloff value.
/// value - The calculated value to process
/// radius - The distance from center to calculate falloff distance
/// x - The x-coordinate of the value position
/// y - The y-coordinate of the value position
/// cx - The x-coordinate of the center position
/// cy - The y-coordinate of the center position
public float RadialFallOff(float value, float radius, int x, int y, float cx, float cy)
{
float dx = cx - x;
float dy = cy - y;
float distSqr = dx * dx + dy * dy;
float radSqr = radius * radius;
if (distSqr > radSqr) return 0f;
return value;
}
This will result in a hard cutoff at radius. If you want a softer transition along the edges, you can use an innerRadius and an outerRadius to produce a feathered effect:
/// value - The calculated value to process
/// innerRadius - The distance from center to start feathering
/// outerRadius - The distance from center to fully fall off
/// x - The x-coordinate of the value position
/// y - The y-coordinate of the value position
/// cx - The x-coordinate of the center position
/// cy - The y-coordinate of the center position
public float FeatheredRadialFallOff(float value, float innerRadius, float outerRadius, int x, int y, float cx, float cy)
{
float dx = cx - x;
float dy = cy - y;
float distSqr = dx * dx + dy * dy;
float iRadSqr = innerRadius * innerRadius;
float oRadSqr = outerRadius * outerRadius;
if (distSqr >= oRadSqr) return 0f;
if (distSqr <= iRadSqr) return value;
float dist = Mathf.Sqr(distSqr);
float t = Mathf.InverseLerp(innerRadius, outerRadius, dist);
// Use t with whatever easing you want here, or leave it as is for linear easing
return value * t;
}
You can use it like such:
float value = Mathf.Max(Mathf.Abs(fallOff_A), Mathf.Abs(fallOff_B));
value = Evaluate(value)
fallOffMap[x,y] = RadialFalloff(value, someRadius, x, y, mapSize / 2f, mapSize / 2f);
// or
fallOffMap[x,y] = FeatheredRadialFalloff(value, someInnerRadius, someOuterRadius, x, y, mapSize / 2f, mapSize / 2f);

How to create a 3D mesh from a heightmap represented as a float array

I am attempting to understand how a 3D mesh has been constructed from a heightmap stored as a one dimensional float array. The only examples I have seen previously have made use of a 2D float array, and I am struggling to wrap my head around the math involved here. Any insight into it would be appreciated. I have commented the code which I do not quite understand yet for your convenience.
Source of code: https://github.com/SebLague/Hydraulic-Erosion
public void ContructMesh () {
Vector3[] verts = new Vector3[mapSize * mapSize];
int[] triangles = new int[(mapSize - 1) * (mapSize - 1) * 6];
int t = 0;
//Note that default mapSize is 255
for (int i = 0; i < mapSize * mapSize; i++) {
//Following code is not properly understood
int x = i % mapSize;
int y = i / mapSize;
int meshMapIndex = y * mapSize + x;
Vector2 percent = new Vector2 (x / (mapSize - 1f), y / (mapSize - 1f));
Vector3 pos = new Vector3 (percent.x * 2 - 1, 0, percent.y * 2 - 1) * scale;
pos += Vector3.up * map[meshMapIndex] * elevationScale; //Elevation scale is 20 by default
verts[meshMapIndex] = pos;
//End of misunderstood code
if (x != mapSize - 1 && y != mapSize - 1) {
t = (y * (mapSize - 1) + x) * 3 * 2;
triangles[t + 0] = meshMapIndex + mapSize;
triangles[t + 1] = meshMapIndex + mapSize + 1;
triangles[t + 2] = meshMapIndex;
triangles[t + 3] = meshMapIndex + mapSize + 1;
triangles[t + 4] = meshMapIndex + 1;
triangles[t + 5] = meshMapIndex;
t += 6;
}
}
Mesh mesh = new Mesh();
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.vertices = verts;
mesh.triangles = triangles;
mesh.RecalculateNormals ();
What specifically do you not understand?
int x = i % mapSize; // Get the x location of the current point
int y = i / mapSize; // Get the y location of the current point
// This should be equal to i, IDK why this is even calculated
int meshMapIndex = y * mapSize + x;
// How far along either direction are we?
Vector2 percent = new Vector2 (x / (mapSize - 1f), y / (mapSize - 1f));
// Make a new vector that scales the X and Y coordinates up.
// The Y coordinate is set to the Z element in this vector
// Presumably because whatever you use to render uses the Y element as "up"
// And the X-Z plane is the horizontal plane
// Also normalize X and Z to lie between -1*scale and 1*scale
Vector3 pos = new Vector3 (percent.x * 2 - 1, 0, percent.y * 2 - 1) * scale;
// Add the value at the current index, times the scale, as the Y element of pos
pos += Vector3.up * map[meshMapIndex] * elevationScale; //Elevation scale is 20 by default
// The X-Z values of pos give you the location of the vertex in the horizontal plane
// The Y value of pos gives you the height
// save the newly calculated pos in verts
verts[meshMapIndex] = pos;

How can I calculate and create positions of triangle shape?

private void FormationTriangle()
{
newpositions = new List<Vector3>();
for (int x = 0; x < squadMembers.Count; x++)
{
for (int y = x; y < 2 * (squadMembers.Count - x) - 1; y++)
{
Vector3 position = new Vector3(x, y);
newpositions.Add(position);
}
}
move = true;
formation = Formation.Square;
}
The loops are wrong. It put the squadMembers in one line one above the other.
Not even close to a triangle shape.
I want the squadMembers to stand in a triangle shape.
This is the moving part: But the problem is with the loops calculating the triangle shape positions. Other formations I did are working fine.
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers[i].transform.LookAt(newpositions[i]);
if (randomSpeed == true)
{
squadMembers[i].transform.position = Vector3.MoveTowards(
squadMembers[i].transform.position, newpositions[i], step[i]);
}
else
{
squadMembers[i].transform.position = Vector3.MoveTowards(
squadMembers[i].transform.position, newpositions[i], step[0]);
}
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) <
threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers[i].transform.rotation = Quaternion.Slerp(
squadMembers[i].transform.rotation, quaternion,
rotateSpeed * Time.deltaTime);
}
else
{
squadMembers[i].transform.rotation = Quaternion.Slerp(
squadMembers[i].transform.rotation, quaternions[i],
rotateSpeed * Time.deltaTime);
}
}
}
}
This answer will produce a triangle arranged like this:
x
x x
x x x
x x x x
x x x x x
Or, if there aren't enough to fill a full triangle:
x
x x
x x x
x x x x
x x x
Since you aren't guaranteed a perfectly triangular number of units, you should overestimate how big your triangle is, keep count of how many units you have placed, and then quit placing them when you reach your limit.
First, find the height of the smallest triangular number greater than your number of units, and that triangular number itself:
int height = Mathf.CeilToInt( (Mathf.Sqrt(8*squadMembers.Count+1f)-1f)/2 )
int slots = (int)(height * (height+1f)/2f)
Then, find the position of the first unit. We need to know how many rows of slots we have and how wide the bottom row of slots is:
float verticalModifier = 0.8f; // 0.8f to decrease vertical space
float horizontalModifier = 1.25f; // 1.25f to increase horizontal space
float width = 0.5f * (height-1f);
Vector3 startPos = new Vector3(width* horizontalModifier, 0f, (float)(height-1f) * verticalModifier);
Then, add until you've added enough
int finalRowCount = height - slots + squadMembers.Count;
for (int rowNum = 0 ; rowNum < height && newpositions.Count < squadMembers.Count; rowNum++) {
for (int i = 0 ; i < rowNum+1 && newpositions.Count < squadMembers.Count ; i++ ) {
float xOffset = 0f;
if (rowNum+1 == height) {
// If we're in the last row, stretch it ...
if (finalRowCount !=1) {
// Unless there's only one item in the last row.
// If that's the case, leave it centered.
xOffset = Mathf.Lerp(
rowNum/2f,
-rowNum/2f,
i/(finalRowCount-1f)
) * horizontalModifier;
}
}
else {
xOffset = (i-rowNum /2f) * horizontalModifier;
}
float yOffset = (float)rowNum * verticalModifier;
Vector3 position = new Vector3(
startPos.x + xOffset, 0f, startPos.y - yOffset);
newpositions.Add(position);
}
}
Let's see what the list of positions contains for a simple value, n = 3
First, loop x from 0 to 2 (3 - 1)
Then for each x, loop from x to 4-x (3*2 - x - 1 - 1)
Remembering that a<b is the same as a<=b-1
That gives us...
0,0
0,1
0,2
0,3
0,4
1,1
1,2
1,3
2,2
Which is a lot of positions. Certainly more than 3 units can occupy! At least it is a triangle:
X\Y 0 1 2 3 4
0 # # # # #
1 # # #
2 #
The main problem is that you're generating way more positions than needed and expecting to fill it somehow.
You need to calculate your width and height based on the area formula for a triangle: A = (b*h)/2 and you may even want b=h, where A = number of units.
So, something like this:
int b = Mathf.CeilToInt(Mathf.Sqrt(squadMembers.Count));
for (int x = 0; x < b; x++)
{
//the divide by 2 is accounted for with this 2*
for (int y = x; y < 2 * (b - x) - 1; y++)
{
Vector3 position = new Vector3(x, y);
newpositions.Add(position);
}
}

Why is my terrain not rendering properly?

Currently, I have a random terrain generator, which I am sure works properly, however, when I attempt to build a set of VertexPositionColor, it does not render properly. This is what I currently have (overhead view):
My code:
List<VertexPositionColor> w = new List<VertexPositionColor>();
int width = 20;
int height = 20;
float terrainScale = 2.0f;
long seed = (DateTime.Now.Millisecond + DateTime.Now.Second * DateTime.Now.Hour);
ProceduralLayeredMapGenerator plmg = new ProceduralLayeredMapGenerator(seed);
Random rand = new Random((int)seed);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Vector3 position = new Vector3();
position.X = x;//(x - width / 2) * terrainScale;
position.Z = y;//(y - height / 2) * terrainScale;
float point = plmg.getPoint(x, y);
Color computedColor = new Color(rand.Next(255), rand.Next(255), rand.Next(255));
position.Y = (point * 2);
w.Add(new VertexPositionColor(position, computedColor));
}
}
colors = w.ToArray();
And then the drawing code:
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, colors, 0, colors.Length / 3, VertexPositionColor.VertexDeclaration);
}
How can I get it to look something more like this:
If you want to draw a TriangleStrip then you must add the vertices in the order in which they will be used to draw the triangles; right now you're adding vertices top-to-bottom, left-to-right. Also, to render a height map like that you'll need to use multiple TriangleStrips.

Categories