Разработка метавселенной (Metaverse)

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка метавселенной (Metaverse)
Сложная
от 2 недель до 3 месяцев
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка метавселенной (Metaverse)

Метавселенная — перегруженный термин. Прежде чем проектировать, нужно выбрать конкретную модель: это persistent 3D world с real-time взаимодействием игроков (как Decentraland, The Sandbox), или это social layer поверх разных приложений, или виртуальные офисы для enterprise? Каждая модель — разный технологический стек.

Я опишу архитектуру web3-native persistent world: многопользовательская 3D среда с ownership NFT земли/ассетов, on-chain экономикой и decentralized governance. Это самый технически сложный и самый интересный вариант.

Архитектурные слои метавселенной

1. Блокчейн слой: ownership и экономика

Всё, что имеет ценность, существует on-chain:

  • LAND NFT — участки виртуальной земли (ERC-721)
  • Avatar NFT — персонажи с attributes
  • Wearables NFT — одежда, предметы (ERC-1155 для fungible, ERC-721 для уникальных)
  • Governance token — участие в DAO управлении
  • In-world currency — ERC-20 токен для экономики

Всё остальное — off-chain (контент земельных участков, 3D ассеты, история взаимодействий).

2. Content layer: что находится на LAND

Владелец LAND деплоит на своём участке контент: 3D сцены (GLTF/GLB файлы), скрипты интерактивности, портали в другие миры. Контент хранится децентрализованно — IPFS или Arweave.

3. Real-time layer: multiplayer движок

Игроки видят друг друга и взаимодействуют в реальном времени — это задача real-time networking, не блокчейна. Game servers (или P2P peer relay) синхронизируют позиции и состояния аватаров.

4. Application layer: dApps и игры на LAND

Владелец земли разворачивает приложения — мини-игры, галереи NFT, виртуальные магазины, концертные площадки. Каждое приложение может иметь собственную on-chain логику.

LAND система: NFT земля и координатная сетка

Координатная система

Классический подход: карта как двумерная сетка координат. Decentraland использует (-150, -150) до (150, 150). The Sandbox — 408×408.

contract LandRegistry is ERC721 {
    int16 public constant MIN_X = -100;
    int16 public constant MAX_X = 100;
    int16 public constant MIN_Y = -100;
    int16 public constant MAX_Y = 100;
    
    // Token ID кодирует координаты: id = (x + 100) * 201 + (y + 100)
    function coordinatesToId(int16 x, int16 y) public pure returns (uint256) {
        require(x >= MIN_X && x <= MAX_X, "X out of range");
        require(y >= MIN_Y && y <= MAX_Y, "Y out of range");
        return uint256(uint16(x - MIN_X)) * 201 + uint256(uint16(y - MIN_Y));
    }
    
    function idToCoordinates(uint256 tokenId) public pure returns (int16 x, int16 y) {
        y = int16(int256(tokenId % 201)) + MIN_Y;
        x = int16(int256(tokenId / 201)) + MIN_X;
    }
    
    // Adjacency check: нужно для Estate (объединение соседних участков)
    function isAdjacent(uint256 tokenId1, uint256 tokenId2) public pure returns (bool) {
        (int16 x1, int16 y1) = idToCoordinates(tokenId1);
        (int16 x2, int16 y2) = idToCoordinates(tokenId2);
        int16 dx = x1 - x2;
        int16 dy = y1 - y2;
        return (dx == 0 && (dy == 1 || dy == -1)) || (dy == 0 && (dx == 1 || dx == -1));
    }
}

Estate: объединение соседних LAND

Владелец нескольких соседних участков может объединить их в Estate для постройки больших сцен:

