Blog

Latest news and updates from the PlayFab developers

by Janeious 2020-04-29

Porting "Fusion Guards" from GameSparks to PlayFab (Part 2)

We are Janeious, an independent game development studio with offices in Ireland and Germany working on our community funded debut title “Fusion Guards”. The game is a turn-based RPG for mobile with a large backend component. After four years of development our game is currently in Beta on Android and iOS. We built our game on GameSparks but recently decided to investigate a migration to PlayFab. This is our journey.

In this tutorial we look at what alternatives PlayFab offers to the loot-drop systems we used in GameSparks.

Expectations

We knew going into this project that PlayFab offers loot-tables as a feature. However, we were pretty skeptical about how many of these features we’d be able to use and how much we could customize things to suit what we needed from loot-drop and inventory items.

Customizing Items

For Fusion Guards, items and inventory are an important part of the game loop. Players get items via loot-drops (as well as through quests and missions), and will be constantly equipping those items, upgrading them, dismantling them, and unlocking new items. So, we don’t just need to be able to deliver items, we need to add some custom information to each item and update that when we want.

Loot-Tables

We also use several different kinds of loot-tables for loot-drops, some of which also involve other tables themselves to deliver items. These tables are available via different types of currencies and drop different amounts of items. So, we expected we’d have to do a lot of customization to achieve what we needed.

Game Context

Fusion Guards is a game about creating squads of super bad-ass robots to send into battle against opponents in an arena. Here you battle your way to the top of the leaderboard and gain rewards for the rank you get and how long you can hold that position. The longer you spend at the top the more players will be trying to take your spot.

So, in our game we have different kinds of items…

  • Pilots
  • Guards

These are unique and unlocked from collecting other items. They can be upgraded, but not levelled up.

  • Weapons
  • Armor
  • Core
  • Sensor

These are not unique; the player can have multiples of these items in their inventory. Players can level them up and dismantle them for bonus currencies.

By putting all this together, we create a unit. Putting some units together we create a squad we can send into battle.

Creating Items

The first things we needed were items. In Fusion Guards you can’t buy items directly, but you can get them from packages and through loot drops. Packages are covered using PlayFab bundles, so we weren’t worried about those for this example.

Catalogs

In GameSparks, the Virtual Goods (VGs) feature covers how we can configure items the player can acquire through cloud-code, store-purchase, bundles, and so on.

These VGs are added to your game and accessible by several features. With PlayFab these VGs are accessed through Catalogs.

A Catalog is a collection of items or item bundles (pretty much the same as GameSparks) but also includes drop-tables and storefronts which can be used in various ways.

There is a lot to Catalogs, but in this tutorial we are only going to be looking at items and drop-tables because that’s all we needed for this part of the migration. Catalogs are easy to set up, and there is a pretty thorough guide on Catalogs in the documentation.

We started with some basic items, just what we’d need for a single unit in our game.

Item Attributes

There are a lot more attributes you can add to an item with PlayFab, than you can with GameSparks VGs. This makes sense because VGs are more about monetisation while outwardly it seems like PlayFab items have more features around inventory and game-configuration. Although, you can set prices on PlayFab items and just purchase them through a storefront.

Some of this might be a bit confusing, so we’ll go through what we did for our setup to clear things up a bit.

Creating a new catalog item
Creating a new catalog item

We wanted to start by mapping as much data directly to items as we could, so you can see in the example above we used existing itemIDs and names from Fusion Guards.

Item Class & Tags

We have different item categories in Fusion Guards, and we distinguish these both as IDs and strings in different places, so we could just use these the way they were.

Consumables

Items granted to the player are not consumable, they remain in the player’s inventory forever unless the player upgrades or dismantles them.

There are other attributes (limited-edition and character-token for example) which we don’t need to deal with at the moment, but we might find a use for them later on.

Custom Data

Our item structure in GameSparks needs to be stored in a custom collection because we use a lot of references to other things (base-stats and special attacks for example).

In GameSparks we’d have to store this information in separate meta-collections, or in property sets which tied to VGs by ID. With PlayFab it was convenient to have custom data we could attach to the item right there in the editor.

There are two options on the custom data, there is a key/value pair option, or JSON. We could have used the key/value pairs, but It was very convenient to be able to just paste the JSON we used in GameSparks here without setting anything else up.

Adding custom JSON data
Adding custom JSON data

After that we set up a few items needed to configure a basic unit in one of our battle squads.

