Show dialog only once, not after level restart - c#

I have my own dialog system which is basically a gameobject with a collider. After triggering collider, Canvas with a Text component should show up. So far, this works. Now, I want to make it happen only once. Here how its work: I start level trigger showing dialog. Game is pretty hardcore so player probably will die. When player dies, I call Application.LoadLevel(Application.loadedLevel); (so basically I restart level)
If I use something like this in scrip with collider
private static bool firstTimeText = true;
void OnTriggerEnter2D(Collider2D coll)
{
if (coll.tag == "Player" && firstTimeText)
{
GetComponent<BoxCollider2D>().enabled = false;
firstTimeText = false;
}
}
everything works like a charm. EXCEPT if I make copy of this gameobject, static variable in script will "handle" that every instantion of this object will have firstTimeText on false after trigger dialog first time. So basically I can use it only once.
So is there any sollution for making trigger which run only once and not reset after Application.LoadLevel?
Also this doesn't work
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}

Static variables are globally identical - therefore if you need to have multiple gameobjects exhibiting this behavior, they'll need to hook into different static values in some way.
One way to do this would be to have a key, and then keeping a static list of all "keys" already displayed. My recommendation would be a HashSet - thus
private static HashSet<string> firstTimeSet = new HashSet<string>();
public string singleRunKey;
void OnTriggerEnter2D(Collider2D coll)
{
if (coll.tag == "Player"
&& firstTimeSet.Add(singleRunKey))
{ GetComponent<BoxCollider2D>().enabled = false; }
}
Note that .Add returns true if the item wasn't in the collection (and adds it), but false if it already was (and does not add it, because no duplicates). All you have to do now is assign singleRunKey a unique value per object via the Inspector, and each one will run exactly once

Consider just having a static class that will set a flag to indicate if the dialog should appear or not. Note that I am not inheriting from MonoBehaviour here.
public static class ApplicationData {
public static bool ShowDialog = true;
}
Usage:
if (ApplicationData.ShowDialog)
{
ShowDialog();
ApplicationData.ShowDialog = false;
}
The static class will retain its values during the lifetime of your application. So, it will retain the FALSE value even if you reload your scene.

Related

How do I prevent the destroying of all instances of an object in Unity?

I have a prefab object called Beam, which contains several things but one is an object that when an instance of it is collided with and triggered, should destroy itself.
Currently, I have the script that generates all of the instances on a variable called Beams. Shown here:
When that runs, it creates clones within it. Seen here:
You will also see in the last image, the Beam prefab that contains the Cookie in it. That cookie is where I have a script that says, if I hit it, destroy. That code looks like this:
...
public class Collectibles : MonoBehaviour
{
GameManager game;
// Start is called before the first frame update
void Start()
{
game = FindObjectOfType<GameManager> ();
}
...
void OnTriggerEnter2D(Collider2D other) {
if(other.tag == "Player"){
string coinType = "Cookie";
game.AddCollectible(coinType);
Destroy(gameObject);
}
}
}
Currently, when I run into a cookie, it runs Destroy(gameObject) and destroys ALL instances of the cookie (one per each Clone).
This code lives on Cookie, not on Beams. Is that correct? Should I have the code somewhere else? I also tried Destroy(this) but that doesn't do what I thought it would do (just the instance).
Is it possible that from where I was calling Destroy, the script doesn't have access to the instances, or am I missing something? Thank you in advance!
If I understand your question, you want that when the "player" collides with an instance of "beam" only destroy the instance of Cookie (or the gameobject that contains the script), in this case it would do so with the tag:
public GameObject[] arrayofcookie;
public int destroyedinstances=1;
//this int will tell how many instances you want to be destroyed (from the last instantiated to the first)
//for this example the last instance will be deleted
public void destroyCookie()
{
arrayofcookie= GameObject.FindGameObjectsWithTag("Cookie");
for (int i = 0; i < destroyedinstances; i++)
{
Destroy(arrayofcookie[i].gameObject);
}
}
You call this method in the cookie script, in the collider or if you prefer with an invoke method after N seconds.
I do not think I have understood your question very well, but in these problems I prefer to use the label and it also depends on the nature of your game

Create a new instance and override the class