contract EstateRegistry is ERC721 {
    struct Estate {
        uint256[] landIds;   // входящие LAND токены
        string name;
        string ipfsHash;     // metadata
    }
    
    mapping(uint256 => Estate) public estates;
    
    function createEstate(
        uint256[] calldata landIds,
        string calldata name
    ) external returns (uint256 estateId) {
        require(landIds.length >= 2, "Need at least 2 parcels");
        
        // Верифицируем ownership и adjacency
        for (uint256 i = 0; i < landIds.length; i++) {
            require(landRegistry.ownerOf(landIds[i]) == msg.sender, "Not land owner");
        }
        require(_isConnectedGraph(landIds), "Parcels not adjacent");
        
        // Переводим LAND в estate контракт (lock)
        for (uint256 i = 0; i < landIds.length; i++) {
            landRegistry.transferFrom(msg.sender, address(this), landIds[i]);
        }
        
        estateId = ++nextEstateId;
        estates[estateId] = Estate({ landIds: landIds, name: name, ipfsHash: "" });
        _mint(msg.sender, estateId);
        
        emit EstateCreated(estateId, msg.sender, landIds);
    }
    
    // BFS проверка что все parcels связаны в один граф
    function _isConnectedGraph(uint256[] calldata ids) internal view returns (bool) {
        if (ids.length == 1) return true;
        
        bool[] memory visited = new bool[](ids.length);
        uint256[] memory queue = new uint256[](ids.length);
        uint256 qHead = 0;
        uint256 qTail = 0;
        
        visited[0] = true;
        queue[qTail++] = ids[0];
        
        while (qHead < qTail) {
            uint256 current = queue[qHead++];
            for (uint256 i = 0; i < ids.length; i++) {
                if (!visited[i] && landRegistry.isAdjacent(current, ids[i])) {
                    visited[i] = true;
                    queue[qTail++] = ids[i];
                }
            }
        }
        
        for (uint256 i = 0; i < ids.length; i++) {
            if (!visited[i]) return false;
        }
        return true;
    }
}

Content система: что деплоится на LAND

Scene descriptor

Каждый LAND имеет scene — набор 3D объектов, скриптов, порталов. Хранится на IPFS:

// Scene descriptor (IPFS JSON)
interface SceneDescriptor {
  version: '2.0';
  landId: number;
  owner: string;           // ethereum address
  title: string;
  description: string;
  
  // 3D контент
  models: Array<{
    src: string;           // ipfs://Qm... ссылка на GLTF/GLB
    position: [number, number, number];
    rotation: [number, number, number];
    scale: [number, number, number];
  }>;
  
  // Скрипты интерактивности
  scripts: Array<{
    src: string;           // ipfs://Qm... JavaScript модуль
    entryPoint: string;    // имя экспортированной функции
  }>;
  
  // Spawn points для аватаров
  spawnPoints: Array<{
    position: [number, number, number];
    cameraTarget: [number, number, number];
  }>;
  
  // Ссылки на adjacent land / порталы
  portals: Array<{
    position: [number, number, number];
    targetLandId: number;
    targetUrl?: string;    // или внешний URL
  }>;
}

Публикация сцены:

contract LandContent {
    // IPFS hash сцены для каждого LAND
    mapping(uint256 => string) public sceneHash;
    mapping(uint256 => uint256) public sceneVersion;
    
    function publishScene(uint256 landId, string calldata ipfsHash) external {
        require(landRegistry.ownerOf(landId) == msg.sender, "Not owner");
        
        // Базовая валидация: непустой hash
        require(bytes(ipfsHash).length == 46, "Invalid IPFS hash"); // Qm... = 46 chars
        
        sceneHash[landId] = ipfsHash;
        sceneVersion[landId]++;
        
        emit ScenePublished(landId, msg.sender, ipfsHash, sceneVersion[landId]);
    }
}

Scene Scripting SDK

Разработчики пишут скрипты для интерактивности на LAND. Это декентрализованная версия Unity/Unreal scripting:

// Metaverse Scene Script SDK (выполняется в sandbox iframe/WebWorker)
import { engine, Transform, MeshRenderer, OnPointerDown } from '@metaverse/sdk';

// Интерактивная дверь
const door = engine.addEntity();
engine.addComponentOrReplace(door, Transform, {
  position: { x: 0, y: 0, z: 5 },
  rotation: { x: 0, y: 0, z: 0, w: 1 },
  scale: { x: 1, y: 1, z: 1 },
});

let isOpen = false;

engine.addComponentOrReplace(door, OnPointerDown, {
  callback: async () => {
    isOpen = !isOpen;
    // Анимируем открытие/закрытие
    const transform = engine.getComponent(door, Transform);
    transform.rotation = isOpen
      ? Quaternion.fromEulerDegrees(0, 90, 0)
      : Quaternion.fromEulerDegrees(0, 0, 0);
  },
  hoverText: isOpen ? 'Close door' : 'Open door',
});

