AI-система динамической подстройки сложности игры (DDA)
Dynamic Difficulty Adjustment меняет параметры игры в реальном времени, удерживая игрока в состоянии потока (Flow по Чиксентмихайи): достаточно сложно для вызова, достаточно легко для прогресса. Resident Evil 4 использовал DDA в 2005 — современные подходы с ML точнее и незаметнее.
Flow Theory и DDA
Состояние потока: когда challenge ≈ skill. Слишком легко → скука. Слишком сложно → тревога.
Наблюдаемые сигналы:
- Deaths per level / lives remaining
- Time to complete section vs median
- Damage taken ratio
- Items used / unused
- Retry count
- Session length и drop-off points
Целевые метрики DDA:
- Death rate: 1–3 deaths per section (чуть выше нуля = вовлечённость)
- Completion rate per session: 70–80%
- Average session length стабильна или растёт
RL агент для DDA
class DDAEnv(gym.Env):
"""Среда для обучения DDA агента"""
def __init__(self):
# observation: агрегированная статистика игрока за последние N событий
self.observation_space = spaces.Box(
low=0, high=1,
shape=(12,), # death_rate, completion_rate, time_ratio, etc.
dtype=np.float32
)
# action: изменение параметров сложности
# [enemy_hp_mult, enemy_damage_mult, spawn_rate, resource_availability]
self.action_space = spaces.Box(
low=np.array([0.5, 0.5, 0.5, 0.5]),
high=np.array([1.5, 1.5, 1.5, 1.5]),
dtype=np.float32
)
def step(self, action):
# применяем параметры
self.game.set_difficulty_params(action)
# симулируем следующие N игровых минут / секций
player_stats = self.game.advance()
obs = self._extract_obs(player_stats)
reward = self._compute_flow_reward(player_stats)
return obs, reward, False, False, {}
def _compute_flow_reward(self, stats):
target_death_rate = 0.15 # 15% section death rate
target_completion = 0.75
target_time_ratio = 1.0 # точно по норме
r = 0
r -= abs(stats['death_rate'] - target_death_rate) * 5
r -= abs(stats['completion_rate'] - target_completion) * 3
r += stats['session_continued'] * 2 # игрок продолжил сессию
return r
Незаметность изменений
Главное требование: игрок не должен замечать DDA. Грубое изменение HP врагов с ×1 до ×0.5 ощущается как читерство.
Техники незаметного изменения:
- Gradual changes: ±5% за раз, не более ±30% от базы
- Diegetic changes: дождь → враги менее точны (логично в рамках игры)
- Respawn positioning: враги появляются дальше от игрока (незаметно)
- Timing windows: окно parry/dodge чуть шире (игрок не считает фреймы)
- Loot probability: чуть больше хила в случайном дропе
def apply_difficulty_change(self, current_params, target_params, max_delta=0.05):
"""Плавное изменение без резких скачков"""
delta = target_params - current_params
delta = np.clip(delta, -max_delta, max_delta)
return current_params + delta
Profiling игрока
Разные игроки хотят разного опыта. DDA должна моделировать тип игрока:
class PlayerModel:
def __init__(self):
self.skill_estimate = 0.5 # 0=новичок, 1=эксперт
self.frustration_tolerance = 0.5 # насколько терпит поражения
self.preferred_style = None # aggressive/cautious/explorer
def update(self, player_events):
# ELO-подобная оценка навыка
if player_events['cleared_hard_section']:
self.skill_estimate = min(1.0, self.skill_estimate + 0.05)
if player_events['deaths_this_session'] > 5:
self.skill_estimate = max(0.0, self.skill_estimate - 0.02)
# классификация стиля
if player_events['stealth_actions'] > player_events['combat_actions']:
self.preferred_style = 'stealth'
Реализация в Unity
public class DDAManager : MonoBehaviour
{
private float[] difficultyParams = {1.0f, 1.0f, 1.0f, 1.0f};
private ONNXInferenceSession policyModel;
void Update()
{
if (Time.frameCount % 300 == 0) // каждые ~5 секунд
{
float[] obs = GatherPlayerStats();
float[] newParams = policyModel.Run(obs);
ApplyGradualChange(difficultyParams, newParams);
ApplyToGameSystems(difficultyParams);
}
}
void ApplyToGameSystems(float[] p)
{
EnemyManager.SetHPMultiplier(p[0]);
EnemyManager.SetDamageMultiplier(p[1]);
SpawnManager.SetSpawnRate(p[2]);
LootManager.SetDropRate(p[3]);
}
}
Метрики эффективности DDA
- Session length vs control group (без DDA): цель +15–25%
- Day 7 retention: игроки с DDA возвращаются чаще
- Completion rate: больше игроков проходит игру до конца
- Negative reviews о сложности: снижение на 20–40%
- Rage-quit events (закрытие через 30 сек после смерти): -30%
Сроки: 4–8 недель
Базовая DDA на правилах (rule-based с порогами) — 1 неделя. RL-based DDA с player profiling, незаметными изменениями, A/B тестированием — 6–8 недель.







