Ви геймдизайнер, і на ручну генерацію 10 000 квестів іде півроку роботи команди з п'яти сценаристів? А гравці все одно проходять гру за 40 годин — світ відчувається мертвим. Типова PCG на шумах Перліна дає одноманітність: кожен ліс — клон попереднього. Єдиний спосіб уникнути цього — використовувати AI-генерацію з семантичним розумінням контексту. Ми вирішуємо цю проблему за допомогою LLM та diffusion models. Процедурна генерація (PCG) — не панацея, але з AI вона перетворюється на інструмент, що створює унікальні світи, наратив та лут на льоту. Наша система генерує контент у 10 разів швидше за ручну роботу, а економія часу на квестах сягає 95%.
Як AI-PCG вирішує проблему одноманітності контенту?
Традиційна PCG покладається на шуми Перліна та шаблони: кожен ліс схожий на попередній. Ми додаємо LLM (GPT-4o, LLaMA 3) та diffusion models (Stable Diffusion). Нейромережа генерує семантично зв'язні легенди світів, адаптивні діалоги NPC з пам'яттю та процедурні текстури — все це прив'язано до seed гравця. Результат: кожне проходження — нова історія. Ключова відмінність — генерація не просто шаблонів, а цілісного наративу. LLM створює історію світу, фракції та їхні взаємини. Діалоги NPC запам'ятовують попередні зустрічі, а не повторюють завчені фрази.
| Компонент | Традиційний PCG | AI-PCG (наш підхід) |
|---|---|---|
| Генерація світу | Perlin noise, ручні біоми | Octave noise + LLM-наратив |
| Квести | Попередньо визначені шаблони | Динамічні цілі з morale choice |
| Діалоги | Гілки зі скриптів | NLP з пам'яттю, емоціями, RAG |
| Лут | Випадкові стати | Афікси за темою світу, баланс під рівень |
| Інтеграція | Вбудований код | REST API + плагіни для Unity/Unreal |
| Тип контенту | Ручна генерація | AI-PCG | Прискорення |
|---|---|---|---|
| Карта світу (100x100) | 2 тижні | 2 години | x168 |
| Квест з розгалуженнями | 8 годин | 15 хвилин | x32 |
| Діалог NPC (10 рядків) | 1 година | 2 секунди | x1800 |
AI-PCG генерує контент у 10 разів швидше за ручну роботу, а діалоги — у 1800 разів швидше. Вартість впровадження: від $10 000 за MVP до $50 000 за повний рушій під ключ.
Чому ми не використовуємо готові генератори?
Готові рішення (наприклад, Wave Function Collapse) хороші для карт, але не для наративу. Наш підхід — гібрид: традиційні алгоритми для карт + LLM для сенсів. Це дає адаптивну складність: система підлаштовує кількість ворогів та складність квестів під рівень гравця, не ламаючи занурення.
Архітектура AI-PCG системи
Генерація світу та біомів
from openai import AsyncOpenAI from dataclasses import dataclass, field import json import random import numpy as np client = AsyncOpenAI() @dataclass class WorldConfig: seed: int size: tuple # (width, height) в тайлах biomes: list[str] # ["forest", "desert", "tundra", "swamp"] civilization_level: str # primitive, medieval, industrial, futuristic magic_system: bool = True danger_zones: int = 5 settlements: int = 10 class ProceduralWorldGenerator: def __init__(self, config: WorldConfig): self.config = config self.rng = random.Random(config.seed) self.np_rng = np.random.default_rng(config.seed) def generate_heightmap(self) -> np.ndarray: """Генерируем карту высот через Perlin noise (opensimplex)""" from opensimplex import OpenSimplex noise = OpenSimplex(seed=self.config.seed) w, h = self.config.size heightmap = np.zeros((h, w)) # Октавный шум для реалистичного рельефа for y in range(h): for x in range(w): nx, ny = x / w, y / h heightmap[y][x] = ( 1.0 * noise.noise2(1 * nx, 1 * ny) + 0.5 * noise.noise2(2 * nx, 2 * ny) + 0.25 * noise.noise2(4 * nx, 4 * ny) + 0.125 * noise.noise2(8 * nx, 8 * ny) ) return (heightmap + 1) / 2 # нормализуем в [0, 1] def assign_biomes(self, heightmap: np.ndarray, moisture_map: np.ndarray) -> np.ndarray: """Biome assignment по Whittaker biome diagram""" biome_map = np.zeros_like(heightmap, dtype=int) BIOME_RULES = [ (0.0, 0.3, "ocean"), (0.3, 0.4, "beach"), (0.4, 0.6, "plains"), (0.6, 0.8, "forest"), (0.8, 0.9, "mountain"), (0.9, 1.0, "snow_peak") ] biome_ids = {b[2]: i for i, b in enumerate(BIOME_RULES)} for y in range(heightmap.shape[0]): for x in range(heightmap.shape[1]): h = heightmap[y][x] m = moisture_map[y][x] # Учёт влажности для смешанных биомов if 0.4 < h < 0.8 and m < 0.3: biome_map[y][x] = biome_ids.get("desert_variant", 2) else: for min_h, max_h, biome_name in BIOME_RULES: if min_h <= h < max_h: biome_map[y][x] = biome_ids[biome_name] break return biome_map def place_settlements(self, heightmap: np.ndarray, biome_map: np.ndarray) -> list[dict]: """Размещаем поселения в пригодных для жилья локациях""" settlements = [] valid_positions = np.argwhere( (heightmap > 0.4) & (heightmap < 0.7) & (biome_map != 0) ) chosen = self.np_rng.choice( len(valid_positions), size=min(self.config.settlements, len(valid_positions)), replace=False ) for idx in chosen: y, x = valid_positions[idx] settlements.append({ "x": int(x), "y": int(y), "type": self.rng.choice(["village", "town", "city", "fortress"]), "population": self.rng.randint(50, 10000), "name": "" # заполним через LLM }) return settlements Наратив та квести
async def generate_world_lore( world_config: WorldConfig, settlements: list[dict], biomes: list[str] ) -> dict: response = await client.chat.completions.create( model="gpt-4o", messages=[{ "role": "system", "content": f"""Ты — нарратив-дизайнер для игры с процедурно сгенерированным миром. Создай связную историю мира. Уровень цивилизации: {world_config.civilization_level}. Магия: {"есть" if world_config.magic_system else "нет"}. Верни JSON: {{ world_name: "...", history_eras: [{{name, years_ago, key_event}}], factions: [{{name, ideology, home_biome, relation_to_others}}], settlement_names: [{{id, name, local_legend}}], notable_artifacts: [{{name, description, location_hint}}], creation_myth: "...", current_conflict: "главный конфликт эпохи" }}""" }, { "role": "user", "content": f""" Биомы: {', '.join(biomes)} Поселений: {len(settlements)}, типы: {[s['type'] for s in settlements[:5]]}... Опасных зон: {world_config.danger_zones} Seed мира: {world_config.seed} """ }], response_format={"type": "json_object"} ) return json.loads(response.choices[0].message.content) QUEST_TEMPLATES = { "fetch": { "structure": "Получи [предмет] от [NPC/локация] и принеси [заказчику]", "complications": ["предмет охраняется", "NPC требует помощи взамен", "несколько претендентов"] }, "eliminate": { "structure": "Уничтожь [угрозу] в [локации]", "complications": ["угроза — невиновная жертва", "финальный босс скрыт", "побочный ущерб"] }, "escort": { "structure": "Сопроводи [персонажа] из [А] в [Б]", "complications": ["персонаж скрывает секрет", "засады на маршруте", "моральный выбор в конце"] }, "investigation": { "structure": "Расследуй [событие] в [месте]", "complications": ["несколько подозреваемых", "ложный след", "улики уничтожены"] } } async def generate_quest( template_type: str, world_lore: dict, player_level: int, location: dict ) -> dict: template = QUEST_TEMPLATES[template_type] complication = random.choice(template["complications"]) response = await client.chat.completions.create( model="gpt-4o", messages=[{ "role": "system", "content": f"""Создай квест для RPG-игры. Уровень игрока: {player_level}. Шаблон: {template['structure']}. Усложнение: {complication}. Используй фракции и историю мира для контекста. Верни JSON: {{ title, description, giver_npc, objectives: [{{id, text, optional: bool}}], rewards: {{xp, gold, items: []}}, moral_choice: {{description, option_a, option_b, consequences}}, estimated_time_minutes: int }}""" }, { "role": "user", "content": f"Мир: {json.dumps(world_lore, ensure_ascii=False)[:1000]}\nЛокация: {location}" }], response_format={"type": "json_object"} ) return json.loads(response.choices[0].message.content) Діалоги та предмети
from dataclasses import dataclass, field @dataclass class NPCProfile: name: str race: str occupation: str faction: str personality: list[str] # ["suspicious", "greedy", "loyal"] knowledge: list[str] # что НПС знает о мире relationship: str # "friendly", "neutral", "hostile" memory: list[dict] = field(default_factory=list) # история диалогов async def generate_npc_response( npc: NPCProfile, player_input: str, game_context: dict ) -> dict: memory_context = "\n".join([ f"[{m['timestamp']}] Игрок: {m['player']} → НПС: {m['npc']}" for m in npc.memory[-5:] # последние 5 обменов ]) response = await client.chat.completions.create( model="gpt-4o", messages=[{ "role": "system", "content": f"""Ты — НПС в RPG. Играй роль строго. НПС: {npc.name}, {npc.race}, {npc.occupation} Фракция: {npc.faction} | Черты: {', '.join(npc.personality)} Отношение к игроку: {npc.relationship} Знает: {', '.join(npc.knowledge)} История диалога: {memory_context} Отвечай в характере персонажа. Не выходи из роли. Верни JSON: {{ speech: "реплика НПС", emotion: "neutral|happy|angry|scared|suspicious", action: null | "give_item" | "start_quest" | "attack" | "flee", hint: null | "подсказка для игрока если уместно" }}""" }, { "role": "user", "content": f"Игрок говорит: {player_input}\nКонтекст: {game_context.get('location', 'unknown')}" }], response_format={"type": "json_object"} ) result = json.loads(response.choices[0].message.content) npc.memory.append({"timestamp": "now", "player": player_input, "npc": result["speech"]}) return result ITEM_RARITIES = { "common": {"prob": 0.60, "affix_count": (0, 1), "base_multiplier": 1.0}, "uncommon": {"prob": 0.25, "affix_count": (1, 2), "base_multiplier": 1.3}, "rare": {"prob": 0.10, "affix_count": (2, 3), "base_multiplier": 1.7}, "epic": {"prob": 0.04, "affix_count": (3, 4), "base_multiplier": 2.5}, "legendary": {"prob": 0.01, "affix_count": (4, 5), "base_multiplier": 4.0}, } def generate_item( item_type: str, player_level: int, world_theme: str, rng: random.Random ) -> dict: # Выбор редкости по весам rarity = rng.choices( list(ITEM_RARITIES.keys()), weights=[v["prob"] for v in ITEM_RARITIES.values()] )[0] spec = ITEM_RARITIES[rarity] base_stats = { "damage": player_level * 5 * spec["base_multiplier"] if item_type == "weapon" else 0, "defense": player_level * 3 * spec["base_multiplier"] if item_type == "armor" else 0, "durability": rng.randint(50, 100) } # Аффиксы из пула по теме мира AFFIXES = { "fantasy": ["of Flames", "of the Ancient", "Cursed", "Holy", "Shadow"], "scifi": ["Mk.II", "Prototype", "Military Grade", "Corrupted", "Quantum"] } prefix_pool = AFFIXES.get(world_theme, AFFIXES["fantasy"]) affixes = rng.sample(prefix_pool, k=rng.randint(*spec["affix_count"])) return { "name": f"{' '.join(affixes)} {item_type.title()}", "rarity": rarity, "type": item_type, "stats": base_stats, "level_requirement": max(1, player_level - 2) } Як ми боремося з галюцинаціями LLM?
Використовуємо few-shot промпти з жорсткою системою ролей та валідацією відповіді за JSON-схемою. Додатково — RAG-шар з базою знань світу. Ключові факти (імена, локації) перевіряються через lookup перед видачею. Це гарантує, що NPC не назве неіснуючу локацію або не переплутає фракції.
Типові помилки при впровадженні AI-PCG
- Передача всього контексту в кожен запит — зростає latency. Ми використовуємо RAG з кешуванням.
- Відсутність контролю над галюцинаціями — вводимо JSON-схеми та валідацію фактів.
- Нехтування latency для real-time діалогів — застосовуємо vLLM та batch inference.
Чому наше рішення витримує продакшн-навантаження?
Ми пройшли шлях від прототипу до інтеграції в комерційні проєкти. На рахунку команди — 7+ років досвіду в ML для ігор, 5 успішних впроваджень PCG-рушіїв. Гарантуємо, що система витримає навантаження 10 000 одночасних гравців при p99 latency відповіді NPC менше 300 мс. Використовуємо vLLM для інференсу та ONNX Runtime для оптимізації моделей на GPU. Застосовуємо MLOps-підхід: моніторинг дрейфу даних, A/B тестування моделей, автоматичний ретрейнінг.
Для адаптації під ваш сетинг використовуємо LoRA (Low-Rank Adaptation) — донавчаємо лише 0.1% параметрів. Це займає 2-3 дні на одному GPU і не потребує повного fine-tuning. Результат: модель генерує контент у вашому стилі без галюцинацій.
Що входить в роботу та терміни
Ми пропонуємо AI-PCG під ключ. Термін виконання від 6 тижнів. Оцінимо ваш проект безкоштовно за 2–3 дні. Вартість впровадження — від $10 000 за MVP до $50 000 за повний рушій. Що входить:
- Аналіз проєкту та підбір архітектури.
- Налаштування LoRA-адаптерів під ваш сетинг.
- Інтеграція через REST API.
- Тестування на навантаження (10 000 CCU).
- Деплой в продакшен.
- Документація: API-специфікація (OpenAPI), приклади інтеграції для Unity та Unreal.
- Навчання команди: 2-денний workshop з донавчання моделей під ваш сетинг.
- Підтримка: 3 місяці безкоштовних фіксів та консультацій по каналу Slack.
- Вихідний код: Python-бекенд, конфіги моделей, міграції БД.
Зв'яжіться з нами — підготуємо індивідуальну комерційну пропозицію. Замовте пілотний проєкт та оцініть ефективність AI-PCG на ваших даних. Пишіть нам — ми допоможемо впровадити генерацію квестів, діалогів та світів у вашу гру.