Adding items
Adding items

Drop Tables

Obviously for a loot-table to work properly we would need more than six basic items, but this was enough to get an idea of how much work it would be when we moved all our items and tables into PlayFab.

To setup a new drop-table we just needed to go to the drop-table tab of Catalog and then add some contents to the table.

Editing a drop table
Editing a drop table

We added all the items we created to the table.

Adding items
Adding items

Once items are added they appeared in the table contents and we could start messing with their weights.

Setting item weights
Setting item weights

We left playing around with the weights for later and began testing loot drops.

(If you want more information on drop-tables you can read a guide in the PlayFab documentation.)

Dropping Single Items

We didn’t actually know how this would work-out at first, so we created a test function in the cloud-script that would allow us to drop items and see the results.

The APIs we used were all taken from the guide linked above, so it wasn’t too hard to figure out what was needed.

handlers.testDropTable = function(args){
  
    this.dropLoot(args.count, args.tableID);
}
 
/**
 * Used to trigger a single loot-drop
 * @param {Number} count
 * @param {String} tableID
 * return {JSON} result
 */
function dropLoot(count, tableID){
    // [1] - Execute a loot-drop from a single table //
    //       The response will return the itemID //
    var itemIDList = [];
    for(var i = 0; i < count; i++){
        var result = server.EvaluateRandomResultTable({ "TableId": tableID });
        if(result){
            // [2] - For our game itemIDs are always a number so we'll parse that //
            var itemID = parseInt(result.ResultItemId);
            log.info("Item Dropped -"+itemID);
            itemIDList.push(itemID);
        }else{
            var errorMessage = "Loot-drop Error ["+currentPlayerId+"]...";
            log.error(errorMessage);
            return {
                "error" : errorMessage
            }
        }
    }
    // [3] - And then we'll grant the item to the user //
    //       This function returns the item details automatically, so we dont need to return any additional details //
    result = server.GrantItemsToUser({ "PlayFabId" : currentPlayerId, "ItemIds" : itemIDList });
    return result;
}

The dropLoot function was our main function. We'll cover that later for dropping loot based off server events. The handler function was just used to check what happens when we run the loot-drop code and items land in the player’s inventory.

Testing
Testing

Multiple Drop Tables – Sources

We knew PlayFab had drop tables when we started investigating, but we didn’t know exactly how we were going to implement them because of how Fusion Guards uses drop-tables.

In fact, we have two separate systems for loot-drops used in very different places.

Drop-Tables

As above, these are lists of items weighted in a table. They use several random items depending on the table (usually one to three items).

They are always called from the server based on something the player is doing and can never be triggered from the client.

For example, if a player tries to replay a quest or boss battle we give them a random item (instead of the special items the boss drops the first time). This is how we manage the difficulty curve for Fusion Guards.

The drop-table function we created above covered this. However, there’s also another kind of loot-drop which is very important to our monetization, and more complicated to implement.

Source Drops

This is the main place players get items in Fusion Guards. We call this the Source.

Source-drops are instigated by the player in a few different ways.

  • Credits: Our basic currency
  • Cells: Our premium currency
  • Timers: Several free drops per day

The most complicated part about these is that they are themselves composed of several drop-tables.

Drops in Fusion Guards
Drops in Fusion Guards

For example, the Rare source above will contain specific kinds of items which are available through specific kinds of loot-tables. Each table is distributed differently. Its weight in the source is distributed differently. It sounds confusing, but our players love to crunch the numbers and calculate odds.

“Drop-Tableception”

We thought this was going to require a lot of custom cloud-code as our GameSparks implementation did, but we found the drop-tables guide already had us covered.

Using PlayFab you can make a drop-table out of other drop tables. All you do is pick drop-tables from the list of options instead of picking items.

Picking drop tables form the list
Picking drop tables from the list

With that do we could play around with the weights for this source.

Adjusting weights
Adjusting weights
Then we added this to a bundle.

For our example we tried using the bundle as our source directly. That way we could test purchasing the bundle from the client directly instead of wrapping the purchase call in a CloudScript function.

Bundles are also available in the Economy -> Catalog view.

Setting up a bundle
Setting up a bundle

Next we set up this bundle to include our drop table (which contained the three other drop tables). The setup was similar to the items we created earlier, but we added a currency price.

Including a drop table in a bundle
Including a drop table in a bundl

Consumable Bundles

