Unity 2D C# - Parenting not working question for my game - c#

So I am trying to make a top-down tycoon game where a truck gives you crates and you sell the crates for money. I have a script for the trucks and it's working just fine except for the first part. So I coded it that when the trucks are spawned 2 crates spawn and are parented to the truck game object, and the issue is that when I run the game, the objects spawn and all, but they aren't parented to the trucks, I have 6 trucks in the scene and only one gets their crate parented. Can someone help?
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CarScript : MonoBehaviour
{
private float speed = 0.01f;
private Vector3 dropoffLocation;
private bool hasCrates = true;
private float dropCratesCooldown = 500f;
GameObject cratess;
private float crateUpgrade = 1;
public GameObject crateUpgrade1;
public GameObject crateUpgrade2;
public GameObject crateUpgrade3;
public GameObject crateUpgrade4;
private void Start()
{
dropoffLocation = new Vector3(3.5f, -7f, 0);
crateUpgrade1 = GameObject.Find("Boxes In Truck 1");
crateUpgrade2 = GameObject.Find("Boxes In Truck 2");
crateUpgrade3 = GameObject.Find("Boxes In Truck 3");
crateUpgrade4 = GameObject.Find("Boxes In Truck max");
if (crateUpgrade == 1)
{
Instantiate(crateUpgrade1);
crateUpgrade1.transform.parent = transform;
//crateUpgrade1.transform.position = new Vector3(0, 0, 0);
}
}
// Update is called once per frame
void Update()
{
if (transform.position.x > 40)
{
Destroy(gameObject);
}
if (transform.position.x > dropoffLocation.x && hasCrates)
{
dropCratesCooldown--;
if (dropCratesCooldown < 0)
{
cratess = transform.GetChild(0).gameObject;
Destroy(cratess);
hasCrates = false;
}
transform.Translate(0, 0, 0);
}
else
{
transform.Translate(speed, 0, 0);
}
}
}
btw unity doesn't even show me that its an error
Thanks in advance!

Instantiate returns the instantiated game object. That's what you want to assign as a child of the truck. The way you're doing it now, you just keep assigning and reassigning crateUpgrade1's parent. That's the reason that only one of your trucks get a crate parented to it.
This is how it should be:
GameObject crate = Instantiate(crateUpgrade1);
crate.transform.parent = transform;
One other thing; the standard way to instantiate new objects is to create a prefab asset in the project, then assign a reference to it to CarScript, and then instantiate instances of that prefab. So the crateUpgrade1 = GameObject.Find("Boxes In Truck 1"); part is a little weird.
This is how it's usually done
public class CarScript : MonoBehaviour
{
// Assign this to your "crate" prefab in the Unity Inspector
[SerializeField] private GameObject cratePrefab;
private void Start()
{
if (crateUpgrade == 1)
{
GameObject crate = Instantiate(this.cratePrefab);
crate.transform.parent = transform;
}
}
}

Related

Unity3D: How can I destroy instances of an obstacle prefab once the player passes them on the z-axis in an endless runner?

I've tried setting different names for the instantiations and Destroy(this), Destroy(this.gameObject), and just Destroy(gameObject) but none of them seem to work...
Here is the code for the instantiations attached to an empty game object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
public GameObject[] obstaclePatterns;
public Transform player;
private float timeBtwSpawn;
public float startTimeBtwSpawn;
public float obstacleSpawnDistance;
public float obstacleSpDMin;
public float obstacleSpDMax;
public float decreaseTime;
public float minTime = 0.65f;
public float obstacleHeightMin = 10f;
public float obstacleHeightMax = 15f;
// makes obstacles randomly ahead of player
private void FixedUpdate() {
if (timeBtwSpawn <= 0) {
obstacleSpawnDistance = Random.Range(obstacleSpDMin, obstacleSpDMax);
int rand = Random.Range(0, obstaclePatterns.Length);
Instantiate(obstaclePatterns[rand], new Vector3(obstaclePatterns[rand].transform.position.x, Random.Range(obstacleHeightMin, obstacleHeightMax), player.position.z + obstacleSpawnDistance), Quaternion.identity);
rand = Random.Range(0, obstaclePatterns.Length);
Instantiate(obstaclePatterns[rand], new Vector3(obstaclePatterns[rand].transform.position.x, Random.Range(obstacleHeightMin, obstacleHeightMax), player.position.z + obstacleSpawnDistance), Quaternion.identity);
rand = Random.Range(0, obstaclePatterns.Length);
Instantiate(obstaclePatterns[rand], new Vector3(obstaclePatterns[rand].transform.position.x, Random.Range(obstacleHeightMin, obstacleHeightMax), player.position.z + obstacleSpawnDistance), Quaternion.identity);
timeBtwSpawn = startTimeBtwSpawn;
if (startTimeBtwSpawn > minTime) {
startTimeBtwSpawn -= decreaseTime;
}
}
else {
timeBtwSpawn -= Time.deltaTime;
}
}
}
And here is the code for the destroy function attached to the obstacle prefab:
using UnityEngine;
public class ObjectDestruction : MonoBehaviour {
public Transform player;
// Update is called once per frame
void FixedUpdate()
{
if (transform.position.z < player.position.z) {
Destroy(gameObject);
Debug.Log("test");
}
}
}
The Debug.Log works fine, but the console keeps producing this error: MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object. Spawner.FixedUpdate () (at Assets/Scripts/Spawner.cs:26)
EDIT: Destroy(this) seems to get rid of the error message, but it does not destroy the objects.
Thank you so much in advance!
I am not familiar with unity3d, but I suspect that when you destroy an object you also need to remove references to it:
Currently, you have an array of gameObjects stored in obstaclePatterns, so when you destroy the object it becomes null, but remains in the obstaclePatterns array and your code keeps trying to use it.
There are two ways to fix this:
When you destroy a GameObject you could remove it from the obstaclePatterns array, for example int x = IndexOf(obstaclePatterns, gameObject); obstaclePatterns[x] = null; or
Make your program check if the GameObject in obstaclePatterns is null, and if so, then do not use it, for example if(obstaclePatterns[x] != null){do stuff here}.
Alternately:
Are you sure that you want to destroy the game object? Wouldn't it be better to just move the object ahead of the player again and re-use it?

