Hello all. I am new to Unity and have been learning it for the past week and I have gotten to the stage where I need a way to have information persist through levels, the best way I have been told to go about it is to use a singleton. I have done a fair bit of c++ programming so I understand a singleton and what it does but I have been unable to make it work using Unity and javascript (I am not looking to use c# for certain reasons). I have had a look around trying to find a javascript implementation and driven myself (almost) crazy trying to get it to work from the numerous C# examples and my own knowledge but I have had no luck. So can someone please help me out in regards to the basic class structure of a singleton in javascript, the creation of the instance and accessing the instance? (I know I have to DontDestroyOnLoad) Thanks to anyone willing to help
Hi, maybe this is going to help you further : http://www.unifycommunity.com/wiki/index.php?title=AManagerClass -- oxl
Thanks for the link, it is one that I found when looking for examples. I don't know why but I was able to get it working this time. It is taking me some time to get my head around the way things are done/work in javascrip/Unity compared to C++ and other (c++)engines I have used. Thanks, and for future reference for anyone else searching for the same thing I will post what my code was since that is one of my main downfalls, taking code excerpts and guessing the rest. GameManager.js Code (csharp): class GameManager extends UnityEngine.MonoBehaviour { public var score : int; // Constructor basically....I think function Start() { score = 10; } } function Awake() { GameObject.DontDestroyOnLoad(this); } TitleMenu.js Code (csharp): var instance : GameManager; function Start() { instance = FindObjectOfType(GameManager); if (instance == null) print("There is no instance"); else print("There is an instance. Score: " + instance.score); instance.score += 1; } Player.js Code (csharp): var managerInstance : GameManager; function Start() { managerInstance = FindObjectOfType(GameManager); print("Score: " + managerInstance.score); }
i tried to implement this, but it doesnt work. i basically pasted your code and still doesnt work. i keep getting in the Debug.Log "there is no instance" i am looking at some implementation of singleton in javascript...anybody that can help me?
Do you want a singleton of any generic non-Unity class or a singleton of a physical GameObject? For a normal singleton it's simple as UnityScript has classes just as any other decent language, so the singleton implementation isn't that much different: (off the top of my head, untested, pseudo-code, etc.) Code (csharp): class MySingletonClass { private static var Instance : MySingletonClass = new MySingletonClass(); public static function GetInstance() : MySingletonClass { return Instance; } //though I don't know if in UnityScript if you can have a private constructor, guess we'll find out! //If not, just don't call it. private function MySingletonClass() { //if the constructor must be public, you can do this: if (Instance != null) { throw new Exception("This is a singleton class! Use MySingletonInstance.GetInstance() instead!"); } } } If you're looking to get a singleton of a GameObject, that'd be an extension of the same: (again, off the top of my head, untested, pseudo-code, etc.) Code (csharp): //in "MySingletonGameObject.js" script that you attach to a GameObject //Naturally, GetInstance() may be null if the attached GameObject has not yet been created by Unity private static var Instance : MySingletonGameObject = null; public static function GetInstance() : MySingletonGameObject { return Instance; } //though I don't know if in UnityScript if you can have a private constructor, guess we'll find out! //If not, just don't call it. public function MySingletonGameObject() { //if the constructor must be public, you can do this: if (Instance != null) { //this might be a bad idea, would throw the exception likely to the Unity player instead of your user code throw new Exception("This is a singleton class! Use MySingletonInstance.GetInstance() instead!"); } } function Awake() { Instance = this; } Just don't add to the scene more than one instance of this script.
ok, i tried this and it is confusing for me, it doesnt work or i dont know how to use it. here is the class i made (non-unity for start), basically same as yours Code (csharp): class MySingletonClass { private static var Instance : MySingletonClass=new MySingletonClass(); public static function GetInstance(): MySingletonClass { return Instance; } private function MySingletonClass() { if(Instance!=null) { Debug.Log("this a singleton class, use MySingletonInstance.GetInstance() instead"); } } } then in the script in which i want to use this class i made variable declaration like this: Code (csharp): var singleton : MySingletonClass; and later in the Awake() i put this: Code (csharp): singleton.GetInstance(); and i get this notification : "this a singleton class, use MySingletonInstance.GetInstance() instead" then i tried this: Code (csharp): singleton=new MySingletonClass(); singleton.GetInstance(); i get error that constructor is inaccessible due to its protection level... please help me resolve this, it seems very confusing
The GetInstance() method returns the single instance (singleton!) of the class you are supposed to use. MySingletonClass.GetInstance().SomeMethod(); or var mySingletonReference:MySingletonClass = MySingletonClass.GetInstance(); mySingletonReference.SomeMethod(); EDIT: since UnityScript is honouring the private constructor, you can remove that extra check/error in it.
You actually don't need to use a singleton, you can use static vars as long as you remember to only create one instance of them. Put this in a script named "master.js": Code (csharp): static var score = 100; From other script; Code (csharp): Debug.Log(master.score);
But this is a GameManager (which screams "Singleton!"), not just a score property. Besides, the beauty of a singleton is that you don't have to remember to create only one instance of them. Better to code/limit the API's intent directly into it rather than keep it in your head. EDIT: but yeah, if you wanted to your GameManager could exist purely of static members. There's usually little difference between a static class and a singleton, so unless you have a specific reason to make a singleton, you might as well make the whole class static.
It could possibly be related to the way my state/scene flow worked. What I had was an initalisation scene that included this GameManager (which is a singleton) as well as another script which only loaded the next scene. Code (csharp): function Start() { Application.LoadLevel("_Title"); } The reason for this is so that when the next scene starts I can just assume that the GameManager has been created without having to worry "Will this code be called because the GameManager is created?" When you run the code do you see the GameManager game object in the list of objects in the scene? (I assume you did create a game object and gave it the GameManager script).
@Scared Hi. Sorry but can you explain more about how you implemented? I tested like this, and just revised GameManager.js to, Code (csharp): class GameManager extends UnityEngine.MonoBehaviour { public var score : int; // Constructor basically....I think function Start() { score = 10; } } function Awake() { GameObject.DontDestroyOnLoad(this); } function Update(){ if(Input.GetKeyDown("3")){ Application.LoadLevel(0); Debug.Log("Scene 1 loaded"); } if(Input.GetKeyDown("4")){ Application.LoadLevel(1); Debug.Log("Scene 2 loaded"); } } And attached image files is showing the status of setting scene 1,2 All attached scripts are from your code example's. (GameManager, TitleMenu, Player) So after game play, I repeatedly press 3,4 key, and whenever I came back to scene1(test1), GameManager gameobject increased by 1. (and also Debug.log's message too) So can you thoroughly explain how you implemented this? Scene status, gameobject status, etc... Thank you in advance.
Instead of that whole complicated instancing thing, isn't it possible to simply do this?: Code (csharp): function Awake () { // Make LevelData a singleton, so we keep ourself alive and kill any clones if ( FindObjectsOfType(LevelData).Length != 1 ) // If one of me already exists I must be a clone, so destroy me (don't feel bad about it) Destroy (gameObject); else // I'm the original, so keep me around DontDestroyOnLoad (gameObject); }
Here's how I do my static classes from memory. Example of a static AudioClip manager. There still needs to be one instance somewhere. In my case I create a GameObject called GameSystem and add these sorts of scripts, then assign the values to the List as required. Code (csharp): class AudioManager : MonoBehaviour { public List<AudioClip> AudioClips; void Start() { _instance = this; } static AudioManager _instance; public static AudioClip GetAudioClip(string name) { if(_instance == null || _instance.AudioClips == null) return null; foreach(AudioClip clip in _instance.AudioClips) { if(clip == null) continue; if(clip.name == name) return clip; } return null; } } from anywhere I can now call Code (csharp): AudioManager.GetAudioClip("example"); this is c# though, so you will have to convert. shouldnt be too hard
I have a similar issue with trying to figure out how to carry information across scenes , my first issue was that my just putting in: functionAwake () { DontDestroyOnLoad (transform.gameObject); } was duplicating my game object with the script on it whenever I returned to the main scene. Now what Essential posted above with: Code (JavaScript): function Awake () { // Make LevelData a singleton, so we keep ourself alive and kill any clones if ( FindObjectsOfType(CountdownTimerManager).Length != 1 ) // If one of me already exists I must be a clone, so destroy me (don't feel bad about it) Destroy (gameObject); else // I'm the original, so keep me around DontDestroyOnLoad (gameObject); } Now this did solve my duplication issue but I'm still having difficulties with trying to figure out why and how to solve another issue which is that my UI text is a child of a panel on a canvas and my timer game object that carries accros to other scenes ends up loosing the UI text. I have tried putting my timer game object as a child of the canvas too but then my timer fails to work at all. I'll post my entire script along with a screen capture of how my Hierarchy is set up. I could REALLY use some help on this. And here is my code for my CountdownTimerManager: Code (JavaScript): #pragma strict var Alarm : AudioClip ; var timer: float = 3600; var isFinishedLevel : boolean = true; public var displayText : UnityEngine.UI.Text; public var timeText : UnityEngine.UI.Text; var minsDisplay : String; var secsDisplay : String; var mySeconds : int = 0; private var oldTimer : float; //Begin New function Awake () { // Make LevelData a singleton, so we keep ourself alive and kill any clones if ( FindObjectsOfType(CountdownTimerManager).Length != 1 ) // If one of me already exists I must be a clone, so destroy me (don't feel bad about it) Destroy (gameObject); else // I'm the original, so keep me around DontDestroyOnLoad (gameObject); } //End New /*function Awake () { DontDestroyOnLoad (transform.gameObject); }*/ function Start(){ oldTimer = timer; } function Update(){ if (!isFinishedLevel) { timer -= Time.deltaTime; } CurrentTime(); } function CurrentTime() { var dt : System.DateTime = System.DateTime.Now; var h : int = dt.Hour; var m : int = dt.Minute; var s : int = dt.Second; timeText.text = h + ":" + m + ":" + s; if(mySeconds != s) { mySeconds = s; Timing(); } } function Timing() { if (timer > 0) { //var minsDisplay : String = parseInt( timer / 60 ).ToString(); minsDisplay = parseInt( timer / 60 ).ToString(); //var secsDisplay : String = parseInt( timer ).ToString(); secsDisplay = parseInt( timer ).ToString(); if ( (timer - ( parseInt(minsDisplay) * 60)) > 10 ) { secsDisplay = parseInt( timer - ( parseInt(minsDisplay) * 60) ).ToString(); } else { secsDisplay = "0" + parseInt( timer - ( parseInt(minsDisplay) * 60) ).ToString(); } //displayText.text = minsDisplay + " : " + secsDisplay; } //Timer Reaches End We Can Do Something Here else { timer += oldTimer; audio.PlayOneShot(Alarm);//Plays Alarm Sound isFinishedLevel = true;//Sets Inspector Value to true or false based on what is set here yield WaitForSeconds (0.8);//Wait Time Setting //Do Something if Desired Debug.Log ("Timer Ended"); } displayText.text = minsDisplay + " : " + secsDisplay; } //Timer Stop Button public function GoTimerStop() { isFinishedLevel = true; } //Timer Start Button public function GoTimerStart() { isFinishedLevel = false; } //Timer Settings public function GoTimerSetting60Sec() { timer = 60; } public function GoTimerSetting5Min() { timer = 300; } public function GoTimerSetting10Min() { timer = 600; } public function GoTimerSetting20Min() { timer = 1200; } public function GoTimerSetting30Min() { timer = 1800; } public function GoTimerSetting40Min() { timer = 2400; } public function GoTimerSetting50Min() { timer = 3000; } public function GoTimerSetting1Hr() { timer = 3600; } public function GoTimerSetting1Point5Hr() { timer = 5400; } public function GoTimerSetting2Hr() { timer = 7200; } public function GoTimerSetting2Point5Hr() { timer = 9000; } public function GoTimerSetting3Hr() { timer = 10800; }