GS2-Enhance
This feature may be completely incomprehensible to some developers.
However, it is a feature that is always present in the de facto standard game systems of mobile games, starting with those in Japan. This game mechanic itself should be useful knowledge for realizing Game as a Service, so for developers who do not understand it we add an explanation of the game mechanism.
If you already have sufficient knowledge of the game mechanics of the enhancement feature, you can safely skip the rest of this explanation section.
Game mechanics of the enhancement feature
Enhancement itself is a very simple game system in which material items can be consumed to add experience to a target. In addition to the method of gaining experience by participating in battle, characters and equipment can be grown in a variety of ways. (Equipment also has levels.)
Now let us explain why this game system plays a role in realizing Game as a Service.
In a nutshell, it is used to pad play time.
You all surely understand that the speed of game play is overwhelmingly faster than the speed of game development. A game that we game developers spent three years building, players finish in 10 hours. But Game as a Service cannot be sustained if this gap is not closed. In other words, it is necessary to debuff the rate at which players consume content. That magic is the enhancement game system.
A typical Game as a Service game holds an event once per month. In each event there is some kind of boss, and the player keeps defeating that boss repeatedly throughout the event period. The bosses come in multiple difficulties matched to the player’s growth stage, and defeating them yields character and equipment enhancement materials.
Using the enhancement materials they have gathered, players convert them into experience for characters and equipment to grow them. As characters and equipment grow, they can challenge higher difficulties of the boss.
In this way, while all players participate in the event, they obtain enhancement materials suited to their character’s growth stage and enjoy the gameplay of growing characters and equipment.
In reality, bosses rarely drop enhancement materials directly. Instead, there may be a gacha that can only be challenged during the event period and players collect points to draw it and obtain materials through the gacha, or there may be a shop open only during the event period where players collect in-game currency from defeating bosses and purchase enhancement materials from the shop. There are various designs that let players decide where to prioritize their growth, but ultimately what is happening is that the time a player needs to grow their character is being stretched to pad play time.
What remains consistent throughout the event is that the player’s character becomes stronger. Once the event ends, the stronger character is used to enjoy higher-difficulty permanent content, leading into the next form of fun.
graph TD BossBattle["Boss Battle"] -- Acquire Event Point --> Shop Shop -- Buy Enhance Materials --> Enhance["Enhance Character"] Enhance -- More Formidable --> BossBattle
Architecture
Enhancement is realized by GS2-Enhance operating on GS2-Inventory, which manages the items used as materials, and either GS2-Inventory or GS2-Dictionary, which manages the target character or equipment, plus GS2-Experience, which manages the experience and levels of that character or equipment.
In the current GS2, enhancement via DirectEnhance, which completes the consumption and experience addition in a single API request, is recommended.
Previously, because automatic transaction execution was not available, a multi-step enhancement process (Progress) divided into preparation, execution, and completion reporting was recommended to prevent acts such as cutting off communication and retrying until a great success appeared. Now that automatic transaction execution and atomic commit mechanisms are in place, that concern has been resolved.
actor Player
participant "GS2-Enhance#Namespace"
participant "GS2-Inventory#ItemSet(Material)"
participant "GS2-Experience#Status"
Player -> "GS2-Enhance#Namespace" : Direct Enhance(Materials/Target Character)
"GS2-Enhance#Namespace" -> "GS2-Inventory#ItemSet(Material)" : Get experience value from metadata
"GS2-Enhance#Namespace" -> "GS2-Inventory#ItemSet(Material)" : Consume
"GS2-Enhance#Namespace" -> "GS2-Experience#Status": Add experience(Key: Target Character/Equipment Id)
"GS2-Enhance#Namespace" -> Player : Enhance resultYou call the enhancement execution API (DirectEnhance) on GS2-Enhance with the parameters of “enhancement target” and “materials used for enhancement”.
GS2-Enhance then retrieves the master data of the material item from GS2-Inventory and obtains, from its metadata, the amount of experience to gain when used as material. Once the amount of experience is determined, the item is consumed and the experience-addition process is executed on GS2-Experience.
Although an enhancement target is specified, it is only used as the key for the experience managed by GS2-Experience; the enhancement target’s information itself is not used directly.
Differences between DirectEnhance and Progress
GS2-Enhance provides two enhancement flows.
| Item | DirectEnhance | Progress (Start / End) |
|---|---|---|
| Number of API calls | 1 | 2 (start / end) |
| Recommended use | All enhancement processing | When you want to obtain a draw result in advance to reflect outcomes such as great success in your effects |
| Atomicity | Consumption and experience addition complete within the request | The draw result is held on the server and confirmed by a subsequent End |
| Abuse via disconnect | None | An in-progress Progress can only be held one at a time; retries are only possible via DeleteProgress |
Unless there is a special requirement, we recommend using DirectEnhance.
sequenceDiagram participant Player participant Enhance as GS2-Enhance participant Inventory as GS2-Inventory participant Experience as GS2-Experience Player ->> Enhance: DirectEnhance(rateName, targetItemSetId, materials) Enhance ->> Inventory: Obtain experience amount from metadata Enhance ->> Inventory: Consume material items Enhance ->> Experience: Add experience Enhance -->> Player: Added experience and great-success multiplier
Enhancement Rate
To limit the materials that can be used for enhancement and the enhancement target, the enhancement rate must be set as master data.
Master data records information such as the GS2-Inventory namespace and inventory name of items that can be used as materials, and the GS2-Inventory namespace and inventory name of items that can be enhancement targets.
Master data is managed in JSON format.
Here is an example of setting metadata for an ItemModel in JSON format such as { "experience": 50 }.
Define the JSON key (experience in the example above) in the acquireExperienceHierarchy of the RateModel.
acquireExperienceHierarchy can also define a hierarchical structure.
For example, if you want to set metadata with a data structure such as { "aaa": { "bbb": { "experienceValue": 100 } } }, specify [ "aaa", "bbb", "experienceValue" ] in acquireExperienceHierarchy.
Main configuration items of RateModel
| Item | Description |
|---|---|
name |
Enhancement rate name |
targetInventoryModelId |
Inventory model ID of the GS2-Inventory holding items to be enhanced |
materialInventoryModelId |
Inventory model ID of the GS2-Inventory holding material items |
acquireExperienceHierarchy |
JSON path used to extract the experience amount from the material item’s metadata |
acquireExperienceSuffix |
Suffix appended to the experience model name key (e.g. :level) |
experienceModelId |
Experience model ID of the GS2-Experience to which experience is added |
bonusRates |
Multipliers and weights for great-success draws |
GS2-Enhance RateModel master data — example of setting experience in metadata:
{
"version": "2020-08-22",
"rateModels": [
{
"name": "enhanceRate",
"description": "",
"metadata": "",
"targetInventoryModelId": "grn:gs2:ap-northeast-1:YourOwnerId:inventory:enhance-inventory:model:character",
"acquireExperienceSuffix": ":level",
"materialInventoryModelId": "grn:gs2:ap-northeast-1:YourOwnerId:inventory:enhance-inventory:model:material",
"acquireExperienceHierarchy": [
"experience"
],
"experienceModelId": "grn:gs2:ap-northeast-1:YourOwnerId:experience:enhance-experience:model:character",
"bonusRates": [
{
"rate": 2,
"weight": 1
},
{
"rate": 1,
"weight": 1
}
]
}
]
}bonusRates can hold multiple entries; each entry has a draw weight (weight) and multiplier (rate).
In the example above, “2x experience” and “1x experience” are drawn at the same weight, so a great success (2x) occurs roughly 50% of the time.
GS2-Experience ExperienceModel master data example:
{
"version": "2019-01-11",
"experienceModels": [
{
"name": "character",
"metadata": "CHARACTER",
"defaultExperience": 0,
"defaultRankCap": 50,
"maxRankCap": 80,
"rankThreshold": {
"metadata": "RANK_THRESHOLD",
"values": [
100,
300,
500,
1000
]
}
}
]
}GS2-Inventory ItemModel master data — example of setting experience in metadata:
{
"version": "2019-02-05",
"inventoryModels": [
{
"name": "character",
"initialCapacity": 1,
"maxCapacity": 1,
"protectReferencedItem": false,
"itemModels": [
{
"name": "character-0001",
"stackingLimit": 1,
"allowMultipleStacks": false,
"sortValue": 0
}
]
},
{
"name": "material",
"metadata": "",
"initialCapacity": 10,
"maxCapacity": 10,
"protectReferencedItem": false,
"itemModels": [
{
"name": "material-0001",
"metadata": "{\"experience\":50}",
"stackingLimit": 99,
"allowMultipleStacks": false,
"sortValue": 0
}
]
}
]
}Limit Break (Unleash)
A “limit break” feature is also provided that consumes items such as duplicates of the same kind as materials to raise the target’s level cap.
It is a mechanism for raising the rankCap managed by GS2-Experience one stage at a time, configured through the master data of UnleashRateModel with the following:
- The inventory model that is the limit-break target
- The associated grade model (GS2-Grade)
- The material items and quantities required to reach each grade
This makes it possible to implement game mechanics such as “collect 4 of the same character to limit-break” or “consume N of a specific material item to unlock the level cap”.
Script Triggers
By configuring enhanceScript on the namespace, you can call custom scripts before and after enhancement execution. You can allow/deny the process or override the gained experience, and choose synchronous or asynchronous execution. Asynchronous execution also supports external integration via GS2-Script or Amazon EventBridge.
Main event triggers and script setting names are:
enhanceScript(completion notification:enhanceDone): before and after enhancement execution
In the script you can reference the enhancement target, the material items, and the multiplier being drawn. This is useful for purposes such as game balance adjustment or changing the boost multiplier during an event period.
Master data operations
Registering master data allows you to configure data and behaviors available to the microservice.
Master data types include:
RateModel: Definition of enhancement materials and targetsUnleashRateModel: Rate definitions for limit break
Master data can be registered via the management console, imported from GitHub, or registered from CI using GS2-Deploy or various language CDKs.
Transaction Actions
GS2-Enhance provides the following transaction actions:
- Consume action: Delete the in-progress enhancement progress information (
Progress) - Acquire action: Execute immediate enhancement (
DirectEnhance), execute limit break (Unleash), start enhancement (Progress)
By using “Execute immediate enhancement (DirectEnhance)” as an acquire action, you can perform operations such as directly granting experience to characters or equipment as a reward for clearing a specific quest or purchasing an item from a shop. Also, by setting “Limit break (Unleash)” as a reward, you can implement operations such as automatically raising the level cap when a specific mission is completed.
Example implementation
Getting a list of enhancement rates
var items = await gs2.Enhance.Namespace(
namespaceName: "namespace-0001"
).RateModelsAsync(
).ToListAsync(); const auto It = Gs2->Enhance->Namespace(
"namespace-0001" // namespaceName
)->RateModels();
TArray<Gs2::UE5::Enhance::Model::FEzRateModelPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Executing enhancement (DirectEnhance)
Specify the ItemSetId and quantity of the material items to consume in materials.
Pass the ItemSetId of the character/equipment you want to enhance as targetItemSetId.
var result = await gs2.Enhance.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Enhance(
).EnhanceAsync(
rateName: "rate-0001",
targetItemSetId: "item-set-0001",
materials: new [] {
new Gs2.Unity.Gs2Enhance.Model.EzMaterial
{
MaterialItemSetId = "material-0001",
Count = 1,
},
}
);
var transaction = result;
await transaction.WaitAsync(); const auto Domain = Gs2->Enhance->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Enhance(
);
const auto Future = Domain->Enhance(
"rate-0001",
"item-set-0001",
[]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Enhance::Model::FMaterial>>>();
v->Add(MakeShared<Gs2::Enhance::Model::FMaterial>()
->WithMaterialItemSetId(TOptional<FString>("material-0001"))
->WithCount(TOptional<int32>(1)));
return v;
}()
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Transaction = Future->GetTask().Result();
const auto Future2 = Transaction->Wait();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;Starting enhancement (Progress)
This is the flow when you want to obtain the draw result in advance.
Only one in-progress Progress can be held per user.
var result = await gs2.Enhance.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Progress(
).StartAsync(
rateName: "rate-0001",
targetItemSetId: "item-set-0001",
materials: new [] {
new Gs2.Unity.Gs2Enhance.Model.EzMaterial
{
MaterialItemSetId = "material-0001",
Count = 1,
},
}
); const auto Future = Gs2->Enhance->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Progress(
)->Start(
"rate-0001", // rateName
"item-set-0001", // targetItemSetId
[]
{
const auto v = MakeShared<TArray<TSharedPtr<Gs2::Enhance::Model::FMaterial>>>();
v->Add(MakeShared<Gs2::Enhance::Model::FMaterial>()
->WithMaterialItemSetId(TOptional<FString>("material-0001"))
->WithCount(TOptional<int32>(1)));
return v;
}()
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Getting in-progress enhancement information
You can obtain the drawn multiplier (great-success flag) and the amount of experience gained, and use them in enhancement effects.
var item = await gs2.Enhance.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Progress(
).ModelAsync();
var experienceValue = item.ExperienceValue;
var rate = item.Rate; const auto Domain = Gs2->Enhance->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Progress(
);
const auto Future = Domain->Model();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();
const auto ExperienceValue = Result->GetExperienceValue();
const auto Rate = Result->GetRate();Completing enhancement (Progress)
Finalize the enhancement started by Start with End, executing consumption and experience addition.
var result = await gs2.Enhance.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Progress(
).EndAsync();
await result.WaitAsync(); const auto Future = Gs2->Enhance->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Progress(
)->End();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Transaction = Future->GetTask().Result();
const auto Future2 = Transaction->Wait();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;Discarding in-progress enhancement information
Used for recovery in cases such as when communication is interrupted and the completion report cannot be made.
await gs2.Enhance.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Progress(
).DeleteProgressAsync(); const auto Future = Gs2->Enhance->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Progress(
)->DeleteProgress();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Other features
Great success in enhancement
In some enhancement specifications, a “great success” occurs at a certain probability, multiplying the gained experience by 1.5x or 2x. With GS2-Enhance you can configure the probability of great success and the experience multiplier in that case as part of the enhancement rate.
The ratio of each entry’s weight to the total of bonusRates weight is the probability of that multiplier being drawn.
Passing parameters to scripts using Config
EnhanceAsync / StartAsync accept a config parameter, which lets you pass arbitrary key-value pairs at script trigger time.
You can convey information such as an in-game event boost state or the enhancement mode the player selected to the script.
Speculative Execute
When the speculativeExecute argument (default true) is enabled, the client-side cache is updated before waiting for the API response, allowing the UI to immediately display the post-experience-addition state.
This delivers a snappy operational feel that is less affected by communication latency.