Spawning a GameObjects have same script but different behavior?

I have a project in my collage for traffic car simulation in unity, and the one of the project requirements is to let the user enter the number of the cars, and each one of them should driving like AI_Driver, and i wrote a script to spawning cars with Instantiate function , i spawned 1000 car and when i am spawning a car, i am making some changes like (the position of the spawned car on the y axis),
the problem is some of the cars has been edited correctly exactly what i want but, the others not applying the changes with the position
.
and here is the script:
public class VehicleSpawner : MonoBehaviour
{
public GameObject[] vehiclePrefabs;
public int vehiclesToSpawn;
public bool navON = false;
public bool aStarON = true;
void Start()
{
StartCoroutine(SpawnMany(0));
}
IEnumerator SpawnMany(int count)
{
while (count < vehiclesToSpawn)
{
GameObject obj = Instantiate(vehiclePrefabs[1]);
GameObject[] spawningPints = GameObject.FindGameObjectsWithTag("spawningPoint");
Transform child = spawningPints[Random.Range(0, spawningPints.Length)].transform;
WaypointNavigator nav = obj.GetComponent<WaypointNavigator>();
nav.spawner = this;
nav.priority = Random.Range(0, 2 * vehiclesToSpawn);
if (!aStarON)
{
nav.currentWaypoint = child.GetComponent<Waypoint>();
nav.navON = navON;
}
else
{
Transform dest = transform.GetChild(Random.Range(0, transform.childCount - 1));
nav.start = child.GetComponent<Waypoint>();
nav.end = dest.GetComponent<Waypoint>();
nav.priority = Random.Range(0, 10000);
nav.aStarON = aStarON;
}
// change in the position
obj.transform.position = new Vector3(
child.GetComponent<Waypoint>().GetPosition().x,
child.GetComponent<Waypoint>().GetPosition().y + 0.5f,
child.GetComponent<Waypoint>().GetPosition().z
);
yield return new WaitForSeconds(0.05f);
count++;
}
}
}
Please any one can help??
Cache spawningPints. Never use Find, because it just runs foreach gameObject in the hierarchy.
Try first to change position, then add Nav component (it might cache position and teleport car back after you change position throw transform).

Why is my code generating two gameObjects(instead of one) at a time when one gameObject out of the Camera view is destroyed?

