How can I set random positions on radius? - c#

In this script I'm creating a circle with specific radius size and get the radius size :
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(LineRenderer))]
public class DrawRadiusAroundTurret : MonoBehaviour
{
[Range(0, 50)]
public int segments = 50;
[Range(0, 5)]
public float xradius = 5;
[Range(0, 5)]
public float yradius = 5;
[Range(0.1f, 5f)]
public float width = 0.1f;
LineRenderer line;
void Start()
{
line = gameObject.GetComponent<LineRenderer>();
line.enabled = true;
line.positionCount = segments + 1;
line.widthMultiplier = width;
line.useWorldSpace = false;
CreatePoints();
}
private void Update()
{
CreatePoints();
}
public Vector3[] CreatePoints()
{
line.widthMultiplier = width;
float x;
float y;
float z;
float angle = 20f;
for (int i = 0; i < (segments + 1); i++)
{
x = Mathf.Sin(Mathf.Deg2Rad * angle) * xradius;
y = Mathf.Cos(Mathf.Deg2Rad * angle) * yradius;
line.SetPosition(i, new Vector3(x, 0f, y));
angle += (380f / segments);
}
var positions = new Vector3[line.positionCount];
return positions;
}
}
And in this script I have this method and I want the last point/s to be set randomly on the radius edge positions :
private void GeneratePointsInTracks()
{
var startPoints = GameObject.FindGameObjectsWithTag("Start Point");
var curvedLines = GameObject.FindGameObjectsWithTag("Curved Line");
for (int i = 0; i < startPoints.Length; i++)
{
for (int x = 0; x < numberOfPointsInTrack; x++)
{
GameObject go = Instantiate(tracksPrefab, curvedLines[i].transform);
go.name = "Point In Track";
go.transform.position = turrent.position + new Vector3(Random.Range(-100f, 100f), Random.Range(-100f, 100f), Random.Range(-100f, 100f));
if(x == numberOfPointsInTrack - 1)
{
go.name = "Last Point In Track";
for(int y = 0; y < drawRadius.CreatePoints().Length; y++)
{
go.transform.position = new Vector3(Random.Range(0,1)[y].x,
drawRadius.CreatePoints()[y].y,
drawRadius.CreatePoints()[y].z);
}
}
}
}
}
I tried this :
go.transform.position = new Vector3(Random.Range(0,1)[y].x,
drawRadius.CreatePoints()[y].y,
drawRadius.CreatePoints()[y].z);
but the random on the x give error :
Cannot apply indexing with [] to an expression of type 'int'
The first script create a circle like this :
And this is an example I drawed in paint just to show what I mean that I said I want the endPoints in the second script to be position randomly on the circle edges :
So each "Last Point In Track" object should be position randomly on the circle edge like in the second screenshot.

If you know the center and radius it is fairly easy to get random points on the circle:
go.name = "Last Point In Track";
Vector2 p = Random.insideUnitCircle.normalized * radius;
go.transform.position = center + new Vector3(p.x, 0, p.y);
To get rid of the edge case where Random.insideUnitCircle is to small to be normalized you should use:
Vector2 RandomOnUnitCircle(){
Vector2 result = Vector2.zero;
do{
result = Random.insideUnitCircle.normalized;
}while(result == Vector2.zero);
return result;
}

I think what you want is this:
Vector3[] points = drawRadius.CreatePoints(); //get all edge points
Vector3 randomPoint = points[Random.Range(0, points.Length)]; //pick a random one
go.transform.position = randomPoint; //set go.transform.position to position of random point
Sorry if I am misunderstanding your intentions, but I hope this works for you!

Related

How to generate random objects inside drawn circle area?

