0
\$\begingroup\$

In this case the 4 walls and objects I spawn inside the 'wall area' are all cubes. This situation could change to where the walls will be spheres and the other objects can be cylinders or cubes (its not limited to cubes only).

What I'm trying to do is to spawn the objects in random positions but within the 'wall area'. The problem is when some cubes (objects) are spawning too close to the walls, part of the cubes will inside the area and some will be outside.

In the screenshot example on the right, a cube is going out of the wall area. In this case the walls area is sized according to the terrain (500x500) but it could be any size like 45x23 or 100x100.

In the script below I'm getting the 4 walls objects in array.

walls = GameObject.FindGameObjectsWithTag("Wall");

Now I wonder how can I use a ray cast to detect if each cube is spawning too close to one of the walls and if so then re-position the cube randomly, over and over again until it's inside the walls area. I'm not sure if using ray cast is the best way but that's what I had in mind.

Part of cube is out of area

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class SpawnObjects : MonoBehaviour
{
    public int numberOfObjects;
    public GameObject objectToPlace;
    public Vector3 newObjectsSize;
    public float spawnSpeed = 0.1f;

    private int wallsLengthX;
    private int wallsLengthZ;
    private int wallsPosX;
    private int wallsPosZ;
    private int currentObjects;
    private GameObject[] walls;
    private List<GameObject> objects = new List<GameObject>();
    private GameObject spawnedObjects;

    void Start()
    {
        var wi = GetComponent<Walls>();
        wallsLengthX = (int)wi.lengthX;
        wallsLengthZ = (int)wi.lengthZ;
        wallsPosX = (int)wi.wallsStartPosition.x;
        wallsPosZ = (int)wi.wallsStartPosition.z;

        walls = GameObject.FindGameObjectsWithTag("Wall");
        spawnedObjects = GameObject.Find("Spawned Objects");

        StartCoroutine(Spawn()); 
    }

    IEnumerator Spawn()
    { 
        for (int i = 0; i < numberOfObjects; i++)
        {
            GameObject newObject = (GameObject)Instantiate(objectToPlace);
            newObject.transform.localScale = GenerateRandomScale(newObject);
            newObject.transform.localPosition = new Vector3(GenerateRandomPositions(newObject).x , GenerateRandomPositions(newObject).y + newObject.transform.localScale.y / 2.0f, GenerateRandomPositions(newObject).z);
            newObject.name = "Spawned Object";
            newObject.tag = "Spawned Object";
            newObject.transform.parent = spawnedObjects.transform;

            objects.Add(newObject);
            yield return new WaitForSeconds(spawnSpeed);
            currentObjects += 1;
        }

        var wp = GetComponent<WayPoints>();
        wp.FindMyObjects();
    }

    private Vector3 GenerateRandomScale(GameObject newObject)
    {
        Vector3 newScale = new Vector3(UnityEngine.Random.Range(5, 100), UnityEngine.Random.Range(5, 100), UnityEngine.Random.Range(5, 10));

        return newScale;
    }

    private Vector3 GenerateRandomPositions(GameObject newObject)
    {
        float paddingX = Mathf.Clamp(newObject.transform.localScale.x, 0, wallsLengthX) / 2f;
        float paddingZ = Mathf.Clamp(newObject.transform.localScale.z, 0, wallsLengthZ) / 2f;
        float originX = wallsPosX + paddingX - wallsLengthX / 2f;
        float originZ = wallsPosZ + paddingZ - wallsLengthZ / 2f;
        float posx = UnityEngine.Random.Range(originX, originX + wallsLengthX - paddingX);
        float posz = UnityEngine.Random.Range(originZ, originZ + wallsLengthZ - paddingZ);
        float posy = Terrain.activeTerrain.SampleHeight(new Vector3(posx, 0, posz));

        return new Vector3(posx, posy, posz);
    }
}
\$\endgroup\$
1
\$\begingroup\$
using UnityEngine;
using System.Collections;

/// <summary>
/// Spawns a prefab randomly throughout the volume of a Unity transform. Attach to a Unity cube to visually scale or rotate. For best results disable collider and renderer.
/// </summary>
public class SpawningArea : MonoBehaviour {

public GameObject ObjectToSpawn;   


    void Spawn() {           


            // Random position within this transform
            Vector3 rndPosWithin;
            rndPosWithin = new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), Random.Range(-1f, 1f));
            rndPosWithin = transform.TransformPoint(rndPosWithin * .5f);
            Instantiate(ObjectToSpawn, rndPosWithin, transform.rotation);      

    }
}

This will allow you difene a spawning volume that you can adjust. Hope this helps.

\$\endgroup\$
0
\$\begingroup\$

There are two solutions that come to my mind for this one:

First one is to just do the math and check whether the object you're about to create is going to be outside the area you want it to be in or not.

The second one is to put invisible "wall" around the area you want your actual walls to stay inside. When you spawn a wall which collides with your invisible one (you will need colliders on both walls) then you just remove that wall from the game. This second method is how I generated "planets" for my game, so that they weren't too close to players or under some UI elements.

There are probably other methods for this, but these are the ones I could think of.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.