I have a dilemma on an algorithm. I'm trying to generate 6 random positions that have a distance between them. My algorithm works, only to check the distance between the current point and the previous point. What I need is for the algorithm to verify the distance between all points and if the distance is less than the indicated value, it will generate a new position.
private void Generate()
{
for (int i = 0; i <= 5; i++)
{
Vector3 pos = PointsGenerator();
Instantiate(point, pos, Quaternion.identity); // just to highlight the points on the screen
}
}
private List<Vector3> Dist = new List<Vector3>();
private bool isOver = false;
private Vector3 PointsGenerator()
{
Vector3 currentPosition = Vector3.zero;
Vector3 oldPosition = Vector3.zero;
float distance = 0f;
do
{
currentPosition = new Vector3(UnityEngine.Random.Range(minX, maxX), UnityEngine.Random.Range(minY, maxY), 0f);
for (int i = 0; i < Dist.Count; i++)
{
distance = Vector2.Distance(currentPosition, Dist[i]);
if (distance <= 4f)
{
isOver = true;
break;
}
else
isOver = false;
}
} while (isOver == true);
Dist.Add(currentPosition);
return currentPosition;
}
Thanks to all who will give advice.
what is the question?
"I need these 6 points and not only have a distance between them, but also the distance from center + radius to be of value"
To achieve that, you will HAVE TO generate points that are on a sphere. For that, generate a (random if needed) center point of your sphere and define the radius.
Then, you need to use random spherical coordinates (radius, randomAngle1, randomAngle2). To convert those into cartesian coordinates, use
x = radius * cos(randomAngle1) * sin(randomAngle2)
y = radius * sin(randomAngle1) * sin(randomAngle2)
z = radius * cos(randomAngle1)
after that you can make the same algorithm as you already have to regenerate new points until they are far away from each other as well
Related
i have mathematical formula and i have spawn points. but, i have so differenet object sizes and radiuses. how i can instantiate a objects in the edge of circle and rotate them onto center circle like picture;
i am bad with this stuf and my code is wrong or not i dont know
```
public void GetObstacle(int count, CircleRotator rotator)
{
float TAU = 6.283185307179586f;
for (int i = 0; i < count; i++)
{
Transform cameraTR = mainCamera.transform;
Transform tr = rotator.transform;
var obj = new GameObject();
float t = (float)i / count;
float angle = t * TAU * 0.25f; //
Vector3 itemCenter = new Vector3(Mathf.Cos(angle) * rotator.GetRadius + tr.position.x,
tr.position.y + Mathf.Sin(angle) * rotator.GetRadius);
obj.transform.position = itemCenter;
Vector3 current = tr.position - cameraTR.position;
Vector3 target = obj.transform.position - cameraTR.position;
obj.transform.Rotate(Vector3.forward, Vector3.Dot(current, target));
obj.transform.SetParent(tr);
}
}
```
where 0.25 in here i dont understand. But without 0.25 all will broke. angle = t * TAU * 0.25f;
help pls or give some adwice. https://ibb.co/qNhZCh3 what i would like but what i have https://ibb.co/NFSRmSn
so many formulas and watched videos. but i can't understand how
Thanks #Ruzihm with help rotations.
spawn outside edge I got this way, where I'm not sure if this is the right way, where Size is the size of y collider. If someone show another good way i will be gratitude!
Vector3 direction = (tr.position - obj.transform.position);
obj.transform.rotation = Quaternion.LookRotation(Vector3.forward, direction);
obj.transform.position -= direction.normalized * obj.Size()/2f;
I am Programming a random "Stone" Spawner and have a big problem at the moment. I have some ideas how to fix it, but want to know a performance friendly way to do it.
So my way to Spawn the Objects on the Sphere Surface is this one:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnObjects : MonoBehaviour
{
public Vector3 centerOfSphere = new Vector3(0, 5000, 0);
public float radiusOfSphere = 5000.0f;
public List<GameObject> stones;
public int stonesToSpawn = 1;
void Start()
{
Vector3 center = transform.position + centerOfSphere; //Setting the center position of the Sphere
//This for loop is spawning the Stones on a random position on Sphere Surface
for (int i = 0; i < stonesToSpawn; i++)
{
Vector3 pos = RandomCircle(center, radiusOfSphere);
Quaternion rot = Quaternion.FromToRotation(Vector3.forward, center - pos);
Instantiate(stones[Random.Range(0, stones.Count)], pos, rot);
}
}
//Method returns a Random Position on a Sphere
Vector3 RandomCircle(Vector3 center, float radius)
{
float alpha = UnityEngine.Random.value * 360;
float beta = UnityEngine.Random.value * 360;
Vector3 pos;
pos.x = radius * Mathf.Cos(beta) * Mathf.Cos(alpha);
pos.y = radius * Mathf.Cos(beta) * Mathf.Sin(alpha);
pos.z = radius * Mathf.Sin(beta);
return pos;
}
}
So thank you for your following explanations! :)
As said to make your live one step easier simply use Random.onUnitSphere so your entire method RandomCircle (change that name btw!) can be shrinked to
private Vector3 RandomOnSphere(Vector3 center, float radius)
{
return center + Random.onUnitSphere * radius;
}
And then in order to have a minimum distance between them there are probably multiple ways but I guess the simplest - brute force - way would be:
store already used positions
when you get a new random position check the distance to already existing ones
keep get new random positions until you have found one, that is not too close to an already existing one
This depends of course a lot on your use case and the amount of objects and the minimum distance etc - in other words I leave it up to you to assure that the requested amount and minimum distance is doable at all with the given sphere radius.
You could always leave an "emergency exit" and give up after e.g. 100 attempts.
Something like e.g.
// Linq offers some handy query shorthands that allow to shorten
// long foreach loops into single calls
using System.Linq;
...
private const int MAX_ATTEMPTS = 100;
public float minimumDistance = 1f;
void Start()
{
var center = transform.position + centerOfSphere;
// It is cheaper to use with square magnitudes
var minDistanceSqr = minimumDistance * minimumDistance;
// For storing the already used positions
// Already initialize with the correct capacity, this saves resources
var usedPositions = new List<Vector3>(stonesToSpawn);
for (int i = 0; i < stonesToSpawn; i++)
{
// Keep track of the attempts (for the emergency break)
var attempts = 0;
Vector3 pos = Vector3.zero;
do
{
// Get a new random position
pos = RandomOnSphere(center, radiusOfSphere);
// increase the attempts
attempts++;
// We couldn't find a "free" position within the 100 attempts :(
if(attempts >= MAX_ATTEMPTS)
{
throw new Exception ("Unable to find a free spot! :'(");
}
}
// As the name suggests this checks if any "p" in "usedPositions" is too close to the given "pos"
while(usedPositions.Any(p => (p - pos).sqrMagnitude <= minDistanceSqr)));
var rot = Quaternion.FromToRotation(Vector3.forward, center - pos);
Instantiate(stones[Random.Range(0, stones.Count)], pos, rot);
// Finally add this position to the used ones so the next iteration
// also checks against this position
usedPositions.Add(pos);
}
}
Where
usedPositions.Any(p => (p - pos).sqrMagnitude <= minDistanceSqr))
basically equals doing something like
private bool AnyPointTooClose(Vector3 pos, List<Vector3> usedPositions, float minDistanceSqr)
{
foreach(var p in usedPositions)
{
if((p - pos).sqrMagnitude <= minDistanceSqr)
{
return true;
}
}
return false;
}
if that's better to understand for you
I have a LineRenderer with only two points, start, and endpoints. I want to divide the line with a given number that will automatically create or add new points to the LineRenderer. For example, if I divide the line by 2, it will add another point in the center of LineRenderer.
EDITED:
Edited my question the second time now with the working script and image result. I don't know if this is the best solution but its working for me. The only thing that I don't know is how to compute the divisor to use based on the number of line.position. And for the duplicate flag. I think it's not the same as what I wanted.
GameObject stroke = new GameObject();
LineRenderer line = stroke.AddComponent<LineRenderer>();
line.startWidth = 0.1f;
line.endWidth = 0.1f;
line.useWorldSpace = true;
float divisor;
Vector3 start = new Vector3(-6.7399,-0.2475,0);
Vector3 end = new Vector3(-3.742899, -0.2475, 0);
line.positionCount = 4;
if(line.position == 4)
divisor = 0.333f; //get by 1/3
else if(line.position == 3)
divisor = 0.5f; //get by 1/2
Vector3 valuePerPosition = Vector3.Lerp(start, end, divisor);
for (int i = 0; i < line.positionCount; i++)
{
Vector3 newPosition = start - valuePerPosition;
line.SetPosition(i, start - (newPosition * i));
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = start - (newPosition * i);
sphere.transform.localScale = new Vector3(0.125f, 0.125f, 0.125f);
}
Following this tutorial, I'm trying to use Line Renderer component to draw a sine wave from point A to B.I'm using the input mouse position. However what I did so far is not working, it just draw the sine wave along the x axis, I need to draw it from the start point to the input mouse position.
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 newPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
newPos.z = 0;
CreatePointMarker(newPos);
GenerateNewLine();
}
}
private void CreatePointMarker(Vector3 pointPosition)
{
Instantiate(linePointPrefab, pointPosition, Quaternion.identity);
}
private void GenerateNewLine()
{
GameObject[] allPoints = GameObject.FindGameObjectsWithTag("PointMarker");
Vector3[] allPointPositions = new Vector3[allPoints.Length];
var pointList = new List<Vector3>();
for (int i = 0; i < 50; i ++)
{
var dir = allPoints[0].transform.position - allPoints[1].transform.position;
float x = dir.x * i;
float y = Mathf.Sin(x * Time.time);
var sine = new Vector3(x, y, 0f);
var tangentLine = allPoints[0].transform.position + sine;
pointList.Add(tangentLine);
}
SpawnLineGenerator(pointList.ToArray());
}
}
private void SpawnLineGenerator(Vector3[] linePoints)
{
GameObject newLineGen = Instantiate(lineGeneratorPrefab);
LineRenderer lRend = newLineGen.GetComponent<LineRenderer>();
lRend.positionCount = linePoints.Length;
lRend.SetPositions(linePoints);
}
As an alternative I would suggest to instead use
lineRenderer.useWorlsSpace = false;
so the points are no longer set in worldspace but in the local space relative to the linerenderer transform.
Now you can simply rotate the linerenderer transform to point towards the latest user input position.
I couldn't use your code example since I don't know what your prefabs are and do so I created my own from the code in the given Video. I hope you can reuse/recreate the parts necessary
public class SinusWave : MonoBehaviour
{
public Vector3 initalPosition;
public int pointCount = 10;
public LineRenderer line;
private Vector3 secondPosition;
private Vector3[] points;
private float segmentWidth;
private void Awake()
{
line = GetComponent<LineRenderer>();
line.positionCount = pointCount;
// tell the linerenderer to use the local
// transform space for the point coorindates
line.useWorldSpace = false;
points = new Vector3[pointCount];
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
// Camera.main.ScreenToWorldPoint needs a value in Z
// for the distance to camera
secondPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition - Vector3.forward * Camera.main.transform.position.z);
secondPosition.z = 0;
var dir = secondPosition - initalPosition;
// get the segmentWidth from distance to end position
segmentWidth = Vector3.Distance(initalPosition, secondPosition) / pointCount;
// get the difference angle in the Z axis between the current transform.right
// and the direction
var angleDifference = Vector3.SignedAngle(transform.right, dir, Vector3.forward);
// and rotate the linerenderer transform by that angle in the Z axis
// so the result will be that it's transform.right
// points now towards the clicked position
transform.Rotate(Vector3.forward * angleDifference, Space.World);
}
for (var i = 0; i < points.Length; ++i)
{
float x = segmentWidth * i;
float y = Mathf.Sin(x * Time.time);
points[i] = new Vector3(x, y, 0);
}
line.SetPositions(points);
}
}
Btw I just assume here that GameObject.FindGameObjectsWithTag("PointMarker"); actually retuns all the so far spawned linePointPrefab objects.
It would be better to store them right away when you spawn them like
private List<Transform> allPoints = new List<Transform>();
...
allPoints.Add(Instantiate(linePointPrefab, pointPosition, Quaternion.identity).transform);
then you can skip the usage of FindObjectsWithType completely
For adopting this local space position also for the line point prefabs you instantiate simply instantiate them as child of the linerenderer transform or spawn them and the linerenderer under the same parent so the relative positions are the same. In this case you wouldn't rotate the linerenderer but the entire parent object so the spawned linepoints are rotated along with it
I have just done the Curves and Splines Tutorials from the catlikecoding.com website (http://catlikecoding.com/unity/tutorials/curves-and-splines/) and after finishing it I came up with an idea. I would like to spawn the items from the SplineDecorator at the same distance from the spline at both sides of the spline. I have tried duplicating the for loop and instantiating the newItem twice but at different position but it doenst work as I want to. Here's the Spline Decorator script that takes a spline and then Instantiate some items along the its path
using UnityEngine;
public class SplineDecorator : MonoBehaviour
{
public BezierSpline spline;
public int frequency;
public float distanceBetweenItems;
public bool lookForward;
public Transform[] items;
private void Awake()
{
if (frequency <= 0 || items == null || items.Length == 0)
{
return;
}
float stepSize = 1f / (frequency * items.Length);
for (int p = 0, f = 0; f < frequency; f++)
{
for (int i = 0; i < items.Length; i++, p++)
{
Transform item = Instantiate(items[i]) as Transform;
Vector3 position = spline.GetPoint(p * stepSize);
item.transform.localPosition = new Vector3(position.x + distanceBetweenItems, position.y, position.z);
if (lookForward)
{
item.transform.LookAt(position + spline.GetDirection(p * stepSize));
}
item.transform.parent = transform;
}
}
}
}
You don't define exactly what "at both sides" means to you, so I'm not sure if this is what you're looking for, but maybe this will get you on the right track.
I replaced the inner loop with the following and got a sort of "race track" feel with distanceBetweenItems = 2:
Transform item = Instantiate(items[i]) as Transform;
Transform item2 = Instantiate(items[i]) as Transform;
Vector3 position = spline.GetPoint(p * stepSize);
Vector3 direction = spline.GetDirection(p * stepSize);
Vector3 cross = Vector3.Cross(direction, Vector3.up);
Vector3 delta = cross.normalized * (distanceBetweenItems/2);
item.transform.localPosition = new Vector3(position.x + delta.x, position.y + delta.y, position.z + delta.z);
item2.transform.localPosition = new Vector3(position.x - delta.x, position.y - delta.y, position.z - delta.z);
if (lookForward)
{
item.transform.LookAt( position + spline.GetDirection(p * stepSize));
item2.transform.LookAt(position + spline.GetDirection(p * stepSize));
}
item.transform.parent = transform;
item2.transform.parent = transform;
What I've done here is use Vector3.Cross() (Unity Documentation) to find the line perpendicular to both the spline and Vector3.up. Then I calculate how far along that line to go by normalizing it and multiplying it by half of distanceBetweenItems. (So that, combined, they're distanceBetweenItems apart.)
If you instead wanted something like tracing the wingtips of a plane flying that spline, you'll need to replace the Vector.up above. The simplest way is to replace it with the 'binormal vector', though there are issues with that. This pdf I just found talks a little about it, and might get you on the right path.
(Goofing around, I was able to get a reasonable approximation by replacing the Vector3 cross = ... line with Vector3 cross = Vector3.Cross(direction, direction - spline.GetDirection(p * stepSize + 0.01f));, though it's screwy on sharp bends, non-mirrored vertices, and at the start/end loop.)