How to reference an Instantiated object in Unity? - unity3d

I have a weapon that I want to instantiate an object (In this case a bullet), then wait until that bullet has hit an object before the weapon is allowed to instantiate another object.
I am currently doing this by having this on the weapon script:
public class weaponScript : MonoBehaviour
{
public gameobject projectilePrefab;
public bool launchable;
void Update{
if(launchable){
Instantiate(projectilePrefab, firePoint.transform.position, transform.rotation);
launchable = false;
}
}
And this on the bullet script:
public class projectile : MonoBehaviour
{
void OnCollisionEnter2D(Collision2D other){
weaponScript.launchable = true;
}
}
This would work perfect for my needs, however this doesn't work because the projectile has no definition of what weaponScript is, therefore it can't set the launchable variable on the weaponScript to true.
I could use the FindObjectOdType() function, and that works when you have one weapon in the scene, but once you have more than one weaponScript in the scene at a time, you start to having problems determining who is who.
Is there a way for the weaponScript to set itself as a variable when it instantiates an object so it would be like:
public class weaponScript : MonoBehaviour
{
public gameobject projectilePrefab;
public bool launchable;
void Update{
if(launchable){
Instantiate(projectilePrefab, firePoint.transform.position, transform.rotation);
[InstanciatedObjectHere].parentWeapon = this.gameobject;
launchable = false;
}
}
That way all the projectile has to do is:
public class projectile : MonoBehaviour
{
void OnCollisionEnter2D(Collision2D other){
parentWeapon.launchable = true;
}
}
Solution:
I know Pastebin won't keep the script forever, so I'm going to put the answer here as an edit: (plus it makes it easier for some else who might stumble upon this to read)
I followed Peridis's answer, but it didn't work immediately, so I ended up tweaking it and came up with this:
public class weaponScript : MonoBehaviour
{
public gameobject projectilePrefab;
public bool launchable;
private projectile projectileScript;
void Update{
if(launchable){
GameObject projectileIntantiated = Instantiate(projectilePrefab, firePoint.transform.position, transform.rotation);
projectileScript = projectileIntantiated.GetComponent<projectile>();
projectileScript.parentWeapon = this.gameObject.GetComponent<weaponScript>();
launchable = false;
}
}
public class projectile : MonoBehaviour
{
public weaponScript parentWeapon;
void OnCollisionEnter2D(Collision2D other){
parentWeapon.launchable = true;
}
}
Thank you for the help Peridis!

You can create a variable of type GameObject when Instantiating the projectile
GameObject projectileIntantiated = Instantiate(projectilePrefab, firePoint.transform.position, transform.rotation);
projectileIntantiated.GetComponent<projectile>().parentWeapon = this.gameobject;

Related

How to fix "The object of type 'Transform' has been destroyed but you are still trying to access it." error in Unity

Alright, So I'm trying to create a 2.5D side scroll game, and in it I have an enemy shooting me and when the player character gets shot, it gets Destroyed and the scene restarts. Whenever the player gets destroyed, I get the error mentioned. I have no clue how to fix this, but my used code is below. Thank you for the help.
public class DestroyPlayer : MonoBehaviour
{
//Destroys Player Character On Contact
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.CompareTag("Player"))
{
Destroy(collision.gameObject);
}
}
}
This is the player spawn script as well:
public class SpawnScript : MonoBehaviour
{
public GameObject player;
public Transform spawnPoint;
//When Player Character is Destroyed, Respawn Player at Point
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Player"))
{
Scene currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
}
}
The Script in where the error is coming from:
public class TurretControl : MonoBehaviour
{
private Transform _Player;
private float dist;
public float maxRange;
public Transform barrel;
public GameObject _projectile;
public float fireRate, nextShot;
public float cannonBallForce = 40.0f;
void Start()
{
_Player = GameObject.FindGameObjectWithTag("Player").transform;
}
private void Update()
{
dist = Vector3.Distance(_Player.position, transform.position);
if(dist <= maxRange)
{
if(Time.time >= nextShot)
{
nextShot = Time.time + 1f / fireRate;
shoot();
}
}
}
void shoot()
{
GameObject clone = Instantiate(_projectile, barrel.position,transform.rotation);
//Forward force
clone.GetComponent<Rigidbody>().AddForce(-barrel.right * cannonBallForce);
Destroy(clone, 20);
}
}

How can I prevent it from slipping when I use NavMeshAgent?

I've written a simple short code using agent.destination from the NavMesh Agent that allows enemies to track players. However, in the process of chasing the target, the enemy keeps slipping and not chasing properly.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using static UnityEngine.EventSystems.EventTrigger;
public class Enemy : MonoBehaviour
{
public Transform target;
public Transform runAwayPos;
public NavMeshAgent agent;
public Transform spwanPosition;
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
void Update()
{
StartCoroutine(Run());
if (Time.timeScale == 0)
{
transform.position = spwanPosition.position;
}
}
IEnumerator Run()
{
agent.speed = 9f;
agent.destination = target.transform.position;
yield return null;
}
private void OnTriggerEnter(Collider collision)
{
if (collision.gameObject.CompareTag("Player"))
{
transform.position = spwanPosition.position;
Debug.Log("hit");
}
}
}
I wonder how the enemy can track a player without slipping.
First of all, you call IEnumarator per frame. this causes unwanted consequences.Use either update or IEnumarator.
In my opininon,
void Update()
{
agent.SetDestination(target.position);
if (Time.timeScale == 0)
{
transform.position = spwanPosition.position;
}
}
By the way, you should use a boolean to check the untracked case.

How to avoid object references for spawned objects in Unity 2D?

At the moment I am programming a Unity 2D game. When the game is running the cars start moving and respawn continuously. I added kind of a life system to enable the possibility to shoot the cars. My issue is that my health bar as well as my score board need references to the objects they refer to, but I am unable to create a reference to an object which is not existing before the game starts. Another issue is that I don't know how to add a canvas to a prefab in order to spawn it with the cars continuously and connect them to the car. Is there a way to avoid these conflicts or how is it possible to set the references into prefabs. I will add the code of the spawner, the car and the the scoreboard. Already thank you in advance
Spawner:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
public GameObject carPrefab;
public GameObject enemyCarPrefab;
public GameObject kugel;
public float respawnTime = 10.0f;
public int counterPlayer1=0;
public int counterPlayer2=0;
public int counterEnergy=0;
// Use this for initialization
void Start () {
StartCoroutine(carWave());
}
private void spawnPlayerCars(){
GameObject a = Instantiate(carPrefab) as GameObject;
a.transform.position = new Vector2(-855f, -312.9426f);
counterPlayer1++;
}
private void SpawnEnemyCars(){
GameObject b = Instantiate(enemyCarPrefab) as GameObject;
b.transform.position = new Vector2(853,-233);
counterPlayer2++;
}
private void SpawnEnergy(){
GameObject c = Instantiate(kugel) as GameObject;
c.transform.position = new Vector2(-995,-192);
counterEnergy++;
}
IEnumerator carWave(){
while(true){
yield return new WaitForSeconds(respawnTime);
if(counterPlayer1<3){
spawnPlayerCars();
Debug.Log(counterPlayer1);
}
if(counterPlayer2<3){
SpawnEnemyCars();
Debug.Log(counterPlayer2);
}
if(counterEnergy<3){
SpawnEnergy();
}
}
}
}
Car:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyCar : MonoBehaviour
{
public float speed = 3f;
int zählerAuto1=0;
private Vector2 screenBounds;
public AnzeigePunktzahlPlayer2 points;
public Spawner sp;
public int maxHealth=100;
public int currentHealth;
public HealthBar healthbar;
void Start () {
screenBounds = Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, Screen.height));
points= GetComponent<AnzeigePunktzahlPlayer2>();
sp= GetComponent<Spawner>();
currentHealth=maxHealth;
healthbar.SetMaxHealth(maxHealth);
}
void Update()
{
Vector2 pos = transform.position;
if(pos.x>-855f){
pos = transform.position;
pos.x-= speed* Time.deltaTime;
transform.position=pos;
zählerAuto1++;
}else{
points.counter++;
Debug.Log(points.counter);
sp.counterPlayer2--;
Debug.Log(sp.counterPlayer2);
Destroy(this.gameObject);
}
}
private void OnCollisionEnter2D(Collision2D other) {
if (other.collider.tag=="Kugel"){
takeDamage(40);
//sp.counterPlayer2--;
if(currentHealth<=0)
{
Destroy(this.gameObject);
}
}
}
public void takeDamage(int damage){
currentHealth-= damage;
healthbar.SetHealth(currentHealth);
}
public void getHealed(int heal){
currentHealth+= heal;
healthbar.SetHealth(currentHealth);
}
}
Scoreboard(one part of it(the other one is almost the same)):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class AnzeigePunktzahlPlayer1 : MonoBehaviour
{
public int counter;
public TextMeshProUGUI textPlayer1;
void Start()
{
// counter=0;
textPlayer1= GetComponent<TextMeshProUGUI>();
}
// Update is called once per frame
void Update()
{
textPlayer1.SetText( counter.ToString());
}
}
You could make the health bars and the canvas children of the Car prefab and have them spawn together.

