Can't load the In App Purchases when reloading the same scene - unity3d

I have an issue with my In App Purchases manager. I load my scene and can successfully purchase items. I go to a new scene in game and then back to that scene and now it says...
`MissingReferenceException: The object of type 'IAPManager' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.`and then points my to this line
moneyController = GetComponent<MoneyController>();
Why does it crash at that point? Do I need to add DontDestroyOnLoad or something? I'm not familiar with that or how to use it though. Am I missing something simple?
Here are some more code snippets that may or may not prove useful. This code is from a tutorial hence why it's difficult for me to pin point the issue.
public static IAPManager Instance{set;get;}
private void Awake() {
Instance = this;
}

you have to put in DontDestroyOnLoad this way you will not get any error for same. below is the code that you can use.
private static IAPManager instance;
public static IAPManager Instance
{
get
{
if (instance == null)
{
GameObject o = new GameObject ("IAPManager");
instance=o.AddComponent<IAPManager>();
DontDestroyOnLoad (o);
}
return instance;
}
}

Related

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.

Problem with dontDestroyOnLoad in different scene

I am making a game that has two scenes (menu scene and game scene). In the menu scene, I create an empty game object just for my music, which includes (audio source (music), button to mute the music, and my script.
Here's the script:
public class Music : MonoBehaviour
{
public static Music Instance;
public AudioSource mainMusic;
public GameObject musicOffImage;
// Keep The Muic Playing In Diffrent Scene
void Awake()
{
if (!Instance)
Instance = this;
else
Destroy(this.gameObject);
DontDestroyOnLoad(this.gameObject);
}
// Method Mute Button
public void MusicOnOff()
{
if (mainMusic.isPlaying)
{
mainMusic.Pause();
musicOffImage.SetActive(true);
}
else
{
mainMusic.UnPause();
musicOffImage.SetActive(false);
}
}
}
With that script, I can play music in different scenes without reloading the music, and the button is working too, but the problem is when I go to the game scene and I back up to the menu scene, somehow the button didn't work. I think it's about the Destroy game object, but I am not sure how to fix it. Any help would mean a lot to me. Thanks.
I assume that everything the Music scripts needs is a child of it so that it is always fine.
However, after Destroy of the instance from the new scene, your buttons from the new scene loose the reference to the Music instance.
Since you have a Singleton there anyway you could as well (ab)use it and have this attached to your button itself
public MusicButton : MonoBehaviour
{
public void MusicOn()
{
Music.Instance.MusicOnOff();
}
}
And reference that instead in your button.
Also the image could e.g. register itself to the Music.Instance like e.g.
public MusicImage : MonoBehaviour
{
private void Start()
{
Music.Instance.musicOffImage = gameObject;
gameObject.SetActive(Music.Instance.mainMusic.isPlaying);
}
}
Alternative
In your question you said all objects are child's of an empty object, however the only object that gets DontDestroyOnLoad is the Music one. The others will get destroyed and reloaded so all these references might get lost as well. You might probably rather DontDestroyOnLoad the entire empty object and only hide/show the button in certain scenes.

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.

How to create game-wide object in Unity3d without singleton?

Is there a way in Unity3d framework to register an object that would be accessible to all entities in all scenes in the game without resorting to singleton pattern (like a game state object)?
You could take the following approach:
In the scene where the GameObject is created, in a MonoBehavior script attached to the GameObject:
Name the GameObject with "name = ..." in Awake() (or in the Editor)
Example: myObject.name = "FindMeInEveryScene";
Ref: https://docs.unity3d.com/ScriptReference/Object-name.html
Call "DontDestroyOnLoad" on the GameObject in Awake()
Example: DontDestroyOnLoad( myObject );
Ref: https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
In the same scene and in subsequent scenes, in MonoBehavior scripts attached to other GameObjects.
Find the object with GameObject.Find(...) in Start()
Example: globalGameObject = GameObject.Find( "FindMeInEveryScene" );
Ref: https://docs.unity3d.com/ScriptReference/GameObject.Find.html
This will allow you to access the GameObject named "FindMeInEveryScene" without a singleton.
To pull it all together...
GlobalGameObject.cs
// Attached to the GameObject that needs to be accessd by any other
// GameObjects and needs to survive between loading scenes
public class GlobalGameObject : MonoBehaviour
{
static bool gameObjectInitialized = false;
void Awake()
{
if ( !gameObjectInitialized )
{
gameObject.name = "FindMeInEveryScene";
DontDestroyOnLoad( gameObject );
gameObjectInitialized = true;
}
}
}
UsingGameObject.cs
// Attached to GameObjects that need access to the GlobalGameObject
public class UsingGameObject : MonoBehaviour
{
private GameObject globalGameObject = null;
void Start()
{
globalGameObject = GameObject.Find( "FindMeInEveryScene" );
}
}
You could possibly do it using events but it isn't really the optimal way to use events. I'm intrigued to know why a singleton pattern doesn't work for you as this is pretty much their main purpose.
If you aren't going to do this with a static or a singleton, I don't see any point in you using any kind of game state object in your system design. It would do the same thing, but slower (avoid GameObject.Find at all costs). It would lack the guaaranteed uniqueness of the singleton and the easy reference provided by both. I don't understand what tradeoffs you mean but I'll assume they're sound to try and answer your question.
Doing this without a central game state object is going to require a more decentralized solution. Tasks that would be performed by a static game manager are now controlled by the individual objects themselves and notified by other objects.
https://unity3d.com/learn/tutorials/topics/scripting/delegates
The beauty of this method is that objects don't need to know about eachother at all. They just need to listen for events and notify the system that an event has occured. I really can't think of another alternative. Hope this helps. Best of Luck!

Unity: Weird scene transition bug after adding a "Persistent-Scene" with a GameManager

I made a pretty basic 2D game to learn. I have 2 Scenes, and switching between them worked great. I used empty gameObjects as Start/Exit point of the Scene, so that the game would know to put player on point X after exiting through point X (for example exit outside house if I walk out the door).
Then I added a "Scene0", to use for persistent general scripts like GameManager, Sounds, Music, etc. With just one object called "Controller" that I DontDestroyOnLoad().
After adding this Scene and then just switching Scenes right away to my MainScene, all of a sudden the game starts acting really strange;
the first time I move from my MainScene (Scene1), to my secondary Scene (Scene2), it works fine, but then when I leave Scene2 to go back to Scene1, the player spawns in the middle of nowhere.
And this ONLY happens if I launch the game from my Persistent Scene.
I have no idea what is wrong, I don't add anything that interferes with my scene transitions, all I've added so far is playerHealth, for testing.
Scripts attached to my (persistent) Controller:
DDOL:
public class DDOL : MonoBehaviour {
// Use this for initialization
void Awake () {
DontDestroyOnLoad (gameObject);
}
}
GameManager:
public class GameManager : MonoBehaviour {
public static GameManager manager;
public int playerMaxHealth;
public int playerCurrentHealth;
void Awake(){
if (manager == null) {
manager = this;
} else if (manager != this) {
Destroy (gameObject);
}
}
// Use this for initialization
void Start () {
SceneManager.LoadScene("test_scene");
}
// Update is called once per frame
void Update () {
}
}
Scripts attached to my StartPoint:
PlayerStartPoint:
public class PlayerStartPoint : MonoBehaviour {
private PlayerController thePlayer;
private CameraController theCamera;
public Vector2 startDir;
public string pointName;
// Use this for initialization
void Start () {
thePlayer = FindObjectOfType<PlayerController> ();
if (thePlayer.startPoint == pointName) {
thePlayer.transform.position = transform.position;
thePlayer.lastMove = startDir;
theCamera = FindObjectOfType<CameraController> ();
theCamera.transform.position = new Vector3(transform.position.x, transform.position.y, theCamera.transform.position.z);
}
}
}
And finally ExitPoint:
LoadNewArea:
public class LoadNewArea : MonoBehaviour {
public string levelToLoad;
public string exitPoint;
private PlayerController thePlayer;
// Use this for initialization
void Start () {
thePlayer = FindObjectOfType<PlayerController> ();
}
void OnTriggerEnter2D(Collider2D other){
if (other.gameObject.name == "Player")
{
SceneManager.LoadScene(levelToLoad);
thePlayer.startPoint = exitPoint;
}
}
}
EDIT:
After moving all my DDOL gameObject to the Pre-Scene, it worked. So, I can assume the fault is inside Player or Cameras Start() functions since when they start in Scene1 they get called every time I enter the Scene (only DDOL).
I tried adjusting their Start()functions like follows:
Original camera:
void Start () {
Debug.Log("Starting camera");
if (!cameraExists) {
cameraExists = true;
DontDestroyOnLoad (gameObject);}
else{
Destroy (gameObject);
}
}
Changed Camera:
void Start () {
DontDestroyOnLoad (gameObject);
}
The exact same changes was made in Player.
Obviously this doesnt work because it creates a new Camera/Player every time I enter Scene1 (btw why does it not try to create them when I enter Scene2?, is it because they start in Scene1?). HOWEVER, the new player/camera do start at the correct position, and if I zoom out I can see the old player/camera at that same wrong position as before. So something weird happens when their Start() is called a second time it seems.
You've now mentioned that you had code something like this,
void Start () {
Debug.Log("Starting camera");
if (!cameraExists) {
cameraExists = true;
DontDestroyOnLoad (gameObject);}
else{
Destroy (gameObject);
}
}
Note that this is unfortunately just "utterly incorrect", heh :)
The issues you mention in the question (preload scenes etc) are just totally unrelated to the problem here.
In Unity if you have a character C that persists between scenes a, b, c as you load those scenes, you must kick-off C in it's own (perhaps otherwise empty) scene, you can not use "a" as a matter of convenience to kick off C.
The pattern is, in each of a, b, c just have a line of code like p = FindObjectOfType<Player>(); which runs when the scene loads, and position C as you wish.
Now, regarding your specific puzzle about the unusual behavior you are seeing.
I understand that you want to know why you are observing what you do.
It is a combination of confusion over the following issues: 1 - difference between Awake and Start, 2 - confusion over script execution order {but see below1} 3 - confusion about Destroy versus DestroyImmediate 4 - Not using Debug.Log enough, and not using gameObject.name in there (it's a common in Unity to be wildly confused about which object is talking in Debug.Log) 5 - where you mention you see the other object "off to the side", it's common to drastically confuse which one is which in such situations 6 - confusion between the computer programming concept of "instantiation" (ie, of a class or object) and "instantiating" (confusingly, it's the same word - utterly unrelated) game objects in nity.
If you fiddle around with all those issues, you'll discover an explanation for the behavior you're seeing!
But it doesn't amount to much; in Unity in the "C .. a b c" example you have to create C separately beforehand.
1 {aside, never fiddle with the script execution ordering system in Unity in an effort to solve problems; it's only there for R&D purposes; however it could in fact help you investigate the behavior at hand in this problem, if you are particularly keen to fully understand why you're seeing what you're apparently seeing}
Use the debugger. Have breakpoints at the relevant spots, like PlayerStartPoint.Start() and LoadNewArea.OnTriggerEnter2D() and check that they are executed
At the right time
The right number of times
With the expected values
This should make you see where things get out of hand.
If you use Visual Studio, install https://marketplace.visualstudio.com/items?itemName=SebastienLebreton.VisualStudio2015ToolsforUnity to be able to debug Unity from within Visual Studio.
If you are not using Visual Studio, you probably should.
Is player persistent between scenes (does he have DontDestroyOnLoad)? If no then this might be the reason - you can either try loading the scenes by using the additive mode or by instantiating the player on scene load in correct position.