Search Unity

2D Game Development!

Discussion in '2D' started by SP-Designs, Jan 10, 2016.

  1. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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!
     
  2. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,153
    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 :)
     
    AndreCabral likes this.
  3. AndreCabral

    AndreCabral

    Joined:
    Jan 11, 2016
    Posts:
    15
    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
     
    vakabaka likes this.
  4. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    ok thanks @vakabaka i will try that but if i need any help(since i recently started using unity), could you help us? :D
     
  5. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,153
    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 :D

    its good one, only little hard for beginners
     
  6. perrinostudios

    perrinostudios

    Joined:
    Sep 11, 2013
    Posts:
    30
  7. Trav3l3r

    Trav3l3r

    Joined:
    Sep 16, 2014
    Posts:
    60
  8. AndreCabral

    AndreCabral

    Joined:
    Jan 11, 2016
    Posts:
    15
    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).
     
  9. Trav3l3r

    Trav3l3r

    Joined:
    Sep 16, 2014
    Posts:
    60
    My bad, I misunderstood your earlier post. Thanks for clearing that up
     
  10. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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?
     
  11. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,153
    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):
    1. public int playedLevel;
    2. void Start {
    3. PlayerPrefs.SetInt ("UnlockedLevel", playedLevel);
    4. PlayerPrefs.Save();
    5. }
    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):
    1. public int ClikedLevel;
    2. public int IdNextLevel;
    3.  
    4. //should be public for ui button
    5. public void ClickOnMap () {
    6. if (PlayerPrefs.GetInt ("UnlockedLevel") >= ClickedLevel)
    7. //old version of load 5.2.4, i didnot using 5.3.1 with scenemanager, so change the script if needed
    8. Application.LoadLevel (IdNextLevel);
    9. }
    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):
    1.  
    2. public void ContinueGame () {
    3. //line vs error, if no IdUnlockedLevel
    4. if (PlayerPrefs.HasKey ("IdUnlockedLevel")
    5. Application.LoadLevel (PlayerPrefs.GetInt ("IdUnlockedLevel"));
    6. }
    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 :)
     
    Last edited: Jan 17, 2016
  12. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    Oh my lord thank you so much this worked! :D :D :D. 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?
     
  13. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Add code to any script on button (or create new script and attach to button):
    Code (CSharp):
    1. public int levelNeeded;
    2.  
    3. void Start()
    4. {
    5.     if(PlayerPrefs.GetInt("UnlockedLevel") < levelNeeded)
    6.         GetComponent<Button>().interactable = false;
    7. }
    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;
     
  14. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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 :D)!
     
  15. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    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.
     
  16. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    Here i have 2 buttons and i used the scripts like this :

    Unity 2016-01-18 23-34-59-50.png Unity 2016-01-18 23-33-56-08.png

    And my code :
    Code (csharp):
    1.   using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections;
    4.  
    5. publicclassLoadNextLevel:MonoBehaviour{
    6.  
    7. publicintclickedLevel;
    8. publicintidNextLevel;
    9.  
    10.  
    11. publicintlevelNeeded;
    12.  
    13. voidStart()
    14. {
    15. if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded)
    16. GetComponent<Button>().interactable=false;
    17. }
    18.  
    19.  
    20. publicvoidClickOnMap(){
    21. if(PlayerPrefs.GetInt("UnlockedLevel")>=clickedLevel){
    22. Application.LoadLevel(idNextLevel);
    23. }
    24. }
    25. }
     
  17. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Strange. What would
    Code (CSharp):
    1. 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):
    1. GetComponent<Button>().interactable=false;
    2. to
    3. 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?
     
    Last edited: Jan 18, 2016
    vakabaka likes this.
  18. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,153
    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):
    1. //here array with your buttons, that should be enabled
    2. public GameObject [] levelButton;
    3.  
    4. void Awake () {
    5. if (PlayerPrefs.HasKey ("UnlockedLevel"){
    6. int level = PlayerPrefs.GetInt ("UnlockedLevel");
    7. for (int i = 0; i < level; i++) {
    8. //here something to enable buttons
    9. levelButton [i].SetActive(true);
    10. //or levelButton [i].GetComponent<Button>().interactable=true;
    11. }
    12. //just enable level1
    13. else
    14. levelButton [0].SetActive(true);
    15. }
     
  19. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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 :D! i do not want them to dissapear,just not available for clicking.
     
  20. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    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):
    1. public void ResetProgress()
    2. {
    3.     PlayerPrefs.SetInt("UnlockedLevel",0);
    4. //Next line isn't mandatory, but saves effect of this between game sessions even if application crashes somehow
    5.     PlayerPrefs.Save();
    6. }
    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):
    1. public void UpdateInteractable()
    2. {
    3.     if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded)
    4.     {
    5.         GetComponent<Button>().interactable=false;
    6.     }
    7.     else
    8.     {
    9.         GetComponent<Button>().interactable=true;
    10.     }
    11. }
    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):
    1. 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):
    1. for(int a=0;a<nextLevelButtons.Length;a++)
    2. {
    3.     nextLevelButtons[a].UpdateInteractable();
    4. }
    or you find all buttons with something like
    Code (CSharp):
    1. Object[] objs = Object.FindObjectsOfType(typeof(LoadNextLevel));
    2. for(int a=0;a<objs.Length;a++)
    3. {
    4.     ((LoadNextLevel)objs).UpdateInteractable();
    5. }
     
    Last edited: Jan 19, 2016
  21. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    Why do i need a reset button?I only want the levels to unlock after the previous one is beat. ;)
     
  22. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    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
     
  23. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    So if i just add a reset button, the whole thing is going to work!?
     
  24. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Should work. That's what testing is for: making sure perhaps is always happening!
     
  25. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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?
     
  26. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    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.
     
  27. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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):
    1.  using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections;
    4.  
    5. publicclassLoadNextLevel:MonoBehaviour{
    6.  
    7. publicintclickedLevel;
    8. publicintidNextLevel;
    9.  
    10.  
    11. publicintlevelNeeded;
    12.  
    13. voidStart()
    14. {
    15. if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded)
    16. GetComponent<Button>().interactable=false;
    17. Debug.Log(PlayerPrefs.GetInt("UnlockedLevel"));
    18. }
    19.  
    20. publicvoidUpdateInteractable()
    21. {
    22. if(PlayerPrefs.GetInt("UnlockedLevel")<levelNeeded)
    23. {
    24. GetComponent<Button>().interactable=false;
    25. }
    26. else
    27. {
    28. GetComponent<Button>().interactable=true;
    29. }
    30. }
    31.  
    32.  
    33. publicvoidClickOnMap(){
    34. if(PlayerPrefs.GetInt("UnlockedLevel")>=clickedLevel){
    35. Application.LoadLevel(idNextLevel);
    36. }
    37. }
    38. }
    39.  
    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...
     
  28. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Do you do something like
    Code (CSharp):
    1. public void LevelComplete_SetUnlockedLevel(int completedLevel)
    2. {
    3.     if(PlayerPrefs.GetInt("UnlockedLevel")<completedLevel)
    4.         PlayerPrefs.SetInt("UnlockedLevel", completedLevel);
    5. }
    when you complete level? (value is level that should be unlocked after that call) To me it seems you don't...
     
  29. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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?
     
  30. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Yes, you should. Otherwise nothing will unlock;)
     
  31. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    This is the script that will run when level 1 is finished is it correct?

    Code (csharp):
    1.  using UnityEngine;
    2. using System.Collections;
    3.  
    4. publicclassLoadWorld1:MonoBehaviour{
    5.  
    6. privateintlevel_completed=2;
    7.  
    8. voidOnTriggerEnter2D(){
    9. Application.LoadLevel("World1");
    10. }
    11.  
    12. publicvoidLevelComplete_SetUnlockedLevel(intcompletedLevel)
    13. {
    14. level_completed=completedLevel;
    15. if(PlayerPrefs.GetInt("UnlockedLevel")<completedLevel)
    16. PlayerPrefs.SetInt("UnlockedLevel",completedLevel);
    17. }
    18.  
    19. }
    20.  
    ? It did not change the value of UnlockedLevel to 2 as i expected!
     
  32. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Um, nope. You call
    Code (CSharp):
    1. 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.
     
  33. vakabaka

    vakabaka

    Joined:
    Jul 21, 2014
    Posts:
    1,153
    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):
    1.  
    2. void Start () {
    3. PlayerPrefs.SetInt ("UnlockedLevel", 0);
    4. // reset other variables in PlayerPrefs, that are not needed
    5.  
    6. // or use PlayerPrefs.DeleteAll();
    7. }
    8.  
    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")
     
    Last edited: Jan 20, 2016
  34. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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):
    1.  using UnityEngine;
    2. using System.Collections;
    3.  
    4. publicclassAutosave:MonoBehaviour{
    5.  
    6. publicintplayedLevel;
    7.  
    8. voidAwake(){
    9. PlayerPrefs.SetInt("UnlockedLevel",playedLevel);
    10.  
    11. PlayerPrefs.Save();
    12. }
    13. }
    ;)
     
  35. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184
    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! :D
     
  36. SP-Designs

    SP-Designs

    Joined:
    Oct 13, 2015
    Posts:
    184

    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):
    1.  
    2. [LIST=1]
    3. [*]using UnityEngine;
    4. [*]using System.Collections;
    5. [*]
    6.  
    7. [*]publicclassLoadWorld1:MonoBehaviour{
    8. [*]
    9.  
    10. [*]privateintlevel_completed=2;
    11. [/LIST]
    12.          
    13. [LIST][*]publicvoidLevelComplete_SetUnlockedLevel(intcompletedLevel)[/LIST]
    14. [LIST][*]{[/LIST]
    15. [LIST][*]level_completed=completedLevel;[/LIST]
    16. [LIST][*]if(PlayerPrefs.GetInt("UnlockedLevel")<completedLevel)[/LIST]
    17. [LIST][*]PlayerPrefs.SetInt("UnlockedLevel",completedLevel);[/LIST]
    18. [LIST][*]// Add that piece of code here? is that right? [/LIST]
    19. [LIST][*]}[/LIST]
    20.  
    21. [LIST=1]
    22. [*]voidOnTriggerEnter2D(){
    23. [*]Application.LoadLevel("World1");
    24. [*]}
    25. [*]
    26.  
    27. [*]}
    28. [*]
    [/LIST]