We suspected that what would happen when we opened the bundle, was we would get three items from the drop-tables and we’d also get the bundle as an item delivered to the player’s inventory.

In testing we confirmed this was happening.

Testing

Test results
Test results

As you can see, we got a log for three random items and for the bundle. In the player’s inventory section (available from the players tab), we could see see we also had the bundle in the player’s inventory.

Examining the player's inventory
This player-view is useful. In GameSparks you’d have to make your own management screen to see the player’s inventory.
We didn’t want this, as it meant there would be a lot of bundles hanging around in the player’s inventory doing nothing.

Luckily, PlayFab has a solution for this that we just ignored when setting the bundle up. At the bottom of the bundle was an info-bar telling us this would be a problem.

PlayFab warning
PlayFab warning

We set the bundles to be consumable and removed after three seconds.

Making a bundle consumable
Making a bundle consumable
Our consumables were all setup and would disappear after the items in the loot-drop were delivered. The next step was to hook up our existing calls to work with purchasing this bundle.

Get All Inventory

Once we were able to deliver items to players, we needed a way for the client to get the player’s inventory. In GameSparks we’d have to create our own requests because our inventory items had more complicated information than just the name and count of the items the player has access to using the GetVirtualGoodsRequest.

With PlayFab there was also a client request we could use to get the player’s inventory, the GetUserInventoryRequest.

The integration was like the requests we integrated in the previous tutorial. We just needed to add a callback for when the request returned the inventory, plus one for when it failed.

Conveniently this request also returns information about virtual currency, which is helps us keep our client in sync with the backend and means we can reduce the number of calls we need for getPlayerDetails (created in the last tutorial).

The difference here is that we needed to parse an array of inventory items which aren’t the same as the structure we used in GameSparks. (This is just part of the migration process.)

There is a class for item instances in the UnitySDK which makes it easy to parse inventory items. Here’s a brief example:

private List<ItemInstance> inventory = new List<ItemInstance>();
    public PlayerInventory(GetUserInventoryResult result)
    {
        if (result != null)
        {
            foreach (var item in result.Inventory)
            {
                inventory.Add(item);
            }
        }
    }

Granting items

For Fusion Guards, we keep some other data on inventory items which we use to track item level, locked (or unlocked) items and so on. We needed to find a way to add additional data onto items and update it later.

In GameSparks we have a custom solution for items, which gives us the option to run whatever code to setup items we choose. However, in PlayFab we had to use the structure for inventory items that’s used by the server.

PlayFab has this covered with the UpdateUserInventoryItemCustomData API.

We noticed a small icon in our list of inventory items in the player view:

Icon in player view
Icon in player view
Selecting this shows a link to the API that controls it.

The example above already has our itemLevel attribute. We’ll show how we set that up.

We needed to add a new handler-function in the CloudScript. This was going to take in the context of the event that triggers it. In this case we tied this function to a Rule (similar to how we added a function that would run when the player registers in the last tutorial).

We needed to get the instanceID of the item.

We knew that it was needed from the API, and we could see there was an instanceID unique to each item.

Instance ID
Instance ID

By logging the context we were able to get what information was available and as we expected the instanceID was in there.

The code we used was as follows:

/**
  
 * Add extra custom attributes to items when they are granted to the player
 * @param {JSON} context - the details of the event that called the function
 */
handlers.setupNewItem = function(context){
    // [1] - We need the instanceID of this item //
    //       This is taken from the context of the even that called this function //
    var itemInstanceID = context.playStreamEvent.InstanceId;
    // [2] - We setup some basic custom data //
    var customData = { "itemLevel" : 1 };
    // [3] - Finally we write the custom data to the new item in the player's inventory //
    var result = server.UpdateUserInventoryItemCustomData({ "Data" : customData, "ItemInstanceId" : itemInstanceId, "PlayFabId" : currentPlayerId });
    if(result){
        log.info(result);
    }else{
        var errorMessage = "Error saving player settings ["+currentPlayerId+"]...";
        log.error(errorMessage);
    }
}

We tied this function to the event that adds items to the player’s inventory using a Rule.

Editing a rule
Editing a rule
Using this method, every time a new item was put into the player’s inventory it was added at level one.

Conclusion

We were getting used to PlayFab, so we didn’t have to look around much to find what we needed to put this together.

Often we made assumptions for each step and were happy to find PlayFab designed the system in the way we expected and the solution worked out in the end.