Im making a terror game, and i want to spawn little collectibles in my scene, my code works fine, but they repeat the world location in every instantiate:
using UnityEngine;
public class objectivesGeneration : MonoBehaviour
{
GameObject[] objSpawn;
GameObject objActual;
public GameObject obj;
int index;
void Start()
{
objSpawn = GameObject.FindGameObjectsWithTag("spawnObj");
}
void Update()
{
if (Input.GetKeyDown(KeyCode.I))
{
generateNewObj();
}
}
public void generateNewObj()
{
index = Random.Range(0, objSpawn.Length);
objActual = objSpawn[index];
createObj();
}
public void createObj()
{
Instantiate(obj, objActual.transform.position, objActual.transform.rotation);
}
}
Can somebody help me?
Your question can be understood in two ways. Leoverload gave the answer to the repeating position.
In case you do not want to repeat the same type of object (so all objects are unique), then do the following:
Turn objSpawn into a List<GameObject> variable and in the start() function, add all instances from the array that's returned from FindGameObjectsWithTag to it.
Turn objSpawn.Length into objSpawn.Count (which does the same but for lists)
In that same function add: objSpawn.Remove(objActual) at the end.
If those objects are destroyed at some point and you want to create new instances of destroyed objects, ensure that in their Destroy event, you Add(gameObject) to the list again.
Am giving you instructions instead of just code so you can learn to do this yourself in the future.
Also I have the feeling you need to learn how arguments\parameters work. Then you can ommit that objActual variable and instead pass the object to createObj(GameObject theObj) directly.
You are spawning the object in the same position objActual.transform.position
You should set the limits of the spawn with 2 variables, for example:
public float startx;
public float endx;
public float starty;
public float endy;
Then you can easily Instantiate randomly in this positions:
float posX = Random.Range(startx, endx);
float posy = Random.Range(starty, endy);
Instantiate(obj, new Vector3(posx, posy, transform.position.z, Quaternion.Identity);
And so you have a random spawn!
Thanks to AlexGeorg for the answer,
this was how my final code was
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class objectivesGeneration : MonoBehaviour
{
public GameObject recipient;
public List<GameObject> objSpawns = new List<GameObject>();
int spawnSelected;
void Start()
{
//Set all the objs with the tag spawnLocation inside of the list
foreach (GameObject OBJ in GameObject.FindGameObjectsWithTag("spawnLocation"))
{
objSpawns.Add(OBJ);
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.I))
{
//Check if there are more possible spawns
if (objSpawns.Count > 0)
generateNewObj(recipient);
}
}
public void generateNewObj(GameObject frasco)
{
//Get a random number of the list which contains one obj
spawnSelected = Random.Range(0, objSpawns.Count);
//Instantiate the GameObject in the location of the random selected object
Instantiate(frasco, objSpawns[spawnSelected].transform.position, objSpawns[spawnSelected].transform.rotation);
//Delete that possible location from the list
objSpawns.RemoveAt(spawnSelected);
}
}
Related
I am recently new to c# and I need some help.
Essentially I have two scripts, one for spawning objects and one for moving an object along a path. I need a way to combine these two mechanics so that when a new object is instantiated it automatically joins the path and follows it.
The path is made using iTween.
![The objects the scripts are attached to] (https://i.stack.imgur.com/QPQn2.png)
I've tried changing the variable m_PlayerObj to the cube prefab and I've tried adding the Path script to the instantiation script but nothing seems to work.
The scripts attached do nkt include these attempts I made as I wanted to make the code very clear.
Spawner script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(cubeprefab, transform.position, Quaternion.identity);
}
}
}
Path script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Path : MonoBehaviour
{
public GameObject m_PlayerObj;
public Transform[] positionPoint;
[Range(0, 1)]
public float value;
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
float tempTime;
// Update is called once per frame
void Update()
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
As stated above, any help would be greatly appreciated as I am really stuck on this conceot and since I am new to Unity I really can’t see a way around it // how to fix it.
Instead of only storing the player object in the Path script, store a collection of objects. That way, the path can keep track of more than one object.
//public GameObject m_PlayerObj; // Get rid of this
public List<GameObject> followers; // Add this
Then, in your Update loop, you can loop through all of them.
void Update()
{
for (var i = 0; i < followers.Length; ++i)
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
}
Of course, now, you need to make sure you pass your cube instance to the Path GameObject when you spawn it, so the path knows about the cube follower. That means your spawner also needs to know about the path.
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var cubeInst = Instantiate(cubeprefab, transform.position, Quaternion.identity);
path.followers.Add(cubeInst);
}
}
}
Now a new problem is going to be that each object is going to be at the same position on the path, because the path only stores one value - a better term might be progress. So if they're all the same, like the cube, you won't be able to tell because they'd overlap.
So you have to decide what you want to do instead. Evenly space them? You could do that with some math. Or have them all start from the beginning and keep track of their progress separately? Then you'd need to store progress for each of them. A better place to do that is probably on the cube object, which means you need to add a new script to your cube prefab:
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}
And, you need to start referring to the prefab by this script, instead of just GameObject:
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
//...
Finally, you need to make sure to use the individual progress for each path follower, rather than a single progress value like your old Path script did:
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
Putting it all together (separate files of course, with their own includes!):
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
public Transform[] positionPoint;
//[Range(0, 1)]
//public float value; // Don't need this anymore either
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
// Update is called once per frame
void Update()
{
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}
I have 2 scripts
one that check if an area is free
one that spawn an object if the area is free
this is to prevent overlapping
checker script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpaceCheck : MonoBehaviour
{
public bool isFree = true;
public GameObject RoomParent;
void OnCollisionEnter(Collision col)
{
isFree = false;
}
void Start()
{
if(isFree == true)
{
RoomParent.SetActive(true);
}
}
void Update()
{
}
}
spawner script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomSpawner : MonoBehaviour
{
public List<Transform> Spawners;
public List<GameObject> Rooms;
int choosenSpawner = 0;
public GameObject SpaceCheck;
void Start()
{
choosenSpawner = Random.Range(0, Spawners.Count);
GameObject manager = GameObject.Find("RoomsManager");
RoomsManager managerProperty = manager.GetComponent<RoomsManager>();
GameObject Space = SpaceCheck;
SpaceCheck spaceProperty = Space.GetComponent<SpaceCheck>();
if (managerProperty.maxRoom > managerProperty.RoomCount && spaceProperty.isFree == true)
{
Instantiate(Rooms[Random.Range(0, Rooms.Count)], Spawners[choosenSpawner].position, Spawners[choosenSpawner].rotation);
RoomsManager.instance.AddRoomCount();
}
}
void Update()
{
}
}
Problem is that they still overlaps
here is the video of the problem: https://www.youtube.com/watch?v=mUjMUSNhDcQ
This question is really vague, but here is an answer that assumes derHugo is correct, and you are asking how to pick random positions without duplicates:
You are storing your rooms inside a list, so all you have to do is check to see if the newly generated position is the same as any of the existing game objects in the list.
So first generate random floats in a new vector x, y, and z, and then check if that position is in the list of previously generated positions. If the list does not contain a gameobject with that position, then its unique, use it and add it to the list. If it isn't unique, throw it out and generate a new one until it is unique.
You'll want to check that the newly generated position is far enough away from the previously existing ones that they don't overlap, taking the size of the object into consideration.
I need help instantiating a variable through a string. I need this for placing the right building based on the building number variable so I can make my code shorter and more readable than just doing many if statements.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class main : MonoBehaviour
{
public Text cashText;
public static float cash = 0f;
public float changeCash = 0f;
public GameObject building1;
public GameObject building2;
public GameObject building3;
// Start is called before the first frame update
void Start()
{
cash = changeCash;
}
// Update is called once per frame
void Update()
{
cashText.text = "cash: " + cash.ToString();
Application.targetFrameRate = 60;
if (Input.GetKeyDown("r"))
{
Destroy(GameObject.FindGameObjectWithTag("building"));
GameObject[] projectiles = GameObject.FindGameObjectsWithTag("projectile");
foreach(GameObject projectilesfind in projectiles)
{
GameObject.Destroy(projectilesfind);
}
Instantiate("building" + menu.buildingNumber);
}
}
}
As said what you are asking for is possible, it is called "reflection" and I would strongly recommend to NOT use this. Therefore I won't even tell you how to do it here. Use the link on own risk ;)
Array + index
I would rather use e.g. an array like
public GameObject[] buildings;
and then later do
// assuming 1-based User input but c# arrays are 0-based
Instantiate(buildings[menu.buildingNumber - 1]);
or if menu.buildingNumber is a string itself then do e.g.
if(int.TryParse(menu.buildingNumber, out var index))
{
Instantiate(buildings[index - 1]);
}
Dictionary + Key
Or depending on your Input you could also use a Dictionary like
public GameObject building1;
public GameObject building2;
public GameObject building3;
private readonly Dictionary<string, GameObject> buildings = new Dictionary<string, GameObject>();
private void Awake()
{
buildings[nameof(building1)] = building1;
buildings[nameof(building2)] = building2;
buildings[nameof(building3)] = building3;
}
then later you can do
if(buildings .TryGetValue("building" + menu.buildingNumber, out var building))
{
Instantiate(building);
}
Note though that this is pretty error prone and you will have to rename your fields / the string in TryGetValue according to your needs all the time
I picked up unity and C# a month ago so I'm still a noobie.
So far I managed to build a simple space-based arcade shooter (i have my ship, i have a way to shoot bullets). What I'm trying to achieve is a way to keep the script that takes my keyboard input to shoot separate from the possible bullet types.
The way my bullet types are currently implemented is by having a gameobject for each with its own scripts for a) taking keyboard input and b) instancing a prefab with different properties to shoot. Currently i have 2 shooting modes, and a separate script lets me swap between them with the spacebar by enabling disabling the gameobjects. An example of the scripts I'm using for one bullet type:
Script for instantiating bullet. One method simply shoots every time a button is pressed, the other "charges" an array of bullets, accompanied in the second script by a "growing aura" signifing the power increase. These two methods have the same name across different bullet classes, but are implemented differently.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletA_Basic : MonoBehaviour
{
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce = 20f;
public float altCooldown = 1f;
public float fireRate = 1f;
public float altFirePowerMultiplier = 1f;
private void Update()
{
}
public void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab, firingPoint);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firingPoint.up * bulletForce, ForceMode2D.Impulse);
}
public void SpecialShoot(int n)
{
StartCoroutine(Special(n));
}
public IEnumerator Special(int n)
{
for (int i = 0; i < n; i++)
{
Shoot();
yield return new WaitForSeconds(0.1f);
}
}
}
Script for taking keyboard input
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting_A : MonoBehaviour //This needs to be copied across all firing types
{
private BulletA_Basic bulletScript; //Change the class to make new projectile types with different firing modes
public Vector3 scaleChange;
private GameObject auraInstance;
private float timePassed = 0f;
private float timePassedMain = 0f;
public float timeToDetonation = 3f;
private void Start()
{
bulletScript = GetComponent<BulletA_Basic>();
}
// Update is called once per frame
void Update()
{
bool isFiring = Input.GetButtonDown("Main Cannon");
bool alternateFire = Input.GetButton("Main Cannon");
timePassedMain += Time.deltaTime;
if (isFiring && timePassedMain > bulletScript.fireRate)
{
bulletScript.Shoot();
timePassedMain = 0;
}
if (alternateFire)
{
timePassed += Time.deltaTime;
if (!auraInstance && timePassed >= bulletScript.altCooldown)
{
auraInstance = Instantiate(bulletScript.aura, bulletScript.chargingPoint);
}
if (alternateFire && auraInstance && timePassed < timeToDetonation)
{
Charge();
//Will need to add shader here
}
else if (timePassed >= timeToDetonation)
{
Destroy(auraInstance);
timePassed = 0;
}
}
else
{
if (auraInstance)
{
Destroy(auraInstance);
int powerAltFire = (int)(bulletScript.altFirePowerMultiplier * (Mathf.Pow(2 , timePassed))); //Equation returns a number of projectiles based on how long charge was held
bulletScript.SpecialShoot(powerAltFire);
}
timePassed = 0;
}
}
void Charge()
{
auraInstance.transform.localScale += scaleChange;
}
}
The key here is the bulletScript field.
Basically i'd like to make the second script general so that i don't have to implement it in a different way and copy-pasting it again and again for each type of bullet I'm going to make, and changing the bulletScript field type each time.
I tried doing it with interfaces but I'm not sure how to implement it in the general script since I need to access each field of the subclasses, which have each their own references (like bulletPrefab, or aura). In general i feel interfaces are not well integrated into unity but that might just be me.
I also tried with delegates, but i had similar problems. I simply changed the type of bulletScript to my delegate type (ShootingDelegate bulletScript), and wrote this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public delegate void ShootDelegate();
public delegate void SpecialShootDelegate(int n);
public class ShootingDelegate : MonoBehaviour
{
public ShootDelegate delShoot;
public SpecialShootDelegate delSpecialShoot;
private int weaponIndex;
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce;
public float altCooldown;
public float fireRate;
public float altFirePowerMultiplier;
// Start is called before the first frame update
void Start()
{
WeaponSwap weapon = GetComponent<WeaponSwap>();
weaponIndex = weapon.weaponIndex;
switch (weaponIndex)
{
case 1:
BulletB_Fan bulletB = GetComponent<BulletB_Fan>();
delShoot = bulletB.Shoot;
delSpecialShoot = bulletB.SpecialShoot;
bulletPrefab = bulletB.bulletPrefab;
aura = bulletB.aura;
firingPoint = bulletB.firingPoint;
chargingPoint = bulletB.chargingPoint;
bulletForce = bulletB.bulletForce;
altCooldown = bulletB.altCooldown;
fireRate = bulletB.fireRate;
altFirePowerMultiplier = bulletB.altFirePowerMultiplier;
break;
case 0:
BulletA_Basic bullet = GetComponent<BulletA_Basic>();
delShoot = bullet.Shoot;
delSpecialShoot = bullet.SpecialShoot;
bulletPrefab = bullet.bulletPrefab;
aura = bullet.aura;
firingPoint = bullet.firingPoint;
chargingPoint = bullet.chargingPoint;
bulletForce = bullet.bulletForce;
altCooldown = bullet.altCooldown;
fireRate = bullet.fireRate;
altFirePowerMultiplier = bullet.altFirePowerMultiplier;
break;
}
}
// Update is called once per frame
void Update()
{
}
}
This is the error it throws:
ArgumentException: Value does not fall within the expected range.
ShootingDelegate.Start () (at Assets/Scripts/ShootingDelegate.cs:54)
which corresponds to this line
delShoot = bullet.Shoot;
I don't really care if a solution employs either interfaces or delegates, those were just things I tried. Any thoughts?
I created a sample just for you.
Here I am creating BulletType parent class for all bullet types. And atting some variable and method for all bullets.
public class BulletType : MonoBehaviour
{
// Protected is less stric than private, more stric than public
// Protected variables only accesible to this class and its child.
protected string name;
protected int bulletDamage;
protected int bulletSpeed;
protected virtual void Start()
{
}
// Virtual means you can override this method in child classes
protected virtual void Damage() { }
public virtual void PlaySound() { }
protected virtual void ShowEffect() { }
}
Now I will add child class and inherit from parent BulletType class. Everything write in comment lines.
// This class inherits from BulletType class.
public class FireBullet : BulletType
{
// If you want to completely ignore parent class variable to by adding new keyword.
new int bulletSpeed = 3;
protected override void Start()
{
// when overriding a method automatically adds this method.
// base means parent class. with base you can access parent methods.
base.Start();
// If you remove base method, parent method won't be called in this class.
// Here I accessed parent class variable and set its value.
// This doesn't effect parent or other child classes. That's the beauty.
name = "Fire Bullet";
}
void StopSound()
{
}
protected override void PlaySound()
{
// Sound played from parent.
base.PlaySound();
// You can add your own variable and methods in parent method.
StopSound();
}
}
// FireBullet inherited from BulletType, and LavaBullet inherited from FireBullet.
// You can do this as much as you want.
public class LavaBullet : FireBullet
{
protected override void PlaySound()
{
// Here base will be FireBullet
base.PlaySound();
}
}
public class IceBullet : BulletType
{
// Add as much as you thing.
}
And for using BulletType script in your player just add this line
public class Player
{
public BulletType currentBulletType;
// You can get child from main class.
FireBullet fireBullet = currentBulletType.GetComponent<FireBullet>();
// Now you can access and use child class methods.
fireBullet.PlaySound();
}
and when you want to change bullet type, just assign new bullettype. Because all bullet type inherit from this class. You can assign every bullet type.
I don't know if it's the answer but,
delShoot = bulletB.Shoot;
delShoot is a reference to a script.
bulletB.Shoot is a function.
I don't think you can make one equal to the other.
If I understand your question, you want to make one script for two type of bullets shoot. You can create a scipts Shoot.cs who is instantiate with a value (1 for the default fire 2 for the second fire) and other scrits defaultFire.cs / secondFire.cs with they own properties.
Like this you'll juste instantiate once your Bullet like this :
public void Start(){
case 1 : Shoot shoot = new Shoot(1)
case 2 : Shoot shoot = new Shoot(2)
}
Hope this help a little..
Hey guys I am beginner in unity. I'm having a problem on instantiating a prefab. The problem in my code is after I click the accept button in my quest panel or the quest has been accepted, the object will spawn too many (endless loop i don't know what is called). I successfully spawn objects to their corresponding points. How can I spawn multiple prefab once? I am not sure if it's to be called on update method.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnFlower : MonoBehaviour {
public GameObject flowers;
public Transform[] spawnPoints;
public float spawnTime = 1.5f;
public int maxCount;
void Update() {
//InvokeRepeating ("SpawnFlowers", spawnTime, spawnTime);
if(QuestManager.questManager.RequestAcceptedQuest(1)) {
SpawnFlowers ();
}
}
public void SpawnFlowers() {
List<Transform> freeSpawnPoints = new List<Transform>(spawnPoints);
for (int i = 0; i < maxCount; i++) {
if (freeSpawnPoints.Count <= 0)
return;
int spawnIndex = Random.Range (0, freeSpawnPoints.Count);
Transform pos = freeSpawnPoints [spawnIndex];
freeSpawnPoints.RemoveAt (spawnIndex);
Instantiate (flowers, pos.position, pos.rotation);
}
}
}