Skip to content

Template: Clash of Clans

Clash of Clans is a popular mobile strategy game developed and published by Supercell. It was first released for iOS devices in August 2012, followed by an Android release in October of the same year. The game has since become a worldwide phenomenon, with millions of active players across the globe.

In the game, players build and upgrade their village, train troops, and battle against other players to earn resources and increase their rank. The game's success is partly due to its addictive gameplay and regular updates, which keep players engaged and constantly improving their strategies.

Screenshot

Clash of Clans has received numerous awards and has been downloaded over 500 million times from the Google Play Store alone. It has also spawned a massive esports scene, with professional players and teams competing in tournaments for cash prizes.

Store Template

Are you looking to create a store system like Clash of Clans in your game? Look no further than Balancy. Our team has thoroughly analyzed and reconstructed the data used by Clash of Clans to create their store system, and we've incorporated that knowledge into our platform. With Balancy, you can see how resources work and what formulas are used to determine their prices.

Screenshot

Although Clash of Clans offers various construction, heroes, and troops that players can purchase, our template focuses solely on resources. However, with Balancy, you can quickly expand your store system to include any new content you need. Plus, you can read the complete deconstruction of all formulas Clash of Clans uses in our blog post, so you can better understand how their store system operates.

How to add the template

Screenshot

What Template includes

The list of Store Items:

Screenshot

The Store:

Screenshot

3 Offers:

Screenshot

A Script which activates all the Offers at the right time:

Screenshot

The config, which is used for all the formulas:

Screenshot

Dynamic reward calculation

We've created a new template called ClashOfClans.ResourcesStoreItem, inherited from Store Item and added a new parameter Filling to it:

Screenshot

We've created all the documents, so players could fill their storage by 10%, 50%, and 100% of their storage capacity. Using the parameter Dynamic Reward we specified the Script, which will be used to calculate the reward at runtime.

Screenshot

The current amount of resources and the storage capacity for each of the resources is saved in Smart Objects

Screenshot

Here is how we calculate the dynamic reward

Screenshot Screenshot

Section for programmers

using System;
using System.Collections.Generic;
using Balancy;
using UnityEngine;

namespace ClashOfClans
{
    public static class ResourcesManager
    {
        public static void Tests()
        {
            TestTimingViaCode();
            TestTimingViaScript();
            TestResources();
        }

        private static void TestTimingViaCode()
        {
            Debug.LogWarning("=>> TestTimingViaCode");

            var gems0 = GetGemsRequiredToAccelerateTime(0);
            Debug.Log("gems0 : " + gems0);
            var gems20 = GetGemsRequiredToAccelerateTime(3600);
            Debug.Log("gems20 : " + gems20);
            var gems93 = GetGemsRequiredToAccelerateTime(8*3600);
            Debug.Log("gems93 : " + gems93);
            var gems1000 = GetGemsRequiredToAccelerateTime(7*24*3600);
            Debug.Log("gems1000 : " + gems1000);
            var gems1123 = GetGemsRequiredToAccelerateTime(8*24*3600);
            Debug.Log("gems1123 : " + gems1123);
        }

        private static void TestTimingViaScript()
        {
            Debug.LogWarning("=>> TestTimingViaScript");

            GetGemsRequiredToAccelerateTimeScriptAsynch(0, gems => Debug.Log("gems0 : " + gems));
            GetGemsRequiredToAccelerateTimeScriptAsynch(3600, gems => Debug.Log("gems20 : " + gems));
            GetGemsRequiredToAccelerateTimeScriptAsynch(8*3600, gems => Debug.Log("gems93 : " + gems));
            GetGemsRequiredToAccelerateTimeScriptAsynch(7*24*3600, gems => Debug.Log("gems1000 : " + gems));
            GetGemsRequiredToAccelerateTimeScriptAsynch(8*24*3600, gems => Debug.Log("gems1123 : " + gems));
        }

        private static void GetGemsRequiredToAccelerateTimeScriptAsynch(int time, Action<int> callback)
        {
            var config = DataEditor.ClashOfClans.Config;
            config.TimeToGemsCalculator.Run(new Dictionary<string, object> { 
                { "Config", config } ,
                { "Time", time } ,
                }, (exit, outputs) =>
            {
                if (outputs.TryGetValue("Gems", out var gems))
                    callback?.Invoke(Balancy.Utils.ToInt(gems));
                else
                {
                    Debug.LogError("Failed to get 'Gems' output");
                    callback?.Invoke(0);
                }
            });
        }

