Stop onDestroy on Scene Load.Unity 2d - c#

So I have a script that adds money when enemy dies (onDestroy). But when the scene changes that also destroys the enemies and adds money. How I can make it only add money when enemy is destroyed by health=0 and not on scene change?
Code:
void onDestroy()
{
AddCoins(Coins);
}

I can see two main ways to do this :
If it's possible, call the AddCoins Method in the code that calls Destroy() when health =0 instead of in OnDestroy(). Something like :
if(health == 0){
AddCoins(coins);
Destroy(gameObject);
}
and no OnDestroy method.
Otherwise, you could set a variable to true just before loading a new scene, and in OnDestroy() check if this variable is false, and only then adding coins.
If it's compatible with your game, I would recommend for the first solution, much cleaner in my opinion.

You can use scene.isLoaded to only check the times when the scene is loaded and is not changing.
void OnDestroy()
{
if(gameObject.scene.isLoaded)
{
AddCoins(Coins);
}
}

I would make it so that the AddCoin() method gets called not on destroy but instead have a check either in the update loop or ideally in your enemy damage function so it gets called only when health changes.
void Update()
{
if(health == 0)
{
AddCoins(Coins)
Destroy(gameobject)
}
}
if you want to use your existing code structure and still have it be called onDestory you could add another check
void onDestroy()
{
if(health == 0)
{
AddCoins(Coins);
}
}

Related

Lives Implementation in Unity2D

I begun the trek into Unity2D development a few months ago, so i'm still fairly new to all the unity engine jargon. In my game, I decided to implement the use of 'Lives'. I scripted out what (in my eyes) should work but every time the player dies instead of decrementing the lives counter and restarting it at the scene, it immediately loads the scene 1 (i.e. the GameOver screen) Is my logic here on this page incorrect or is there just a better way overall to handle lives than PlayerPrefs? (ALSO: Its worth mentioning that the Lives are instantiated/sent to playerprefs in the player script, I don't figure I need to include that one here)
This is the main block of code on my destroyer objects' script to account for 'death':
public static int lives;
void Start()
{
lives = PlayerPrefs.GetInt("lives");
}
void OnTriggerEnter2D(Collider2D other)
{
//If the trigger happens to be tagged as a 'Player', does this.
if (other.tag == "Player")
{
lives--;
if (lives < 0)
{
PlayerPrefs.DeleteAll();
SceneManager.LoadScene(1);
} else
{
SceneManager.LoadScene(0);
PlayerPrefs.SetInt("lives", lives);
}
}
if (other.gameObject.transform.parent)
{
Destroy(other.gameObject.transform.parent.gameObject);
}
else {
Destroy(other.gameObject);
}
}
As I mentinoned in comment, I think you forget to swithOn isTrigger field from Inspector which is under the Collider area.
Probably you thought like you are using onCollisionEnter. Also if you check the differences between OnTriggerEnter and OnCollisionEnter it will be very helpful.
As #Ugur Tufekci answers, But make sure to add BoxCollider2d not Box Collider.The simple box collider is 3d but your are detecting that onTriggerEnter2D that means 2d box collider has to be added

Is this the right way i should be coding my game to reset the score once the player dies?

Score does not reach 0 once the player dies.
I tried all different kinds of methods and changing values but nothing seemed to work.
{
//when the player's box collider collides with another box collider
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
//scene moves to a game over screen
SceneManager.LoadScene("Player Death");
//Reset score everytime player dies
void resetScore()
{
PlayerPrefs.SetInt ("Score", 0);
}
}
}
// Start is called before the first frame update
void Start()
{ }
// Update is called once per frame
void Update()
{
}
}
It was just building the score up from where it left off and didnt do as i programmed.
You are declaring a new method resetScore() when a player dies, but you're never calling it.
What you should do is declare the method outside of OnTriggerEvent() and call it when a player dies:
//when the player's box collider collides with another box collider
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
//scene moves to a game over screen
SceneManager.LoadScene("Player Death");
//Reset score everytime player dies
resetScore();
}
}
// Start is called before the first frame update
void Start()
{ }
// Update is called once per frame
void Update()
{
}
void resetScore()
{
PlayerPrefs.SetInt("Score", 0);
}
Have you tried to reset the score right when the player dies? I assume you reset it on game start. Try doing it right after the player dies, or use it in a constructor when creating a new game/player. It isn´t clear how you implemented it, it would help if you post more pieces of code.
[EDIT]
Firstly, you are declaring the ResetScore Method, but you don´t call it to execute. So you need to declare it outside the "OnTriggerEnter" method, and then call it inside the method like this :
ResetScore();
Secondly, Why don´t you try to use property instead of SetInt?
public int Score { get; set;}
Then you can change it like this :
if (other.gameObject.tag == "Player")
{
Player.Score = 0;
//scene moves to a game over screen
SceneManager.LoadScene("Player Death");
//Reset score everytime player dies
}

