inventory headache

i’m trying to code an inventory working from the list-based inventory i used in Game Maker. here is the basic code:
inv_add

//In this script we will add items to the inventory
var found = -1;

for (var i = 0; i < global.invMax; i++) {
    if (global.inv[i, 0] == argument0) {
        found = i; //We affect to found the place of the item if we find it
        break;
    }
}

//If it doesn't exist

if (found == -1) {
    //Let's check if there is an empty place to add our item
    for (var j = 0; j < global.invMax; j++) {
        if (global.inv[j, 0] == global.NOTHING) {
            found = j;
            break;
        }
    }
}

//should have an "inventory is full" contingency

//Now let's add our item to the place we have *
global.inv[found, 0] = argument0; //Argument 0 is the id of the item
global.inv[found, 1] += argument1; //Argument 1 is the amount of the item to add

inv_init

//This script will initialize the inventory
global.inv[0, 0] = 0;//Inv = the inventory array, invMax = max items
global.invMax = 30;//The max items can hold
//Initialize the inv array
for (var i = 0; i < global.invMax; i++) {
    global.inv[i, 0] = 0;//This is the item id, every item has its id in the inv_data_item()
    global.inv[i, 1] = 0; //This is the amount of the current item
    }

inv_data_item

//In this script we will store our items' data
//Let's start with some id
//Note from George: going to store the numbers in an external file and read them into this somehow
//This is just to remember
global.NAME = 0;
global.DESC = 1;
global.PRICE = 2;
global.OBJ_NAME = 3;

//Id of items
global.NOTHING = 0;
global.HP_POTION = 1;
global.MP_POTION = 2;
global.SWORD = 3;
global.SHIELD = 4;

//The data
global.item[0, 0] = global.NOTHING;//the id
global.item[0, 1] = "Nothing";//the name
global.item[0, 2] = "Nothing";//the description
global.item[0, 3] = 0;//price
global.item[0, 4] = "nothing";//object name ???

global.item[1, 0] = global.HP_POTION;//the id
global.item[1, 1] = "HP Potion";//the name
global.item[1, 2] = "Heals HP.";//the description
global.item[1, 3] = 10;//price
global.item[1, 4] = "obj_HP_POTION";//object name

global.item[2, 0] = global.MP_POTION;//the id
global.item[2, 1] = "MP Potion"//the name
global.item[2, 2] = "Restores MP"//the description
global.item[2, 3] = 10;//price
global.item[2, 4] = "obj_MP_POTION";//object name

global.item[3, 0] = global.SWORD;//the id
global.item[3, 1] = "Sword";//the name
global.item[3, 2] = "Sharp object."//the description
global.item[3, 3] = 100;//price
global.item[3, 4] = "obj_SWORD";//object name

global.item[4, 0] = global.SHIELD;//the id
global.item[4, 1] = "Shield";//the name
global.item[4, 2] = "Blocks attacks.";//the description
global.item[4, 3] = 50;//price
global.item[4, 4] = "obj_SHIELD";//object name

i need to use some kind of external file editor for the list of all items and i have no idea where to even start with that. there is JSON and i found an SQL editor in the assets store. i’m going to have to change a lot of things going from GM to Unity. is this framework still feasible for unity? there aren’t global variables in unity so i’m not sure what i’m going to do to replace that. any feedback?

i decided i might as well create a thread for this because i’m probably going to have more questions as i proceed.

i can add more code in additional posts if you ask for it. this inventory is actually why i quit Game Maker but i’m glad to be using Unity now. it’s better in a lot of ways.

You can make a global store for data. To get exactly the same behaviour as your bottom script, you’d simply do:

public static class global {

    public const int NAME = 0;
    public const int DESC = 1;
    public const int PRICE = 2;
    public const int OBJ_NAME = 3;

    //Id of items
    public const int NOTHING = 0;
    public const int HP_POTION = 1;
    public const int MP_POTION = 2;
    public const int SWORD = 3;
    public const int SHIELD = 4;

    public static object[,] item;