I am writing Scripts for flappy bird type game on my own(best way to learn unity scripting).
To optimize perfromance i want to generate only 5 Obstacle gameObjects(ObjectsOnScreen) at a time.So i wrote a code to destroy gameObject when its 16 units away from the player.And also to generate only one new Obstacle when there are less than 5 ObjectsOnScreen.
But my code is destroying one obsatcle and at the same time generating two objects (instead of one).So ultimately the objects will increase over time intead of remaining constant.
Please tell me mistakes in my code.thanks in advance.
//GameManager script::
public class GameManager : MonoBehaviour
{
static public int ObjectsOnScreen = 0;
public float poleDistance;
public int gapWidth;
static public bool reset=false;
float x = 8;
int gap;
public GameObject obstacle;
void FixedUpdate()
{
if(reset)
{
UnityEngine.SceneManagement.SceneManager.LoadScene(0);
}
if(ObjectsOnScreen<5)
{
gap = Random.Range(-6, 6);
GenerateObstacle(x, gap);
x += poleDistance;
Debug.Log("gap = " + gap);
ObjectsOnScreen++;
}
}
void GenerateObstacle(float x , float gap)
{
GameObject Top = Instantiate(obstacle, new Vector3(x, 8, 0) , Quaternion.identity);
GameObject Bottom = Instantiate(obstacle, new Vector3(x, -8, 0), Quaternion.identity);
Top.transform.localScale = new Vector3(1, (8 - gap)-gapWidth, 1);
Bottom.transform.localScale = new Vector3(1, -(8 + gap)+gapWidth, 1);
}
}
//Obstacle Script::
public class ObstacleScript : MonoBehaviour
{
GameObject player;
void Awake()
{
player = GameObject.Find("Player");
}
void FixedUpdate()
{
if ((transform.position.x - player.transform.position.x) <= -16)
{
GameManager.ObjectsOnScreen--;
Destroy(gameObject);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
PlayerScript player = collision.gameObject.GetComponent<PlayerScript>();
if(player!=null)
{
GameManager.reset = true;
}
}
}
You're calling instantiate twice in the GenerateObstacle function which will instantiate two objects (instead of one) of course...
GameObject Top = Instantiate(obstacle, new Vector3(x, 8, 0) , Quaternion.identity);
GameObject Bottom = Instantiate(obstacle, new Vector3(x, -8, 0), Quaternion.identity);
But then you're doing ObjectsOnScreen++, which only increments by one. Then later the two obstacles delete themselves, which effectively decrements twice.
You should probably increment by two ObjectsOnScreen += 2 to get an accurate count.

Unity How to limit Spawner?

I am working on a game like 2 cars. So there will be two lines and I have used two object spawner which is going to spawn two shapes, i,e Circle and Square. So when player collides with circle score should update. And when Square falls player is supposed to avoid it by going to another lane. But what is the problem is something both the spawner spawns square simultaneously or with small gap. So player is not able to escape. Any solution for this. Well I guess it does not help much but here is my script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Instantiter : MonoBehaviour {
public GameObject[] gameobject;
public float SpawnDelay= 3f;
private GameObject objectkeeper;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
float Spawntime = SpawnDelay * Time.deltaTime; // 1 *1/60
if (Random.value < Spawntime) {
Spawn ();
}
}
void Spawn(){
int number = Random.Range (0, 2);// creating random number between 0 and 1
objectkeeper = Instantiate (gameobject [number], this.transform.position, Quaternion.identity) as GameObject;
objectkeeper.transform.parent = this.transform;
}
void OnDrawGizmos(){
Gizmos.DrawWireSphere (this.transform.position, 0.5f);
}
}
Thank you for your time and consideration
Try this,
It will only spawn one object at a time between the min-max period
It does not cleanup old objects
It allows for more than 2 prefabs
I tried keeping to your code format as much as possible
Disclaimer : I do not currently have a visual studio/mono develop open (in a boring meeting) so i have not tested this :]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Instantiter : MonoBehaviour {
public GameObject[] prefabs;
// Adding a min-max allows full control of spawning behaviour without editing code again.
// for a fixed time use the same value
public float MinimumSpawnDelay = 3f;
public float MaximumSpawnDelay = 6f;
private GameObject spawnedObject;
// Made this static so it retains it's value across all instances of this script.
// If you want each Instantiter object to function on it's own, remove the static keyword
private static float nextSpawnTime;
// Use this for initialization
void Start () {
// Artificial delay so we do not spawn an object directly at startup
SetNextSpawnTime();
}
// Update is called once per frame
void Update () {
if (Time.time >= nextSpawnTime) {
Spawn ();
}
}
void Spawn(){
// allows you to add more objects to the prefabs array without changing code.
var prefabToSpawn = prefabs[Ranom.Range(0, prefabs.Length)];
spawnedObject = Instantiate (prefabToSpawn, transform.position, Quaternion.identity);
spawnedObject.transform.parent = transform;
SetNextSpawnTime();
}
void OnDrawGizmos() {
Gizmos.DrawWireSphere (transform.position, 0.5f);
}
void SetNextSpawnTime(){
// a simple variable to hold when we should spawn another object, efficient.
nextSpawnTime = Time.time + Random.Range(MinimumSpawnDelay, MaximumSpawnDelay);
}
}
Try using a static variable -
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Instantiter : MonoBehaviour {
public GameObject[] gameobject;
public float SpawnDelay= 3f;
private GameObject objectkeeper;
// Static variable shared across all instances of this script
public static float nextSpawn = 0f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
// check if SpawnDelay duration has passed
if (Time.time >= nextSpawn) {
// Now spawn after ramdom time
float Spawntime = SpawnDelay * Time.deltaTime; // 1 *1/60
if (Random.value < Spawntime) {
Spawn ();
}
}
}
void Spawn(){
int number = Random.Range (0, 2);// creating random number between 0 and 1
objectkeeper = Instantiate (gameobject [number], this.transform.position, Quaternion.identity) as GameObject;
objectkeeper.transform.parent = this.transform;
// Set the nextSpawn time to after SpawnDelay Duration
nextSpawn = Time.time + SpawnDelay;
}
void OnDrawGizmos(){
Gizmos.DrawWireSphere (this.transform.position, 0.5f);
}
}

