What could cause PlayerPrefs to not save a string? - unity3d

I have the below code. What could possibly make this not work? Other PlayerPrefs seem to work fine. The log always shows that it restores "" and yet it always saves my actual text.
EDIT:
I've just discovered that my "OnDisable" code is being called before "Start". I didn't really think that was possible, but that's the problem. So I guess my question is changing a bit...
EDIT 2:
OnDisable was being called before Start because another "Awake" function was disabling this object which apparently immediately runs OnDisable.
public InputField ModuleList;
void Start()
{
ModuleList.text = PlayerPrefs.GetString("ModuleSet", "");
Debug.Log("Restoring " + PlayerPrefs.GetString("ModuleSet", ""));
}
public void OnDisable()
{
Debug.Log("Saving " + ModuleList.text);
PlayerPrefs.SetString("ModuleSet", ModuleList.text);
}

call
PlayerPrefs.Save();
for saving data
Make sure you do not disable you GO before Start() method being called

Related

After pause menu score doesn't work anymore

does anyone know how to fix this? In my unity game when I go to pause menu and then continue playing, my scoreboard stops updating. I have two scoreboards, one in game and one in pause menu. The one in pause menu works well and updates but the one in game freezes after once visited in pause menu.
Here is my pausecodes and codes to add money (score):
public void PauseGame()
{
Time.timeScale = 0;
}
public void UnPauseGame()
{
Time.timeScale = 1;
}
}
if (collision.gameObject.tag == "Respawn") // When player lifts fish up
{
Destroy(this.gameObject);
// TODO: Player gets money (points) when this happens
textManager.instance.AddMoney();
Debug.Log("Add money");
}
public class textManager : MonoBehaviour
{
public static textManager instance;
public Text moneyText;
public int money;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
Data data = SaveSystem.LoadData();
money = data.balance;
moneyText.text = "Balance: " + money.ToString() + " $";
}
public void AddMoney()
{
money = money + 10;
moneyText.text = "Balance: " + money.ToString() + " $";
SaveSystem.SaveData(this);
}
public int findMoney()
{
return money;
}
}
Please ask more info if needed.
I have tried to delete the one scoreboard in pause menu and after that the in-game pause menu started working right, but I would like to have still that other scoreboard too.
if (collision.gameObject.tag == "Respawn") // When player lifts fish up
{
// TODO: Player gets money (points) when this happens
textManager.instance.AddMoney();
Debug.Log("Add money");
Destroy(this.gameObject);
}
As I said this is the fix for what you posted as for the pause unpause problem you have to post the actual code where you do the pause unpause behavior so can people help you out my friend. As for what you posted what you've been doing is destroying the script just before excuting the call to textManager.instance.AddMoney(); and this will never run in the order you set in your code.
The scope of a static field is global, this means there can only be one.
Your textManager (side note: a classes first letter has to be upper case) is certainly attached to multiple GameObjects, once to the object displaying the score in the scene UI and once to a UI element in the pause menu.
As soon as you pause your game the first time, the TextManager attached to the object in the pause menu will run Awake() and the instance (please capitalise your properties as well) will be overriden and the reference to the ingame TextManager gets discarded.
The sloppy fix is re-initializing the ingame TextManager when you unpause the game, assigning it to be the Instance again. I'd not recommend doing that though.
The better solution is to implement an event on the player that gets triggered when the score changes and making the player instance a Singleton object since there will be only one player in all circumstances.
The UI elements displaying the score can then subscribe to this event in OnEnable() and unsubscribe in OnDisable() (do not forget to unsubscribe from events).
Addendum: You should not destroy your object before all code has been executed. Your code will still work because of how things are managed on the C++ layer of Unity, but it is definitely bad practice.

I'm getting an UnassignedReferenceException when the reference is set. I've seen other questions similar but not exactly the same

