June 3, 2014

Power of JSON in Game Development - Items

Hey there, it's been a while since I last time wrote anything here, so... It's time to fix that!

This time, I'm going to tell you about JSON and how it's one of the greatest things ever. Lately, I've been fiddling around with a game that relies heavily on persistent data - I need to store high scores, whether or not the player has seen a certain tutorial tip, what kind of items there are available and so on. All this could be achieved via simple binary files and code to represent everything, but this would soon become rather cumbersome to deal with.

Let's think of an item system in a generic RPG game. You have your swords, bows, daggers and wands, as well as helmets, chest armors, gloves and boots. All of them share similarities: each of them has a unique name, they only fit certain slots, and they provide certain bonuses. Now, often times you would rush into coding and do something like this:


public class Item {

    String name;
    Slot slot;
    Sprite sprite;
    public Item(String name) {
        this.name = name;
    }

}

public class Sword extends Item {
  
    public Sword(String name) {
        super(name);
    }
}

public class SwordOfDoom extends Sword {

    public SwordOfDoom() {
        super("Sword of Doom");
        slot = Slot.MainHand;
    }

}

Now, this seems viable at first, but what if there's tons of items in your game? I'm sure you don't want to go creating as many classes as there are items! To make this so much more easier, let's use JSON!

What's JSON?

As you may already know, JSON is a simple way of storing data in text form. It's really lightweight and allows for easy transfer and storage of data. What you may not know is that you can store Java objects in JSON too. Whaaat?

Yeah, it's true. In libgdx, there's really good JSON classes that do this for you. You can create all your items in JSON and the game will create the actual objects automatically. Talk about awesome, eh?

How?

If you use libgdx, it's rather simple. Let's take item system as an example and write some nifty JSON:

{
    items: [
        SwordOfDoom: {
            class: com.manabreak.awesomerpg.Sword,
            name: "Sword of Doom",
            texture: "swordofdoom",
            damageMin: 7,
            damageMax: 9
        },
        DaggerOfKnowledge: {
            class: com.manabreak.awesomerpg.Dagger,
            name: "Dagger of Knowledge",
            texture: "dagger_green",
            damageMin: 5,
            damageMax: 15
        }
    ]
}

I think it's already pretty clear what's going on in here. We have an array of JSON objects called 'items'. Each item has a name (SwordOfDoom and DaggerOfKnowledge in this case), and each of these items has a set of properties, such as which class to instantiate, which name it should use in-game and so on. As you can also see here, you can mention the name of the texture you want to use and any other properties such as damage output.

Now, to load these items in the game, we'll have to create the classes first:


public class Item {

    // Name of the item
    private String name;

    // Texture to use for the item
    private String texture;

    // We need an empty constructor for (de)serialization
    public Item() {}

}

public class Weapon extends Item {
    
    // Minimum damage
    int damageMin = 0;

    // Maximum damage
    int damageMax = 0;

    // Again, empty constructor
    public Weapon() {}
}

public class Sword extends Weapon {

    public Sword() {}

}

public class Dagger extends Weapon {

    public Dagger() {}

}


Now we have our classes. Notice how the names of the fields are exactly the same as in the JSON. This is important for the reflection and (de)serialization. Now, all we need to do is write a controller that will load the JSON for us:

public class ItemController {

    // Hashmap where we store our items as "item name"-"item" pairs
    private HashMap<String, Item> items = new HashMap<String, Item>();

    public ItemController() {
        loadItemsFromJSON();
    }

    private void loadItemsFromJSON() {
        Json json = new Json();
        items = json.fromJson(HashMap.class, Gdx.files.internal("items.json"));
    }
}


If everything went OK, you should now have both of your weapons in the hash map!

Why separate classes for Sword and Dagger? Why not just Weapon?

This is to provide different functionality to different items. If both swords and daggers behave similarly, then it's OK to use a single class for both of them. However, if you have different functionality for them, you'll need to write the code for both of them. While you still need to write code for each different item type, you still end up writing a lot less code as you don't have to write separate classes for each individual item. You can write hundreds of different swords in the JSON file, and if they all behave the same, you'll only need the one Sword class! Hooray!

Hope you find this post useful. It's written in Java and uses libgdx as an example, but similar reflection systems can be used in other languages and with other tools as well. Cheers!




2 comments:

  1. Very nice and well explained article!
    I think it would be awesome if you can give an example of the toJson function so the article will cover the entire workflow: Creating game objects - Persist them in Json - Loading them back again in our game...

    I have a question also. I haven't seen the syntax in:
    private HashMap
    And I wasn't able to find it in some documentation... Can you explain to me what this syntax means?

    Keep writing!
    Javi

    ReplyDelete
    Replies
    1. Whoops, the syntax in the HashMap part is a mistake caused by the HTML formatting, I'll fix it right away!

      As for the toJson function, I'm planning on writing about that on the next post. It'll probably be using a different thing as an example, though, something that has a clear benefit from the persistent storage. Maybe a high score system? :)

      Delete