Life bar doesn't change after players destroy collider

I am trying to do a 2d game and my object doesn't affect the player's lifebar after they collide. The player's healthbar will will get a bigger lifetime but I think something it's wrong with the scripts. (Also the collider of object that needs to be destroy is "is trigger" checked). I put this PowerUp on the object, and the character script and healthbar script on the player.
Character.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
public class Character : MonoBehaviour
{
Rigidbody2D rb;
float dirX;
[SerializeField]
float moveSpeed = 5f, jumpForce = 600f, bulletSpeed = 500f;
Vector3 localScale;
public Transform barrel;
public Rigidbody2D bullet;
// Use this for initialization
void Start()
{
localScale = transform.localScale;
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
dirX = CrossPlatformInputManager.GetAxis("Horizontal");
if (CrossPlatformInputManager.GetButtonDown("Jump"))
Jump();
if (CrossPlatformInputManager.GetButtonDown("Fire1"))
Fire();
}
void FixedUpdate()
{
rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
}
void Jump()
{
if (rb.velocity.y == 0)
rb.AddForce(Vector2.up * jumpForce);
}
void Fire()
{
var firedBullet = Instantiate(bullet, barrel.position, barrel.rotation);
firedBullet.AddForce(barrel.up * bulletSpeed);
}
}
HealthBar.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HealthBar : MonoBehaviour
{
public Slider slider;
public Gradient gradient;
public Image fill;
public float health = 100;
public void SetMaxHealth(int health)
{
slider.maxValue = health;
slider.value = health;
fill.color = gradient.Evaluate(1f);
}
public void SetHealth(int health)
{
slider.value = health;
fill.color = gradient.Evaluate(slider.normalizedValue);
}
}
PowerUp.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PowerUp : MonoBehaviour
{
public float multiplayer = 1.4f;
public GameObject pickupEffect;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
Pickup(other);
}
}
void Pickup(Collider player)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
HealthBar stats =player.GetComponent<HealthBar>();
stats.health *= multiplayer;
// Remove Effect
Destroy(gameObject);
}
}
First of all as others already mentioned for a 2D game with Rigidbody2D and (hopefully) Collider2D components you want to use OnTriggerEnter2D instead!
The Physics and Physics2D are two completely separated engines and don't know each other. A 3D OnTriggerEnter will never get called for 2D physics events like collisions etc.
Then note that
Also the collider of object that needs to be destroy is "is trigger" checked
this is exactly the wrong way round. If your object shall track a OnTriggerEnter then the one that enters (the player) should have isTrigger enabled! In most cases you don't want to do this because the player shall e.g. actually collide with the floor and not fall through it etc.
So what you would need is rather putting an additional script on the player itself that waits for other trigger objects to enter!
Then to be sure either debug your code line by line or add additional logs in order to see what happens:
PowerUp
// put this on the pickup item
public class PowerUp : MonoBehaviour
{
// Make these private, nobody else needs access to those
// (Encapsulation)
[SerializeField] private float multiplayer = 1.4f;
[SerializeField] private GameObject pickupEffect;
public void Pickup(HealthBar stats)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
stats.health *= multiplayer;
// Remove Effect
Destroy(gameObject);
}
}
PowerUpDetector (on the player)
// put on player!
public class PowerUpDetector : MonoBehaviour
{
// reference this via the Inspector
[SerializeField] private HealthBar healthbar;
private void Awake()
{
if(!healthBar) healthBar = GetComponent<HealthBar>();
}
private void OnTriggerEnter2D(Collider2D other)
{
// or whatever tag your powerups have
if (!other.CompareTag("PowerUp"))
{
Debug.LogWarning($"Registered a collision but with wrong tag: {other.tag}", this);
return;
}
var powerup = other.GetComponent<PowerUp>();
if(!powerup)
{
Debug.LogError($"Object {other.name} is tagged PowerUp but has no PowerUp component attached", this);
return;
}
Debug.Log("Found powerup, pick it up!", this);
powerup.Pickup(healthbar);
}
}
Well, and then what you do is only changing the float value
stats.health *= multiplayer;
but you never update the GUI accordingly like you would do when instead using
stats.SetHealth(stats.health * multiplayer)
(Btw: I think you mean multiplier ;) )
I would suggest to rather implement a property like e.g.
public class HealthBar : MonoBehaviour
{
// Make these private, nobody else needs access to those
// (Encapsulation)
[SerializeField] private Slider slider;
[SerializeField] private Gradient gradient;
[SerializeField] private Image fill;
[SerializeField] private float health = 100;
public float Health
{
get { return health; }
set
{
health = value;
slider.value = health;
fill.color = gradient.Evaluate(slider.normalizedValue);
}
}
// be careful with variable names if you have this name already
// for a class field .. was ok this time but might get confusing
public void SetMaxHealth(int value)
{
slider.maxValue = value;
// The property handles the rest anyway
Health = value;
}
}
so now instead of calling SetHealth you simply assign a new value to Health and its setter is automatically execute as well so your GUI is updated.
public void Pickup(HealthBar stats)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
stats.Health *= multiplayer;
Destroy(gameObject);
}
Your qustions are below,
player will get a bigger lifetime
Also the collider of object that needs to be destroy
What situation possibly you could have
Check your Character is Triggered, so it calls 'OnTriggerEnter' and also check tag.
Check your powerUp item that checked true as trigger, 'isTrigger'
Check if collider or hit object scale is tiny, it won't work with collider.
If you have nothing with above check-list, then it should be working