As the title says i'm getting the UnassignedReferenceException error for a variable already set. Using ScriptableObjects im working on an inventory system(partially from scratch) and im trying to access the EquipmentUi in another class using a GameObject to hold the prefab containing the Character script. There is no issue with this working to access the Character script as shown in the picture because i can access the name. However, when i try and access the EquipmentUI of that character it gives the error. This isnt the last part i need access to but i have figured out that the UI is the part i cant access, i need the script held in it(which has worked before in another class).
The variable is already assigned, there is no other object in my scene with the same script attached, and the code can access other parts of the script i want access to which is why i made a new post after seeing the other posts and not seeing one that had quite the same issue.
using System.Collections;
using System.Collections.Generic;
using UnitEngine;
[CreateAssetMenu(fileName = "New Character Equipment", menuName = "Inventory/Character/CharacterEquipment)]
public class CharacterEquipmentObject : ScriptableObject
{
[SerializeField]
protected Player user;
[SerializeField]
private GameObject equipmentUser; // Only used to get the user because SOs cant getObject<>
[SerializeField]
public EquipmentObject[] equipment = new EquipmentObject[8];
[SerializeField]
EquipmentDisplay equipmentUI;
//private string[] SlotList = new string[8]{"Helmet", "Shoulders", "Chest", "MainHand", "OffHand", "Ring", "Legs", "Feet"}; may need later
public void start() // must be called in display
{
user = equipmentUser.GetComponent<Player>(); // set the user to be used in other classes\
Debug.Log("user in CEO: " + user.characterName); // this displays fine
Debug.Log("user " + user.characterName + "'s equipment: " + user.EquipmentUI.name); // This is what the editor says is empty when the slot has it assigned
equipmentUI = user.EquipmentUI.GetComponentInChildren<EquipmentDisplay>();
Debug.Log("equipmentUI: " + equipmentUI.name);
}
Thanks for any help in advance.
Player class:
public class Player : Character
{
[SerializeField]
protected Character[] companions = new Character[3];
[SerializeField]
GameObject InventoryUI;
public GameObject EquipmentUI;
bool isSelling = false;
public string characterName;
protected override void Start()
{
InventoryUI.SetActive(false);
EquipmentUI.SetActive(false);
Debug.Log("Player Equipment UI: " + EquipmentUI.GetComponentInChildren<EquipmentDisplay>().name);
base.Start();
}
private void OnTriggerEnter(Collider other)
{
var item = other.GetComponent<InteractItem>();
if(item)
{
characterInventory.addItem(item._item, 1);
useItem(item._item);
}
Destroy(other.gameObject);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.I))
{
InventoryUI.SetActive(!InventoryUI.activeSelf);
}
if(Input.GetKeyDown(KeyCode.C))
{
EquipmentUI.SetActive(!EquipmentUI.activeSelf);
if(EquipmentUI.activeSelf == true)
{
EquipmentUI.GetComponentInChildren<EquipmentDisplay>().updateEquipmentSlots();
}
}
}
Thanks for all the help on here but i figured out it is a ScriptableObjects issue as i went in and changed the way it is run, even putting the function to edit the users InventoryObject into the player itself it still gave the same issue.
In the player function Update() that does not have the InventoryObject(ScriptableObject) the update runs fine but as soon as the player function EmptySlot() is called from the InventoryObject it sends the same error.
Not entirely sure why this is but SOs are unable to access the GameObject that holds the script for the inventory display in them whatsoever. If anyone has a reason as to why this is I would love to know, but i am going to move on from this particular part and change how it works.
You might be experiencing a bug I have been facing where the element variable info does not update to show variable changes.
To force it to update, cause an error in your code. Switch to unity, when it shows you the error go back and fix it, and see if the variable value changes then.

transporting a bool across scene's while changing it

For my game you need to complete a mini-game to unlock abilities. But I atually have no clue how to do it cause the value gets resetted to false whenever I load the main-level.
Code playerMovement:
static bool FistAttackEnabled;
void Update ()
{
if (FistAttackEnabled == true)
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Attack");
PlayerMovement.SetFloat("Attacking", 1f);
HitArea.SetActive(true);
}
}
}
Code miniGame:
void Start()
{
FistAttackEnabled = Player.GetComponent<Player_Movement>().FistAttackEnabledPortable;
}
void Update()
{
if (SheepsAmountGuess == NeededAni)
{
FistAttackEnabled = true;
}
}
But this doesnt work. I tried making a portable bool (FistAttackEnabledStatic = FistAttackEnabled) Because you cant transport static bool value's across scripts, but this also didn't work. Does anyone have a clue how to do it?
PS: The code is bigger but it doesn't have anything to do with the attack.
Each time the scene is loaded the scripts are reloaded, so variables will go to their "default" state
You can avoid the destruction of the gameObject by usign DontDestroyOnLoad(this.gameObject);
Since the GameObject wont be deleted each time you reload the scene a new copy will be created to solve that you should use a singleton(look for it you will find information easyly)
Both things will solve the problem temporaly but once you close the game everything will go to the original state. You should use some method to save the progress, PlayerPrefs is a really easy way to do it.

