Collision detection for individual objects in a list Unity3D - c#

I want to have it so that a single game object from a list is destroyed when it hits the ground. So far I am able to spawn random prefabs in set spawn locations in the scene successfully, but I am having trouble implementing the collision detection for when they fall to the ground. I have tried both raycasting and OnCollisionEnter but neither is working, the collision is not being detected. I am missing something but not sure what. Below is my attempt with OnCollisionEnter:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectSpawner : MonoBehaviour
{
public List<GameObject> prefabObjects;
public List<GameObject> locations;
public List<GameObject> spawnedObjects;
void Start()
{
//create empty list of spawned objects
spawnedObjects = new List<GameObject>();
SpawnObject(RandomObjects());
}
// Update is called once per frame
void Update()
{
}
void SpawnObject(GameObject obj)
{
GameObject newObject = Instantiate(obj, transform.position, Quaternion.identity );
spawnedObjects.Add(newObject);
}
GameObject RandomObjects()
{
int rand = Random.Range(0, prefabObjects.Count);
return prefabObjects[rand];
}
void OnCollisionEnter(Collision collision)
{
for (var i = 0; i < spawnedObjects.Count; i++){
GameObject obj = spawnedObjects[i];
if (collision.collider.tag == "Ground"){
Debug.Log("Hit ground");
spawnedObjects.Remove(obj);
Destroy(obj);
}
}
}

Your void OnCollisionEnter(Collision collision) method is in the wrong place, you dont want the ObjectSpawner itself to collide but the instancianted obj's. Place a new script on your prefabs and implement OnCollisionEnter there. For the method to trigger, be sure to set up colliders and rigidbodies correctly. Your prefabs and the ground should have colliders and either one of these a non-kinematic rigidbody too.

Related

Game object can't detect others when using collision.gameObject.tag in unity

I have designed a scenario as below:
I created an object spawner to collide with things tagged as "ob", using a boolean to manage the timing to spawn new obstacles. Nonetheless, after some testing, I found that it seemed like nothing had ever collided with one another(Judged by the strings I put in the if condition had never shown in the console.) Can't figure out why! Please HELP!!!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class newObSpawning : MonoBehaviour
{
public GameObject[] oblist;
private bool hasOb=true;
public float adjustSpawnx;
void Update()
{
if (!hasOb)
{
spawnObs();
hasOb = true;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("ob"))
{
hasOb = true;
Debug.Log("hasOb"); //just for testing
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("ob"))
{
hasOb = false;
Debug.Log("hasOb");
}
}
public void spawnObs()
{
int i = Random.Range(0, oblist.Length);
Debug.Log(i);
float y = 7.87f;
GameObject newob = Instantiate(oblist[i], new Vector3(transform.position.x+ adjustSpawnx,y,0), Quaternion.identity);
}
}
obspawner carry a "follow player" script to move at the same pace as the player,and it went just well
Your player doesn't seem to have a Rigidbody2D component. Add Rigidbody2D to your player. A collider can only emit OnTriggerEnter2D or OnTriggerExit2D events if there is a rigidbody on the object too
You have to tag the object "ob" that you want to register the collision with, most probably, I think what you are searching for is the collision of your player with object spawner so tag the player with "ob".

How to transform position from one object to the current player position?

So i have this little particlesystem attached on my player so if he dies he explodes. But i cant simply attach the particlesystem under the player because if i destroy my player the childs of the gameobjects get destroyed as well. The animation runs if he dies but not on his current spot so some ideas for that? Maybe to transform the position to the current position of the player while he dies?
Here my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerdeath : MonoBehaviour
{
public ParticleSystem death_explosion;
// Start is called before the first frame update
void Start()
{
death_explosion.Stop();
}
// Update is called once per frame
void Update()
{
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "deathcube")
Destroy(gameObject);
Debug.Log("collision detected");
death_explosion.Play();
}
}
What you could do is create a prefab of the particle object and reference it inside your script like so:
public ParticleSystem death_explosion_Prefab;
And instead of attaching it to the Player as a child, instantiate it on collision:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "deathcube")
{
Debug.Log("collision detected");
Instantiate(death_explosion_Prefab, gameObject.transform.position, Quaternion.identity);
Destroy(gameObject);
}
}
got it :) --> add death_explosion.transform.position = GameObject.Find("player").transform.position;

Unity3D Shooter: Using tags to switch level after killing all enemies