My problem is that I have a plugin and on that plugin I need to get a specific boolean type variable. It has 2 script, here's the script.
RCC_SETTING : ScriptableObject
// Main Controller Settings
public bool useAutomaticGear = true;
RCC_CARCONTROLLERV3
public bool automaticGear{get{return RCCSettings.useAutomaticGear;}}
Now I created my own script called
MANUALTRANSMISSION
private void Start()
{
RCC_Settings RCCSettings = new RCC_Settings();
RCCSettings.useAutomaticGear = false;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
this.GetComponent<RCC_CarControllerV3>().currentGear += 1;
}
}
What it does is that it doesn't change gear until I press the space. Now the problem is that all of gameobject that has a RCC_CARCONTROLLER script is affected. I want to something like override that so that only my player will be the one who's going for a MANUAL TRANSMISSION.
This should be done easily. First, create this flag bool in your RCC_CarControllerV3 like this:
public bool isPlayer;
Then, check it on the inspector of your Player (only on it).
And then, instead of this:
this.GetComponent<RCC_CarControllerV3>().currentGear += 1;
Do this:
var controller = this.GetComponent<RCC_CarControllerV3>();
if(controller.isPlayer)
controller.currentGear += 1;
But the weird fact here is that you mentioned that this value is changed for all your gameObjects (that has this Component). Because GetComponent will only retrieve the top-level Components of your main gameObject.
To do this, you would need to use GameObject.GetComponentInChildren. But you're not doing this...
Please, I would suggest you to edit your question and put more code. In this way, we could help you better.

How to get access to the target that has been instantiated in Unity?