// NFT gate: только владельцы определённого NFT могут войти
import { checkNFTOwnership } from '@metaverse/blockchain';

engine.addComponentOrReplace(nftGate, OnPointerDown, {
  callback: async () => {
    const userAddress = await engine.getUserAddress();
    const hasNFT = await checkNFTOwnership(userAddress, NFT_CONTRACT, TOKEN_ID);
    
    if (!hasNFT) {
      engine.showNotification('You need the Golden Pass NFT to enter');
      return;
    }
    
    engine.teleportPlayer({ x: INSIDE_X, y: 0, z: INSIDE_Z });
  },
});

Scripting sandbox — изолированный WebWorker или iframe без прямого доступа к DOM. API работы с блокчейном предоставляется через постсообщения, не через прямой доступ к кошельку.

Real-time networking: синхронизация аватаров

Architecture: area servers

World делится на regions (каждый регион = N×N LAND). Каждый регион обслуживается одним game server. При переходе игрока между регионами — handoff на другой сервер.

                    ┌─────────────────────────────┐
                    │    Load Balancer / Router    │
                    │   (по координатам игрока)    │
                    └──────────┬──────────────────┘
                               │
          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
  │  Area Server │    │  Area Server │    │  Area Server │
  │  Region A    │    │  Region B    │    │  Region C    │
  │  (-100,-100) │    │  (0,0)       │    │  (100,100)   │
  │  to (0,0)   │    │  to (100,100)│    │  ...         │
  └──────────────┘    └──────────────┘    └──────────────┘
// Area Server: управление аватарами в регионе
import { WebSocketServer } from 'ws';
import { World } from '@dimforge/rapier3d'; // физический движок

class AreaServer {
  private players = new Map<string, PlayerState>();
  private physicsWorld = new World({ x: 0, y: -9.81, z: 0 });
  
  handlePlayerJoin(playerId: string, ws: WebSocket, position: Vector3) {
    const state: PlayerState = {
      id: playerId,
      position,
      rotation: Quaternion.identity(),
      animation: 'idle',
      ws,
    };
    
    this.players.set(playerId, state);
    
    // Отправляем новому игроку состояния всех в регионе
    const snapshot = this.getRegionSnapshot();
    ws.send(JSON.stringify({ type: 'region_snapshot', players: snapshot }));
    
    // Уведомляем остальных о новом игроке
    this.broadcast({ type: 'player_joined', player: state }, playerId);
  }
  
  handleMovement(playerId: string, movement: MovementPacket) {
    const player = this.players.get(playerId);
    if (!player) return;
    
    // Server-side validation движения (anti-cheat)
    if (!this.isValidMovement(player, movement)) {
      // Корректируем позицию
      player.ws.send(JSON.stringify({
        type: 'position_correction',
        position: player.position,
      }));
      return;
    }
    
    player.position = movement.position;
    player.rotation = movement.rotation;
    player.animation = movement.animation;
    
    // Broad-cast всем в регионе (delta compression)
    this.broadcastMovement(playerId, movement);
  }
  
  // 20 updates/sec для smooth movement
  private startTickLoop() {
    setInterval(() => this.tick(), 50);
  }
  
  private tick() {
    this.physicsWorld.step();
    
    // Собираем dirty states и рассылаем батчем
    const updates = this.getDirtyPlayerStates();
    if (updates.length > 0) {
      this.broadcast({ type: 'batch_update', updates });
    }
  }
}

Proximity-based broadcasting

Не нужно рассылать позицию игрока всем в регионе — только тем, кто рядом. Interest Management:

const VISIBILITY_RADIUS = 100; // метров

function getVisiblePlayers(playerId: string): string[] {
  const player = players.get(playerId)!;
  return Array.from(players.values())
    .filter(p => p.id !== playerId)
    .filter(p => distance(p.position, player.position) < VISIBILITY_RADIUS)
    .map(p => p.id);
}

Это снижает bandwidth с O(N²) до O(N × K), где K = среднее число видимых игроков.

In-world экономика

Marketplace контракт

Владелец LAND продаёт виртуальные товары внутри своего мира:

