-1

I'm really struggling with creating a weapon/item unlock system via an in-game shop. The player should only be able to select weapons at indexes that have been unlocked or bought.

I've wasted two days on the best way to achieve this and I'm still not anywhere near a solution.

Currently this is what I have:

I have a WeaponUnlock script that handles a dictionary of weapons and another WeaponSwitcher script that access vales from that script to check if it is unlocked or not.

public class WeaponUnlockSystem : MonoBehaviour
{

    private Dictionary<string, bool> weaponUnlockState;    

    void Start()
    {
        // Seeding with some data for example purposes
        weaponUnlockState = new Dictionary<string, bool>();
        weaponUnlockState.Add("Shotgun", true);              
        weaponUnlockState.Add("Rifle", true);
        weaponUnlockState.Add("FireballGun", false);
        weaponUnlockState.Add("LaserGun", false);
        weaponUnlockState.Add("FlamethrowerGun", false);
        weaponUnlockState.Add("SuperGun", false);
    }

    public bool IsWeaponUnlocked(string weapon_)
    {
        if (weaponUnlockState.ContainsKey(weapon_))
            return weaponUnlockState[weapon_];              // this will return either true or false if the weapon is unlocked or not
        else
            return false;
    }

    public void UnlockWeapon(string weapon_)            //this will be called by the button..
    {
        if (weaponUnlockState.ContainsKey(weapon_))
            weaponUnlockState[weapon_] = true;
    }
}

The WeaponSwitchergets the name of the weapon from the nested children every time the nextWeapon button is pressed:

weaponName = this.gameObject.transform.GetChild(weaponIdx).name.ToString();     //get the name of the child at current index

        if (weaponUnlock.IsWeaponUnlocked(weaponName))      //ask weaponUnlockSystem if this name weapon is unlocked or not, only enable the transform if true is returned
            selectedWeapon = weaponIdx;

This...... works..... but I know is not practical. As the script sits on a game object and at every time it is called the Start() is called that will reset all the unlocked values. Also will I have to make it persistent via DontDestroyOnLOad() through scenes?

I can get use PlayerPrefs and set a value for every weapon, but that is also not ideal and also to avoid it being reset every time someone opens my game I would have to perform a checks of PlayerPrefs.HasKey() for every weapon. Also not practical.

There must be a better way of doing this. Its funny that I cannot find much help online for this and dont know how everyone is getting around this.

Thanks

StuckInPhDNoMore
  • 2,507
  • 4
  • 41
  • 73
  • In what way do you judge this impractical? Sure there are other solutions but we'd need context on what you are unhappy with. – Adam G May 27 '19 at 11:44
  • As the script sits on a game object and at every time it is called the ``Start()`` is called that will reset all the unlocked values. Also will I have to make it persistent via ``DontDestroyOnLoad()`` through scenes? – StuckInPhDNoMore May 27 '19 at 11:51
  • Just create a class that is not a monobehaviour, with "standard" bool variables and use a reference to that class in something like a GameManager ? – Jichael May 27 '19 at 12:10
  • 1
    Possible duplicate of [Best way to save data in Unity game](https://stackoverflow.com/questions/34426570/best-way-to-save-data-in-unity-game) – derHugo May 27 '19 at 16:20

2 Answers2

1

I had a Unity project recently where we needed to hold and manipulate some simple data between scenes. We just made a static class that doesn't extend monobehaviour. Also, if you're looking for efficiency, why not just maintain an array of unlocked weapons?

All in all, why not try something like this:

public static class WeaponUnlockSystem
{

    private static string[] unlockedWeapons = InitialWeapons();

    private static string[] InitialWeapons(){
        string w = new string[]{
            "Shotgun",
            "Rifle",
        }
    }

    public static bool IsWeaponUnlocked(string name)
    {
        int i = 0;
        bool found = false;
        while(i < unlockedWeapons.Length && !found){
            if (string.Equals(unlockedWeapons[i],name)){
                found = true;
            }
            i++;
        }
        return found;
    }

    public static void UnlockWeapon(string name)
    {
        string[] weapons = new string[unlockedWeapons.Length+1];
        int i = 0;
        bool found = false;
        while(i < unlockedWeapons.Length && !found){
            if (string.Equals(unlockedWeapons[i],name)){
                found = true;
            } else {
                weapons[i] = unlockedWeapons[i];
            }
        }
        if(!found){
            weapons[unlockedWeapons.Length] = name;
            unlockedWeapons = weapons;
        }
}

I'm not running my c# IDE, so I apologise if there's any syntax errors. Should be efficient, simple and - as it's static - you shouldn't need to put it on an empty GameObject to have it work.

CasualViking
  • 262
  • 1
  • 9
1

You create a class that is not a GameObject, just a normal class. There you can initialize the dictionary and have methods to unlock or check if a weapon is unlocked.

As I don't know what your architecture of the rest of the code looks like, I will go ahead and assume that you have some kind of a persistent player model.

You could just add the WeaponUnlockSystem to your player GameObject and manage the unlocks from there. To me this would make the most sense as the player is probably the one unlocking the weapons so the WeaponUnlockSystem should be attached to him.

Here is a really basic code example:

public class WeaponUnlockSystem {

    private Dictionary<string, bool> weaponUnlockState;

    public WeaponUnlockSystem() 
    {
        weaponUnlockState = new Dictionary<string, bool>();
        weaponUnlockState.Add("Shotgun", true);              
        weaponUnlockState.Add("Rifle", true);
        weaponUnlockState.Add("FireballGun", false);
        weaponUnlockState.Add("LaserGun", false);
        weaponUnlockState.Add("FlamethrowerGun", false);
        weaponUnlockState.Add("SuperGun", false);
    }

    public bool IsWeaponUnlocked(string weapon)
    {
        if (weaponUnlockState.ContainsKey(weapon))
            return weaponUnlockState[weapon];         
        else
            return false;
    }

    public void UnlockWeapon(string weapon) 
    {
        if (weaponUnlockState.ContainsKey(weapon))
            weaponUnlockState[weapon] = true;
    }
}

public class Player : MonoBehaviour {

    private WeaponUnlockSystem weaponUnlockSystem;

    void Start()
    {
        weaponUnlockSystem = new WeaponUnlockSystem();
        ...
    }

    public void NextWeapon(string weapon) 
    {
        ...
    }
}

I would also advice against using a static class for convenience here. Static should only be used, if you don't want to change the state of an object or don't need to create an instance of an object.

Mirko Brandt
  • 121
  • 2