The first script draw circle that i can control it's radius size and make the circle tin or wider :
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[ExecuteAlways]
[RequireComponent(typeof(UnityEngine.LineRenderer))]
public class DrawCircle : MonoBehaviour
{
[Range(1, 50)] public int segments = 50;
[Range(1, 500)] public float xRadius = 5;
[Range(1, 500)] public float yRadius = 5;
[Range(0.1f, 5)] public float width = 0.1f;
[Range(0, 100)] public float height = 0;
public bool controlBothXradiusYradius = false;
public bool draw = true;
[SerializeField] private LayerMask targetLayers;
[SerializeField] private LineRenderer line;
private void Start()
{
if (!line) line = GetComponent<LineRenderer>();
if (draw)
CreatePoints();
}
private void Update()
{
if (Physics.CheckSphere(transform.position, xRadius, targetLayers))
{
Debug.Log("player detected");
}
else
{
Debug.Log("player NOT detected");
}
}
public void CreatePoints()
{
line.enabled = true;
line.widthMultiplier = width;
line.useWorldSpace = false;
line.widthMultiplier = width;
line.positionCount = segments + 1;
float x;
float y;
var angle = 20f;
var points = new Vector3[segments + 1];
for (int i = 0; i < segments + 1; i++)
{
x = Mathf.Sin(Mathf.Deg2Rad * angle) * xRadius;
y = Mathf.Cos(Mathf.Deg2Rad * angle) * yRadius;
points[i] = new Vector3(x, height, y);
angle += (380f / segments);
}
// it's way more efficient to do this in one go!
line.SetPositions(points);
}
#if UNITY_EDITOR
private float prevXRadius, prevYRadius;
private int prevSegments;
private float prevWidth;
private float prevHeight;
private void OnValidate()
{
// Can't set up our line if the user hasn't connected it yet.
if (!line) line = GetComponent<LineRenderer>();
if (!line) return;
if (!draw)
{
// instead simply disable the component
line.enabled = false;
}
else
{
// Otherwise re-enable the component
// This will simply re-use the previously created points
line.enabled = true;
if (xRadius != prevXRadius || yRadius != prevYRadius || segments != prevSegments || width != prevWidth || height != prevHeight)
{
CreatePoints();
// Cache our most recently used values.
prevXRadius = xRadius;
prevYRadius = yRadius;
prevSegments = segments;
prevWidth = width;
prevHeight = height;
}
if (controlBothXradiusYradius)
{
yRadius = xRadius;
CreatePoints();
}
}
}
#endif
}
The second script is generating objects i'm using the range slider to change the amount of object to be generated :
now i want to be able to use both scripts to be able to generate the objects inside the drawn circle area when i will change the rang slider of the amount of objects to generate the variable numberOfObjects it will generate the objects inside the drawn circle area and will position the objects on the terrain depending the terrain height.
From the answer proposed, you'd only need to randomize the radius, and repeat the method call for as many objects you'd like. Like so:
private void SpawnSphereOnEdgeRandomly3D(float maxRadius)
{
float radius = Random.Range(-0f, maxRadius);
Vector3 randomPos = Random.insideUnitSphere * radius;
randomPos += transform.position;
randomPos.y = 0f;
Vector3 direction = randomPos - transform.position;
direction.Normalize();
float dotProduct = Vector3.Dot(transform.forward, direction);
float dotProductAngle = Mathf.Acos(dotProduct / transform.forward.magnitude * direction.magnitude);
randomPos.x = Mathf.Cos(dotProductAngle) * radius + transform.position.x;
randomPos.z = Mathf.Sin(dotProductAngle * (Random.value > 0.5f ? 1f : -1f)) * radius + transform.position.z;
GameObject go = Instantiate(_spherePrefab, randomPos, Quaternion.identity);
go.transform.position = randomPos;
}

Create a hexagonal grid where I can get the position of the corners

I find a lot of examples of creating a hex grid like the following. But I'm having a hard time understanding how I might have a list of the corners in a hex grid. Basically I'd want a character to move along the line of the grid instead of the center. so I want to grab the position of the next corner in the hex grid and move them there.
I was thinking of using a basic hex grid code to create the prefabs in the right place and then just add empty game objects on each corner of the prefab, but then I have a bunch of overlapping positions that are shared corners for each hexagon. I thought I could delete them if they are overlapping but it just all seems too brute force and hard to keep track of. I'd love to hear some ideas on approaching something like this. By the way I'd also want to know the center of the hex besides knowing the corners.
this code successfully creates a hex grid pattern where I can add a hex shaped game object to instance.
using UnityEngine;
public class Grid : MonoBehaviour
{
public Transform hexPrefab;
public int gridWidth = 11;
public int gridHeight = 11;
float hexWidth = 1.732f;
float hexHeight = 2.0f;
public float gap = 0.0f;
Vector3 startPos;
void Start()
{
AddGap();
CalcStartPos();
CreateGrid();
}
void AddGap()
{
hexWidth += hexWidth * gap;
hexHeight += hexHeight * gap;
}
void CalcStartPos()
{
float offset = 0;
if (gridHeight / 2 % 2 != 0)
offset = hexWidth / 2;
float x = -hexWidth * (gridWidth / 2) - offset;
float z = hexHeight * 0.75f * (gridHeight / 2);
startPos = new Vector3(x, 0, z);
}
Vector3 CalcWorldPos(Vector2 gridPos)
{
float offset = 0;
if (gridPos.y % 2 != 0)
offset = hexWidth / 2;
float x = startPos.x + gridPos.x * hexWidth + offset;
float z = startPos.z - gridPos.y * hexHeight * 0.75f;
return new Vector3(x, 0, z);
}
void CreateGrid()
{
for (int y = 0; y < gridHeight; y++)
{
for (int x = 0; x < gridWidth; x++)
{
Transform hex = Instantiate(hexPrefab) as Transform;
Vector2 gridPos = new Vector2(x, y);
hex.position = CalcWorldPos(gridPos);
hex.parent = this.transform;
hex.name = "Hexagon" + x + "|" + y;
}
}
}
}