contract InWorldMarketplace {
    struct Listing {
        address seller;
        address nftContract;
        uint256 tokenId;
        uint256 price;          // в in-world currency (ERC-20)
        uint256 landId;         // на каком LAND выставлен товар
        bool active;
    }
    
    // Royalty для владельца LAND: 2.5% от продаж на его земле
    uint256 public constant LAND_ROYALTY = 250; // basis points
    
    function buy(uint256 listingId) external {
        Listing storage listing = listings[listingId];
        require(listing.active, "Not active");
        
        uint256 landRoyalty = listing.price * LAND_ROYALTY / 10_000;
        uint256 sellerProceeds = listing.price - landRoyalty;
        
        // Покупатель платит in-world currency
        worldToken.transferFrom(msg.sender, listing.seller, sellerProceeds);
        worldToken.transferFrom(msg.sender, landRegistry.ownerOf(listing.landId), landRoyalty);
        
        // Передаём NFT
        IERC721(listing.nftContract).transferFrom(
            address(this), msg.sender, listing.tokenId
        );
        
        listing.active = false;
        emit Sale(listingId, msg.sender, listing.price);
    }
}

Play-to-earn механики

In-world активности могут генерировать in-world currency:

  • Посещение событий на LAND (check-in reward)
  • Выполнение квестов, созданных владельцами LAND
  • Участие в мини-играх

Важно: emission rate должен быть контролируемым для предотвращения инфляции. Рекомендация: weekly emission cap + halvening механизм по аналогии с Bitcoin.

DAO и Governance

// Governance через Compound-style voting
contract MetaverseDAO is Governor, GovernorTimelockControl {
    // Holding LAND даёт voting power
    function _getVotes(
        address account,
        uint256 blockNumber,
        bytes memory
    ) internal view override returns (uint256) {
        // 1 LAND = 1 vote + bonus за стейкинг governance token
        uint256 landVotes = landRegistry.balanceOf(account); // at blockNumber
        uint256 tokenVotes = govToken.getPastVotes(account, blockNumber);
        return landVotes + tokenVotes;
    }
}

Governance решает: размер карты (новые LAND), параметры экономики, whitelist контентных форматов, upgrade контрактов.

Технический стек

3D рендеринг

Three.js + React Three Fiber — для web-native метавселенной. Хорошая поддержка GLTF, PBR материалы, performance оптимизации через instancing и LOD.

Babylon.js — альтернатива, особенно хорошо с WebXR (VR/AR). Decentraland использует Babylon.js.

Unity WebGL — лучший graphics quality, но большой bundle size (50–200 MB), медленный initial load. Подходит для desktop-ориентированного продукта.

Полный стек

Слой Технология
Blockchain Polygon PoS или Arbitrum (низкие gas для LAND transactions)
LAND/NFT Solidity ERC-721 + ERC-1155, Foundry
Governance OpenZeppelin Governor + TimelockController
Storage IPFS (Pinata/Web3.Storage) + Arweave для permanent content
Real-time Node.js + uWebSockets.js (высокая производительность)
Physics Rapier3D (Rust/WASM, быстрее Cannon.js)
3D Web Three.js + React Three Fiber + Drei
Avatar system ReadyPlayerMe SDK или кастомные VRM avatars
State Redis для region state, PostgreSQL для persistent data
Indexing The Graph (события LAND transfers, scene updates)

Этапы разработки

Фаза Содержание Срок
Foundation LAND контракты, coordinate system, basic marketplace 4–6 нед
Content system Scene descriptor, IPFS storage, scene publishing 3–4 нед
3D Client Three.js world, scene loading, basic navigation 6–8 нед
Real-time Area servers, avatar sync, proximity system 6–8 нед
Economy In-world token, marketplace, play-to-earn 4–6 нед
Scripting SDK Scene scripting sandbox, NFT gate APIs 4–6 нед
Governance DAO contracts, voting UI 3–4 нед
Audit LAND, marketplace, economy контракты 5–8 нед
Alpha launch Private alpha с ограниченной картой 2–4 нед

Реалистичный срок от нуля до public alpha: 12–18 месяцев для команды 8–12 человек (2–3 blockchain, 2–3 3D/frontend, 2 backend, 1 PM, 1 designer). Это один из наиболее scope-сложных проектов в Web3.

Главный риск не технический, а продуктовый: без контента на LAND и активного комьюнити мир будет пустым. Параллельно с разработкой нужна программа для early LAND holders и content creators.