MissingReferenceException in AudioSource on DontDestroyOnLoad GameObject

I was getting Missing Reference Exception within my Audio Source when I change my game scene and again move back to the same scene.
Before I change the main menu scene, it is working fine but after changing and moving back to the main menu scene, it is started showing this exception.
Here is the code I have written for AudioManager:
public class AudioManager : MonoBehaviour
{
static AudioManager instance;
//
[SerializeField] AudioClip buttonClickClip;
[SerializeField] AudioSource myAudioSource;
private void Awake()
{
instance = this;
}
public static AudioManager Instance
{
get
{
return instance;
}
}
public void PlayButtonClickSound()
{
if (GameManager.Instance.IsEnableSounds)
myAudioSource.PlayOneShot(buttonClickClip);
}
}
Here is the code that I wrote for DontDestroyOnLoad purpose:
public class DontDetroyOnLoad : MonoBehaviour
{
private static bool created = false;
void Awake()
{
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
}
else
Destroy(this.gameObject);
}
}
Now please give me some suggestion to solve this problem.
You are referencing the AudioManager that you are destroying in the script that is trying to play the sound.
Think of it this way. You have
AudioManager A - DontDestroyOnLoad
AudioManager B - That gets destroyed cause A exists
In your scripts you are referencing AudioManager A when you first start up. Then when you leave the Scene and return you are now referencing AudioManager B, which got destroyed because A exists. All you need to do is always reference AudioManager A, not B.
Appreciate the answer from #jfish. That clearly describes the situation.
Here let me elaborate mine, on the example of using DontDestroyOnLoad
[SerializeField] AudioSource audioSource;
void Awake()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("music");
if (objs.Length > 1)
{
Destroy(this.gameObject);
//Here, get the previous audioSource from previous game object
audioSource = objs[0].GetComponent<AudioSource>();
}
DontDestroyOnLoad(this.gameObject);
}
On first load, it gets from Editor.
Subsequently, it gets from previous game object.