Unity C# 4.5.2 2D destroy instantiated particle system prefab - c#

I acquired this snippit of code and it instantiates a particle system prefab. The problem I'm having is the clones do not get destroyed after the 5 second delay. Any advice is appreciated.
private ParticleSystem instantiate(ParticleSystem prefab, Vector3 position)
{
ParticleSystem newParticleSystem = Instantiate(
prefab,
position,
Quaternion.identity
) as ParticleSystem;
if(newParticleSystem.gameObject != null)
{
Destroy(
newParticleSystem.gameObject,
newParticleSystem.startLifetime
);
}
return newParticleSystem;
}

Your code relies on whateveris called ParticleSystem to keep track of when to Destroy the system. What I would do is this:
private ParticleSystem instantiate(ParticleSystem prefab, Vector3 position)
{
ParticleSystem newParticleSystem = Instantiate(
prefab,
position,
Quaternion.identity
) as ParticleSystem;
newParticalSystem.AddComponent<TimedDestroy>().delay = newParticleSystem.startLifetime;
return newParticleSystem;
}
and then add this script to your project:
using UnityEngine;
public class TimedDestroy : MonoBehaviour
{
public float delay;
void Start()
{
Invoke("destruct",delay);
}
public void destruct()
{
Destroy(gameObject);
}
}

Related

Destroyed Bullet Won't Make Clones and Shoot After around 3 seconds

I made a script that shoots a bullet and destroys it after 3 seconds, however it destroys the original bullet after it is shot which makes unity unable to make copies of it to shoot another bullet, An okay solution might be to make a copy of the bullet and shoot the copy however I do not know how to do that.
This is the script for the gun
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletPrefab, bulletLife);
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation);
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
This is the script for the bullet.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shot : MonoBehaviour
{
// Start is called before the first frame update
public float speed = 3000f;
void Update()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
}
From what I see, you destroy the bulletPrefab but you never actually assign an object for it. You can have a separate GameObject and do that when instantiating inside the Shoot method. Try this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private GameObject bulletShot // This is what I added
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletShot); // Removed the timer from here
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
bulletShot = Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation); // Assigned the bullet to the separate GameObject
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
That way, when you try to destroy it, there will actually be something to destroy. However, this will create a new issue. The previously instantiated object will be destroyed instantly when you shoot again. You can fix that by creating a list and adding the bullets there. However, that is NOT efficient. And since you are going to start all over, I suggest you go for an Object Pool. What is that? Glad you asked!
There is a very good video by Jason Weimann on YouTube about object pooling. You might want to give it a go, it is old but definitely not outdated.
https://www.youtube.com/watch?v=uxm4a0QnQ9E
You might need to keep a reference to the shot bullet, to be able to destroy with a coroutine after 3 seconds, it later on like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private Queue<GameObject> myQ = new Queue<GameObject>(); //queue to keep track of the shot bullet and handle the delayed destroy
private IEnumerator coroutine;
void Start() {
coroutine = DelayedDestroy(3f);
}
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
//Destroy(bulletPrefab, bulletLife); Commented to avoid immediate destruction
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
myQ.Enqueue(Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation));
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
StartCoroutine(coroutine);
}
private IEnumerator DelayedDestroy(float waitTime) {
yield return new WaitForSeconds(waitTime);
GameObject nullcheck = myQ.Dequeue();
if (nullcheck != null) { //in case the queue is empty
Destroy(nullcheck );
}
}
}
Did not debug that, it is to give you the idea. You can check the Queue data structure and coroutines.
Check pooling to achieve what you are making even better :)

Multiple instance of Game Object while setting Game object Active

