GS2-Stamina
Stamina is a feature that realizes in-game limits on the number of times something can be done. GS2-Limit expresses limits like “3 times a day,” but GS2-Stamina expresses limits with a combination of time progression and consume actions, such as “use stamina that recovers 1 point every 8 hours to enforce a limit of 3 times per day.”
In the case of stamina, by varying the points consumed, you can realize specifications such as “action A is 3 times per day, action B is 5 times per day.” By providing guidelines for experience or in-game currency obtained per stamina point, you can eliminate “efficient” actions in the game and make it easier to adjust the game balance, since no single action becomes optimal regardless of what is chosen.
Developers should strive to have players log in every day, so this kind of specification is not often used, but for players, a specification in which 1 point recovers every 8 hours and accumulates up to 15 points means they can use up all their points without waste by logging in only once every 5 days.
Stamina
Stamina is configured with “recovery interval,” “recovery amount,” and “maximum value.”
graph LR Time["Time progression"] -- "every recoverIntervalMinutes" --> Recover["Recover<br/>(+recoverValue)"] Recover --> Value["Current value"] Consume["Consume action"] -- "-consumeValue" --> Value Value -- "exceeds maxValue" --> Overflow["Overflow state"]
| Parameter | Description |
|---|---|
value |
Current stamina value held |
maxValue |
The upper limit reachable by natural recovery |
recoverIntervalMinutes |
Minutes required to recover 1 point |
recoverValue |
Value increased per recovery |
overflowValue |
Amount exceeding the maximum value |
nextRecoverAt |
Scheduled date/time of the next recovery |
Overflow
Stamina can be recovered beyond the maximum value. If the maximum is exceeded, no recovery over time occurs. Also, even when the maximum is exceeded, you can set a “true maximum” beyond which no further addition is allowed (for UI reasons, etc.).
Why allow overflow?
This specification may seem strange to developers who have not had the opportunity to play games that incorporate stamina.
Generally, stamina has items or in-game purchases that recover it. This specification was created to minimize the stress on the player when using such items or purchasing stamina.
Here is a concrete example.
In this game, 1 point of stamina recovers every 5 minutes, and your maximum stamina is 50 points. You need 10 stamina points to play the next quest you want to play. However, your current stamina is only 9 points, and you must wait 5 minutes before you can play the next quest. So you decide to purchase stamina. Purchasing stamina recovers 50 stamina points. However, if you purchase stamina right now, even though 50 points recover, 9 points will be wasted. So you wait 5 minutes for 1 point to recover, play the quest to bring stamina to 0, and then purchase stamina to play the next quest.
This does not allow players to play the game comfortably. That is why games appeared that introduced a specification allowing stamina to be recovered beyond the maximum value.
Let’s see how the user experience changes when the overflow specification is added to stamina.
Currently your stamina is only 9 points, and you must wait 5 minutes before you can play the next quest. So you purchase stamina, recover 50 stamina points, and now have 59 points. In this state, recovery of stamina over time no longer occurs, but you immediately start the next quest, bringing stamina to 49 points.
Integration with GS2-Experience
By registering MaxStaminaTable / RecoverIntervalTable / RecoverValueTable as master data, you can automatically change the maximum value, recovery interval, and recovery amount based on the GS2-Experience rank (player level, etc.).
You can easily express the common growth element of automatically increasing the stamina cap as the player levels up.
Synchronizing natural recovery
Calling the Apply API can immediately reflect natural stamina recovery for the time elapsed since the last access.
Normally, this is automatically reflected inside the stamina retrieval API, so there is little need to call it explicitly. Use it when you need to strictly synchronize the stamina value on the server side.
Script Triggers
Setting overflowTriggerScript on the namespace allows you to execute a custom script when stamina exceeds the maximum value via natural recovery. This enables applications such as converting overflowed stamina into another resource at overflow time.
The main configurable event triggers and script setting names are as follows:
overflowTriggerScript: When stamina overflows
Adjustment via Buffs
By integrating with GS2-Buff, you can use buffs to adjust maxValue, recoverIntervalMinutes, recoverValue, as well as action parameters such as consumeValue and recoverValue, allowing flexible adjustment of the stamina cap, recovery speed, and consumption amount for events and similar purposes.
This can be used for time-limited gameplay adjustments such as “make the stamina cap 1.5x for a limited time” or “double the recovery speed for one hour right after login.”
Transaction Actions
GS2-Stamina provides the following transaction actions:
- Verify actions: verify the current value, verify the maximum value, verify the recovery speed, verify the recovery amount, verify the overflow amount
- Consume actions: consume stamina, decrement the maximum value
- Acquire actions: recover (add) stamina, increment/set the maximum value, set the recovery speed, set the recovery amount
By using “increment maximum value” as an acquire action, you can safely execute processes within a transaction to automatically expand the maximum stamina capacity, such as when an item is purchased at a shop or when the player’s rank increases. This makes it easier to design rewards that make players feel their growth. Additionally, by setting “recover stamina” as a reward, you can fully restore stamina upon completion of a specific mission to encourage continued play.
Master Data Operation
Registering master data allows you to configure data and behaviors available to the microservice.
Master data types include the following:
StaminaModel: Definition of recovery amount, recovery interval, maximum value, and whether overflow is allowedMaxStaminaTable: A table that derives the maximum value from a GS2-Experience rankRecoverIntervalTable: A table that derives the recovery interval from a GS2-Experience rankRecoverValueTable: A table that derives the recovery amount from a GS2-Experience rank
The following is a JSON example of master data.
{
"version": "2019-04-23",
"staminaModels": [
{
"name": "stamina-0001",
"metadata": "Stamina for quests",
"recoverIntervalMinutes": 5,
"recoverValue": 1,
"initialCapacity": 50,
"isOverflow": true,
"maxCapacity": 999
}
]
}Master data can be registered via the Management Console, by reflecting data from GitHub, or by setting up workflows to register via CI using GS2-Deploy.
Example Implementation
Get the current stamina value
The stamina value returned reflects natural recovery up to the moment of retrieval.
var item = await gs2.Stamina.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Stamina(
staminaName: "stamina-0001"
).ModelAsync(); const auto Domain = Gs2->Stamina->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Stamina(
"stamina-0001" // staminaName
);
const auto Item = Domain->Model();Get a list of stamina
Among multiple StaminaModels registered in the namespace, you can retrieve the current value of all stamina the player is using in a single call.
var items = await gs2.Stamina.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).StaminasAsync(
).ToListAsync(); const auto Domain = Gs2->Stamina->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
);
const auto It = Domain->Staminas(
);
TArray<Gs2::UE5::Stamina::Model::FEzStaminaPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}Consume stamina
It is not recommended to consume stamina directly via this API.
By consuming stamina through a service such as GS2-Quest, you can safely handle the consumption together with processing such as starting a quest or obtaining items as a single transaction.
var result = await gs2.Stamina.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Stamina(
staminaName: "stamina-0001"
).ConsumeAsync(
consumeValue: 50
);
var item = await result.ModelAsync(); const auto Domain = Gs2->Stamina->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Stamina(
"stamina-0001" // staminaName
);
const auto Future = Domain->Consume(
50
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Apply natural recovery
Immediately reflects natural recovery for the time elapsed since the last access. Normally this is reflected automatically inside API calls, so there is little need to call it explicitly.
var result = await gs2.Stamina.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).Stamina(
staminaName: "stamina-0001"
).ApplyAsync(
);
var item = await result.ModelAsync(); const auto Domain = Gs2->Stamina->Namespace(
"namespace-0001" // namespaceName
)->Me(
AccessToken
)->Stamina(
"stamina-0001" // staminaName
);
const auto Future = Domain->Apply(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;Applied example: gathering feature in a city-building game
By applying the stamina mechanism, you can also implement a gathering feature in a city-building game. By setting up buildings in the stamina model and expressing the stamina value as the production output of the buildings, you can realize a gathering system in which resources accumulate over time.
For example, you can treat “an iron ore mine that produces iron ore” as a single stamina model, where the recovery interval represents the mining interval, the recovery amount the amount mined per cycle, and the maximum value the warehouse cap.
When the player performs a collection action, the entire amount is consumed with Consume, and the resulting point count is stored as items in GS2-Inventory.