I am making a game where I need to spawn a player in the start of the game. Then I will get transform.position of that player but I dont know how to do that. Here is my code:
GameObject playerball;
Transform spawpoint;
bool spaw
private void Start(){
spaw = false;
}
private void Update()
{
if(!spaw)
{
Instantiate(playerball, spawpoint.position, spawpoint.rotation);
spaw=true;
}
//????????????
}
As you see, I have no idea how to get access to that playerball. That playerball is the prefab that holds the control script, and this script has 'Transform public Camera' but in the prefab 'public Transform Camera ' is 'None(Transform)'. So I need that, when I have instantiated the playerball, I can control it, that mean I can set which is 'public Transform camera'. How can I do that?
Instantiate returns a reference to the object you have just instantiated. You just have to keep that reference, and to work on it for your own needs:
GameObject instance = Instantiate(playerball, spawpoint.position, spawpoint.rotation);
Also, you are missing a semicolon on this line:
bool spaw
Whole code would then give:
GameObject playerball;
Transform spawpoint;
bool spaw;
GameObject playerballObject;
private void Start(){
spaw = false;
}
private void Update()
{
if(!spaw)
{
playerballObject = GameObject.Instantiate(playerball, spawpoint.position, spawpoint.rotation);
spaw=true;
// Do any first time modification you need on your object.
}
// Use your reference there to update the state of your object.
}
Alternative script, making a few changes that Isuka did not, for efficiency, clean-code, and good practice.
public GameObject playerballPrefab;
public Transform spawpoint;
private GameObject playerballInstance;
private void Update()
{
if(playerballInstance == null)
{
playerballInstance = Instantiate(playerballPrefab, spawpoint.position, spawpoint.rotation);
// Other spawn logic / modification
}
//do stuff with playerballInstance every frame
}
Notice that we no longer need the bool spawn variable (er, spaw which is misspelled) as we can check to see if the playerballInstance is null, and if it is, spawn it. Any time it is null (e.g. if it is destroyed by any means) we will spawn a new one (which I renamed to playerballPrefab just so that the variable's name indicates that this object is not in the scene hierarchy; purely optional), just as if spawn was reset to false by an external script, but without any potential conflicts (e.g. setting spawn to false without destroying the instance and vice versa).
Additionally we hold that reference in perpetuity so that the ball can be acted upon continuously, rather than for the single frame it was instantiated. Isuka edited their answer to include this change.
playerballInstance can be set as either public (because we wish an outside script to modify the value) or private (because no other script should be touching it). The prefab and transform are declared public because presumably they are defined in the editor / inspector (and private fields are not listed and not serializable).
Start method is no longer needed because we no longer have a spawn value to make sure is set as we want (as well as not needed originally, as the default value for a bool is false and the fact that we could declare bool spawn = false without the Start() method anyway).
Marking Start() and Update() as private is also not strictly necessary, but not wrong. Similarly marking it public is not necessary or wrong. I personally either leave it off (inheriting its protection level from MonoBehaviour) or mark it public (as all MonoBehaviours have this method declared). It makes no difference to the engine what protection level is used, as it is invoked through voodoo.

Unity3d: Adding gameobject to List from an array

Thanks for help in advance. Here is a short snippet of the code that I am having an issue with.
GameObject[] allMotor_array;
public List<GameObject> BrokenMotor_list = new List<GameObject>();
void Start()
{
allMotor_array = GameObject.FindGameObjectsWithTag ("Motors");
}
void Update()
{
foreach (GameObject motor in allMotor_array)
{
if(motor.GetComponent<Pump_event>().damaged)
{
BrokenMotor_list.Add(motor);
}
}
}
I have an array of Gameobjects that is created on Start, each of the gameobjects in the array have a script called Pump_event. What I want to do is add the gameobject with a true boolean (damaged) to the list so that I can create a GUI list of all the motors that are damaged (and then take further action on those motors).
With the current code it instantiates the array fine, but when One of the motors boolean changes to true the list tends to continuously add the motor gameobject to the list on each update cycle. So what I want is to figure out a way of adding the gameobject to the list ONCE.
Having it in the update() is probably not the best method but I really am stuck on how to approach this.
G
The Solution to my problem
Thanks for your answers, you all had well thought out responses. I appreciate it. I didn't go with 1 persons method but instead adapted logical approaches found here to work with my script/s.
Here is what I did.
In my pump_event script the events are sorted in a Case and switch as damage increased on the pump the event would escalate. So I added in a section to that script to include "reporting" the damage.
public class Pump_event : MonoBehaviour
//The damage has taken place and event_category=0\\
switch (event_category)
{
case 0:
Master_script.GetComponent<Control_room>().AddtoList (gameObject);
event_category = 1;
break;
I took advice not to insert these types of programing and placed it into its separate class which works out well.
public class Master_script: MonoBehaviour
public void AddtoList(GameObject motor_tobadded)
{
BrokenMotor_list.Add(motor_tobadded);
}
This also eliminated the need on having an array holding all of the pump event controllers as well.
Now the script all works fine. It may not be most efficient but it is doing its job.
Thank you again to all that helped.
In your Pump_event Script you can have a event Action which you register in this snippet and whenever damaged is set true you need to fire the event.
Example:
// in Pump_event Class
public static event Action<GameObject> OnDamagedValueChanged;
private bool _damaged;
public bool Damaged
{
get { return _damaged;}
set
{
_damaged = value;
if(_damaged)
{
if(OnDamagedValueChanged != null)
OnDamagedValueChanged(gameObject);
}
}
}
In your Current Class where you have array of GameObjects:
void OnEnable()
{
Pump_event.OnDamagedValueChanged += HandleOnDamagedValueChanged;
}
void OnDisable()
{
Pump_event.OnDamagedValueChanged -= HandleOnDamagedValueChanged;
}
void HandleOnDamagedValueChanged(GameObject obj)
{
if (!BrokenMotor_list.Contains (obj))
{
BrokenMotor_list.Add (obj);
}
}
Using Actions is a better approach than doing it in Update Method. It is not good for performance to keep checking for a bool in iteration in update method. and try to avoid GetComponent and Find/FindObjectWithTag Methods in Update. It is not good practice. I hope this is helpful.
According to the code you have posted, the problem lies within the fact that the damaged property is never reset. One solution would be to reset this property once you add it to the list, like so:
if(motor.GetComponent<Pump_event>().damaged)
{
motor.GetComponent<Pump_event>().damaged = false;
BrokenMotor_list.Add(motor);
}
However, multiple copies of the same object could still be added to your list if the motor is damaged again.
To go around this, you could use a HashSet. The hash set will allow only one copy of an object to exist within it, thus, if an object is already present is will not be added again.
The catch is that you will need to override the GetHashCode and Equals methods for your GameObject class since these will be used internally by the hash set to place items within itself and identify duplicates.
check if list already contains motor.
if(motor.GetComponent<Pump_event>().damaged)
{
if(BrokenMotor_list.Contains(motor))
{
BrokenMotor_list.Add(motor);
}
}
although on msdn describes how to implement IEquatable in case if you want compare different objects(with different references) https://msdn.microsoft.com/ru-ru/library/vstudio/bhkz42b3%28v=vs.100%29.aspx
if (!BrokenMotor_list.Contains (motor)) {
BrokenMotor_list.Add (motor);
}
You'd better do this after damage event occur by add a delegate.

Saving PlayerPrefs for multiple instances

I have setup a simple shop where when the player clicks to 'BUY' a character, the item gets unlocked and stays saved as unlocked. This is in one scene (Shop scene).
I have a second scene where the character gets unlocked based on this purchase for the player to be able to select (character scene).
I am placing the scripts on empty gameObjects on each scene. It works fine when it is just buying one character. But how do I replicate this for multiple characters.
I could place the scripts on individual buttons and place corresponding gameobjects under the inspector but this would not be practical if I have like a 100 characters.
Please advice how I could make this work across multiple instances. Thought of tagging and that too doesn't seem feasible. I am open for suggestions if there is a better way of doing this. Thanks.
//Class controlling Shop Scene via an empty Gameobject
public class ShopManager : MonoBehaviour
{
private bool unlocked;
public GameObject greyImg;
void Start()
{
unlocked = PlayerPrefs.GetInt("unlocked") == 1;
greyImg.SetActive(unlocked);
}
public void Buy()
{
unlocked = true;
PlayerPrefs.SetInt("unlocked", 1);
PlayerPrefs.Save();
greyImg.SetActive(true);
}
}
This is how the unity setup looks for shop scene. If the item is already bought, the grey image is set to active thus not allowing the user to click the green buy button any more.
When character is unlocked/bought
//Class controlling Character select scene via an empty Gameobject
public class CharacterManager : MonoBehaviour
{
private bool unlocked;
public GameObject greySelect;
void Start()
{
unlocked = PlayerPrefs.GetInt("unlocked") == 1;
}
void Update()
{
if (unlocked)
{
greySelect.SetActive(false);
}
}
}
This is how Unity setup looks for Character select scene. If the character is already unlocked, the grey select image is set to inactive and the orange select button will be visible thus allowing the character to be selected.
when character unlocked
There are probably many ways in which to tackle this problem. Here's one;
You're going to need a separate player prefs entry for each character. So, you'll need a nice way to keep track of the characters you have and their unlock state. Instead of saving 'unlocked' in player prefs, why not create a class that contains unlocked information?
class UnlockedCharacters
{
bool characterAUnlocked = false;
bool characterBUnlocked = false;
bool characterCUnlocked = true;
}
You can then serialize this whole class and save the whole class inside player prefs. Then, when you load your game you can load this class from player prefs to populate your character information. This way, data is managed and is consistent across saved states.
You could go one step further and keep everything relating to your characters inside of a Dictionary whereby your int is an enum referring to a character and the bool is its unlock state. You can then save/load this dictionary again using player prefs.
Inside the class you could have helper methods with your generic gameobject scripts call to keep things nice and encapsulated.
This way, your individual GameObjects which handle specific characters can hold a reference to this enum and you can use your general script to set/modify your data contents based on your enum field that you can then set via your inspector, or in initialisation code for your object.
EDIT FOR COMMENT BELOW - AN EXAMPLE:
Your generic character controller would go on the individual objects, and by changing your CHAR_TYPE the same script will work to unlock multiple characters.
class CharacterManager
{
public enum CHAR_TYPE = { characterA, characterB, characterC }
private Dictionary<CHAR_TYPE, bool> characterUnlockState;
void Start()
{
// Seeding with some data for example purposes
characterUnlockState = new Dictionary<CHAR_TYPE, bool>();
characterUnlockState.Add(CHAR_TYPE.characterA, false);
characterUnlockState.Add(CHAR_TYPE.characterB, false);
characterUnlockState.Add(CHAR_TYPE.characterC, true);
}
public bool IsCharacterUnlocked(CHAR_TYPE character)
{
if (characterUnlockState.Contains(character)) return characterUnlockState[character];
return false;
}
public void UnlockCharacter(CHAR_TYPE character)
{
if (characterUnlockState.Contains(character)) characterUnlockState[character] = true;
}
}
class GenericCharacterController
{
public CHAR_TYPE character;
public CharacterManager manager;
public void UnlockButtonPressed()
{
manager.UnlockCharacter(character);
}
}
There are a number of ways to approach this. You are going to need an ID of some kind for each item in your store. So I would make a StoreItem script that you place on each thing for sale. The StoreItem script would need an ItemID property at the minimum. Then your StoreManager could check if it's unlocked like this:
PlayerPrefs.GetInt(selectedStoreItem.ItemID + "_unlocked");
Also, while this approach would work, it's not recommended. It's possible for playerprefs to be manipulated by the user outside of your game. So they could potentially give themselves free items. I don't know how important that is to you. The better approach would be store this info on a server somewhere and have the game sync up with that to determine what items the player owns.

Categories