How to spawn randomly without the next spawning object spawning near - c#

I am doing a personal project which is about an aim trainer. What I'm trying to do is that the object that spawns randomly doesn't spawn near the previous one.
I'm not very good at mathematical logic so what I've tried to do doesn't work. Any ideas, thanks :)
The code I've tried isn't doing what I want, the spawns randoms keep appearing near the last spawn, and I don't want that. What I really want is not to repeat the position of the last spawn.
I tried this:
public class Spawn : MonoBehaviour
{
public Vector3 GerRandomPosition()
{
Vector3 center = boxCollider.center + transform.position;
float minX = center.x - boxCollider.size.x / 2f;
float maxX = center.x + boxCollider.size.x / 2f;
float minY = center.y - boxCollider.size.y / 2f;
float maxY = center.y + boxCollider.size.y / 2f;
auxRandomX = randomX;
randomX = Random.Range(minX, maxX);
auxRandomY = randomY;
randomY = Random.Range(minY, maxY);
float distanceX = Mathf.Abs(auxRandomX - randomX);
while (distanceX < 3.2f)
{
randomX = Random.Range(minX, maxX);
distanceX = Mathf.Abs(auxRandomX - randomX);
}
float distanceY = Mathf.Abs(auxRandomY - randomY);
while (distanceY < 3.2f)
{
randomY = Random.Range(minY, maxY);
distanceY = Mathf.Abs(auxRandomY - randomY);
}
Vector3 randomPosition = new Vector3(randomX, randomY, transform.position.z);
return randomPosition;
}
}

TLDR; Move/Check the previous spawn point with a set distance spacing.
Bruteforce Solution
Vector3 lastSpawnPoint = Vector3.zero; // or somewhere far away for start
const float SPAWN_SPACING = 1f;
public Vector3 GetSpawnPoint(){
// Keep generating a new position until it is far away from the previous spawn point
Vector3 newPoint;
do {
newPoint = GetRandomPosition();
} while (Vector3.Distance(newPoint, lastSpawnPoint) <= SPAWN_SPACING)
// Shouldn't be a long/infinite loop if the play-area is big enough.
lastSpawnPoint = newPoint;
return newPoint;
}
Bruteforce it, works very nicely if your game has a large playing/spawning field.
No Bruteforce Solution
Vector3 lastSpawnPoint = Vector3.zero;
const float MIN_SPAWN_SPACING = 1f;
const float MAX_SPAWN_SPACING = 10f;
public Vector3 GetSpawnPoint(){
if (lastSpawnPoint == Vector3.zero){
return GetRandomPosition();
}
// We generate a direction and spacing,
// and move the spawn-point based on that.
float spacing = Random.Range(MIN_SPAWN_SPACING, MAX_SPAWN_SPACING);
Vector3 direction = Random.insideUnitCircle.normalized;
lastSpawnPoint += (direction * spacing);
return lastSpawnPoint;
}
Works nicely, and you can set a max spawn spacing too.
Though currently it is able to generate anywhere, so you might want to configure this to limit where it can spawn at.

Related

My Game's Infinite Platform Generation does Not Work

