Spawned object doesn't spawn again - c#

I'm working in a mobile game that has multiples areas in the same scene. Each area has a trigger and when the player enters in it several objects are spawned to pick up them. How can I do to deactivate a picked up object so when the player enters in this area again this object doesn't spawn again?
This is my code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using PathologicalGames;
public class InOutZone_ZONAS: MonoBehaviour {
//Objetos
[Header("Objetos")]
public List<GameObject> spawnPositions;
public List<GameObject> spawnObjects;
private GameObject[] despawnObjects;
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
SpawnObjectsZ ();
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
GameObject[] despawnObjects = GameObject.FindGameObjectsWithTag("ItemZona");
for (int i = 0; i < despawnObjects.Length; i++)
{
PoolManager.Pools ["Objetos"].Despawn (despawnObjects[i].transform);
Debug.Log("Despawnea Objetos");
}
}
}
void SpawnObjectsZ()
{
foreach (GameObject spawnPosition in spawnPositions) {
int selection = Random.Range (0, spawnObjects.Count);
PoolManager.Pools ["Objetos"].Spawn (spawnObjects [selection], spawnPosition.transform.position, spawnPosition.transform.rotation);
}
}
}

How can I do to deactivate a picked up object so when the player
enters in this area again this object doesn't spawn again?
There are many ways to do this. You can simply remove the GameObject from the List after you spawn it with spawnObjects.Remove(spawnObjects[selection]);
public class Playervitals : MonoBehaviour
{
//Objetos
[Header("Objetos")]
public List<GameObject> spawnPositions;
public List<GameObject> spawnObjects;
private GameObject[] despawnObjects;
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
SpawnObjectsZ();
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
GameObject[] despawnObjects = GameObject.FindGameObjectsWithTag("ItemZona");
for (int i = 0; i < despawnObjects.Length; i++)
{
PoolManager.Pools["Objetos"].Despawn(despawnObjects[i].transform);
Debug.Log("Despawnea Objetos");
}
}
}
void SpawnObjectsZ()
{
for (int i = 0; i < spawnPositions.Count; i++)
{
GameObject spawnPosition = spawnPositions[i];
int selection = Random.Range(0, spawnObjects.Count);
PoolManager.Pools["Objetos"].Spawn(spawnObjects[selection], spawnPosition.transform.position, spawnPosition.transform.rotation);
spawnObjects.Remove(spawnObjects[selection]);
}
}
}

Related

I want to enable my collider again after disabling it

When my character hits the gameobject collider, an enemy will be spawned and the collider is disabled, cuz I do not want to spawn multiple enemies. When my character dies and I have to start from the beginning, the collider should be enabled again to spawn the enemy again.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerSpawner : MonoBehaviour
{
public EnemySpawn enemyspawn;
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
enemyspawn.SpawnEnemy();
gameObject.GetComponent<BoxCollider2D>().enabled = false;
}
}
}
//in other class
private void SetHealth(float health)
{
var actualNextHealth = Mathf.Min(m_maxHealth, health);
m_currentHealth = actualNextHealth;
if (m_healthBar != null && m_maxHealth > 0f)
m_healthBar.SetHealth(actualNextHealth / m_maxHealth);
if (m_currentHealth <= 0f)
{
UpdateHighscore();
Die();
}
}
private void Die()
{
m_character.NotifyDied();
if (m_canRespawn)
{
SetVulnerable();
RemovePoison();
m_hazards.Clear();
gameObject.transform.position = m_spawnPosition;
SetHealth(m_maxHealth);
}
else {
Destroy(gameObject);
}
}
You can create a static variable in the trigger script that you assign the Collider value to it.
When an enemy is spawned it deactivates, as in your code.
public class TriggerSpawner : MonoBehaviour
{
public static Collider2D spawnCollider;
public EnemySpawn enemyspawn;
void Start() => spawnCollider.GetComponent<Collider2D>();
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Player"))
{
enemyspawn.SpawnEnemy();
spawnCollider.enabled = false;
}
}
}
When you die, it will reactivate.
private void Die()
{
m_character.NotifyDied();
if (m_canRespawn)
{
TriggerSpawner.spawnCollider.enabled = true;
SetVulnerable();
RemovePoison();
m_hazards.Clear();
gameObject.transform.position = m_spawnPosition;
SetHealth(m_maxHealth);
}
else {
Destroy(gameObject);
}
}
With minimal changes on your code, I'd suggest this:
private void Die()
{
m_character.NotifyDied();
if (m_canRespawn)
{
SetVulnerable();
RemovePoison();
m_hazards.Clear();
gameObject.transform.position = m_spawnPosition;
SetHealth(m_maxHealth);
gameObject.GetComponent<BoxCollider2D>().enabled = true; // add this line
}
else {
Destroy(gameObject);
}
}