I am new to Unity and was trying, after some suggestions, to use tags to know the number of enemies i have in each level and move to the next scene right after eliminating all enemies. This is the script i use on enemy gameobjects. I've also tagged each of them with the "enemy" tag in unity inspector but it still doesn't work when i run the game. After killing all the enemies, it didnĀ“t change to next scene (Success!). Any ideas on what I'm doing wrong? Any other suggestions?
Thanks a lot for the help.
Enemies Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class BadguyScript : MonoBehaviour
{
public GameObject[] enemies;
public int maxHealth;
public int curHealth;
private Animator myAnimator;
private bool isDead;
[SerializeField]
private float DespawnTime = 2.5f;
[SerializeField]
private string DeathAnimHash = "isDead";
void Start()
{
myAnimator = GetComponent<Animator>();
myAnimator.enabled =true;
myAnimator.SetBool (DeathAnimHash ,isDead);
maxHealth = 1;
curHealth = maxHealth;
}
void Update()
{
if (curHealth < 1)
{
isDead = true;
myAnimator.SetBool (DeathAnimHash ,isDead);
Destroy(gameObject,DespawnTime);
}
enemies = GameObject.FindGameObjectsWithTag("enemy"); // Checks if enemies are available with tag "Enemy".
if (enemies.Length == 0)
{
SceneManager.LoadScene("SucessScene"); // Load the scene with name "SucessScene"
}
}
void OnTriggerEnter2D(Collider2D col)
{
if (isDead)
return;
if (col.tag == "bullet")
{
curHealth -= 1;
Destroy(col.gameObject);
}
}
}
I would create a script holder gameobject for this and put a GameManager script inside it. And inside GameManager.cs which should be a singleton class you can have a property like this:
int _enemyNumber;
public int EnemyNumber{
get{
return _enemyNumber;
}
set{
_enemyNumber = value;
}
}
And when you need to change these values, use some functions you will create inside this game controller such as:
public void DecreaseEnemyCount(){
//do the logic here
}
public void SetEnemyCount(){
//do the logic here
}
Also you can find information about creating a singleton class here
You create a list with all enemies, its a good practice, cause you'll gain performance. But you're verifing if enemies.Lenght == 0, what will never occur, because before you are adding the gameObject in the list enemies = GameObject.FindGameObjectsWithTag("enemy");
In the start method, you can search for all enemies and add then in your array, and in the update or onTriggerEnter you remove it from your array and validate the array lenght. I think it'll be more easy.
Instead of adding the script to a new gameManager script attached to an empty game object cause now once all enemies are killed the script will not be in work but if added to an empty gameobject it will be working always.

Destroy particle system when initalised programmatically

I have a game with a player and an enemy.
I also have a particle system for when the player dies, like an explosion.
I have made this particle system a prefab so I can use it multiple times per level as someone might die a lot.
So in my enemy.cs script, attached to my enemy, I have:
public GameObject deathParticle;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Player" && !player.dead){
player.dead = true;
Instantiate(deathParticle, player.transform.position, player.transform.rotation);
player.animator.SetTrigger("Death");
}
}
So this plays my particle system when the player gets killed by an enemy. Now on my player script, I have this. This specific function gets played after the death animation:
public void RespawnPlayer()
{
Rigidbody2D playerBody = GetComponent<Rigidbody2D>();
playerBody.transform.position = spawnLocation.transform.position;
dead = false;
animator.Play("Idle");
Enemy enemy = FindObjectOfType<Enemy>();
Destroy(enemy.deathParticle);
}
This respawns the player like normal but in my project each time I die I have a death (clone) object which I don't want. The last 2 lines are meant to delete this but it doesn't.
I have also tried this which didn't work:
Enemy enemy = FindObjectOfType<Enemy>();
ParticleSystem deathParticles = enemy.GetComponent<ParticleSystem>();
Destroy(deathParticles);
There is no need to Instantiate and Destroy the death particle, that will create a lot of overhead, you can simply replay it when you want it to start and stop it, when you dont need it
ParticleSystem deathParticleSystem;
private void OnTriggerEnter2D(Collider2D collision)
{
#rest of the code
deathParticleSystem.time = 0;
deathParticleSystem.Play();
}
public void RespawnPlayer()
{
//rest of the code
deathParticleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
}
or you can enable and disable the gameObject associated with your particle prefab
public GameObject deathParticlePrefab;
private void OnTriggerEnter2D(Collider2D collision)
{
#rest of the code
deathParticlePrefab.SetActive(true);
}
public void RespawnPlayer()
{
//rest of the code
deathParticlePrefab.SetActive(false);
}
You could always create a new script and assign it to the prefab that destroys it after a certain amount of time:
using UnityEngine;
using System.Collections;
public class destroyOverTime : MonoBehaviour {
public float lifeTime;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
lifeTime -= Time.deltaTime;
if(lifeTime <= 0f)
{
Destroy(gameObject);
}
}
}
So in this case you would assign this to your deathParticle. This script is useful in a multitude of scenarios if you're instantiating objects so that you don't have a load of unnecessary objects.