I have followed a YouTube tutorial on how to make a doodle jump replica in 5 minutes.
The problem is, this tutorial is rushed and does not provide all the information needed and thus I have encountered one big problem.
The infinite random platform generation keeps skewing off to either the left or right side of the screen after a certain point, and I have no clue as to why.
Here is the video tutorial: https://www.youtube.com/watch?v=IUzI95mmbwA
I am just wondering if anyone on here could be kind enough to help me on this small problem as it is for my school project and I am not sure how to fix it.
GameManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject platformPrefab;
public int platformCount = 300;
// Use this for initialization
void Start ()
{
Vector3 spawnPosition = new Vector3();
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
spawnPosition.x += Random.Range(-5f, 5f);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
}
}
Platforms being close together issue
So you generate your platforms using
void Start ()
{
Vector3 spawnPosition = new Vector3();
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
spawnPosition.x += Random.Range(-5f, 5f);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
}
which basically means that the next platform always has an offset to the one before in a range +/- 5.
This means however, that the next value would always have to counterbalance the current offset in order to be fully balanced in total. Since you use a random and not an equal distribution thiis is not guaranteed!
In order to make sure platforms don't get "skewed" to one side infinitely I would simply make sure they still "fit into the screen" and clamp the position accordingly like e.g.
public class GameManager : MonoBehaviour
{
public GameObject platformPrefab;
public int platformCount = 300;
[SerializeField] private Camera mainCamera;
// Adjust via the Inspector in order to also take platform width into account!
public float platformWidth;
[Min(0)] public float xRange = 5f;
float minX;
float maxX;
// Use this for initialization
void Start ()
{
if(!mainCamera) mainCamera = Camera.main;
// get the screen boarders in world space
minX = mainCamera.ScreenToWorldPoint(new Vector3(0, 0, -mainCamera.transform.position.z)).x + platformWidth / 2f;
maxX = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width, 0, -mainCamera.transform.position.z)).x - platformWidth / 2f;
Vector3 spawnPosition = Vector3.zero;
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
spawnPosition.x += Random.Range(-xRange, xRange);
// now simply clamp the x position so the platform
// doesn't go over the screen boarders
spawnPosition.x = Mathf.Clamp(spawnPosition.x, minX, maxX);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
}
}
You could of course also instead of only clamping the position actively counter balance the range and shift it left or right accordingly like e.g.
Vector3 spawnPosition = Vector3.zero;
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
var min = -5f;
var max = 5f;
var distanceToLeftBorder = spawnPosition.x - minX;
var distanceToRightBorder = maxX - spawnPosition.x;
if(distanceToLeftBorder < xRange)
{
min += xRange - distanceToLeftBorder;
max += xRange - distanceToLeftBorder;
}
else if(distanceToRightBorder < xRange)
{
min -= xRange - distanceToRightBorder;
max -= xRange - distanceToRightBorder;
}
spawnPosition.x += Random.Range(min, max);
// now simply clamp the x position so the platform
// doesn't go over the screen boarders
spawnPosition.x = Mathf.Clamp(spawnPosition.x, minX, maxX);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
To your final issue the platforms being too close I would take yet another different approach:
Instead of randomly place at offset +/- 5 rather separate the sign from the distance and do
var sign = Mathf.Sign(Random.value);
var distance = Random.Range(platformWidth + SPACING, maxDistance);
spawnPosition.x += distance * sign;
which would make sure that the next platform is at minimum the platform width plus an optional spacing (both positive!) and not any closer to the current platform.
I would then instead of clamping simply check if you would hit the border and switch the sign like
var sign = Mathf.Sign(Random.value);
var distance = Random.Range(platformWidth + SPACING, maxDistance);
if(spawnPosition.x + distance * sign < minX || spawnPosition.x + distance * sign > maxX)
{
sign *= -1;
}
spawnPosition.x += distance * sign;

How to rotate around an object without using unity's built-in functions?

i want to rotate a cube around a 1x1 pipe with arrow keys. (left and right).
The problem is i cannot use built-in functions which sets transform's position and location directly. (Such as transform.lookAt, transform.Rotate or transform.RotateAround). Because I need the vector values of rotation's euler and position for multiple stuff before i modify the value of the transform i want to rotate.
I tried different techniques but no luck so far.
I tried using sin-cos for rotating but could not figure out how to make it work for both rotation and position.
_timer += Time.deltaTime * _larvaSpeed;
float x = -Mathf.Cos(_timer) * distanceBetweenCenter;
float y = Mathf.Sin(_timer) * distanceBetweenCenter;
Here is what i want to achieve. By pressing right or left, move and rotate the object around the pipe.
The result i want. (If i pressed right arrow key a litte bit).
I would appreciate any help. Thank you!
here is the solution using circle mathematics and I strongly recommended not use it, it's just to understand the circular move using circle equation as #FaTaLL ask in the comments
Circle equation...
(x1 - x2)^2 + (y1 - y2)^2 = r^2
x1, y1 is the cube position
x2, y2 is the pipe position
r is the distance between cube and pipe;
using UnityEngine;
public class Rotating : MonoBehaviour
{
public GameObject pipe;
public float Delta;
Vector3 nextpos;
bool compareY;
bool next;
int switchx;
float storeVarAxis;
float x, y, r;
private void Start()
{
next = true;
switchx = 1;
compareY = true;
x = transform.position.x - pipe.transform.position.x;
y = transform.position.y - pipe.transform.position.y;
storeVarAxis = y;
r = Mathf.Sqrt(x * x + y * y);
}
private void Update()
{
if (next)
{
if (compareY == true)
{
y -= Delta * Time.deltaTime;
if (y <= -storeVarAxis)
{
y = -storeVarAxis;
compareY = false;
switchx = -1;
}
}
else
{
y += Delta * Time.deltaTime;
if (y >= storeVarAxis)
{
y = storeVarAxis;
compareY = true;
switchx = 1;
}
}
float v = r * r - y * y;
x = Mathf.Sqrt(Mathf.Abs(v));
nextpos = new Vector3(pipe.transform.position.x + x * switchx, pipe.transform.position.y + y, transform.position.z);
next = false;
}
transform.position = Vector3.MoveTowards(transform.position, nextpos, 1f * Time.deltaTime);
if(Vector3.Distance(transform.position, nextpos) < .05) transform.position = nextpos;
if (transform.position.x.Equals(nextpos.x) && transform.position.y.Equals(nextpos.y)) next = true;
}
}
well, the recommended way is using this simple script
using UnityEngine;
public class Rotating : MonoBehaviour
{
public float speed;
public GameObject pipe;
float r, angle;
Vector3 startpos;
private void Start()
{
r = Mathf.Abs(transform.position.y - pipe.transform.position.y);
angle = 0;
transform.position = pipe.transform.position;
startpos = transform.position;
}
void Update()
{
angle = angle + speed * Time.deltaTime;
transform.rotation = Quaternion.EulerAngles(0,0, angle);
transform.position = startpos + (transform.rotation * new Vector3(r, 0, 0));
}
}
I think Quaternion * Vector3 is what you are looking for. Luckily the box's rotation in its own local coordinates is the same rotation you need to apply to the box's position.
public float speed; //how fast to rotate
public float radius; //radius of the cylinder
public float angle; //angle around it
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
angle = angle + speed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.RightArrow))
{
angle = angle - speed * Time.deltaTime;
}
//figure out the rotation (from euler angles i guess??)
var quat = Quaternion.EulerAngles(new Vector3(0, angle, 0));
//ok uh what is the box position?? lets just multiply
var unrotated_position = new Vector3(radius, 0, 0);
var rotated_position = quat * unrotated_position;
this.transform.position = rotated_position;
//oh yea and also rotate the box in its own local coordinates
this.transform.rotation = quat;
}