I can't Save Multiple PlayerPrefs on my Main Menu

Hi I'm a student in game development, making a game where players can grab certain amounts of coins on each maps, every maps has different type of coins data, like a highscore. But it only saves 1 playerprefs only. Why is that happening?
This is my Scene Management Script;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;
public class changeScene : MonoBehaviour
{
public TextMeshProUGUI desertCoinAmount;
public TextMeshProUGUI plainsCoinAmount;
void Update()
{
desertCoinAmount.text = PlayerPrefs.GetInt("DesertCoins").ToString();
plainsCoinAmount.text = PlayerPrefs.GetInt("PlainsCoins").ToString();
}
public void mainMenu()
{
SceneManager.LoadScene("mainMenu");
PlayerPrefs.Save();
}
public void desertLevel()
{
SceneManager.LoadScene("ancientDesertLEVEL");
Time.timeScale = 1f;
PlayerPrefs.Save();
}
public void plainsLevel()
{
SceneManager.LoadScene("Plain Biome");
Time.timeScale = 1f;
PlayerPrefs.Save();
}
public void jungleLevel()
{
SceneManager.LoadScene("Jungle Biome");
Time.timeScale = 1f;
}
}
And This is my PlayerController;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PlayerController : MonoBehaviour
{
public GameObject winPopup, losePopup;
public GameObject heart1, heart2, heart3;
public float gravityScale = 10f;
private Rigidbody rb;
public TextMeshProUGUI coinText;
public AudioSource coinSound;
int coin_sumDesert;
int coin_sumPlains;
int life_sum = 3;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
GetComponent<Rigidbody>().AddForce(Physics.gravity * gravityScale, ForceMode.Force);
}
void Update()
{
PlayerPrefs.SetInt("DesertCoins", coin_sumDesert);
PlayerPrefs.SetInt("PlainsCoins", coin_sumPlains);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Coins")
{
coin_sumDesert++;
coinText.text = coin_sumDesert.ToString();
Destroy(other.gameObject);
coinSound.Play();
}
if (other.gameObject.tag == "PlainsCoins")
{
coin_sumPlains++;
coinText.text = coin_sumPlains.ToString();
Destroy(other.gameObject);
coinSound.Play();
}
if (other.gameObject.tag == "finishLine")
{
winPopup.SetActive(true);
}
if (other.gameObject.tag == "obstacles")
{
Debug.Log("Collide Detected");
life_sum--;
if (life_sum == 2)
{
heart1.SetActive(false);
}
else if (life_sum == 1)
{
heart2.SetActive(false);
}
else if (life_sum == 0)
{
heart3.SetActive(false);
losePopup.SetActive(true);
Time.timeScale = 0.0f;
}
}
}
}
I would appreciate the reply (this is my first time using StackOverflow xD)
If you have multiple PlayerController then obviously they write to the same PlayerPrefs keys.
Whenever you save a player's data, make sure you differentiate them e.g. Score1, Score2, etc.
This is a way among many others to achieve it:
The player, very simple, append index to player pref to differentiate among many:
public sealed class Player : MonoBehaviour
{
private const string ChocolateBarsKey = "ChocolateBars";
[SerializeField]
[HideInInspector]
private int Index;
private int ChocolateBars
{
get => GetInt(ChocolateBarsKey);
set => SetInt(ChocolateBarsKey, value);
}
private int GetInt([NotNull] string key, int defaultValue = default)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return PlayerPrefs.GetInt($"{key}{Index}", defaultValue);
}
private void SetInt([NotNull] string key, int value)
{
PlayerPrefs.SetInt($"{key}{Index}", value);
}
[NotNull]
internal static Player Create([NotNull] GameObject parent, int index)
{
if (parent == null)
throw new ArgumentNullException(nameof(parent));
var controller = parent.AddComponent<Player>();
controller.name = $"{nameof(Player)} {index}";
controller.Index = index;
return controller;
}
}
The factory, scriptable singleton won't lose state on assembly reload, whereas if you'd used a static int for player count, it would reset itself to zero at assembly reload because static fields are not serialized by Unity.
public sealed class PlayerFactory : ScriptableSingleton<PlayerFactory>
{
[SerializeField]
private int PlayerCount;
[NotNull]
public Player Create(GameObject parent)
{
return Player.Create(parent, ++PlayerCount);
}
}
Now if you don't want to store score data within Player, it'll be another pattern. I leave that to you as an exercise.

