So a while back I began learning C#, and still I struggle with some basic things. At any rate, my latest endeavor is a small UFO game where you abduct cows. I am making progress on that, and got side tracked into a few things.
The original problem was detecting GameObjects within a cone, then calculating their magnitude from the center to select the closest cow inside the cone for abduction. I found that it would be easy if you were on a flat plane, but some weird things happen on realistic terrain.
So during that planning stage, for better or worse, I decided I’d need to map the local Vertices and match the Y value of my base circle to the closest pair of vertices who’s angle is less than a limit on the Y.
I started with making a grid…
public class GridCreator : MonoBehaviour
{
public Vector2 worldGridSize;
public float nodeRadius;
float nodeDiameter;
int gridSizeX, gridSizeY;
public Node[,] CreateGrid(Vector3 raycastPoint)
{
nodeDiameter = nodeRadius * 2;
gridSizeX = Mathf.RoundToInt(worldGridSize.x / nodeDiameter);
gridSizeY = Mathf.RoundToInt(worldGridSize.y / nodeDiameter);
Node[,] grid = new Node[gridSizeX, gridSizeY];
Vector3 worldBottomLeft = raycastPoint - (Vector3.right * (worldGridSize.x / 2)) - (Vector3.forward * (worldGridSize.y / 2));
for (int x = 0; x < gridSizeX; x++)
{
for (int y = 0; y < gridSizeY; y++)
{
Vector3 nodeLocation = worldBottomLeft + Vector3.right * (x * nodeDiameter + nodeRadius) + Vector3.forward * (y * nodeDiameter + nodeRadius);
grid[x, y] = new Node(nodeLocation);
}
}
return grid;
}
Which is a 2D array of Nodes –
public class Node
{
public Vector3 worldPosition;
public Node(Vector3 _worldPosition)
{
worldPosition = _worldPosition;
}
}
And that led me to some cool results when I used the gizmos.
private void OnDrawGizmos()
{
if (drawGridGizmos)
{
if (drawSquareGrid)
{
if (Physics.Raycast(new Ray(raycastOrigin.position, Vector3.down), out RaycastHit _hit, float.MaxValue, layerMask))
{
Gizmos.color = Color.white;
Gizmos.DrawWireCube(_hit.point, new Vector3(worldGridSize.x, 1, worldGridSize.y));
Node[,] squareGrid = CreateSquareGrid(_hit.point);
float squareNodeDiameter = nodeRadius * 2;
int squareGridSizeX = Mathf.RoundToInt(worldGridSize.x / squareNodeDiameter);
int squareGridSizeY = Mathf.RoundToInt(worldGridSize.y / squareNodeDiameter);
Vector3 previousNodePosition = Vector3.positiveInfinity;
for (int x = 0; x < squareGrid.GetLength(0); x++)
{
for (int y = 0; y < squareGrid.GetLength(1); y++)
{
Vector3 currentNodePosition = squareGrid[x, y].worldPosition;
// Draws a black cube at each node location except the first, middle, and last
Gizmos.color = Color.black;
Gizmos.DrawCube(currentNodePosition, Vector3.one * squareNodeDiameter / 4);
if (x == 0 && y == 0)
{
// First Node -- Draws a green cube
Gizmos.color = Color.green;
Gizmos.DrawCube(currentNodePosition, Vector3.one * squareNodeDiameter / 4);
}
// Checks if a center node is in the grid
//looks for that center node
if (currentNodePosition == _hit.point)
{
// Center Node -- Draws a blue cube
Gizmos.color = Color.blue;
Gizmos.DrawCube(currentNodePosition, Vector3.one * squareNodeDiameter / 4);
}
if (x == squareGridSizeX - 1 && y == squareGridSizeY - 1)
{
// Last Node -- Draws a red cube
Gizmos.color = Color.red;
Gizmos.DrawCube(currentNodePosition, Vector3.one * squareNodeDiameter / 4);
}
// Checks if this is the first node
if (previousNodePosition != Vector3.positiveInfinity)
{
// Draws a line from each node to the next without drawing lines from the last node in a column to the first node of the next column
if (y != 0)
{
if (currentNodePosition != previousNodePosition)
{
Gizmos.color = Color.white;
Gizmos.DrawLine(previousNodePosition, currentNodePosition);
previousNodePosition = currentNodePosition;
}
}
// Sets the previous node to the current node in the event we begin a new column
else
{
previousNodePosition = currentNodePosition;
}
// Draws a perpendicular line from the last nodes to the first ones
if (x == squareGridSizeX - 1)
{
Gizmos.color = Color.white;
Gizmos.DrawLine(currentNodePosition, squareGrid[0, y].worldPosition);
}
}
// Sets the first node as the previous node on first iteration
else
{
previousNodePosition = currentNodePosition;
}
}
}
}
}
So then I tried turning this concept into a circle –
public Node[,] CircleGrid(int steps, float radius, Vector3 centerPoint, int ringCount)
{
float concentricRadius;
int concentricSteps;
Node[,] nodes = new Node[ringCount, steps + 1];
for (int ring = 0; ring < ringCount; ring++)
{
concentricRadius = radius - (radius * ((float)ring / (float)ringCount));
concentricSteps = steps - (steps * (Mathf.RoundToInt((float)ring)) / Mathf.RoundToInt((float)ringCount));
for (int currentStep = 0; currentStep < concentricSteps; currentStep++)
{
float circumferenceProgress = (float)currentStep / concentricSteps;
float currentRadian = circumferenceProgress * 2 * Mathf.PI;
float xScaled = Mathf.Cos(currentRadian);
float yScaled = Mathf.Sin(currentRadian);
float x = xScaled * concentricRadius;
float y = yScaled * concentricRadius;
Vector3 currentPosition = centerPoint + new Vector3(x, 0, y);
nodes[ring, currentStep] = new Node(currentPosition);
}
}
if(nodes[nodes.GetLength(0) - 1, nodes.GetLength(1) - 1] == null)
{
nodes[nodes.GetLength(0) - 1, nodes.GetLength(1) - 1] = new Node(centerPoint);
}
return nodes;
}
And Likewise drew it with Gizmos –
if (drawConcentricCircles)
{
if (Physics.Raycast(new Ray(raycastOrigin.position, Vector3.down), out RaycastHit hit, float.MaxValue, layerMask))
{
Node[,] circleNodes = CircleGrid(circleGridNodeCount, majorCircleRadius, hit.point, circleGridRingCount);
if (circleNodes != null)
{
for (int x = 0; x < circleNodes.GetLength(0); x++)
{
int segments = circleGridNodeCount - (circleGridNodeCount * (Mathf.RoundToInt((float)x)) / Mathf.RoundToInt((float)circleGridRingCount));
for (int y = 0; y < segments; y++)
{
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(circleNodes[x, y].worldPosition, .25f);
}
}
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(circleNodes[circleNodes.GetLength(0) - 1, circleNodes.GetLength(1) - 1].worldPosition, .30f);
}
}
}
And now I am at the point where I am trying to find the vertices of the mesh the raycast hits so I can plot my circle and this is what I’ve got –
if (drawVerts)
{
if (Physics.Raycast(new Ray(raycastOrigin.position, Vector3.down), out RaycastHit hit, float.MaxValue, layerMask))
{
Mesh mesh = hit.collider.gameObject.GetComponent<MeshFilter>().sharedMesh;
Matrix4x4 localToWorld = hit.transform.localToWorldMatrix;
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
float closestVerticeDistance = float.MaxValue;
int closestVert = int.MinValue;
for (var i = 0; i < vertices.Length; i++)
{
Vector3 world_v = localToWorld.MultiplyPoint3x4(mesh.vertices[i]);
vertices[i].Set(world_v.x, world_v.y, world_v.z);
Gizmos.color = Color.black;
Gizmos.DrawCube(world_v, Vector3.one * .25f);
if ((vertices[i] - hit.point).magnitude < closestVerticeDistance)
{
closestVerticeDistance = (vertices[i] - hit.point).magnitude;
closestVert = i;
}
}
for (int i = 0; i < vertices.Length; i++)
{
if (i == closestVert)
{
Vector3 world_v = localToWorld.MultiplyPoint3x4(mesh.vertices[i]);
Gizmos.color = Color.red;
Gizmos.DrawCube(world_v, Vector3.one * .50f);
}
}
}
}
I am just looking for some more experienced programmers to criticize this work, offer insight to the problem, and feedback as to how I can optimize my workflow or utilize other techniques to make cleaner more efficient code.
Thanks for reading, and thanks in advance for any advice!