How do I make a pool cue rotate around the cue ball?

I want the pool cue to rotate around the cue ball as the player drags the mouse, I've played some pool games on the internet and they all seem to work this way. This will eventually be a browser game.
This game isn't mine, but it has the basic mechanic that I want (I don't care about the lines that are displayed, I just want the rotate and hit mechanic) https://www.crazygames.com/game/8-ball-billiards-classic
I have tried some orbiting scripts so far, none of them work.
I've tried some scripts that make objects orbit based on time hoping to make it so it orbits with the mouse dragging instead of time. I also can't get the cue to constantly face the cue ball.
This code has gotten me the closest.
public int vertexCount = 40;
public float lineWidth = 0.2f;
public float radius;
public bool circleFillscreen;
//circle variables
static float timeCounter = 0;
float width;
float height;
private LineRenderer lineRenderer;
private void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
SetupCircle();
}
void Update()
{
timeCounter += Time.deltaTime;
float x = Mathf.Cos(timeCounter);
float y = 0;
float z = Mathf.Sin(timeCounter);
}
private void SetupCircle()
{
lineRenderer.widthMultiplier = lineWidth;
if (circleFillscreen)
{
radius = Vector3.Distance(Camera.main.ScreenToWorldPoint(new Vector3(0f, Camera.main.pixelRect.yMax, 0f)),
Camera.main.ScreenToWorldPoint(new Vector3(0f, Camera.main.pixelRect.yMin, 0f))) * 0.5f - lineWidth;
}
float deltaTheta = (2f * Mathf.PI) / vertexCount;
float theta = 0F;
lineRenderer.positionCount = -vertexCount;
for (int i = 0; i < lineRenderer.positionCount; i++)
{
Vector3 pos = new Vector3(radius * Mathf.Cos(theta), radius * Mathf.Sin(theta), 0f);
lineRenderer.SetPosition(i, pos);
theta += deltaTheta;
}
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
float deltaTheta = (2f * Mathf.PI) / vertexCount;
float theta = 0f;
Vector3 oldPos = Vector3.zero;
for (int i = 0; i < vertexCount + 1; i++)
{
Vector3 pos = new Vector3(radius * Mathf.Cos(theta), 0F,radius * Mathf.Sin(theta));
Gizmos.DrawLine(oldPos, transform.position + pos);
oldPos = transform.position + pos;
theta += deltaTheta;
}
}
#endif
}
Not really getting any error messages, code "works" but doesn't work.

How to calculate the trajectory of bullet in Unity2D?

