Hi there peeps! I am making a 2d game with a friend of mine similar to the super mario game and we want to have like a menu that will show all the available levels of the game.But i want some to make the menu interactive so that if you do not finish level 1 you cannot go to level 2 (it should be locked) and when you do level two unlocks.Like on Android games but for PC!Do i need a UI or a GUI for this? and does anyone have a tutorial about it or a code that i can copy and change?Thanks!
you can try to do that with PlayerPrefs. As one example: you have scene with "start"-"continue" buttons. If someone click "start", then set one variable in PlayerPrefs to 0. PlayerPrefs.SetInt ("UnlockedLevel", 0); If your level is played, then set this variable to number of the played level. PlayerPrefs.SetInt ("UnlockedLevel", playedLevel); Now, you have somewhere your interactive map. If someone clicked on level 1, then check the variable in PlayerPrefs: void ClickOnMap () { if (PlayerPrefs.GetInt ("UnlockedLevel") > ClickedLevel) //here load your level else //this level is locked } or you can disable the buttons, and then enable depended on variable in playerprefs. or... something other
Besides playerprefs, you can try to use data serialization. It is more complex than the playerprefs, but have some advantages (playerprefs can be easily altered by the player if he opens the file with notepad), and some disadvantages (more complexity on the code and don't work on some platforms, like windows phone, for example). There is a good official tutorial about it if you want to see how it works: http://unity3d.com/pt/learn/tutoria...ersistence-data-saving-loading?playlist=17117
ok thanks @vakabaka i will try that but if i need any help(since i recently started using unity), could you help us?
community here is good, just ask, someone will help ps. dont try to ask "how i can make the game ?". Its just to hard to answer its good one, only little hard for beginners
I agree with Andre! http://unity3d.com/pt/learn/tutoria...ersistence-data-saving-loading?playlist=17117
About playerprefs not working on windows phone, see this http://docs.unity3d.com/ScriptReference/PlayerPrefs.Save.html i.e. do something like Code (CSharp): void OnDestroy() { PlayerPrefs.SetInt("LevelIndex", 1); PlayerPrefs.Save(); }
Hi Trav3l3r. Thanks for your response, but actually PlayerPrefs works well on Windows Phone. What I had trouble was using data serialization on Windows Phone. Windows Phone don't have the necessary classes to use data serialization. I had some trouble with data serialization on ios too, but I think it was some permission error I have to solve. One of the advantages of PlayerPrefs is that it works very well on almost every platform (it worked on every platform I tested).
So can i do something like : Private int levelNumber; If my player presses the Start button i do : PlayerPrefs.SetInt ("UnlockedLevel", 0); If my player plays level1 do i do : PlayerPrefs.SetInt ("UnlockedLevel", playedLevel); ? Then on my Level Selector Map , can i do : // when the player clicks on lvl 1 for example: void ClickOnMap () { if (PlayerPrefs.GetInt ("UnlockedLevel") > ClickedLevel) // Loading next level else PlayerPrefs.SetInt("UnlockedLevel"),0); } ???? Can i use this?
If my player presses the Start button i do : PlayerPrefs.SetInt ("UnlockedLevel", 0); its ok If my player plays level1 do i do : PlayerPrefs.SetInt ("UnlockedLevel", playedLevel); here you can set playedlevel manual. add empty gameObject to level. Name it (something like "AutoSave"). Add c# script: Code (CSharp): public int playedLevel; void Start { PlayerPrefs.SetInt ("UnlockedLevel", playedLevel); PlayerPrefs.Save(); } you can make prefab of this autoSave set in inspector level number 1 now if this level is starting, the variable in playerprefs will be saving with level number. add autoSave to next level too. Selector map. I think, you can use ui button on it. Add ui button, name it level1: add c# script to it. Code (CSharp): public int ClikedLevel; public int IdNextLevel; //should be public for ui button public void ClickOnMap () { if (PlayerPrefs.GetInt ("UnlockedLevel") >= ClickedLevel) //old version of load 5.2.4, i didnot using 5.3.1 with scenemanager, so change the script if needed Application.LoadLevel (IdNextLevel); } add this script to ui button level1; set in inspector ClikedLevel = 1; IdNextLevel = here ID of next level, can be something other as 2. Check build settings ->scenes in build. In inspector of level1-> on click set runtime object to level1 funktion to "nameofthisscript" ->then choose ClickOnMap you should not have only start button in your start scene. If someone will continue game and clicks "start", then the UnlockedLevel will be saved again as 0. So make Continue button. and add c# script something like as for ClickOnMap. BuildId can be different as number of played level, so maybe you will make an other variable for id. you can add in autosave line: PlayerPrefs.SetInt ("IdUnlockedLevel", Application.loadedLevel); as said, it is for Unity 5.2.4. For 5.3.x you should use scenemanager. Now you have id of playedLevel. You can use it for Continue button, like Code (CSharp): public void ContinueGame () { //line vs error, if no IdUnlockedLevel if (PlayerPrefs.HasKey ("IdUnlockedLevel") Application.LoadLevel (PlayerPrefs.GetInt ("IdUnlockedLevel")); } add this script to continue ui button and set it like level1 ui button. its only idea, how to make it. I am sure, it can be done much better and sorry for my bad english
Oh my lord thank you so much this worked! . Btw do you know how i can disable a button from been clicked before a certain level is passed? (For example : if player does not play Level1, Level2 button should not be available for clicking.But if the player plays level1, then level2 should now be available for clicking etc.Can you help me with this last one too?
Add code to any script on button (or create new script and attach to button): Code (CSharp): public int levelNeeded; void Start() { if(PlayerPrefs.GetInt("UnlockedLevel") < levelNeeded) GetComponent<Button>().interactable = false; } Should work if "UnlockedLevel" is set to anything. If it isn't... Well, you can write PlayerPrefs.HasKey(...). It disables button if UnlockedLevel is less than levelNeeded set in MonoBehaviour. Note: uses Button class so needs using UnityEngine.UI;
what do you mean if "UnlockedLevel" is set to anything? i do not think i have defined it anywhere, isn't it just a string?(Thank btw for replying )!
I mean PlayerPrefs.GetInt("UnlockedLevel"). According to previous posts it is set in Start() as well so there's possible situation when button script I posted will run before this code causing error as "UnlockedLevel" in PlayerPrefs is not defined. One solution is to change Start which sets that value to something like Awake() (which is called before) or check if PlayerPrefs.HasKey("UnlockedLevel") before and consider that value as 0 if it is not. EDIT: actually forget it, GetInt returns 0 if value is not set. I didn't read manual properly. Code I posted before should work without issues.
Here i have 2 buttons and i used the scripts like this : And my code : Code (csharp): using UnityEngine; using UnityEngine.UI; using System.Collections; publicclassLoadNextLevel:MonoBehaviour{ publicintclickedLevel; publicintidNextLevel; publicintlevelNeeded; voidStart() { if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded) GetComponent<Button>().interactable=false; } publicvoidClickOnMap(){ if(PlayerPrefs.GetInt("UnlockedLevel")>=clickedLevel){ Application.LoadLevel(idNextLevel); } } }
Strange. What would Code (CSharp): Debug.Log(PlayerPrefs.GetInt("UnlockedLevel")); say in console? I have suspicion that you have it set to high value on your PC from previous tests. Oh, and I see another problem(separate from 'interactable' not disabling): you have 'transition' on button set to none. This way button won't change graphically if it's disabled. If you need it to disappear completely, it's better to change Code (CSharp): GetComponent<Button>().interactable=false; to gameObject.SetActive(false); But if you need to change it somehow without it disappearing, you should tweak that value. One additional question: do you load menu scene after each level or do you keep it while playing?
sorry, I dont know, how to do exactly. Teravisor is on the right way If you have separated scene for map: I would disable all level-Buttons and make gameObject in the scene. Then add script with awake () funktion, that can enables the buttons. Code (CSharp): //here array with your buttons, that should be enabled public GameObject [] levelButton; void Awake () { if (PlayerPrefs.HasKey ("UnlockedLevel"){ int level = PlayerPrefs.GetInt ("UnlockedLevel"); for (int i = 0; i < level; i++) { //here something to enable buttons levelButton [i].SetActive(true); //or levelButton [i].GetComponent<Button>().interactable=true; } //just enable level1 else levelButton [0].SetActive(true); }
This is what debug.log says in the console : 1 UnityEngine.Debug:Log(Object) LoadNextLevel:Start() (at Assets/Scripts/LoadNextLevel.cs:17) And i changed the transition ! i do not want them to dissapear,just not available for clicking.
Well, my guess was right. You have your progress set to 1 with only buttons for level 0 and 1. Which means you have all levels open! You should make some debug button which will reset "UnlockedLevel". (It might not be debug - maybe player wants to reset all his/her progress? Depends on you). A button that resets progress should call: Code (CSharp): public void ResetProgress() { PlayerPrefs.SetInt("UnlockedLevel",0); //Next line isn't mandatory, but saves effect of this between game sessions even if application crashes somehow PlayerPrefs.Save(); } Also there's a little problem as we need to refresh buttons now. Way 1: Reload scene you're on. If you need to do it without reloading scene(either save menu user was in or scene is too large to reload - so it will take time to do so) way 1 isn't appropriate. Way 2: Add method to LoadNextLevel script Code (CSharp): public void UpdateInteractable() { if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded) { GetComponent<Button>().interactable=false; } else { GetComponent<Button>().interactable=true; } } and call it on all buttons after you do any PlayerPrefs.SetInt("UnlockedLevel", 0) in ResetProgress(). To call it on all buttons you either make array and store all buttons in it like Code (CSharp): public LoadNextLevel[] nextLevelButtons; and fill it in editor(array should not be in button, instead in something neutral like same place you place ResetProgress()) and cycle through all its elements like Code (CSharp): for(int a=0;a<nextLevelButtons.Length;a++) { nextLevelButtons[a].UpdateInteractable(); } or you find all buttons with something like Code (CSharp): Object[] objs = Object.FindObjectsOfType(typeof(LoadNextLevel)); for(int a=0;a<objs.Length;a++) { ((LoadNextLevel)objs).UpdateInteractable(); }
How else are you going to test that levels unlock properly and buttons don't work when they aren't unlocked? About players: that's why I said
Hmm, looks like this is not working.Maybe i am doing something wrong here, but i added a reset button, added the script for it on a reset button and then below the resetprefs code i added the buttons array and filled it in the inspector.Is there another way i can get this to work with player prefs?
Learn to debug: 1. Did variable PlayerPrefs.GetInt("UnlockedLevel") set itself to 0? 2. Is UpdateInteractable() getting called on all buttons? 3. Is GetComponent<Button>().interactable=false; actually setting it to false? If so, then what else is changing that value? You can answer those questions either by placing Debug.Log("something"); into right places or running debugger (google for "Monodevelop Unity debugger" or "Microsoft visual studio Unity debug" depending on what you use) Debugging and testing are vital parts of programming.
UnlockedLevel is not setting itself to 0 but instead it's set to 1 and when level 1 is done it's still set to 1. UpdateInteractable() is called on both of my buttons and this is the code : Code (csharp): using UnityEngine; using UnityEngine.UI; using System.Collections; publicclassLoadNextLevel:MonoBehaviour{ publicintclickedLevel; publicintidNextLevel; publicintlevelNeeded; voidStart() { if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded) GetComponent<Button>().interactable=false; Debug.Log(PlayerPrefs.GetInt("UnlockedLevel")); } publicvoidUpdateInteractable() { if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded) { GetComponent<Button>().interactable=false; } else { GetComponent<Button>().interactable=true; } } publicvoidClickOnMap(){ if(PlayerPrefs.GetInt("UnlockedLevel")>=clickedLevel){ Application.LoadLevel(idNextLevel); } } } and about question 3, i think it does although i tried to do Debug.Log(getcomponent<button>().interactable); but it did no show anything in the console. And by the way only level 1 is unlocked, level 2 is locked but does not get unlocked so i believe the problem must be in the same script where it loads the next level.Some variable must be changing the .interactable value...
Do you do something like Code (CSharp): public void LevelComplete_SetUnlockedLevel(int completedLevel) { if(PlayerPrefs.GetInt("UnlockedLevel")<completedLevel) PlayerPrefs.SetInt("UnlockedLevel", completedLevel); } when you complete level? (value is level that should be unlocked after that call) To me it seems you don't...
I don't in my project, when you finish each level i just load level"World1"(the level menu scene), should i add that and set the completedLevel to 2 if level1 is completed?
This is the script that will run when level 1 is finished is it correct? Code (csharp): using UnityEngine; using System.Collections; publicclassLoadWorld1:MonoBehaviour{ privateintlevel_completed=2; voidOnTriggerEnter2D(){ Application.LoadLevel("World1"); } publicvoidLevelComplete_SetUnlockedLevel(intcompletedLevel) { level_completed=completedLevel; if(PlayerPrefs.GetInt("UnlockedLevel")<completedLevel) PlayerPrefs.SetInt("UnlockedLevel",completedLevel); } } ? It did not change the value of UnlockedLevel to 2 as i expected!
Um, nope. You call Code (CSharp): LevelComplete_SetUnlockedLevel(level_completed); right before loading "World1". Don't worry method is called like this, I've run out of fantasy in my head You can rename it if you want.
ok, I am little lost now Variables, that are saved in PlayerPrefs, will be not reseted if you close PlayMode (or Unity). So if you have tested the scene 2 and then will run scene 1, they stay saved from scene 2. You can make reset button for them, but do not forget about awake () and start () funktions (they can be essential for correctly work, if you using them for setting in playerprefs). So make better one scene, that can reset your saves. Code (CSharp): void Start () { PlayerPrefs.SetInt ("UnlockedLevel", 0); // reset other variables in PlayerPrefs, that are not needed // or use PlayerPrefs.DeleteAll(); } Start this scene, then start test-scene Or start your game with your start scene (where you should clear playerprefs, if player clicks "new game")
I did what you said but still it does not work... any ideas/? And in case i forgot to mention, i also have another script in level 1 : Code (csharp): using UnityEngine; using System.Collections; publicclassAutosave:MonoBehaviour{ publicintplayedLevel; voidAwake(){ PlayerPrefs.SetInt("UnlockedLevel",playedLevel); PlayerPrefs.Save(); } }
Nevermind i just found out, that if i change the AutoSave script's playedlevel to 2 in the inspector for level1 and so on, it will work!
Hey one last thing i need if possible! I want to add an image to my button(or on the scene so i can have it on top of my button) like a lock image that will go away when the level beneath it is unlocked.I think i know where to put that part of the code but i don't know how to set the reference for it! Code (csharp): [LIST=1] [*]using UnityEngine; [*]using System.Collections; [*] [*]publicclassLoadWorld1:MonoBehaviour{ [*] [*]privateintlevel_completed=2; [/LIST] [LIST][*]publicvoidLevelComplete_SetUnlockedLevel(intcompletedLevel)[/LIST] [LIST][*]{[/LIST] [LIST][*]level_completed=completedLevel;[/LIST] [LIST][*]if(PlayerPrefs.GetInt("UnlockedLevel")<completedLevel)[/LIST] [LIST][*]PlayerPrefs.SetInt("UnlockedLevel",completedLevel);[/LIST] [LIST][*]// Add that piece of code here? is that right? [/LIST] [LIST][*]}[/LIST] [LIST=1] [*]voidOnTriggerEnter2D(){ [*]Application.LoadLevel("World1"); [*]} [*] [*]} [*] [/LIST]