How to generate random objects inside drawn circle area? - c#

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;
}

Related

How to draw a circle around specific object and/or around the mouse click position?

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
This create a circle around the object the script is attached to.
But I want to change the script a bit and to add a target variable and if the target is not null make a circle around the target even if the script is not attached to this target. Maybe using a flag that will decide if to create the circle around the target or around the object the script is attached to like now.
I added at the top a target variable :
public Transform target;
Then changed both x and y lines :
x = target.position.x + Mathf.Cos(Mathf.Deg2Rad * angle) * xRadius;
y = target.position.y + Mathf.Sin(Mathf.Deg2Rad * angle) * yRadius;
But it's not drawing around the target, when I assign a transform to target it's just creating the circle around the transform the script is attached to and not around the target.
Another thing I'm trying to do is to add a mouse down click event and when the mouse is hold down and dragged it will create the circle around the clicked mouse position and will change the circle radius while holding the mouse down and dragging the mouse.
But first how to draw the circle around the target ?
For drawing around the target the circle I tried to add a LineRenderer component to the target object in the Start() but it didn't change anything.
if(target.GetComponent<LineRenderer>() == null)
{
target.gameObject.AddComponent<LineRenderer>();
}
You are setting
line.useWorldSpace = false;
which means the positions you provide have to be relative to this objects transform.
Anyway this is not what you want seemingly so simply remove that line or rather set it to
line.useWorldSpace = true;
or if you actually want to keep the functionality of local space, meaning if you move this transform then the line will be moved along with it - which I doubt but just for completeness - you could use
points[i] = transform.InverseTransformPoint( new Vector3(x, height, y));
A general question: Why does your circle have 380 and not 360 degrees...?

How can I change the circle to be drawn horizontal instead vertical?

using UnityEngine;
using System.Collections;
using System;
[RequireComponent(typeof(LineRenderer))]
public class DrawRadiusAround : MonoBehaviour
{
[Range(1, 50)] public int segments = 50;
[Range(1, 5)] public float xRadius = 5;
[Range(1, 5)] public float yRadius = 5;
[Range(0.1f, 5f)] public float width = 0.1f;
public bool controlBothXradiusYradius = false;
public bool draw = true;
[SerializeField] private LineRenderer line;
private void Start()
{
if (!line) line = GetComponent<LineRenderer>();
CreatePoints();
}
//private void Update()
//{
//
//}
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, 0f, 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 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)
{
CreatePoints();
// Cache our most recently used values.
prevXRadius = xRadius;
prevYRadius = yRadius;
prevSegments = segments;
prevWidth = width;
}
if (controlBothXradiusYradius)
{
yRadius = xRadius;
}
}
}
#endif
}
Sounds like you rather wanted to use e.g.
points[i] = new Vector3 (x, y, 0f);
In general however your LineRenderer already has
line.useWorldSpace = false;
which means the positions you give it are used in local space
=> All you need to do is rotate the object the LineRenderer is attached to according to your needs actually.

How can I draw a circle using linerenderer and make it the circle as object child of the object is should draw around?

When I attach this script to an object it will draw a circle around it.
Now I want to make that the script the linerenderer will be attached to empty gameobject that will be a child of the object that it will draw the circle around.
I want that so I can drag the empty gameobject and change the circle height. That way I can simple duplicate the empty gameobject and have two circles one on ground on higher. and both circle will be the same radius around the parent object the parent object will be the center.
using UnityEngine;
using System.Collections;
using System;
[ExecuteAlways]
[RequireComponent(typeof(LineRenderer))]
public class DrawRadiusAroundTurret : MonoBehaviour
{
[Range(1, 50)] public int segments = 50;
[Range(1, 5)] public float xRadius = 5;
[Range(1, 5)] public float yRadius = 5;
[Range(0.1f, 5f)] public float width = 0.1f;
public bool controlBothXradiusYradius = false;
public bool draw = true;
[SerializeField] private LineRenderer line;
private void Start()
{
if (!line) line = GetComponent<LineRenderer>();
CreatePoints();
}
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, 0f, 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 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)
{
CreatePoints();
// Cache our most recently used values.
prevXRadius = xRadius;
prevYRadius = yRadius;
prevSegments = segments;
prevWidth = width;
}
if (controlBothXradiusYradius)
{
yRadius = xRadius;
}
}
}
#endif
}
The main goal is to have multiple circles around the Missles Turrent but that I will be able to change the circles height with a simple drag.
The solution is to create empty gameobject/s as children each child position set to 0,0,0
Then adding to each child a linerenderer component and the script.
The script :
using UnityEngine;
using System.Collections;
using System;
[ExecuteAlways]
[RequireComponent(typeof(LineRenderer))]
public class DrawRadiusAroundTurret : MonoBehaviour
{
[Range(1, 50)] public int segments = 50;
[Range(1, 5)] public float xRadius = 5;
[Range(1, 5)] public float yRadius = 5;
[Range(0.1f, 5f)] public float width = 0.1f;
public bool controlBothXradiusYradius = false;
public bool draw = true;
[SerializeField] private LineRenderer line;
private void Start()
{
if (!line) line = GetComponent<LineRenderer>();
CreatePoints();
}
//private void Update()
//{
//
//}
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, 0f, 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 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)
{
CreatePoints();
// Cache our most recently used values.
prevXRadius = xRadius;
prevYRadius = yRadius;
prevSegments = segments;
prevWidth = width;
}
if (controlBothXradiusYradius)
{
yRadius = xRadius;
}
}
}
#endif
}
Screenshot :

