Objects appeared in each other - c#

I have a spawn obstacles for a player, and when a game begins, obstacles appear in each other. I tried to solve this problem through the while and physics 2d cycle, but when I started the game, the Unity crashed.
And I also tried through Raycast2D to check when the beam collides with some kind of collider, and change its position. But ray a small didn't always work.
First way
while (!Physics.CheckBox(pos, BlotPref[1].transform.localScale))
{
pos = new Vector2(Random.Range(-1f, 1f), YPosSetter());
}
Second way
while (hit.collider == null)
{
transform.position = new Vector2(Random.Range(-1f, 1f), YPosSetter());
}
Spawner
private void SpawnerPaper()
{
for (int i = 0; i < PlaningSpawn; i++)
{
if (rndType <= _dbPaperSpawn)
{
var blot = Instantiate(BlotPref[Random.Range(0, BlotPref.Length)], new Vector2(Random.Range(-1f, 1f), Random.Range(30f, 70f)), Quaternion.identity);
blot.transform.SetParent(paper.transform);
}
}
}

one solution is to test collider if obstacles is near the random position with Physics2D.OverlapCircleAll and using Layers:
of course obstacles have collider...
private void SpwanerPaper()
{
for (int i = 0; i < PlaningSpawn; i++)
{
if (rndType <= _dbPaperSpawn)
{
while (true)
{
var position = new Vector2(Random.Range(-1f, 1f), Random.Range(30f, 70f));
// i have choosen radius 1f, you could increase it if needed
Collider2D[] colliders = Physics2D.OverlapCircleAll(position, 1f, obstacleLayer);
if (colliders.Length == 0)
{
//so no obstacles present, spawn here
var blot = Instantiate(BlotPref[Random.Range(0, BlotPref.Length)],
position, Quaternion.identity);
blot.transform.SetParent(paper.transform);
break;// get out while loop
}
// so obstacles within 1m of the player loop again and give new random position
}
}
}
}
Following the situation you could use too Physics2D.CircleCastAll
If you dont know Layers, you have a tutorial youtube HERE

Related

Checking collisions in near objects for random object spawning in Unity 5