    static global() {
        item = new object [5, 5];

        //The data
        item[0, 0] = NOTHING; //the id
        item[0, 1] = "Nothing"; //the name
        item[0, 2] = "Nothing"; //the description
        item[0, 3] = 0; //price
        item[0, 4] = "nothing"; //object name ???

        item[1, 0] = HP_POTION; //the id
        item[1, 1] = "HP Potion"; //the name
        item[1, 2] = "Heals HP."; //the description
        item[1, 3] = 10; //price
        item[1, 4] = "obj_HP_POTION"; //object name

        item[2, 0] = MP_POTION; //the id
        item[2, 1] = "MP Potion"; //the name
        item[2, 2] = "Restores MP"; //the description
        item[2, 3] = 10; //price
        item[2, 4] = "obj_MP_POTION"; //object name

        item[3, 0] = SWORD; //the id
        item[3, 1] = "Sword"; //the name
        item[3, 2] = "Sharp object."; //the description
        item[3, 3] = 100; //price
        item[3, 4] = "obj_SWORD"; //object name

        item[4, 0] = SHIELD; //the id
        item[4, 1] = "Shield"; //the name
        item[4, 2] = "Blocks attacks."; //the description
        item[4, 3] = 50; //price
        item[4, 4] = "obj_SHIELD"; //object name
    }
}

Now, this is horrible, horrible stuff. Don’t actually do it. Here’s how you check the price of your sword:

int sword_cost = (int) global.item[global.SWORD, global.PRICE];
//or worse:
int sword_cost = (int) global.item[3, 3];

If you want to have global definitions of items, and put them in code, that’s fine. But while the above stuff works, it’s going to cause you to have to cast back and forth all the time, and not be pretty. Here’s a C#-y way to do the same thing:

//Wrapper class containing old info in a prettier way
public class ItemInfo {
    //Make all of these readonly so you can be sure they won't be changed.
    public readonly string name;
    public readonly string description;
    public readonly int price;

    // why is this different from the name? Keeping it in since
    // it's in your original code, but it should probably be something else.
    public readonly string object_name;

    public ItemInfo(string name, string description, int price, string object_name) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.object_name = object_name;
    }
}

//Keep the named IDs, but put them in an enum.
public enum ItemID {
    NOTHING = 0,
    HP_POTION = 1,
    MP_POTION = 2,
    SWORD = 3,
    SHIELD = 4,
}

//Replacement of item
public static class ItemDatabase {

    public static Dictionary<ItemID, ItemInfo> items;

    static ItemDatabase() {
        items = new Dictionary<ItemID, ItemInfo>();

        //You probably don't need an explicit representation of nothing? Kept it in.
        items[ItemID.NOTHING] = new ItemInfo("Nothing", "Nothing", 0, "nothing");
        items[ItemID.HP_POTION] = new ItemInfo("HP Potion", "Heals HP", 10, "obj_HP_potion");
        items[ItemID.MP_POTION] = new ItemInfo("MP Potion", "Restores MP", 10, "obj_mp_potion");
        items[ItemID.SWORD] = new ItemInfo("Sword", "Shard object", 100, "obj_SWORD");
        items[ItemID.SHIELD] = new ItemInfo("Shield", "Blocks attacks", 50, "obj_SHIELD");
    }
}

Compare checking the price:

//before:
int sword_cost = (int) global.item[global.SWORD, global.PRICE];

//after:
int sword_price = ItemDatabase.items[ItemID.SWORD].price;

It’s a bit less cumbersome (you get to write two less words!), and it’s typed correctly, so you don’t have to cast. The real beauty is in methods using it, though. If you have a function that shows info about a weapon, the old signature would be:

void ShowInfo(string itemName, string itemDescription, int price) {
    ...
}

While the new signature is simply:

void ShowInfo(ItemInfo info) {
    ...
}

You’re also safe from writing bugs where you send the shield’s name and the sword’s description, since they’re stuck together in the same object.

When you’re ready to start using an external store, get a simple JSON plugin, and store the item descriptions in a JSON file. It’s the fastest and simplest way to do things.
Databases (SQL) are for handling very much data very fast. Don’t do it before you actually need the speed, as it’s harder to work with than a simple text editor.

A good simple json plugin is simpleJSON. Unity has a built-in JSON handler, but it’s a bit cumbersome to use.

When you do that, you replace the static constructor (static ItemDatabase) above with a function that reads items from the JSON file instead of writing them out. Otherwise it’s exactly the same setup.

You could also store the items in what’s called a ScriptableObject, which is a way to create custom Unity assets. The cool thing there is that you can write a nice inspector for the item instead of having to use a text editor, but I wouldn’t worry about that if you’re still learning Unity. Do one thing at the time.

Hope that helps!

2 Likes

thanks Baste! this takes me a long way and is pretty much exactly the info i was looking for. thanks! see you around on the forums.

can i nest the iteminfo and itemdatabase etc. classes in a monobehavior container class or would that cause problems?

edit: i’m pretty sure i know the answer after reading about monobehaviour again but a lot of times asking questions uncovers information i didn’t know about before …

As in having them as nested classes?

Sure, and if you’re only working with them inside of the class, that’s often helpful.

1 Like