OnTriggerEnter2D Called Twice in Unity

Hi guys I am having a problem in unity with OnTriggerEnter2D.
The player has 2 bullets, any time the player bullet hits the enemy it should increment the score by 100, but for some reason it is incrementing the score by 200 if two player bullets hit the enemy. Both bullets fire at the same time and are at either side of the players gun. So it should only ever increment the score by 100 and not 200.
Here is my code:
void OnTriggerEnter2D(Collider2D col)
{
if(col.tag == "PlayerBullet")
{
this.score += 100;
}
}
Other information:
My player bullet and enemy both have Box Collider and RigidBody2D attached to them. They both have the isTrigger option checked.
If you want it to work just once you can ignore collision after first increment of the score(aka after first collision) like this:
void OnTriggerEnter2D(Collider2D col)
{
if(col.tag == "PlayerBullet")
{
this.score += 100;
Physics2D.IgnoreCollision(col, GetComponent<Collider2D>())
}
}
Note that After this point all collisions between PlayerBullet and Enemy will be ignored.
If I understand it correctly, the player has two guns, and I assume they are fired at the same time.
In this case, we can make it more beautiful with condition-condition statements.
I'm leaving the code block below.
if (Input.GetMouseButtonDown(0) && Input.GetMouseButtonDown(1))
{
Debug.Log("Fire Metod(Score)");
}
else
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Fire Metod(Score)");
}
if (Input.GetMouseButtonDown(1))
{
Debug.Log("Fire Metod(Score)");
}
}
Often times you can set a flag to tell the game to only increment once. A flag is just a bool value, and can be private within the scope of the class. Sort of like:
if(canHit)
{
canHit = false;
//add score
canHit = true;
}
That basic formula should get you the results you want.
I'm upgrading my comment to an answer because after reading your question and the comment responses I believe you need a non-code answer.
You want two objects to work together and count as one 'hit' when they collide with another object. That sounds to me like a situation where you should be placing those objects inside another one, let's call it a 'shot' object, and you should be doing collisions and whatnot based on that. That way, if both bullets hit only one 'shot' hit, so it will count with the code as-is and it sounds like it will be implemented more as you've expected it to be.

How can I call IEnumerator when a boolean is true?

I'm using the IEnumerator function and have had some issues with my if statement working:
IEnumerator Spawning()
{
Debug.Log("Spawning being called...");
if (GameObject.Find("FPSController").GetComponent<BoxCollide>().hitTrigger == true)
{
Debug.Log("CUSTOMER SHOULD SPAWN!");
while (countStop == false) {
yield return new WaitForSeconds(2);
Debug.Log("Fisher Spawned!");
counter++;
spawnNewCharacter.SpawnCharacter();
if (counter >= 3){
countStop = true;
}
}
}
}
After some debugging, it turns out that my if statement actually works. This issue is actually the IEnumerator function being called as soon as I run my game. I need a way to call this IEnumerator function only when my hitTrigger == true, but I can't seem to get anything to work.
I've tried this on top of the IEnumerator function:
void Update()
{
if (GameObject.Find("FPSController").GetComponent<BoxCollide>().hitTrigger == true)
{
Debug.Log("Spawning now...");
StartCoroutine(Spawning());
}
}
But still can't even get any of the Debug.Log's to come through. Would appreciate some help on this!
Side Information
Find() and GetComponent()
You don't want to use GameObject.Find(...) in the Update method, as it's an expensive call. The Update method is called each frame, so you'd call GameObject.Find(...) 60 times in 1 second at 60fps.
So when you use GetComponent() or Find() you want to save a reference to these objects like shown in the snippets below.
Better locations to use methods like GetComponent() or GameObject.Find() are the Awake() and Start() methods.
Awake
Awake is used to initialize any variables or game state before the
game starts. Awake is called only once during the lifetime of the
script instance. Awake is called after all objects are initialized so
you can safely speak to other objects or query them using eg.
GameObject.FindWithTag. [...]
Explanation is taken from the linked documentation.
Start
Start is called on the frame when a script is enabled just before any
of the Update methods is called the first time. Like the Awake
function, Start is called exactly once in the lifetime of the script.
However, Awake is called when the script object is initialised,
regardless of whether or not the script is enabled. Start may not be
called on the same frame as Awake if the script is not enabled at
initialisation time.
Explanation is also taken from the linked documentation
Possible Solution
Add the first Component (FPSControllerCollission) onto the object that holds your FPSController.
It makes use of unities OnTriggerEnter & OnTriggerExit methods.
This script is gonna set the IsTriggered bool to true, when a trigger entered the space of the box collider.
Note: A collider acts as a trigger, when the "Is Trigger" checkbox on
the component is checked.
You can do similar with OnCollisionEnter/Exit to recognize, when a Collider enters the space of the box collider.
Note that the following is only an example and you'll have to tweak /
integrate it into your code
[RequireComponent(typeof(BoxCollider))]
public class FPSControllerCollission : MonoBehaviour {
public bool IsTriggered;
private void OnTriggerEnter(Collider other) {
this.IsTriggered = true;
}
private void OnTriggerExit(Collider other) {
//Maybe check the tag/type of the other object here
this.IsTriggered = false;
}
}
The following SpawnController class could be integrated in the class you allready have.
public class SpawnController : MonoBehaviour {
private FPSControllerCollission _fpsControllerCollission;
private void Awake() {
this._fpsControllerCollission = FindObjectOfType<FPSControllerCollission>();
}
private void Update() {
if (this._fpsControllerCollission.IsTriggered) {
StartCoroutine(nameof(Spawning));
}
}
IEnumerator Spawning() {
Debug.Log("Spawning being called...");
if (this._fpsControllerCollission == true) {
Debug.Log("CUSTOMER SHOULD SPAWN!");
bool countStop = false;
int counter;
while (countStop == false) {
yield return new WaitForSeconds(2);
Debug.Log("Fisher Spawned!");
counter++;
spawnNewCharacter.SpawnCharacter();
if (counter >= 3) {
countStop = true;
}
}
}
}
}