Find and return nearest gameObject with tag, Unity

im trying to make a game where you have to merge 2 gameObjects to get the next one and so one. First i tried so that when the gameobject is released and it's touching another gameobject it deletes both of those and then instantiates the next one. But this didn't quite work bc of the triggers "covering" each other or some other wierd but. So i thought i could detect the collision so that every gameobject that touches the trigger gets added to a list and then removed on exit. What i'm not trying to figure out is how to calculate the distance between the gameObjects and if they are close enough (mergingthreshold) then they both get destryed and the next gameobject gets instantiated, but i can't quite figure out how to get this distance calculating and returning of what gameObject that is. So all help would be appreciated! (don't leave out any details, cause I'm quite the beginner)
Thanks!
Here's the code I've gotten so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Merging : MonoBehaviour
{
List<GameObject> NearGameobjects = new List<GameObject>();
void Start()
{
}
void Update()
{
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Enter!");
NearGameobjects.Add(col.gameObject);
}
}
void OnTriggerExit2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Exit!");
NearGameobjects.Remove(col.gameObject);
}
}
}
If you're going to use the list and have added all objects into this list you could use a for each loop and check the distance between your main object and all the other objects in the list if its closer add that to the closets object
public class NewBehaviourScript : MonoBehaviour {
List<GameObject> NearGameobjects = new List<GameObject>();
GameObject closetsObject;
private float oldDistance = 9999;
private void Something()
{
foreach (GameObject g in NearGameobjects)
{
float dist = Vector3.Distance(this.gameObject.transform.position, g.transform.position);
if (dist < oldDistance)
{
closetsObject = g;
oldDistance = dist;
}
}
}
}
You could just order your list by the distance using Linq OrderBy:
using SystemLinq;
...
public void CheckNearest()
{
if(NearGameobjects.Count == 0) return;
// This orders the list so the closest object will be the very first entry
var sorted = NearGameobjects.OrderBy(obj => (col.transform.position - transform.position).sqrMagnitude);
// currently closest
var closest = sorted.First();
if (Vector3.Distance(this.gameObject.transform.position, closest.transform.position) < mergingThreshold)
{
Destroy(gameObject);
Destroy(closetsObject);
Instantiate(NextPF, transform.position, Quaternion.identity);
}
}
private void OnTriggerEnter2D(Collider2D col)
{
// Rather use CompareTag
if (col.CompareTag(tag))
{
Debug.Log("Enter!");
NearGameobjects.Add(col.gameObject);
}
}
private void OnTriggerExit2D(Collider2D col)
{
if (col.CompareTag(tag))
{
Debug.Log("Exit!");
NearGameobjects.Remove(col.gameObject);
}
}
For anyone wondering, this was the total code that worked perfectly thanks to CubeCrafter360!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;
public class Merging : MonoBehaviour
{
List<GameObject> NearGameobjects = new List<GameObject>();
GameObject closetsObject;
private float oldDistance = 9999;
public GameObject NextPF;
public float mergingThreshold = 0.3f;
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Enter!");
NearGameobjects.Add(col.gameObject);
}
}
void OnTriggerExit2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Exit!");
NearGameobjects.Remove(col.gameObject);
}
}
public void CheckNearest()
{
foreach (GameObject g in NearGameobjects)
{
float dist = Vector3.Distance(this.gameObject.transform.position, g.transform.position);
if (dist < oldDistance)
{
closetsObject = g;
oldDistance = dist;
}
}
if (Vector3.Distance(this.gameObject.transform.position, closetsObject.transform.position) < mergingThreshold)
{
Destroy(gameObject);
Destroy(closetsObject);
Instantiate(NextPF, transform.position, Quaternion.identity);
}
}
}
And then i just called the function in my movement script:
private void OnMouseUp()
{
GetComponent<Merging>().CheckNearest();
}

Particle System not working on multiple enemies