I'm doing the 2D game with bullets moving in parabola, but before I shoot the projectile I want to be able to calculate its trajectory.
float velocity = Mathf.Min(Mathf.Max(distance, 1), 2.5f);
float cos = Mathf.Cos( (gameObject.transform.localEulerAngles.z + 90) * Mathf.Deg2Rad);
float sin = Mathf.Sin( (gameObject.transform.localEulerAngles.z + 90) * Mathf.Deg2Rad);
newGO.GetComponent<Rigidbody2D>().AddForce(velocity * new Vector3(cos, sin))
This code adds the rigidbody2d to my bullet (newGO). I try to count a point after 1 sec of flying using it:
Debug.Log(new Vector3(source.transform.position.x + velocity * 1 * cos, source.transform.position.y + velocity * sin * 1 + Physics2D.gravity.y * 0.5f * 1));
Unfortunately it doesn't return the correct result. What should I do?
Thanks for help and sorry for my English.
I think being a projectile you should use Rigidbody2D.velocity to set the initial velocity of the projectile rather than Rigidbody2D.AddForce. (Although the documentation does not recommend)
This simple script predicts where the attached object will be in a time determined by the variable "timeLimit" and moves a "target" gameobject to that point.
public class Velocity : MonoBehaviour {
public GameObject target;
private float HoriInitialSpeed = 5;
private float VertInitialSpeed = 5;
private float timeLimit = 2;
private float timeElapsed=0;
void Start ()
{
float HoriPredict = HoriInitialSpeed * timeLimit;
float VertiPredict = VertInitialSpeed* timeLimit + (0.5f * Physics2D.gravity.y * timeLimit * timeLimit);
target.transform.Translate(HoriPredict, VertiPredict,0);
GetComponent<Rigidbody2D>().velocity = new Vector2(HoriInitialSpeed, VertInitialSpeed);
}
void Update () {
timeElapsed += Time.deltaTime;
if(timeElapsed >= timeLimit)
{
//stop the bullet
GetComponent<Rigidbody2D>().velocity = new Vector2(0, 0);
GetComponent<Rigidbody2D>().gravityScale = 0;
}
}
}
Note: the bullet and the target must be in the same point inicially

Unity2D Smooth Rotation using Slerp

I'm making a patrol script for a game object. I want the object to rotate smoothly and slowly to face it's patrol target.
Unfortunately, the object snaps to it's new location.
I've asked on unity forums and can't get an answer.
How can I get the rotation to be smooth and slow?
Here's my code.
public Transform[] patrol;
public int Currentpoint;
public float moveSpeed;
public Vector3 john = new Vector3(0,1,0);
public Vector3 targetLocation;
void Start()
{
}
void Update()
{
if (transform.position == patrol [Currentpoint].position) {
Currentpoint++;
if (Currentpoint >= patrol.Length) {
Currentpoint = 0;
}
targetLocation = patrol [Currentpoint].position;
Vector3 targetDir = targetLocation - transform.position;
float angle = Mathf.Atan2 (targetDir.y, targetDir.x) * Mathf.Rad2Deg;
transform.localRotation = Quaternion.SlerpUnclamped (transform.localRotation, Quaternion.AngleAxis (angle, Vector3.forward), Time.deltaTime * 3);
Debug.Log (Currentpoint);
}
transform.position = Vector3.MoveTowards (transform.position, patrol [Currentpoint].position, moveSpeed * Time.deltaTime);
}
public static Quaternion Slerp(Quaternion a, Quaternion b, float t); The parameter t is clamped to the range [0, 1]
your times 3 is throwing it out so it is not between 0-1 ... I believe
If you like some mathematics then I have a solution for you.
You want to rotate object around another, like Earth and Sun. May be some other solutions may available but I would do it through LookAt and parametric equation of circle.
x = r * cos(theta) + displacementX
y = r * sin(theta) + displacementY
where r is radius, distance in your case
displacementX and displacementY are the distance from origin. If both (displacementX and displacementY) is 0 then it will rotate around origin (0,0). In other words displacementX and displacementY is the origin of rotation.
In Object(Earth) script, do it as follow
public Transform _sun;
float _theta = 0;
void Start ()
{
StartCoroutine ("ChangeAngle");
}
void Update ()
{
transform.LookAt (_sun);
float newX = (5 * Mathf.Cos (_theta)) + _sun.position.x;
float newY = (5 * Mathf.Sin (_theta)) + _sun.position.y;
transform.position = new Vector3 (newX, newY, _sun.position.z);
}
IEnumerator ChangeAngle ()
{
while (true) {
yield return new WaitForSeconds (0.01f);
_theta += 0.1f;
if (_theta >= 360)
_theta = 0;
}
}
You can further play with it
I found an answer,
It turns out that placing the rotational instructions within the if statement was the problem. I converted the rotation to a function, then placed the function above the general patrol movement in the same loop segment.
I don't know why it worked.
Thanks to everyone for their help.

Categories