Unity3D: Custom UnityEvent AddListener not firing

I have my own custom UnityEvent and am trying to add a listener.
I have used AddListener on numerous other UI objects, such as buttons, dropdowns, toggles, etc. so I understand the process. However, when I Invoke my UnityEvent, it simply doesn't fire.
I'm receiving no error messages, and after doing reading and research, everything looks correct. So, not sure what to do further.
This is an object that emits when it's rotated.
This is the basics of my code:
using UnityEngine.Events;
public class Rotator: MonoBehaviour
{
public UnityEvent OnRotate;
int angle = 0;
int newAngle = 0;
void Start()
{
OnRotate = new UnityEvent();
}
void Update()
{
newAngle = (int)transform.rotation.eulersAngles.z;
if (newAngle != angle)
{
print ("Actual Instance ID: " + GetInstanceID());
print ("Invoking!");
OnRotate.Invoke();
angle = newAngle;
}
}
}
and
public class Owner: MonoBehaviour
{
public Rotator rotator;
void Start()
{
print ("Rotator Instance ID: " + rotator.GetInstanceID());
rotator.OnRotate.AddListener(
() => UpdateRotation()
);
}
void UpdateRotation()
{
print ("Invoked!");
}
}
When the Rotator has it's angle changed, I get this in the console:
Actual Instance ID: 11234
Rotator Instance ID: 11234
Invoking!
The instance ID is to make sure I'm working with the same objects and not going in circles for nothing. They match, so I'm listening to the object that's firing.
However, the listener isn't firing. I've tried different combinations with delegates, etc. but it's all the same. No errors. It just doesn't invoke.
Obviously, I'm doing something wrong, but what is it?
Thanks for any help.
Somehow your answered your new edited version of the question with exactly the code you previously provided in the First Version of your Question!
As I tried to tell you ... if you anywhere in your code do OnRotate = new UnityEvent() of course you thereby erase any persistent callbacks and any runtime callbacks added before that moment!
In short
Simply leave it as
public UnityEvent OnRotate;
and you don't even have to think about it anymore.
For understanding why it also works if you put it in Awake please simply have a look at the Order of Execution for Event Functions
&rightarrow; First Awake and OnEnabled is called for every GameObject/Component. Then all Start methods are called as soon as the GameObject/Component is active.
Within each of these blocks (Awake + OnEnable) and (Start) the order of execution between different component types is not guaranteed unless you explicitly configure it via the Script Execution Order Settings where you could define that Owner is simply run before Rotator .. then having both in Start would also work again.
Why does it also work if you do it on the public field?
&rightarrow; Because this field is serialized. That means it is initialized automatically in the Inspector and then stored together with the Scene or prefab asset including any persistent callbacks.
And then Later Unity re-uses the serialized Version of the field so actually you can completely remove the new UnityEvent(); since it doesn't have any effect on a serialized field! It will always be initialized automatically anyway!
Ok, I found out what the issue was.
My question now is "why?".
I changed my code from:
public UnityEvent OnRotate;
void Start() {
OnRotate = new UnityEvent();
}
to
public UnityEvent OnRotate = new UnityEvent();
void Start() {
}
And now it works.
Although, now that I think about it, Awake() is the method where they all fire before initialization, whereas Start() is when the object is created. So the Start() of the Rotator is probably getting called after the Owner is adding a listener.

UNITY An object reference is required to access non-static member `Outline.OutlineMode'

I'm trying to create an outline when you are near it, but i'm getting all the time the same error.
void Update () {
if (Input.GetKeyDown(KeyCode.E)){
var outline = gameObject.AddComponent<Outline>();
outline.OutlineMode = Outline.Mode.OutlineAll;
outline.OutlineColor = Color.yellow;
outline.OutlineWidth = 5f;
}
}
void OnTriggerStay(Collider other) {
if (Outline.OutlineMode == Outline.Mode.OutlineAll) {
Debug.Log("test");
}
}
If i press E it works, and if i change it to ontriggerstay works too, but im trying that it only applies one time, because im getting some errors if its on. I have to say that im using an asset, called quick outline
Srry for my very bad english and explanation and thank you
add the outline to your object in Awake() then set it to disabled.
then enable it in OnTriggerEnter() and disable it in OnTriggerExit()
this will keep you from making multiple copies, and it will only be active when you are in range of your trigger