Is it possible to call a function on Unity Program Start?

I was wondering if there was a way in Unity that when I start my program on a scene it fires a function first, I should add that I want this one function to work regardless of what scene I'm in. So a simple Start function wont cut it. Not sure if this is possible in Unity?
public void ProgramBegins()
{
//FIRES FIRST ON ANY SCENE
//DO STUFF
}
RuntimeInitializeOnLoadMethodAttribute can help you :)
using UnityEngine;
class MyClass
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void OnBeforeSceneLoadRuntimeMethod()
{
Debug.Log("Before scene loaded");
}
}
Here you can find the execution order of all functions in unity: http://docs.unity3d.com/Manual/ExecutionOrder.html
Awake is the first function to be executed in your standalone application. For it to run you need to have a GameObject with a attached script containing the Awake function. This should be added to every scene if you want it to run regardless of the scene.
You still have to decide what is the startup scene of your game. So it is enough to add the GameObject there, if you actually want it to run just in the start of the program.
I use a prefab _AppStartup which is just an empty game object having a script AppStartup. Drag this in every scene and configure AppStartup to be executed as first like #maZZZu has stated.
AppStartup performs the following jobs:
Global initialisation tasks when the app is started
Switch to boot scene if have start an arbitrary scene in editor mode
Scene specific initialisation tasks
public class AppStartup : MonoBehaviour
{
const int bootSceneNo = 0;
public static bool veryFirstCallInApp = true;
void Awake ()
{
if (veryFirstCallInApp) {
ProgramBegins ();
if (Application.loadedLevel != bootSceneNo) {
// not the right scene, load boot scene and CU later
Application.LoadLevel (bootSceneNo);
// return as this scene will be destroyed now
return;
} else {
// boot scene stuff goes here
}
} else {
// stuff that must not be done in very first initialisation but afterwards
}
InitialiseScene ();
veryFirstCallInApp = false;
DestroyObject (gameObject);
}
void ProgramBegins()
{
// code executed only once when the app is started
}
void InitialiseScene ()
{
// code to initialise scene
}
}
So all you have to do is drag this prefab in every scene manually and give it -100 or whatever in the script execution order. Especially when the project grows and relies on a predefined scene flow it will save you al lot time and hassle.
Yes...
Decorate your method with [RuntimeInitializeOnLoadMethod].
It will be invoked as soon as the scene has finished loading (after Awake events).
[RuntimeInitializeOnLoadMethod]
static void OnRuntimeMethodLoad() {
Debug.Log("After Scene is loaded and game is running");
}
Documentation: https://docs.unity3d.com/ScriptReference/RuntimeInitializeOnLoadMethodAttribute.html
//try this it work in my case
public static bool isFirstLoad;
void Awake()
{
//your work that run only once even restart this scene
mainPanel.SetActive(!isFirstLoad);
if(!isFirstLoad)
{
isFirstLoad=true;
}
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(this.gameObject);
}
}

Categories