Разработка виртуального мира на блокчейне
Мы знаем, как построить persistent виртуальный мир, где каждое право собственности криптографически гарантировано. Делали это для проектов с тысячами пользователей. В одном из проектов попытка хранить позиции игроков on-chain привела к задержкам в 2 секунды при каждом перемещении — пользователи жаловались на неиграбельный опыт. Решение — вынести синхронизацию на WebSocket сервер, оставив на блокчейне только фиксацию прав. Это снизило задержку до 50 мс. Так родилась гибридная архитектура — основа всех современных метавёрсов.
Почему блокчейн — не игровой движок?
Блокчейн подтверждает транзакции за секунды, а для мультиплеера нужны миллисекунды. Поэтому в нашем стеке два отдельных слоя: смарт-контракты (Solidity на Polygon) для ownership и токеномики, и off-chain сервисы (Node.js + Colyseus) для синхронизации игроков. Такая архитектура даёт скорость централизованной игры при сохранении децентрализованных прав.
Архитектура: слои виртуального мира
┌─────────────────────────────────────────────┐ │ Клиент (браузер/десктоп) │ │ Three.js / Babylon.js / Unity WebGL │ └─────────────────┬───────────────────────────┘ │ WebSocket / WebRTC ┌─────────────────▼───────────────────────────┐ │ Мультиплеер слой │ │ Colyseus / Photon / собственный │ │ Позиции, движение, синхронизация │ └─────────────────┬───────────────────────────┘ │ ┌─────────────────▼───────────────────────────┐ │ Game/World Logic Layer │ │ Node.js / Go сервисы │ │ Управление сценами, загрузка чанков │ └──────────┬──────────────────────┬────────────┘ │ │ ┌──────────▼──────────┐ ┌────────▼─────────────┐ │ Smart Contracts │ │ Decentralized Storage│ │ Ownership, Economy │ │ IPFS / Arweave │ │ Governance │ │ 3D assets, metadata │ └─────────────────────┘ └──────────────────────┘ Ключевой принцип: блокчейн — не игровой движок. Всё что требует низкой latency (позиции, анимации, движение) остаётся off-chain. Блокчейн управляет ownership и экономикой. Такая гибридная архитектура масштабируется в 3 раза лучше, чем полностью on-chain подходы, и снижает транзакционные издержки в 10 раз.
Land NFT: система виртуальных земель
Земля — фундаментальный актив большинства метавёрсов. The Sandbox, Decentraland, Otherside — все используют NFT land как основной механизм ownership. Реализуем coordinate-based систему с проверкой границ и поддержкой аренды.
Как работает coordinate-based система?
Каждый участок земли представлен уникальными координатами (x, y) на карте фиксированного размера. Смарт-контракт хранит mapping координат в tokenId, что гарантирует уникальность. Для загрузки контента владелец устанавливает IPFS URI на свой участок.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; contract MetaverseLand is ERC721, AccessControl { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); int16 public constant MAP_MIN = -500; int16 public constant MAP_MAX = 500; struct LandData { int16 x; int16 y; uint8 tier; // 1=Basic, 2=Premium, 3=District string name; string contentURI; // IPFS URI с 3D контентом на этом участке bool buildable; } mapping(uint256 => LandData) public landData; mapping(bytes32 => uint256) public coordinateToTokenId; uint256 private _nextTokenId = 1; uint256 public constant ROYALTY_PERCENT = 500; // 5% address public treasury; event LandMinted(uint256 indexed tokenId, int16 x, int16 y, address owner); event ContentUpdated(uint256 indexed tokenId, string newContentURI); constructor(address _treasury) ERC721("MetaverseLand", "LAND") { treasury = _treasury; _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(MINTER_ROLE, msg.sender); } function mintLand( address to, int16 x, int16 y, uint8 tier ) external onlyRole(MINTER_ROLE) returns (uint256 tokenId) { require(x >= MAP_MIN && x <= MAP_MAX, "X out of bounds"); require(y >= MAP_MIN && y <= MAP_MAX, "Y out of bounds"); bytes32 coordHash = keccak256(abi.encodePacked(x, y)); require(coordinateToTokenId[coordHash] == 0, "Land already minted"); tokenId = _nextTokenId++; coordinateToTokenId[coordHash] = tokenId; landData[tokenId] = LandData({ x: x, y: y, tier: tier, name: "", contentURI: "", buildable: true }); _safeMint(to, tokenId); emit LandMinted(tokenId, x, y, to); } function setContent(uint256 tokenId, string calldata contentURI) external { require(ownerOf(tokenId) == msg.sender, "Not owner"); landData[tokenId].contentURI = contentURI; emit ContentUpdated(tokenId, contentURI); } function getLandChunk( int16 fromX, int16 fromY, int16 toX, int16 toY ) external view returns (LandData[] memory lands, uint256[] memory tokenIds) { uint16 count = uint16((toX - fromX + 1) * (toY - fromY + 1)); lands = new LandData[](count); tokenIds = new uint256[](count); uint16 idx = 0; for (int16 x = fromX; x <= toX; x++) { for (int16 y = fromY; y <= toY; y++) { bytes32 coordHash = keccak256(abi.encodePacked(x, y)); uint256 tid = coordinateToTokenId[coordHash]; tokenIds[idx] = tid; if (tid != 0) lands[idx] = landData[tid]; idx++; } } } } Для аренды земли используем стандарт ERC-4907, который добавляет временную роль пользователя. Владелец может передать права на использование участка на определённый срок без передачи права собственности. Это идеально для ивентов и магазинов.
3D движок и рендеринг
Three.js + React Three Fiber
Для браузерного метавёрса используем Three.js через React Three Fiber (R3F) + Rapier для физики. R3F даёт реактивную композицию сцены и интеграцию с React-экосистемой.
import { Canvas } from "@react-three/fiber"; import { Physics, RigidBody } from "@react-three/rapier"; import { Environment, PerspectiveCamera, PointerLockControls } from "@react-three/drei"; function MetaverseWorld({ playerPosition, nearbyLands }: WorldProps) { return ( <Canvas shadows> <PerspectiveCamera makeDefault fov={75} /> <PointerLockControls /> <Environment preset="sunset" /> <Physics> <RigidBody type="fixed" colliders="trimesh"> <TerrainMesh heightMap={worldHeightmap} /> </RigidBody> {nearbyLands.map(land => ( <LandParcel key={land.tokenId} position={[land.x * PARCEL_SIZE, 0, land.y * PARCEL_SIZE]} contentURI={land.contentURI} owner={land.owner} /> ))} <PlayerController initialPosition={playerPosition} /> </Physics> </Canvas> ); } function LandParcel({ contentURI, position }: LandParcelProps) { const { scene } = useGLTF(ipfsToHttp(contentURI)); return <primitive object={scene} position={position} />; } Level of Detail (LOD) и чанковая загрузка
Метавёрс с тысячами участков нельзя рендерить целиком. Используем чанковую архитектуру с четырьмя уровнями детализации: immediate zone (0–50 м, полный detail), near (50–200 м, LOD1), far (200–500 м, LOD2), very far (billboard). Чанк 10x10 участков загружается 3x3 вокруг игрока — это даёт до 900 участков в памяти, а остальные выгружаются.
const CHUNK_SIZE = 10; function useChunkLoader(playerPosition: Vector3) { const [loadedChunks, setLoadedChunks] = useState<Set<string>>(new Set()); useEffect(() => { const chunkX = Math.floor(playerPosition.x / (CHUNK_SIZE * PARCEL_SIZE)); const chunkZ = Math.floor(playerPosition.z / (CHUNK_SIZE * PARCEL_SIZE)); const chunksToLoad = []; for (let dx = -1; dx <= 1; dx++) { for (let dz = -1; dz <= 1; dz++) { chunksToLoad.push(`${chunkX + dx},${chunkZ + dz}`); } } setLoadedChunks(new Set(chunksToLoad)); }, [Math.floor(playerPosition.x / 50), Math.floor(playerPosition.z / 50)]); return loadedChunks; } Мультиплеер: синхронизация игроков
Используем Colyseus — Node.js фреймворк для state-синхронизации. Комната вмещает до 100 игроков. Позиции обновляются 20 раз в секунду с серверной валидацией скорости (античит).
import { Room, Client, MapSchema, Schema, type } from "@colyseus/core"; class Player extends Schema { @type("number") x: number = 0; @type("number") y: number = 0; @type("number") z: number = 0; @type("number") rotY: number = 0; @type("string") animation: string = "idle"; @type("string") walletAddress: string = ""; @type("string") displayName: string = ""; } class WorldState extends Schema { @type({ map: Player }) players = new MapSchema<Player>(); } export class WorldRoom extends Room<WorldState> { maxClients = 100; onCreate() { this.setState(new WorldState()); this.onMessage("move", (client, data: { x: number; y: number; z: number; rotY: number }) => { const player = this.state.players.get(client.sessionId); if (!player) return; const speed = Math.hypot(data.x - player.x, data.z - player.z); if (speed > MAX_SPEED_PER_TICK) return; player.x = data.x; player.y = data.y; player.z = data.z; player.rotY = data.rotY; }); } async onJoin(client: Client, options: { walletAddress: string }) { const player = new Player(); player.walletAddress = options.walletAddress; player.displayName = await getDisplayName(options.walletAddress); this.state.players.set(client.sessionId, player); } onLeave(client: Client) { this.state.players.delete(client.sessionId); } } Каждый игрок подключается через верификацию кошелька (подпись сообщения). Это даёт Sybil-устойчивость и связь аккаунта с on-chain идентичностью.
Децентрализованное хранение контента
Пользователи загружают 3D модели для своих участков. Мы используем IPFS + Pinata для файлов и Arweave для ценных активов. В блокчейн пишется только CID. Для загрузки и получения контента написали pipeline с кешированием через HTTP gateway. Arweave — альтернатива для permanent storage: один раз платишь за вечное хранение. Подходит для ценных активов, где continuity критична.
Экономика и токеномика
Dual token model
Большинство метавёрсов используют два токена: governance (ERC-20, ограниченный supply) для DAO и utility (инфляционный, с sink-механизмами) для in-world покупок. Мы реализуем сжигание токенов при строительстве и staking земли для снижения оборота.
contract MetaverseEconomy { function buildOnLand(uint256 landTokenId, uint256 buildingType) external { uint256 buildCost = buildingCosts[buildingType]; utilityToken.burnFrom(msg.sender, buildCost); require( land.ownerOf(landTokenId) == msg.sender || land.userOf(landTokenId) == msg.sender, "No rights to build" ); buildings[landTokenId][buildingType] = true; emit BuildingPlaced(landTokenId, buildingType, msg.sender); } function buyLand(uint256 tokenId) external payable { LandListing memory listing = listings[tokenId]; require(msg.value >= listing.price, "Insufficient payment"); uint256 royalty = (listing.price * ROYALTY_PERCENT) / 10_000; uint256 sellerAmount = listing.price - royalty; payable(listing.seller).transfer(sellerAmount); payable(treasury).transfer(royalty); land.safeTransferFrom(listing.seller, msg.sender, tokenId); delete listings[tokenId]; } } Play-to-earn механики строим на utility: создатели популярного контента получают долю дохода от зон, event-хосты — спонсорские токены, staking земли даёт yield. Без этих sink-механизмов экономика быстро лопается.
Подробнее о sink-механизмах
Типичные механизмы сжигания токенов
- Строительство: каждый новый объект сжигает utility-токены. - Стейкинг земли: заморозка токенов на определённый срок. - Комиссии за операции: часть комиссий сжигается. - Ивенты: покупка билетов за токены с последующим сжиганием.Технический стек и инфраструктура
| Компонент | Технологии | Назначение |
|---|---|---|
| 3D движок | Three.js + R3F, или Babylon.js | Рендеринг мира |
| Физика | Rapier (Rust/WASM) | Коллизии, физика |
| Мультиплеер | Colyseus или Nakama | Синхронизация игроков |
| Смарт-контракты | Solidity + Foundry | Land NFT, Economy |
| Хранение | IPFS + Pinata, Arweave | 3D контент, метаданные |
| Индексирование | The Graph | Карта мира, ownership |
| Бэкенд | Node.js / Go | Game logic, API |
| БД | PostgreSQL + Redis | Аналитика, кеш |
| Сеть | Polygon PoS или Ethereum L2 | Дешёвые транзакции |
Процесс разработки
- Концепция и токеномика: 4–6 недель.
- Смарт-контракты: 6–10 недель.
- 3D движок (MVP): 8–12 недель.
- Мультиплеер: 4–6 недель.
- Система контента: 6–8 недель.
- Маркетплейс: 4–6 недель.
- Аудит: 4–8 недель.
- Alpha-тестирование: 4–6 недель.
- Запуск: 2 недели.
Реалистичный timeline от нуля до alpha: 12–18 месяцев при команде 6–10 человек. Среднее отклонение от плана не превышает 20%. Проекты, обещающие "метавёрс за 3 месяца", как правило, доставляют технологически несостоятельный продукт.
Что входит в работу
- Полная документация архитектуры и API
- Исходный код смарт-контрактов с тестами
- Клиентский код на TypeScript (R3F, Colyseus)
- Деплой и настройка инфраструктуры (AWS, Pinata, The Graph)
- Обучение вашей команды (2 недели)
- Поддержка 3 месяца после запуска
Мы работаем по гибкой модели: вы получаете демо каждые 2 недели и можете корректировать приоритеты.
Главная ошибка при разработке метавёрсов — начинать с блокчейна. Начинайте с игры: если виртуальный мир интересен без NFT — блокчейн добавит ценности. Если без NFT в него никто не идёт — NFT это не исправят.
Закажите консультацию — мы оценим ваш проект за 2 дня и предложим прозрачный план разработки. Свяжитесь с нами, чтобы обсудить детали.