I have almost finished my first game in Unity, which is a simple maze with characters collecting different coins. As it was a hassle to manually put all the different coins in the terrain, I decided to try and make a random generator for the coins.
The script is largely working, but the problem is that it's spawning objects most of the time inside walls. So I tried to add rigid bodies to coins, but this is messing with the coins position as the coins have a script for rotation. I also have the coins collision triggered because they perform a certain action when colliding with the character.
So after some research I saw that I need to check in my generator script for a near object collision and somehow change the spawn position of the coin if it's true. I'm trying to figure out how I can do that. My script is attached to my terrain and it looks like this:
using System.Collections;
using CustomArrayExtensions;
using UnityEngine;
public class ObjectSpawner : MonoBehaviour
{
[SerializeField] public GameObject[] letters;
GameObject selectedObject;
private int amount = 0;
public int limit;
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
selectedObject = letters.GetRandom();
while (amount < limit)
{
Instantiate(selectedObject, new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f)), Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "Fox_Main")
{
Debug.Log("Collision Detected");
}
}
}
So i tried both answers and they didn't help me.Coins and other objects is still spawning close/inside the walls.My boxes have box collider and rigidbody with all positions/rotations locked(i need it that way so that i can destroy it later without moving it with the character),walls have box collider.So i tried to modify your solutions following Unity Documentation.I failed too.Here is what i have done so far.
See here
using System.Collections;
using UnityEngine;
public class RandomCrates : MonoBehaviour
{
public GameObject[] crates;
private GameObject selectedCrate;
public int limit = 0;
public float x, y, z;
public float forX_From,forX_To,forZ_From,forZ_To;
private int mask = 1 << 7;
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
for (int i = 0; i < crates.Length; i++)
{
for (int j = 0; j < limit; j++)
{
Vector3 freePos = GetFreePosition();
Instantiate(crates[i], freePos, Quaternion.identity);
yield return new WaitForSeconds(0.0f);
}
}
}
Vector3 GetFreePosition()
{
Vector3 position;
Collider[] collisions = new Collider[1];
do
{
position = new Vector3(Random.Range(forX_From, forX_To), 10.0f, Random.Range(forZ_From, forZ_To));
} while (Physics.OverlapBoxNonAlloc(position, new Vector3(x, y, z), collisions, Quaternion.identity, mask) > 0);
return position;
}
}
Unity provides a simple way to detect collisions in specific position with Physics.OverlapSphere:
public class ObjectSpawner : MonoBehaviour
{
[SerializeField] public GameObject[] letters;
GameObject selectedObject;
private int amount = 0;
public int limit;
private float radius = 2f; //Radius of object to spawn
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
selectedObject = letters[Random.Range(0, letters.Length)];
while (amount < limit)
{
Vector3 spawnPos = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
//Check collisions
if (DetectCollisions(spawnPos) > 0)
continue;
Instantiate(selectedObject, spawnPos, Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
private int DetectCollisions(Vector3 pos)
{
Collider[] hitColliders = Physics.OverlapSphere(pos, radius);
return hitColliders.Length;
}
}
In this way, if there is a collision in spawn position, the coin will not be spawned.
There are different ways of approaching this problem and given more information one could come up with a smarter approach. However, to provide you with a simple solution to your problem: You could just pick random positions until you find a "free spot".
Assuming your coins to have a size of 1, you could do something like:
IEnumerator SpawnObjects()
{
selectedObject = letters.GetRandom();
while(amount < limit)
{
Vector3 freePos = GetFreePosition();
Instantiate(selectedObject, freePos), Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
Vector3 GetFreePosition()
{
Vector3 position;
Collider[] collisions = new Collider[1];
do
{
position = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
}
while(Physics.OverlapSphereNonAlloc(position, 1f, collisions) > 0);
return position;
}
Please note that Physics.OverlapSphereNonAlloc() can also accept a LayerMask for filtering collisions based on layers. You might want to look into this, to prevent detecting collision with your terrain, which may well keep the do-while loop from exiting and freeze Unity.

Material determination of submeshes through raycasting in Unity3d

I'm attempting to place props randomly on a mesh. The mesh has 2 materials and I'd like to only place the props on one of the materials. I'm currently doing this through raycasting, determining the material at the point hit and if the material is correct I place a prop at that point. I'm having an issue where the material is being determined incorrectly and I'm not certain why. Code is below.
using UnityEngine;
public class BoundingBoxTry : MonoBehaviour {
// Use this for initialization
public MeshFilter meshFilter;
private Renderer rend;
private Mesh mesh;
private Bounds bnds;
private float nextX, nextZ, maxX, minX, maxZ, minZ;
public GameObject rayOrigin;
public GameObject propPreFab;
private MeshCollider thisCollider;
void Start () {
thisCollider = GetComponentInChildren<MeshCollider>();
rend = GetComponentInChildren<Renderer>();
mesh = GetComponentInChildren<MeshFilter>().sharedMesh;
bnds = rend.bounds;
}
void Update () {
nextX = Random.Range(0, bnds.size.x);
nextZ = Random.Range(0, bnds.size.z);
nextX = nextX-bnds.size.x/2;
nextZ = nextZ-bnds.size.z/2;
nextX = bnds.center.x-nextX;
nextZ = bnds.center.z-nextZ;
RaycastHit hit = new RaycastHit();
Vector3 randomCoord = new Vector3(nextX, 0, nextZ);
rayOrigin.transform.position = new Vector3(nextX, 100, nextZ);
bool didHit = Physics.Raycast(rayOrigin.transform.position, -new Vector3(0, 1, 0), out hit);
int vertCounter = 0;
if(didHit && hit.collider == thisCollider){
for (int sMC = 0; sMC < mesh.subMeshCount; sMC++)
{
int[] subMesh = mesh.GetTriangles(sMC);
vertCounter = vertCounter + subMesh.Length;
if(hit.triangleIndex < vertCounter){
if(rend.sharedMaterials[sMC] == rayOrigin.GetComponent<Renderer>().sharedMaterial){
GameObject prop = Instantiate(propPreFab, hit.point, Quaternion.identity);
}
else{
}
}
}
}
}
}
I've also attached a picture to demonstrate the behavior I'm seeing.
Of course, as soon as I ask and sleep on the problem I'm able to figure it out. Arman's link was helpful as well. The issue I was seeing was caused by my not fully understanding the data I was getting from the triangleIndex call.
When calling triangleIndex you receive a vertex index back. To get the triangle hit from that vertex you must multiply the vertex index by 3 and then validate which subMesh was struck by the raycast.

Spawn number of GameObjects randomly around a circle at a minimum distance from each other

Currently I have two balls that spawn randomly around a circle, with a minimum distance of 0.3 between them so they do not collide. However, I want the player to set the amount of balls to spawn, and I need these balls to spawn randomly but with a minimum amount of distance from each other.
With my code, any ball amount that is greater than 2 may have a chance of two balls spawning on top of each other, since I only reference the position of the last spawned ball with spawnConstant.
When I tried to figure it out with arrays, I ran into the same problem, having the position only refer to one position in the array. Attached is the current code:
IEnumerator SpawnBalls () {
Vector3 spawnConstant = new Vector3 ();
Vector3 spawnPosition = new Vector3 ();
for (int i = 0; i < ballNumber; i++) {
if (i == 0) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("wolo");
spawnConstant = spawnPosition;
} else if (i > 0) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
while (Vector3.Distance (spawnPosition, spawnConstant) <= 0.3f) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
}
spawnConstant = spawnPosition;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("yolo");
}
}
yield return null;
}
How can I make each ball refer to the positions of all the previous ones and spawn a minimum distance away from them?
IEnumerator SpawnBalls () {
Vector3[] ballArray = new Vector3[ballNumber];
Vector3 spawnPosition = new Vector3 ();
int index = 0;
for (int i = 0; i < ballNumber; i++) {
// Create first ball and store coordinates into our ballArray at index 0
if (i == 0) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("wolo");
ballArray[index] = spawnPosition;
// Else we already have one ball
} else if (i > 0) {
// Create new spawn coordinates
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
// Loop through indicies of array that have been filled
for( int k = 0; k <= index; k++ ) {
// Check that each value in array meets distance requirement
if(Vector3.Distance (spawnPosition, ballArray[k]) <= 0.3f) {
// If not, create new coordinates and reset k
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
k = 0;
}
// Update array
ballArray[index++] = spawnPosition;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("yolo");
}
}
yield return null;
}

How to show vertices on a cube when selected in Unity (during runtime)?

I'm trying to write a script that makes a cube turn blue and display the vertices of the cube on it when selected during runtime. So basically when clicked, it will show all the vertices and then allow a user to select different vertices of the cube.
This is what i have so far, essentially only turning the cube blue. How can I display vertices of the cube as spheres? I want to be able to select these vertices later.
using UnityEngine;
using System.Collections;
public class Cube : MonoBehaviour {
void OnMouseDown() {
Renderer rend = GetComponent<Renderer>();
rend.material.color = Color.blue;
//insert method to display vertices
}
}
Since both the cube and spheres are primitives:
you find the vertices like this (assuming cube is a GameObject):
Vector3[] vertices = cube.GetComponent<MeshFilter>().mesh.vertices;
And then to create your spheres (need using System.Linq):
GameObject[] spheres = vertices.Select(vert =>
{
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere)
sphere.transform.position = vert;
return sphere;
})
.ToArray();
UPDATE: Okay i see your problem with too many vertices now, and this is how i would do it, and i would personally make threshold be the radius of your spheres, so that once the spheres begin overlapping they just become one:
float threshold = 0.1f;
public void CreateSpheres()
{
List<GameObject> spheres = new List<GameObject>();
foreach (Vector3 vert in vertices)
{
if (spheres.Any(sph => (sph.transform.position - vert) < threshold)) continue;
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere)
sphere.transform.position = vert;
spheres.Add(sphere);
}
}
Couldn't get the Select Function to work as show in one of the previous example. The coordinates were found using GetComponent<MeshFilter>.mesh.vertices; as described in the other answer.
void OnMouseDown() {
Renderer rend = GetComponent<Renderer>();
rend.material.color = Color.blue;
Vector3[] vertices = GetComponent<MeshFilter>().mesh.vertices;
Vector3[] verts = removeDuplicates(vertices);
drawSpheres(verts);
}
The vertices array contains 24 elements for the reason shown here. So the function removeDuplicates was written to get rid of the duplicate vertices. This function is shown below:
Vector3[] removeDuplicates(Vector3[] dupArray) {
Vector3[] newArray = new Vector3[8]; //change 8 to a variable dependent on shape
bool isDup = false;
int newArrayIndex = 0;
for (int i = 0; i < dupArray.Length; i++) {
for (int j = 0; j < newArray.Length; j++) {
if (dupArray[i] == newArray[j]) {
isDup = true;
}
}
if (!isDup) {
newArray[newArrayIndex] = dupArray[i];
newArrayIndex++;
isDup = false;
}
}
return newArray;
}
Lastly the Spheres were drawn using the new verts Array using the drawSpheres function shown here:
void drawSpheres(Vector3[] verts) {
GameObject[] Spheres = new GameObject[verts.Length];
for (int i = 0; i < verts.Length; i++) {
Spheres[i] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
Spheres[i].transform.position = verts[i];
Spheres[i].transform.localScale -= new Vector3(0.8F, 0.8F, 0.8F);
}
}