I am newbie to Unity 3D. I have created a game object(A missile) at position x:-1 y:-3 z:0 Added a component script which moves the ball in upward direction. Saved it as a prefab. Then deleted the Game object from the scene.
Component Script used
using UnityEngine;
public class Missile : MonoBehaviour
{
[SerializeField] protected Rigidbody2D rb;
public float speed = 4;
void Start()
{
Invoke("Object", 2.0f);
}
void Object()
{
float x = Random.Range(-2.0f, 2.0f);
float y = -5.0f;
rb.MovePosition(new Vector3(x, y, 0));
Debug.Log("Position of Rigid Body: " + rb.position);
rb.velocity = Vector2.up * speed;
}
virtual protected void Die()
{
Destroy(gameObject);
}
}
Invoked a script which takes prefab as input parameter and spawns the Game object at random position & moves up as per the script component of missile. I need the missile to trigger at random location, but the missile is getting triggered both at position x:-1 y:-3 z:0(Game object position) & some random location. How can I avoid the Game object position while spawning
Script used to invoke the prefabs
using UnityEngine;
using System.Collections;
using TMPro;
public class MissileSpawnner : MonoBehaviour
{
[SerializeField] GameObject[] missilePrefabs;
[SerializeField] int missileCount;
[SerializeField] float spawnDelay;
GameObject[] missiles;
#region Singleton class: MissileSpawnner
public static MissileSpawnner Instance;
void Awake()
{
Instance = this;
}
#endregion
void Start()
{
PrepareMissiles();
StartCoroutine(SpawnMissiles());
}
IEnumerator SpawnMissiles()
{
for (int i = 0; i < missileCount;i++)
{
Debug.Log("Object set as active");
missiles[i].SetActive(true);
yield return new WaitForSeconds(spawnDelay);
}
}
void PrepareMissiles()
{
missiles = new GameObject[missileCount];
int prefabsCount = missilePrefabs.Length;
for (int i = 0; i < missileCount; i++)
{
missiles[i] = Instantiate(missilePrefabs[Random.Range(0, prefabsCount )]);
missiles[i].SetActive(false);
}
}
}
Change this:
missiles[i] = Instantiate(missilePrefabs[Random.Range(0, prefabsCount )]);
to
missiles[i] = Instantiate(missilePrefabs[Random.Range(0, prefabsCount )], <vector3position>, <quaternionrotation>);
and you will specify its position and rotation, just replace vector3position with a vector3 of your choosing and quaternionrotation with a quaternion rotation of your choosing. You can use Quaternion.Identity if you don't want any rotation. You can also add a parent if you choose as well.
missiles[i] = Instantiate(missilePrefabs[Random.Range(0, prefabsCount )], <vector3position>, <quaternionrotation>, <transformparent>);
Just replace transformparent with the gameobject you want to be the parent of the bullet, if this override can prove useful to you.
More reading on Object.Instantiate
https://docs.unity3d.com/ScriptReference/Object.Instantiate.html

OnCollisionEnter not working Unity3D

I'm trying to build a game where u need to dodge falling objects. I've made a hazard but it seems as if the hazard 'clone' is behaving diffrently.
I've made a collision script when the hazard hits the platform it needs to disappear. This works for the hazard object, but not the hazard clone objects that fall.
As u can see in the first screenshot, the red circled block behaves
like it use to. But the blue circled once (clones) fall right through
objects.
As u can see in the second screenshot, the red circled one is gone,
because it hit the platform. But still the blue once fall right
through.
Thanks in advance!
Below u will find the Collision script, below that is the Hazard Spawn script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HazardCollisionFunctions : MonoBehaviour {
#region Variables
//Public
//Private
#endregion
#region UnityFunctions
void Start()
{
}
void Update()
{
}
#endregion
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.tag == "platform")
{
this.gameObject.SetActive(false);
}
if(collision.gameObject.tag == "Player")
{
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnHazards : MonoBehaviour {
#region Variables
//Public
//Private
[SerializeField]
public float minX = 0.0f;
[SerializeField]
public float maxX = 0.0f;
[SerializeField]
private GameObject[] hazards; //potential array of hazards
[SerializeField]
private float timeBetweenSpawns = 0.0f;
private bool canSpawn = false;
private int amountOfHazardsToSpawn = 0;
private int hazardToSpawn = 0;
#endregion
#region UnityFunctions
public void Start()
{
canSpawn = true; //Temp start
}
public void Update()
{
if(canSpawn == true)
{
StartCoroutine("GenerateHazard");
}
}
#endregion
private IEnumerator GenerateHazard()
{
canSpawn = false;
timeBetweenSpawns = Random.Range(0.5f, 2.0f); //Testing values
amountOfHazardsToSpawn = Random.Range(1, 5); //Testing values
for(int i = 0; i < amountOfHazardsToSpawn; i ++)
{
Vector3 spawnPos = new Vector3(Random.Range(minX, maxX), 8.0f, 0.0f); //Gen spawnpoint for the hazard
Instantiate(hazards[hazardToSpawn], spawnPos, Quaternion.identity); //Spawn the hazard
}
yield return new WaitForSeconds(timeBetweenSpawns);
canSpawn = true;
}
}
OnCollisionEnter
OnCollisionEnter takes Collision object as a parameter and it requires the isTrigger property of the attached Collider component to be FALSE.
void OnCollisionEnter(Collision collision)
{
foreach (ContactPoint contact in collision.contacts)
{
Debug.DrawRay(contact.point, contact.normal, Color.white);
}
}
OnTriggerEnter
OnTriggerEnter takes Collider object as a parameter and it requires the isTrigger property of the attached Collider component to be TRUE.
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("CheckPoint"))
{
Destroy(other.gameObject);
}
}
If you are instantiating the object from prefab, make sure that
prefab have required components (rigidbody/collider) and
properties to achieve the desired behaviour.
To detect Collision/Trigger, at least one of the object must have a
physics component (Rigidbody)
Rigidbody MUST be attached to the moving object.
Hope this helps :)