How can I make a circle from grid of GameObjects?

What I am trying to achieve is something like this:
What I have so far is the edges for the circles.
I know this would involve a nested for loop. This is what I have so far:
public GameObject player;
private GameObject playerGrid;
public int numOfObjects;
private Vector3 centerPos;
public int size = 2;
public Vector2 speed = new Vector2(50, 50);
private float smoothTime = 0.25f;
void Start()
{
playerGrid = new GameObject();
centerPos = transform.position;
for (int i = 0; i < numOfObjects; i++)
{
float pointNum = (i * 1.0f) / numOfObjects;
float angle = pointNum * Mathf.PI * 2;
float r = size / 2 * (Mathf.PI);
float x = Mathf.Sin(angle) * r;
float y = Mathf.Cos(angle) * r;
Vector3 pointPos = new Vector3(x, y, 0) + centerPos;
GameObject obj = Instantiate(player, pointPos, Quaternion.identity);
obj.transform.SetParent(playerGrid.transform);
}
}
I am stuck on how to implement the conditional for the nested for loop. Also, I have trouble understanding the calculations of column positions in the nested for loop. I believe the conditional would be the start and end of I for that column or row: for(int j = i + 1; j < i - 1, j++)
For the col positions, I would think it would be incrementing the angle enough to give the square its space for that column: float x = (Mathf.Sin(angle) + somethingHere) * r;
I just not sure how to progress from here.
Here's a simple way to draw a circle:
public float circleRadius = 5f;
public float objectSize = 1f;
void OnDrawGizmos()
{
for (var x = -circleRadius; x <= circleRadius; x++)
{
for (var y = -circleRadius; y <= circleRadius; y++)
{
var pos = new Vector3(x, 0f, y);
if (pos.magnitude >= circleRadius) continue;
Gizmos.DrawSphere(pos * (objectSize * 2f), objectSize);
}
}
}

Can I have some help optimizing this script

I wrote a infinite terrain script which works! Saddly everytime the player moves a chunk it lags for a moment. I know my code isn't great but I'm here to learn why :D
I'm unsure of what else to do. I've looked online and found no simple or understandable solution to me because I just don't know enough so I tried to write it on my own and it works but barley.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GEN_InfiniteTerrain : MonoBehaviour
{
public GameObject targetObject;
public GameObject chunkObject;
public int chunkSize;
public float unitSize;
public int renderDistance;
Dictionary<Vector2, GameObject> gridOfChunks = new Dictionary<Vector2, GameObject>();
List<Vector2> expectedChunkGridPositions = new List<Vector2>();
public float noiseScale;
// Infinite terrain values
float absoluteChunkSize;
private void Start()
{
// Calculate absolute chunk size
GetAbsoluteChunkSize();
// Generate base world
GenerateBase();
}
Vector2 lastTargetGridPosition = Vector2.zero;
private void LateUpdate()
{
// Get the targets position in world space
Vector3 targetAbsolutePosition = targetObject.transform.position;
// Convert the targets world position to grid position (/ 10 * 10 is just rounding to 10)
Vector2 targetGridPosition = new Vector2();
targetGridPosition.x = Mathf.RoundToInt(targetAbsolutePosition.x / 10) * 10 / absoluteChunkSize;
targetGridPosition.y = Mathf.RoundToInt(targetAbsolutePosition.z / 10) * 10 / absoluteChunkSize;
if (targetGridPosition - lastTargetGridPosition != Vector2.zero)
{
GenerateExpectedChunkAreas(targetGridPosition);
UpdateChunkPositions(targetGridPosition);
}
lastTargetGridPosition = targetGridPosition;
}
void GenerateBase()
{
for (int x = -renderDistance / 2; x < renderDistance / 2; x++)
{
for (int z = -renderDistance / 2; z < renderDistance / 2; z++)
{
Vector2 gridPosition = new Vector2(x, z);
Vector3 worldPosition = new Vector3(x * (unitSize * chunkSize), 0, z * (unitSize * chunkSize));
GameObject chunk = Instantiate(chunkObject, worldPosition, Quaternion.identity);
chunk.GetComponent<GEN_Chunk>().gridPosition = gridPosition;
gridOfChunks.Add(gridPosition, chunk);
}
}
GenerateExpectedChunkAreas(Vector2.zero);
}
void GenerateExpectedChunkAreas(Vector2 targetGridPosition)
{
expectedChunkGridPositions.Clear();
for (int x = -renderDistance / 2; x < renderDistance / 2; x++)
{
for (int z = -renderDistance / 2; z < renderDistance / 2; z++)
{
Vector2 gridPosition = new Vector2(x, z) + targetGridPosition;
expectedChunkGridPositions.Add(gridPosition);
}
}
}
void UpdateChunkPositions(Vector2 targetGridPosition)
{
List<Vector2> positionsWithoutChunks = new List<Vector2>();
List<Vector2> positionsWithOldChunks = new List<Vector2>();
for (int chunkCount = 0, x = -renderDistance / 2; x < renderDistance / 2; x++)
{
for (int z = -renderDistance / 2; z < renderDistance / 2; z++)
{
Vector2 gridPosition = new Vector2(x, z) + targetGridPosition;
if(!gridOfChunks.ContainsKey(gridPosition))
{
positionsWithoutChunks.Add(gridPosition);
}
chunkCount++;
}
}
foreach (GameObject chunk in gridOfChunks.Values)
{
if(!expectedChunkGridPositions.Contains(chunk.GetComponent<GEN_Chunk>().gridPosition))
{
positionsWithOldChunks.Add(chunk.GetComponent<GEN_Chunk>().gridPosition);
}
}
for (int i = 0; i < positionsWithOldChunks.Count; i++)
{
Vector3 worldPosition = new Vector3(positionsWithoutChunks[i].x * absoluteChunkSize, 0, positionsWithoutChunks[i].y * absoluteChunkSize);
gridOfChunks[positionsWithOldChunks[i]].transform.position = worldPosition;
// Recalculating noise for chunk based on its new position does lag more but even WITHOUT this it still stutters when player moves around. ( plan to learn threading just to calculate noise on seperate threads )
// gridOfChunks[positionsWithOldChunks[i]].GetComponent<GEN_Chunk>().ApplyNoise();
}
}
void GetAbsoluteChunkSize()
{
absoluteChunkSize = unitSize * chunkSize;
}
}
I need some smooth working infinite terrain (in quotes 'infinite')
And I'd like to learn too!