Changing an object change all the instantiated objects

I know that the topic of the question has been already asked and some answer have been given to it, but i'm asking it again because i fail misserably when i try to apply the answers from the other topics.
The problem that i have is that, i have a prefab (quad) 'Star' with a material into it and an script that makes it change color after some time:
using UnityEngine;
public class Star: MonoBehaviour {
public enum StarType {
BRIGHT,
DARK,
F,
K,
L,
P
}
private Ship ship;
private StarType starType;
private float timeLimit = .30f, timer;
private System.Random rand;
public void Awake() {
ship = FindObjectOfType<Ship>();
timer = .0f;
rand = new System.Random();
}
public void Start() {
refactor();
}
public void Update() {
timer += Time.deltaTime;
if(timer > timeLimit) {
refactor();
timer = .0f;
}
}
public void LateUpdate() {
transform.eulerAngles = new Vector3(ship.transform.eulerAngles.x,
ship.transform.eulerAngles.y,
ship.transform.eulerAngles.z);
}
public void refactor() {
starType = (StarType) rand.Next(0,
System.Enum.GetValues(typeof(StarType)).Length);
switch(starType ) {
case StarType.BRIGHT:
renderer.material.color = Color.white;
break;
case StarType.DARK:
renderer.material.color = Color.black;
break;
case StarType.F:
renderer.material.color = Color.red;
break;
case StarType.K:
renderer.material.color = Color.cyan;
break;
case StarType.L:
renderer.material.color = Color.green;
break;
case StarType.P:
renderer.material.color = Color.magenta;
break;
}
}
public StarType getStarType() {
return starType;
}
}
And this stars being instantiated in a Chunk class:
public class Chunk : MonoBehaviour {
public Star star;
private IList<Star> stars;
public void Awake() {
stars = new List<Star>();
Star auxStar = (Star) Instantiate(star, transform.position,
Quaternion.identity);
auxStar.transform.SetParent(transform);
stars.Add(auxStar);
}
}
I was assuming that each star would change the color independently, but instead of that they all change to the same color.
I've tried using sharedMaterial instead of material as i read it on some asnwers, though the result and behaviour seems the same?, and i tried giving random init colors to the stars, but the most that i get is having a few of them changing to a color different from the rest, though still the same between them (And i'm trying to change colors all the time, not only upon creation).
I've read too creating a material and asigning to each one at instantiating, but i had no luck:
auxStar.renderer.material = (Material)
Instantiate(star.renderer.material);
Does someone know how can the problem be handled?
According to Unity API reference, you are doing the right thing.
The issue in your code is that you are instantiating a MonoBehaviour
public Star star;
...
Star auxStar = (Star) Instantiate(star, transform.position, Quaternion.identity);
where star is a MonoBehaviour, you are not instantiating the actual GameObject with the Mesh Render and/or Material. Instead make sure you are instantiating the right GameObject Prefab, not just the component, like this:
public GameObject starPrefab;
...
GameObject starClone = (GameObject) Instantiate (starPrefab);
Star auxStar = starClone.GetComponent<Star> ();
In that way you have control of the GameObject, if you need, and every component associated with the Cloned Object, in this case Star. You don't have to instantiate the Prefab Material cause it will be already instantiated.
Here is a simple Example:
using UnityEngine;
using System.Collections;
public class SpawnStar : MonoBehaviour {
public GameObject StarPrefab;
public bool InstantiateStar = false;
void Update () {
if (InstantiateStar) {
GameObject starClone = (GameObject)Instantiate (StarPrefab);
float r = Random.Range (0f, 1f);
float g = Random.Range (0f, 1f);
float b = Random.Range (0f, 1f);
float a = Random.Range (0f, 1f);
starClone.renderer.material.color = new Color (r, g, b, a);
InstantiateStar = false;
}
}
}
Side Note: Make sure to not modify the Prefab during gameplay since is the source of all other cloned objects.
Try using Random.Range() As in System.Random is generated by system time. As we know that Time is not Really Random. It still depends on mil sec times and generate the the logic of Random as we can't really tell which mil sec the random will run.
Since System.Random runs on 1 timeline which your scripts are using. Random.Range() is a bet you can consider since it uses SystemTime and independent time On Run of the platform. Shares with Time.DeltaTime.

Categories