Why isn't my program recognizing my target object

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AIController : MonoBehaviour {
[Header("Player interaction")]
[SerializeField] private GameObject target;
[SerializeField] private int damage;
[SerializeField] private float attackDelay;
[SerializeField] private float stunTime;
[SerializeField] private int health;
public int Health{
get{return health; }
set{ health = value;
agent.speed = runningSpeed;
agent.SetDestination(target.transform.position);
onPatrol = false;
if (health <= 0){
Destroy(gameObject);
}
}
}
// Update is called once per frame
void Update () {
CanAttack(target);
}
here i want to make sure that the AI will attack the player only when he is in front of the AI and within .5 meters. the reason i use this instead of OnTriggerEnter is that the AI will only end up attacking once and then just stand on top of the player without really doing anything further
void CanAttack (GameObject other)
{
Vector3 vectorToTarget = other.transform.position - transform.position;
float angleToTarget = Vector3.Angle(transform.forward, vectorToTarget);
if (angleToTarget <= FOV)
{
RaycastHit raycastData;
if (Physics.Raycast(transform.position, vectorToTarget, out raycastData, .5f))
{
GameObject newTarget = raycastData.collider.gameObject;
Attack(target);
}
}
}
I have also used Attack(other) and this still does not work
void Attack(GameObject other)
{
if (!hasAttacked)//if the AI has not recently attacked
{
if (!Player.isShieldOn)//if the player shield is not up
{
if (!Player.hasShieldBraclet)//if the player has the shield braclet
{
other.gameObject.GetComponent<Player>().Health -= damage - ShieldBraclet.dmgReduction;//reduce the amount of damage taken
}
else
{
other.gameObject.GetComponent<Player>().Health -= damage;//otherwise take normal damage
}
StartCoroutine("DelayAttack");//delay time until AI can attack again
}
else
{
StartCoroutine(Stun(stunTime));//if the shield was up stun this enemy
}
}
}
this is used to make sure that the AI waits a moment before attacking again that way the player doesn't die instantly upon touching the AI.
IEnumerator DelayAttack()
{
hasAttacked = true;//the AI has attacked
yield return new WaitForSeconds(attackDelay);//wait to attack again
hasAttacked = false;//the AI can now attack again
}
}
For the most part everything runs fine but when it reaches the code to deal damage to the player it tells me that the object reference is not set to an instance of an object. How can I fix this?
The Specific error code is: NullReferenceException: Object reference not set to an instance of an object Enemy.Attack(UnityEngine.GameObject other).
This is referring to the line
other.gameObject.GetComponent<Player>().Health -= damage - ShieldBraclet.dmgReduction;//reduce the amount of damage taken

audio.play() not working when object is destroyed