        private static void TestResources()
        {
            var defaultResources = DataEditor.ClashOfClans.Config.Resources;

            var gold50 = GetGemsRequiredToBuyItems(defaultResources.Gold, 50);
            Debug.Log("gold50 = gems: " + gold50);
            var gold5000 = GetGemsRequiredToBuyItems(defaultResources.Gold, 5000);
            Debug.Log("gold5000 = gems: " + gold5000);
            var gold50000 = GetGemsRequiredToBuyItems(defaultResources.Gold, 50000);
            Debug.Log("gold50000 = gems: " + gold50000);
            var gold1M = GetGemsRequiredToBuyItems(defaultResources.Gold, 1000000);
            Debug.Log("gold1M = gems: " + gold1M);
            var gold20M = GetGemsRequiredToBuyItems(defaultResources.Gold, 20000000);
            Debug.Log("gold20M = gems: " + gold20M);

            var dark1 = GetGemsRequiredToBuyItems(defaultResources.DarkElixir, 1);
            Debug.Log("dark1 = gems: " + dark1);
            var dark50 = GetGemsRequiredToBuyItems(defaultResources.DarkElixir, 50);
            Debug.Log("dark50 = gems: " + dark50);
            var dark500 = GetGemsRequiredToBuyItems(defaultResources.DarkElixir, 500);
            Debug.Log("dark500 = gems: " + dark500);
            var dark8000 = GetGemsRequiredToBuyItems(defaultResources.DarkElixir, 8000);
            Debug.Log("dark8000 = gems: " + dark8000);
            var dark1M = GetGemsRequiredToBuyItems(defaultResources.DarkElixir, 1000000);
            Debug.Log("dark1M = gems: " + dark1M);
        }

        public static int GetGemsRequiredToAccelerateTime(int time)
        {
            if (time <= 0)
                return 0;

            var timePrices = Balancy.DataEditor.ClashOfClans.Config.TimePrice;

            var index = -1;
            for (int i = 1; i < timePrices.Length; i++)
            {
                if (timePrices[i].Time >= time)
                {
                    index = i;
                    break;
                }
            }

            if (index == 0)
                return timePrices[0].Gems;

            if (index == -1)
                index = timePrices.Length - 1;

            var left = timePrices[index - 1];
            var right = timePrices[index];

            return left.Gems +
                   Mathf.RoundToInt((float)(right.Gems - left.Gems) * (time - left.Time) / (right.Time - left.Time));
        }

        public static int GetGemsRequiredToBuyItems(Balancy.Models.SmartObjects.Item item, int count)
        {
            if (count <= 0)
                return 0;

            var itemConfig = GetItemConfig(item);
            if (itemConfig == null)
                return 0;

            var index = -1;
            for (int i = 0; i < itemConfig.Price.Length; i++)
            {
                if (itemConfig.Price[i].Amount >= count)
                {
                    index = i;
                    break;
                }
            }

            if (index == 0)
                return itemConfig.Price[0].Gems;

            if (index == -1)
                index = itemConfig.Price.Length - 1;

            var left = itemConfig.Price[index - 1];
            var right = itemConfig.Price[index];

            return left.Gems +
                   Mathf.RoundToInt((float)(right.Gems - left.Gems) * (count - left.Amount) / (right.Amount - left.Amount));
        }

        private static Balancy.Models.ClashOfClans.ResourcePrice GetItemConfig(Balancy.Models.SmartObjects.Item item)
        {
            var config = Balancy.DataEditor.ClashOfClans.Config;
            foreach (var resourcePrice in config.Prices)
            {
                if (resourcePrice.Resource == item)
                    return resourcePrice;
            }

            return null;
        }
    }
}

What's next

  1. Import the Clash of Clans template.
  2. Deploy the changes.
  3. Open the Unity project and generate the code.
  4. Initialize Balancy.
  5. Add the following code in the callback: ClashOfClans.ResourcesManager.Tests();