Розробка системи енергії/витривалості GameFi
Система енергії — механіка обмеження ігрової активності. Гравець витрачає енергію на дії (битви, фарму, крафт), енергія відновлюється зі часом або через покупку. У Web2 це просто лічильник у базі даних. У Web3 це on-chain ресурс, що створює і можливості (tradeable енергія, verifiable regen) і проблеми (gas за кожне оновлення, cheating prevention).
Правильна архітектура енергетичної системи — одна з ключових інженерних задач GameFi. Неправильна реалізація або робить гру неиграбельною (занадто багато on-chain операцій) або відкриває еплойти (безплатна енергія через manipulation).
Ключева проблема: time-based regen без постійного on-chain оновлення
Інтуїтивне рішення: зберігати енергію в mapping, оновлювати щосекунди. Погано — нескінченна кількість транзакцій.
Правильний підхід: lazy evaluation. Зберігати не поточну енергію, а момент останньої зміни і значення у той момент. Поточна енергія обчислюється on-the-fly при кожному читанні:
contract EnergySystem {
struct EnergyState {
uint128 storedEnergy; // енергія на момент lastUpdate
uint64 lastUpdateTime;
uint64 maxEnergy;
}
mapping(address => EnergyState) private energyStates;
uint256 public constant REGEN_RATE = 1e18; // 1 енергія у секунду (18 decimals)
uint256 public constant MAX_ENERGY = 100e18;
// Обчислюємо поточну енергію без запису в storage
function currentEnergy(address player) public view returns (uint256) {
EnergyState storage state = energyStates[player];
uint256 elapsed = block.timestamp - state.lastUpdateTime;
uint256 regenerated = elapsed * REGEN_RATE;
uint256 total = uint256(state.storedEnergy) + regenerated;
uint256 max = state.maxEnergy == 0 ? MAX_ENERGY : uint256(state.maxEnergy);
return total > max ? max : total;
}
// Оновлюємо storage тільки при фактичному використанні/зміні енергії
function _updateEnergyState(address player) internal {
EnergyState storage state = energyStates[player];
state.storedEnergy = uint128(currentEnergy(player));
state.lastUpdateTime = uint64(block.timestamp);
}
function spendEnergy(address player, uint256 amount) internal {
uint256 current = currentEnergy(player);
require(current >= amount, "Insufficient energy");
_updateEnergyState(player);
energyStates[player].storedEnergy -= uint128(amount);
}
}
Ключовий момент: currentEnergy() — view функція, не витрачає gas. Storage оновлюється тільки при spendEnergy/addEnergy — тобто при реальній ігровій дії. Значна економія газу: не потрібні окремі транзакції для regen.
Привязка до NFT: енергія персонажа
Енергія прив'язана до конкретного NFT, не до EOA кошелька. Важливо: гравець може мати кілька персонажів з незалежною енергією, торгувати персонажами разом з їхньою поточною енергією.
contract CharacterEnergySystem {
struct CharacterEnergy {
uint128 storedEnergy;
uint64 lastUpdate;
uint8 tier; // tier впливає на max energy і regen
}
mapping(uint256 => CharacterEnergy) public characterEnergy; // tokenId → енергія
// Regen rate залежить від tier персонажа
function regenRateForTier(uint8 tier) public pure returns (uint256) {
if (tier == 3) return 3e18; // 3 од./сек для legendary
if (tier == 2) return 2e18; // 2 од./сек для rare
return 1e18; // 1 од./сек для common
}
// Max энергія залежить від tier
function maxEnergyForTier(uint8 tier) public pure returns (uint256) {
return 100e18 + uint256(tier) * 50e18; // 100, 150, 200 для tier 1,2,3
}
function currentEnergy(uint256 tokenId) public view returns (uint256) {
CharacterEnergy storage ce = characterEnergy[tokenId];
uint8 tier = nftContract.getTier(tokenId);
uint256 elapsed = block.timestamp - ce.lastUpdate;
uint256 regen = elapsed * regenRateForTier(tier);
uint256 total = uint256(ce.storedEnergy) + regen;
uint256 max = maxEnergyForTier(tier);
return total > max ? max : total;
}
}
Економічна модель: sink і source
Енергетична система працює як регулятор економіки. Важливо балансувати:
Sources (звідки берется енергія):
- Regen зі часом (безплатно, обмежено max)
- Purchase за game token (sink для токена)
- Staking NFT більш високого tier → бонусний regen
- Daily login reward (один раз у 24 години)
Sinks (куди йде енергія):
- Боєві дії
- Фарму ресурсів
- Крафт предметів
- PvP ставки
| Параметр | Рекомендації |
|---|---|
| Regen rate | Заповнення з 0 до max за 8–12 годин |
| Max energy | 1–3 ігрові сесії по 2–3 години |
| Premium refill | Макс 2–3 повні refill на день |
| Tier multiplier | Макс 2x–3x, не більше |
Тестування
// Foundry тест regen механіки
function test_energyRegenOverTime() public {
uint256 tokenId = 1;
// Витрачаємо всю енергію
vm.prank(player);
game.spendAllEnergy(tokenId);
assertEq(energy.currentEnergy(tokenId), 0);
// Пропускаємо 50 секунд
vm.warp(block.timestamp + 50);
// Перевіряємо regen (tier 1: 1 од./сек)
assertEq(energy.currentEnergy(tokenId), 50e18);
// Пропускаємо ще 200 секунд — повинні упертися в max (100)
vm.warp(block.timestamp + 200);
assertEq(energy.currentEnergy(tokenId), 100e18);
}
Сроки
Базова система (lazy regen, spend на дії, cooldowns)—2–3 тижні. Повна система (tier-based regen, ERC-20 energy token, DEX інтеграція, anti-cheat signed actions, analytics dashboard)—5–7 тижнів.