How can I fix this imperfect circle I made using LineRenderer?

So I made this shape which I applied to a sprite via this script:
using UnityEngine;
using System.Collections;
public class CircleShapeGenerator : MonoBehaviour
{
public int segments = 100;
public float radius = 1;
public Color c1 = new Color( 1, 1, 1, 0.1f );
public Color c2 = new Color( 1, 1, 1, 0.1f );
LineRenderer line;
void Start ()
{
line = gameObject.AddComponent<LineRenderer>();
line.material = new Material(Shader.Find("Particles/Additive"));
line.SetWidth(0.05F, 0.05F);
line.SetVertexCount (segments + 1);
line.useWorldSpace = false;
}
void Update()
{
line.SetColors(c1, c2);
float angle = 20f;
for (int i = 0; i < (segments + 1); i++)
{
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( i, new Vector3( x,y,0) );
angle += (360f / segments);
}
}
}
As you can see in the screenshot, the start and end do not connect as they should. How can I fix this? I found this snippet of code on the entire internet but all give this result. Can somebody fix this or provide, perhaps, a spline solution? I think its overkill to go to a Shader solution (0 experience with shaders).
This solution could be a little bit complicated. But it'll works.
The idea is
1) Draw first segment as small segmented area.
2) Draw from seconds to last -1 segments as big segment.
3) Draw last segment as small segmented area too.
It makes seamless edge between the start segment and the end segment.
And total segment's count is not too many.
total segment = segment + 2 * subsegment
This is sample code.
using UnityEngine;
using System.Collections;
public class CircleShapeGenerator : MonoBehaviour {
public int segments = 100;
public int edgeSegments = 10;
public float radius = 1f;
int vertCount;
float increAngle, increAngleEdge;
public Color c1 = new Color( 1, 1, 1, 1f );
public Color c2 = new Color( 1, 1, 1, 1f );
LineRenderer line;
void Start ()
{
vertCount = segments + 2*edgeSegments - 2 + 1;
increAngle = 360f / segments;
increAngleEdge = increAngle/edgeSegments;
line = gameObject.AddComponent<LineRenderer>();
line.material = new Material(Shader.Find("Particles/Additive"));
line.SetWidth(0.05F, 0.05F);
line.SetVertexCount (vertCount);
line.useWorldSpace = false;
}
void Update()
{
line.SetColors(c1, c2);
//draw first segment
float angle = 0;
for (int i = 0; i < edgeSegments; i++)
{
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( i, new Vector3(x, y, 0) );
angle += increAngleEdge;
}
//draw from seconds to last-1 segment
angle -= increAngleEdge;
for (int i = 0; i < segments-2; i++)
{
angle += increAngle;
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( edgeSegments + i, new Vector3(x, y, 0) );
}
//draw last segment
for (int i = 0; i < edgeSegments+1; i++)
{
angle += increAngleEdge;
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( edgeSegments + segments - 2 + i, new Vector3(x, y, 0) );
}
}
}

Categories