I have a particle system for when the enemy is destroyed. I have multiple enemies coming from the same path (using the same prefab) and the particle system is only working on the first enemy. Can someone tell me why it is not working on the others as well? Thank you.
EnemyShooting scrip (this is where the piece of code is for the explosion):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShooting : MonoBehaviour {
[SerializeField] float EnemyLaserSpeed = 10f;
[SerializeField] float EnemyLaserFireTime;
[SerializeField] GameObject LaserBulletEnemyPreFab;
[SerializeField] int MaxNumberOfHits = 1;
public Transform explosion;
int CurrentNumberOfHits = 0;
Coroutine FireCoroutine;
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag == "PlayerLaser")
{
if (CurrentNumberOfHits < MaxNumberOfHits)
{
CurrentNumberOfHits++;
Destroy(collider.gameObject);
Score.ScoreValue += 2;//The user will be rewarded 1 point
}
if (explosion)//EXPLOSION CODE
{
GameObject exploder = ((Transform)Instantiate(explosion, this.transform.position, this.transform.rotation)).gameObject;
Destroy(exploder, 2.0f);
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
}
private void Fire()
{
FireCoroutine = StartCoroutine(ShootContinuously());
}
void BecomeVisible()
{
Fire();
}
IEnumerator ShootContinuously()
{
while (true)
{
GameObject LaserBulletEnemy = Instantiate(LaserBulletEnemyPreFab, this.transform.position, Quaternion.identity) as GameObject;
LaserBulletEnemy.GetComponent<Rigidbody2D>().velocity = new Vector2(0, EnemyLaserSpeed);
EnemyLaserFireTime = Random.Range(0.5f, 0.9f);
yield return new WaitForSeconds(EnemyLaserFireTime);
}
}
// Use this for initialization
void Start () {
BecomeVisible();
}
// Update is called once per frame
void Update () {
DestroyEnemy();
}
}
EnemySpawner : (I thought this script might help in a way so I attached it)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour
{
[SerializeField] GameObject EnemyPreFab;
[SerializeField] int MaxEnemies = 30;
[SerializeField] float EnemySpawnTime = 1.00001f;
[SerializeField] GameObject FirstWaypoint;
int CurrentNumOfEnemies = 0;
public int EnemiesToNextLevel = 7;
public int KilledEnemies = 0;
public LevelManager myLevelManager;
public static EnemySpawner Instance = null;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while (CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
if (Instance == null)
Instance = this;
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
public void OnEnemyDeath()
{
CurrentNumOfEnemies--;
/*
if (CurrentNumOfEnemies < 5)
{
// You killed everyone, change scene:
LaserLevelManager.LoadLevel("NextLevelMenu");
}
*/
KilledEnemies++;
if (KilledEnemies >= EnemiesToNextLevel)
{
LaserLevelManager.LoadLevel("NextLevelMenu");
}
}
}

Enable/disable mesh renderer of multiple gameobjects

How can I enable/disable mesh renderer of multiple gameobjects when the player enters in a collider? This is my code but it doesn't works.
using UnityEngine;
using System.Collections;
public class SueloManager : MonoBehaviour {
private GameObject suelo;
void Start ()
{
suelo = GameObject.FindGameObjectsWithTag ("SueloWireframe");
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player") {
suelo.GetComponent<Renderer> ().enabled = false;
Debug.Log ("Oculta suelo");
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player") {
suelo.GetComponent<Renderer> ().enabled = true;
Debug.Log ("Aparece suelo");
}
}
}
FindGameObjectWithTag returns a single GameObject and FindGameObjectsWithTag returns array of GameObject. Just like Kroltan mentioned, you have to change suelo to an array then use loop to enable and disable all of them. Having the loop in a simple re-usable function should simplify this. Look at the EnableRenderer function in the solution below.
private Renderer[] sueloRenderers;
void Start()
{
GameObject[] suelo = GameObject.FindGameObjectsWithTag("SueloWireframe");
sueloRenderers = new Renderer[suelo.Length];
for (int i = 0; i < sueloRenderers.Length; i++)
{
sueloRenderers[i] = suelo[i].GetComponent<Renderer>();
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
EnableRenderer(sueloRenderers, false);
Debug.Log("Oculta suelo");
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
EnableRenderer(sueloRenderers, true);
Debug.Log("Aparece suelo");
}
}
void EnableRenderer(Renderer[] rd, bool enable)
{
for (int i = 0; i < rd.Length; i++)
{
rd[i].enabled = enable;
}
}

Categories