How can I set random positions on radius?

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!

Unity camera starting position

Hello so I created a camera for a RTS game I'm making but on start the camera is on the minHight position and I cant figure out how to change it hope someone helps the code is a bit of a mess its just for a test here is the code I want it to start from the maxHight position that is given and not on the minHight .
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RtsCamera : MonoBehaviour {
public float zoomSensitivy = 15;
private Transform m_Transform;
public float screenEdgeBorder = 25f;
public bool useScreenEdgeInput = true;
public float screenEdgeMovementSpeed = 3f;
// zoom stuff
public float heightDampening = 5f;
public float scrollWheelZoomingSensitivity = 25f;
public float maxHeight = 10f; //maximal height
public float minHeight = 15f; //minimnal height
public LayerMask groundMask = -1;
public bool autoHeight = true;
public bool limitMap = true;
public float limitX = 50f; //x limit of map
public float limitZ = 50f; //z limit of map
private float zoomPos = 0; //value in range (0, 1) used as t in Matf.Lerp
public bool useScrollwheelZooming = true;
Vector3 tempPos;
public string zoomingAxis = "Mouse ScrollWheel";
private float ScrollWheel
{
get { return Input.GetAxis(zoomingAxis); }
}
private Vector2 MouseAxis
{
get { return new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")); }
}
private Vector2 MouseInput
{
get { return Input.mousePosition; }
}
private void Start()
{
m_Transform = transform;
}
// Update is called once per frame
void Update () {
Move();
LimitPosition();
HeightCalculation();
}
private void Move()
{
if (useScreenEdgeInput)
{
Vector3 desiredMove = new Vector3();
Rect leftRect = new Rect(0, 0, screenEdgeBorder, Screen.height);
Rect rightRect = new Rect(Screen.width - screenEdgeBorder, 0, screenEdgeBorder, Screen.height);
Rect upRect = new Rect(0, Screen.height - screenEdgeBorder, Screen.width, screenEdgeBorder);
Rect downRect = new Rect(0, 0, Screen.width, screenEdgeBorder);
desiredMove.x = leftRect.Contains(MouseInput) ? -1 : rightRect.Contains(MouseInput) ? 1 : 0;
desiredMove.z = upRect.Contains(MouseInput) ? 1 : downRect.Contains(MouseInput) ? -1 : 0;
desiredMove *= screenEdgeMovementSpeed;
desiredMove *= Time.deltaTime;
desiredMove = Quaternion.Euler(new Vector3(0f, transform.eulerAngles.y, 0f)) * desiredMove;
desiredMove = m_Transform.InverseTransformDirection(desiredMove);
m_Transform.Translate(desiredMove, Space.Self);
}
}
private void LimitPosition()
{
if (!limitMap)
return;
m_Transform.position = new Vector3(Mathf.Clamp(m_Transform.position.x, -limitX, limitX),
m_Transform.position.y,
Mathf.Clamp(m_Transform.position.z, -limitZ, limitZ));
}
private void HeightCalculation()
{
float distanceToGround = DistanceToGround();
if (useScrollwheelZooming)
zoomPos += ScrollWheel * Time.deltaTime * scrollWheelZoomingSensitivity;
zoomPos = Mathf.Clamp01(zoomPos);
float targetHeight = Mathf.Lerp(minHeight, maxHeight, zoomPos);
float difference = 0;
if (distanceToGround != targetHeight)
difference = targetHeight - distanceToGround;
m_Transform.position = Vector3.Lerp(m_Transform.position,
new Vector3(m_Transform.position.x, targetHeight + difference, m_Transform.position.z), Time.deltaTime * heightDampening);
}
private float DistanceToGround()
{
Ray ray = new Ray(m_Transform.position, Vector3.down);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, groundMask.value))
return (hit.point - m_Transform.position).magnitude;
return 0f;
}
}
Set the value of the variable zoomPos on line 25 to either 1 or 0 depending on whether you want it at the min height or the max height, you could do this either in the Start() or just at the declaration of the variable.
This is because in the HeightCalculation function it sets the height to somewhere between the min and max based off the value of zoomPos, as you use the scrollwheel this value changes and so does the zoom. Altering zoomPos before you run the game allows you to effectively set a default starting height.

Categories