I want to create a terrain made out of randomly generated objects. I have created couple of prefabs and made a script that has all 3 prefabs in an array, and spawns them randomly.
The problem that I have is that the objects (of different sizes) are being spawned in 1 unit distance, so basically over each other, instead of right after each other.
The second problem is that I am not sure how to limit the spawning to a decent number. I have created a script that destroys the objects once I pass them, but in the code, at the moment, they are spawning infinitely, too fast (I know the code isn't the nicest, I am still practicing).
I did look for possible solutions or similar problems, but haven't found anything that can help me.
Here is the code from my script:
using UnityEngine;
using System.Collections;
public class SpawnScript : MonoBehaviour {
public GameObject[] obj;
public Vector3 pos = new Vector3(-8,-4,0);
public float size = 1.0f;
private Vector3 dir = Vector3.right;
void Start () {
Spawn();
}
void Spawn() {
for (int i = 0; i<30; i++)
{
Instantiate (obj [Random.Range (0, obj.Length)], pos, Quaternion.identity);
pos += dir * size;
}
Invoke ("Spawn", 2);
}
}
I hope I can get any advises, references or help.
They are being spawned at 1 unit distance because you defined size as 1.0f. Take the real size of the object instead:
GameObject go = obj[Random.Range(0, obj.Length)];
Vector3 size = go.renderer.bounds.size;
pos += new Vector3(dir.x * size.x, dir.y * size.y, dir.z * size.z);
Instantiate(go, pos, Quaternion.identity);
See: Find Size of GameObject
Related
My game characters keep spawning at one position and they get squashed together i want them to spawn one after another from the door of the shop and after they recieve their product they leave from the door again This is a picture of how they look when they spawn since i'm putting them in a group spawn:
This is the script i'm using to spawn them:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterSpawn : MonoBehaviour
{
[SerializeField]
private float spawnRadius = 7, time = 1.5f;
public GameObject[] enemies;
public Transform groupTransform;
void Start()
{
StartCoroutine(SpawnAnEnemy());
}
IEnumerator SpawnAnEnemy()
{
Vector2 spawnPos = GameObject.Find("Bengal").transform.position;
Instantiate(enemies[Random.Range(0, enemies.Length)], spawnPos, Quaternion.identity, groupTransform);
yield return new WaitForSeconds(time);
StartCoroutine(SpawnAnEnemy());
}
}
EDIT: They still spawn together even after trying the solution bellow check this picture:
I think you will need two scripts for this. One to handle selecting enemy to spawn and another to handle where to place the enemy.
This will pick a random enemy prefab:
public GameObject[] enemies; //Add to enemy prefabs
int spawnEnemy; //Random enemy index
void Start()
{
Pick();
}
void Pick()
{
spawnEnemy = Random.Range(0, enemies.Length);
GameObject Spawn = Instantiate(enemies[spawnEnemy], transform.position, Quaternion.identity);
This will place the enemy prefab in a random position. To avoid overlapping, a float "spaceBetweenEnemies" is introduced.
//Add to your enemy manager. Create an empty game object if you don't have one
//This will place the enemies in random position
public GameObject enemyDistribution;
GameObject spawn;
public int numberOfEnemiesToSpawn;
public float spaceBetweenEnemies = 2; //This is the space between enemies. To avoid overlapping
public float xPos = 5; //x axis
public float yPos = 5; //y axis
void Start()
{
for (int i = 0; i < numberOfEnemiesToSpawn; i++)
{
SpreadItem();
}
}
void SpreadItem()
{
Vector3 ranPos = new Vector3(Random.Range(-xPos, xPos) + spaceBetweenEnemies, Random.Range(-yPos, yPos) + spaceBetweenEnemies, 0f) + transform.position; //z axis is set to zero
spawn = Instantiate(enemyDistribution, ranPos, Quaternion.identity);
}
This may not prevent the enemies from spawning outside the screen parameters. I recommend writing a simple script to attract the enemies towards the centre of the screen of to the player. You can use an "OnTriggerEnter2d" method to check for collision between enemies and to move away from each other. There are good tutorials on "Flocking" on YouTube. You enemies will behave like swarms. This might come in handy for your game.
Hope this helps.
Add a float value called spaceBetweenObjects and add it to the spawnPos
//start with and small value and gradually increase or decrease it until you are happy with the result.
public float spaceBetweenObjects = 1;
Instantiate(enemies[Random.Range(0, enemies.Length)], spawnPos + spaceBetweenObjects, Quaternion.identity, groupTransform);
I have a spawner object. Every time a gameobject is spawned, I want that object to move randomly (wandering). The problem in my script is that the gameobject movement is very random (jittery). How can I solve this?
void Start ()
{
InvokeRepeating("SpawnNPC", Spawntime, Spawntime);
}
// Update is called once per frame
void Update () {
population = GameObject.FindGameObjectsWithTag("NPCobject");
for (int i = 0; i < population.Length; i++)
{
getNewPosition();
if (population[i].transform.position != pos)
{
population[i].transform.position = Vector3.MoveTowards(population[i].transform.position, pos, .1f);
}
}
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
I made the New randomize vector in different method, because I plan to change it with pathfinder function and make it in different thread/task.
You are choosing a new direction every single frame. That will always be very jittery. You wan't to only change direction after, at least, a small interval. Here is a link to one way to do that from Unity's website. https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
What about using Navigation? As you said wandering, I thought it would give you a nice result and also make your code simple.
The following screenshot is a sample with Navigation. The moving game objects are also changing their direction nicely, although it cannot be seen in the sample because the game object is capsule...
Ground game object in the sample program has NavMesh. See here to build NavMesh.
Agent game object has NavMeshAgent Component. See here to set it up.
Th Behaviour class below is for Agent game object.
using UnityEngine;
using UnityEngine.AI;
public class NavAgentBehaviour : MonoBehaviour {
public Transform[] Destinations { get; set; }
// Use this for initialization
void Start ()
{
InvokeRepeating("changeDestination", 0f, 3f);
}
void changeDestination()
{
var agent = GetComponent<NavMeshAgent>();
agent.destination = Destinations[Random.Range(0, Destinations.Length)].position;
}
}
The next Behaviour class is just for spawning the Agent and setting up the destinations. On Unity, set it to whatever game object in the scene, and allocate game objects to the fields.
using UnityEngine;
public class GameBehaviour : MonoBehaviour {
public GameObject Agent;
public Transform SpawnPoint;
public Transform Destination1;
public Transform Destination2;
public Transform Destination3;
// Use this for initialization
void Start()
{
Agent.SetActive(false);
InvokeRepeating("Spawn", 0f, 2f);
}
void Spawn() {
var newAgent = Instantiate(Agent);
newAgent.GetComponent<NavAgentBehaviour>().Destinations = new[] { Destination1, Destination2, Destination3 };
newAgent.transform.position = SpawnPoint.position;
newAgent.SetActive(true);
}
}
Increase the number of destination, to make the moves look more random. By the way, the destinations do not need to be specified by game objects, which is only for making it easy to see the sample program's behaviour.
The source of the jitteriness comes from the fact that you are updating the position to move every frame so your objects never have a consistent location to move to. I would instead suggest attaching a new script to each of your objects that individually handles their movement. In that script you could do something like the following, which has a delay to keep the target position for more than 1 frame.
float delaytimer;
Vector3 pos;
void Start () {
getNewPosition(); // get initial targetpos
}
void Update () {
delaytimer += Time.deltaTime;
if (delaytimer > 1) // time to wait
{
getNewPosition(); //get new position every 1 second
delaytimer = 0f; // reset timer
}
transform.position = Vector3.MoveTowards(transform.position, pos, .1f);
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
You are changing the direction they are moving in every frame, thats what is causing the jitter.
You could wait a few moments before you change the direction, perhaps something like this.
// Outside update
float betweenChanges = 2;
float lastChange = 0;
// Inside update
if(Time.realtimeSinceStartup > lastChange)
{
// Change directions
// ...
lastChange = Time.realTimeSinceStart + betweenChanges;
}
You could also solve this by using InvokeRepeating or a Coroutine.
If you dont want all the NPC's to change direction at the same time and still plan on controlling every NPC from the same class like in your example, you should perhaps add a timer for each NPC instance instead and use that to decide when to change its direction.
A better idea would be to let each NPC have its own Movement-script.
I'm working on a small game project for school in UNITY, specifically a clone of a snake game called Rattler Race. I'm pretty much a complete beginner with the engine, hence why I'm struggling a bit. The game we're suppose to make has to have 30 unique levels that have ascending complexity, something like this: Final level
My main problem at the moment is spawning food for the snake so it doesn't overlap with any inner walls or the borders.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnFood : MonoBehaviour
{
public GameObject FoodPrefab;
public Transform BorderTop;
public Transform BorderBottom;
public Transform BorderLeft;
public Transform BorderRight;
public Transform Obstacle;
Vector2 pos;
// Use this for initialization
void Start ()
{
for (int i = 0; i < 10; i++) {
Spawn();
}
}
void Spawn()
{
pos.x = Random.Range(BorderLeft.position.x + 5,BorderRight.position.x - 5);
pos.y = Random.Range(BorderBottom.position.y + 5, BorderTop.position.y - 5);
Instantiate(FoodPrefab, pos, Quaternion.identity);
}
// Update is called once per frame
void Update ()
{
}
}
The code is very simple ofcourse because at the moment the game field is empty with no obstacles:
Current state
However my problem is if I was to scale up that tiny red obstacle, like so:
Big Obstacle
The food would spawn behind it(every level has 10 food objects) and be impossible to get. My idea was to build levels off of one object(the "Obstacle") and its copies, I don't really know if that idea is sound yet but I wanted to try.
If theres any way or any method to spawn food objects in locations so they don't overlap with the obstacle, like to check if the space is already occupied or a method that checks if a would be food object intersects with an existing obstacle, I would be very grateful if someone teaches me, because I'm really lost at the moment.
Assuming your food is exactly one unit long, you could have a coroutine which would check if there is something around that food gameObject, if there isn't anything, you can spawn it.
public LayerMask layerMask; //Additional variable so you can control which layer you don't want to be detected in raycast
void Spawn(){
StartCoroutine(GameObjectExists());
}
IEnumerator GameObjectExists(){
do{
yield return null;
pos.x = Random.Range(BorderLeft.position.x + 5,BorderRight.position.x - 5);
pos.y = Random.Range(BorderBottom.position.y + 5, BorderTop.position.y - 5);
}while(Physics2D.OverlapCircle(new Vector2(pos), 1f, layerMask); //1f is the radius of your food gameObject.
Instantiate(FoodPrefab, pos, Quaternion.identity);
yield return null;
}
So basically I have a spawning script (2D game) that spawns in a enemy after spawn delay. I've also adding an array of enemy prefabs so that the SpawnerScript will spawn in a random enemy. However I'm having some problems with spawning in the prefabs in a random position within a transform (a.k.a: a 3D cube). You see My spawner isn't spawning in anything when I play the game, I made sure that I have included the size of how many enemies I want to spawn in. and made sure I have attach my prefabs. I also made sure that my unity project file hasn't got a bug. Maybe my code is wrong, i'm not sure.
My SpawnerScript:
public float RateOfSpawn = 1;
public float spawnTime = 2;
public GameObject[] enemy;
void Start(){
InvokeRepeating ("addEnemy", spawnTime, spawnTime);
}
void Spawn () {
// Random position within this transform- 3Dcube
var x1 = transform.position.x - GetComponent<Renderer> ().bounds.size.x / 2;
var x2 = transform.position.x + GetComponent<Renderer> ().bounds.size.x / 2;
var spawnPoint = new Vector2 (Random.Range (x1, x2), transform.position.y);
int enemyIndex = enemy.Length;
Instantiate (enemy[enemyIndex],spawnPoint,Quaternion.identity);
}
}
Thank you :)
You have to call the proper function to make it work.
void Start()
{
InvokeRepeating ("Spawn", spawnTime, spawnTime);
}
You have to call "Spawn" instead of "addEnemy".
Also change Instanatiate for proper instantiation.
Instantiate (enemy[Random.Range (0, enemyIndex)], spawnPoint, Quaternion.identity);
enemy[Random.Range (0, enemyIndex)] helps you choose random enemy from array of enemies' index 0 to enemyIndex-1.
The cause of exception is putting the length of array as index. Max Index limit is always length-1.
I am not sure how to approach this problem or whether there are any built in Unity functions that can help with this problem so any advice is appreciated.
Here is an image that'll help describe what I want to do:
I want to spawn Game Objects around a given point within the limits of a set radius. However their position in this radius should be randomly selected. This position should have the same Y axis as the origin point (which is on the ground). The next main problem is that each object should not clash and overlap another game object and should not enter their personal space (the orange circle).
My code so far isn't great:
public class Spawner : MonoBehaviour {
public int spawnRadius = 30; // not sure how large this is yet..
public int agentRadius = 5; // agent's personal space
public GameObject agent; // added in Unity GUI
Vector3 originPoint;
void CreateGroup() {
GameObject spawner = GetRandomSpawnPoint ();
originPoint = spawner.gameObject.transform.position;
for (int i = 0; i < groupSize; i++) {
CreateAgent ();
}
}
public void CreateAgent() {
float directionFacing = Random.Range (0f, 360f);
// need to pick a random position around originPoint but inside spawnRadius
// must not be too close to another agent inside spawnRadius
Instantiate (agent, originPoint, Quaternion.Euler (new Vector3 (0f, directionFacing, 0f)));
}
}
Thank you for any advice you can offer!
For personal space you can use colliders to avoid overlapping.
For spawning in circle you can use Random.insideUnitSphere. You can modify your method as,
public void CreateAgent() {
float directionFacing = Random.Range (0f, 360f);
// need to pick a random position around originPoint but inside spawnRadius
// must not be too close to another agent inside spawnRadius
Vector3 point = (Random.insideUnitSphere * spawnRadius) + originPoint;
Instantiate (agent, point, Quaternion.Euler (new Vector3 (0f, directionFacing, 0f)));
}
Hope this helps you.
For spawning the object within the circle, you could define the radius of your spawn circle and just add random numbers between -radius and radius to the position of the spawner like this:
float radius = 5f;
originPoint = spawner.gameObject.transform.position;
originPoint.x += Random.Range(-radius, radius);
originPoint.z += Random.Range(-radius, radius);
For detecting if the spawn point is to close to another game object, how about checking the distance between them like so:
if(Vector3.Distance(originPoint, otherGameObject.transform.position < personalSpaceRadius)
{
// pick new origin Point
}
I'm not that skilled in unity3d, so sry for maybe not the best answer^^
Also:
To check which gameobjects are in the spawn area in the first place, you could use the Physics.OverlapSphere Function defined here:
http://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html