WaitForSeconds in Unity3D

I have an IENumerator in Unity wich activates a floor object every 0.70f-0.82f, so the gaps between the floor objects are randomly generated. The problem is, that when someone gets an FPS drop, the gaps are going to be huge and they player has no chance to reach the next floor. Can I make the "yield WaitForSeconds" fps-independent?
This is the code:
IEnumerator spawnFloors () {
while (playerControll.isAlive) {
for (int i = 0; i < floors.Length; i++) {
spawnWait = Random.Range (0.70f, 0.82f); // 0.65f, 0.85f; aktuell = 0.70f, 0.82f;
Vector2 spawnPosition = new Vector2 (transform.position.x, transform.position.y);
Quaternion spawnRotation = Quaternion.identity;
for (int a = 0; a < floors.Length; a++) {
int randomFloor = Random.Range (0, 9);
if (!floors [randomFloor].activeInHierarchy) {
floors [randomFloor].SetActive (true);
break;
}
}
//Instantiate(floors[Random.Range (0, 3)], spawnPosition, spawnRotation);
yield return new WaitForSeconds (spawnWait);
}
}
}`
Have you considered using FixedUpdate() instead?
In case you are wondering what's the difference, unity has a tutorial for it.
It's simply used for frame-independent updates in your game.

Categories

Resources