Unity Engine - Instantiate a prefab by collision

I have a GameObject called Player.
Attached to Player there is a script component called Player ( same )
Inside the Player's script, I have a field called _weaponPrefab ( GameObject type )
In the Inspector, I can easily drag & drop any prefabs from my Prefab folder, inside the _weaponPrefab variable.
All good so far. What I want to archive is: to be able to add my Prefabs based on a Collision2D. So if my Player collides with a Prefab, let's say a Sword, the prefab of that sword will be automatically attached and insert into the _weaponPrefab field inside the Player script.
Below my Player script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] private float _speed = 5.0f;
[SerializeField] private float _fireRate = 0.2f;
private bool _canFire = true;
[SerializeField] private GameObject _weaponPrefab; <- to populate runtime
// Use this for initialization
void Start ()
{
transform.position = Vector3.zero;
}
// Update is called once per frame
void Update ()
{
if (Input.GetKeyDown(KeyCode.Space) && _canFire)
StartCoroutine(Shoot());
}
void OnTriggerEnter2D(Collider2D other)
{
//here i don't know how to continue.
}
public IEnumerator Shoot()
{
_canFire = false;
Instantiate(_weaponPrefab, transform.position + new Vector3(0, 1, 0), Quaternion.identity);
yield return new WaitForSeconds(_fireRate);
_canFire = true;
}
}
Edit: i just wrote a comment of where i don't know how to proceed.
There are many ways to accomplish what you desire.
My suggestion may be out of your comfort zone at first, but it will provide you with most flexibility and eventually easy of programming/ designing/ maintaining your game (in my opinion).
First make a scriptable object (what is a scriptable object and how do i use it?)
using UnityEngine;
using System.Collections;
using UnityEditor;
public class Item
{
[CreateAssetMenu(menuName = "new Item")]
public static void CreateMyAsset()
{
public GameObject prefab;
// you can add other variables here aswell, like cost, durability etc.
}
}
Create a new item (in Unity, Assets/new Item)
Then create a monobehaviour script which can hold the item. In your case let's name it "Pickup".
public class Pickup : MonoBehaviour
{
public Item item;
}
Finally in your player script, change your on TriggerEnter to:
void OnTriggerEnter2D(Collider2D other)
{
if(other.GetComponentInChildren<Pickup>())
{
var item = other.GetComponentInChildren<Pickup>().item;
_weaponPrefab = item.prefab;
}
}
When you instantiate a GameObject you need to pass basically 3 parameters (However not all of them are mandatory, check):
The prefab
The position
The rotation
Let's assume you have a placeholder in the hand or in the back of your character, to keep there the weapon once collected. This placeholder can be an empty gameobject (no prefab attached, but will have a transform.position component)
Now let's assume you have a list of weapons in the scene with their prefab and with a different tag each. Then you can do something like this:
GameObject weapon;
GameObject placeholder;
public Transform sword;
public Transform bow;
...
void OnTriggerEnter2D(Collider2D other)
{
//You can use a switch/case instead
if(other.gameObject.tag == "sword"){
Instantiate(sword, placeholder.transform.position, Quaternion.identity);
}else if(other.gameObject.tag == "bow"){
Instantiate(bow, placeholder.transform.position, Quaternion.identity);
}...
}
What I'll do is load the prefabs first in a Dictionary<string, GameObject> from the resources folder.
First I populate the dictionary with all the weapon prefabs in the Start method with the tag of the object as a key. Then in the OnTriggerEnter2D I check if the tag is in the dictionary then I instantiate it from there.
Take a look at the code :
private Dictionary<string, GameObject> prefabs;
// Use this for initialization
void Start () {
var sword = (GameObject) Resources.Load("Weapons/sword", typeof(GameObject));
prefabs.Add("sword", sword);
// Add other prefabs
};
void OnTriggerEnter2D(Collider2D other)
{
if (prefabs.ContainsKey(other.tag))
Instantiate(prefabs[other.tag]);
}
NOTE : your prefabs needs to be in the folder Assets/Resources/Weapons because I used Resources.Load("Weapons/sword", typeof(GameObject));

Categories