I'm trying to play a sound when a GameObject is destroyed. However the sound won't play. I've tried it this way and by assigning the audio clip to a variable but neither seem to work. If I set the sound to play on awake it plays when the GameObject spawns, so I know the sound clip works - but it won't play when it is destroyed.
using UnityEngine;
using System.Collections;
public class DestroyByContact : MonoBehaviour {
public GameObject explosion;
public GameObject explosion02;
public GameObject explosionShot;
public int scoreValue;
public GameController gameController;
public int health;
public AudioClip explosionSound01;
void start () {
}
void Update () {
if (health <= 0) {
this.gameObject.GetComponent<AudioSource> ().Play ();
Instantiate(explosion, transform.position, transform.rotation);
Instantiate(explosion02, transform.position, transform.rotation);
GameObject gc = GameObject.Find ("GameController");
GameController gcs = gc.GetComponent<GameController> ();
gcs.AddScore (scoreValue);
Destroy(gameObject);
}
if (this.gameObject.tag == "Asteroid") {
if (this.gameObject.transform.position.x < -16) {
Destroy (gameObject);
Destroy (transform.parent.gameObject);
}
}
}
void OnTriggerEnter2D(Collider2D other){
if (other.tag == "Boundary") {
return;
}
if (other.tag == "Asteroid") {
return;
}
if (other.tag == "Player") {
Instantiate(explosion, transform.position, transform.rotation);
Instantiate(explosion02, transform.position, transform.rotation);
Destroy (gameObject);
}
if (other.tag == "Bullet") {
Instantiate(explosionShot, transform.position, transform.rotation);
other.gameObject.GetComponent<AudioSource> ().Play ();
health -= 10;
GameObject gc = GameObject.Find ("GameController");
GameController gcs = gc.GetComponent<GameController> ();
gcs.AddScore (10);
Destroy (other.gameObject);
}
}
}
Cutting this down to the essential lines, we're left with just two:
this.gameObject.GetComponent<AudioSource>().Play();
Destroy(gameObject);
Destroying a GameObject will also destroy any components attached to it. You've told the AudioSource to play, and then immediately destroyed it. Once it has been destroyed, it no longer exists and therefore cannot play any sound.
To avoid this, you can create a separate GameObject to contain an AudioSource that will play the sound, then destroy that once it's finished.
Unity actually has a helper function, AudioSource.PlayOneShot, to do exactly that:
[RequireComponent(typeof(AudioSource))]
public class ExampleClass : MonoBehaviour {
public AudioClip impact;
AudioSource audio;
void Start() {
audio = GetComponent<AudioSource>();
}
void OnCollisionEnter() {
audio.PlayOneShot(impact, 0.7F);
}
}
If you need to have more control, you could create and manage your own GameObject, perhaps by instantiating a prefab:
public class ExampleTwoClass : MonoBehaviour {
public AudioSource audioPrefab;
void OnCollisionEnter() {
GameObject clone = Instantiate(audioPrefab, transform.position, transform.rotation) as GameObject;
AudioSource cloneAudio = clone.GetComponent<AudioSource>();
cloneAudio.play();
//destroy clone once audio finishes
Destroy(clone, cloneAudio.clip.length + 0.1f);
}
}
Well, the game object you're trying to play the sound on is being destroyed, so the audio source is going to be destroyed along with it.
You'll have to create an empty game object for the audio source to be attached to, placed at the destroyed object's position, and destroy that one after the sound effect finishes. Or another, similar solution, depending on your exact needs (Destroy has a bit of overhead, so using a pooling system changes the answer).
You can easily change your script to work, you just need to use Destroy(GameObject,float) method instead of Destroy(Gameobject).
This is a handy solution.
if (health <= 0) {
this.gameObject.GetComponent<AudioSource> ().Play ();
Instantiate(explosion, transform.position, transform.rotation);
Instantiate(explosion02, transform.position, transform.rotation);
GameObject gc = GameObject.Find ("GameController");
GameController gcs = gc.GetComponent<GameController> ();
gcs.AddScore (scoreValue);
Destroy(gameObject,1f);// here use time parameter.
}
assign the the depends on your music's time if your exploit sound 1 sec then assign time Destroy(gameObject,1.25f).
You can use this static method:
AudioSource.PlayClipAtPoint(soundClip, Camera.main.transform.position);
Destroy(gameObject);
To set the sound clip from the inspector, add a member to the class:
[SerializeField]
private AudioClip soundClip;

Categories