Разработка виртуального мира на блокчейне

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка виртуального мира на блокчейне
Сложный
от 2 недель до 3 месяцев
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    902
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1120
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    588
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    854

Разработка виртуального мира на блокчейне

Блокчейн-метавёрс — это не просто "3D игра с NFT". Это persistent виртуальный мир, где право собственности на цифровые активы (земли, здания, предметы, аватары) криптографически гарантировано, а экономика управляется кодом, а не централизованным оператором. Технически это один из самых сложных продуктов в Web3: здесь пересекаются real-time 3D рендеринг, мультиплеер сети, smart contract системы, децентрализованное хранение данных и complex tokenomics. Разберём каждый слой.

Архитектура: слои виртуального мира

┌─────────────────────────────────────────────┐
│             Клиент (браузер/десктоп)         │
│        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 и экономикой.

Land NFT: система виртуальных земель

Земля — фундаментальный актив большинства метавёрсов. The Sandbox, Decentraland, Otherside — все используют NFT land как основной механизм ownership.

Coordinate-based land system

// 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; // hash(x,y) → tokenId
    uint256 private _nextTokenId = 1;

    // Royalties на вторичный рынок
    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);
    }

    // Владелец устанавливает 3D контент своего участка
    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) {
        // Для небольших чанков (например, 20x20 = 400 участков)
        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)

ERC-4907 добавляет роль user к ERC-721: временный пользователь, который может использовать NFT до определённого времени, но не может им торговать. Идеально для аренды участков:

import "@openzeppelin/contracts/token/ERC721/extensions/ERC4907.sol";

contract MetaverseLandWithRent is MetaverseLand, ERC4907 {
    // Владелец сдаёт участок на определённое время
    function rentLand(
        uint256 tokenId,
        address tenant,
        uint64 expires
    ) external {
        require(ownerOf(tokenId) == msg.sender, "Not owner");
        setUser(tokenId, tenant, expires);
    }

    // Кто может строить на участке: владелец или арендатор
    function canBuild(uint256 tokenId) external view returns (address) {
        address user = userOf(tokenId);
        return user != address(0) ? user : ownerOf(tokenId);
    }
}

3D движок и рендеринг

Three.js + React Three Fiber

Для браузерного метавёрса — Three.js через React Three Fiber (R3F) + Rapier для физики:

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 /> {/* FPS управление */}
      <Environment preset="sunset" />

      <Physics>
        {/* Terrain */}
        <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>
  );
}

// Ленивая загрузка 3D контента участка из IPFS
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 zone (50–200 метров) — LOD1 модели (50% полигонов)
  • Far zone (200–500 метров) — LOD2 (10% полигонов), нет физики
  • Very far — только 2D billboard sprites
  • Beyond — не загружается
const CHUNK_SIZE = 10; // 10x10 участков на чанк

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));

    // Загружаем 3x3 чанка вокруг игрока
    const chunksToLoad = [];
    for (let dx = -1; dx <= 1; dx++) {
      for (let dz = -1; dz <= 1; dz++) {
        chunksToLoad.push(`${chunkX + dx},${chunkZ + dz}`);
      }
    }

    // Выгружаем далёкие чанки для экономии памяти
    const newLoaded = new Set(chunksToLoad);
    setLoadedChunks(newLoaded);
  }, [Math.floor(playerPosition.x / 50), Math.floor(playerPosition.z / 50)]);

  return loadedChunks;
}

Мультиплеер: синхронизация игроков

Colyseus: state синхронизация

Colyseus — Node.js фреймворк для мультиплеер игр с real-time state синхронизацией:

// Server: Colyseus Room
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; // до 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);
  }
}
// Client: подключение к Colyseus
import Colyseus from "colyseus.js";

const client = new Colyseus.Client("wss://world.example.com");

const room = await client.joinOrCreate("world_room", {
  walletAddress: connectedWalletAddress,
  chunkId: getCurrentChunk(playerPosition),
});

// Получаем обновления состояния всех игроков
room.state.players.onAdd((player, sessionId) => {
  addPlayerAvatar(sessionId, player);
  player.onChange(() => updatePlayerPosition(sessionId, player));
});

room.state.players.onRemove((player, sessionId) => {
  removePlayerAvatar(sessionId);
});

// Отправляем свою позицию (throttled до 20 раз в секунду)
const sendPosition = throttle((position: Vector3) => {
  room.send("move", { x: position.x, y: position.y, z: position.z, rotY: camera.rotation.y });
}, 50);

Децентрализованное хранение контента

Пользователи загружают 3D модели для своих участков. Это не идёт в блокчейн — туда идёт только CID (Content Identifier).

IPFS + Pinata

import { PinataSDK } from "pinata";

const pinata = new PinataSDK({ pinataJwt: PINATA_JWT });

async function uploadLandContent(
  gltfFile: File,
  textureFiles: File[],
  metadata: LandMetadata
): Promise<string> {
  // Загружаем 3D модель
  const modelUpload = await pinata.upload.file(gltfFile);

  // Загружаем текстуры
  const textureUploads = await Promise.all(
    textureFiles.map(f => pinata.upload.file(f))
  );

  // Создаём metadata JSON с ссылками
  const fullMetadata = {
    ...metadata,
    model: `ipfs://${modelUpload.IpfsHash}`,
    textures: textureUploads.map(u => `ipfs://${u.IpfsHash}`),
  };

  const metadataUpload = await pinata.upload.json(fullMetadata);
  return `ipfs://${metadataUpload.IpfsHash}`;
}

// Конвертация IPFS URI в HTTP gateway для браузера
function ipfsToHttp(uri: string): string {
  if (uri.startsWith("ipfs://")) {
    return `https://gateway.pinata.cloud/ipfs/${uri.slice(7)}`;
  }
  return uri;
}

Arweave — альтернатива для permanent storage. Один раз платишь за вечное хранение. Подходит для ценных активов, где continuity критична. Интеграция через Bundlr/Irys.

Экономика и токеномика

Dual token model

Большинство метавёрсов используют два токена:

Governance token (ERC-20) — ограниченный supply, для DAO голосований, staking. Например, MANA в Decentraland, SAND в The Sandbox.

Utility token / Credits — для in-world транзакций, покупок, создания контента. Может быть инфляционным с балансирующими sink механизмами.

contract MetaverseEconomy {
    // Создание контента на участке требует сжигания utility token
    function buildOnLand(uint256 landTokenId, uint256 buildingType) external {
        uint256 buildCost = buildingCosts[buildingType];
        utilityToken.burnFrom(msg.sender, buildCost); // sink механизм

        // Верифицируем ownership или аренду
        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);
    }

    // Marketplace: P2P торговля с royalty
    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 механики

Устойчивые P2E механики должны иметь реальный utility за каждым reward:

  • Контент создание — пользователи, создающие популярные участки, получают долю от трафика/продаж
  • Event hosting — проведение мероприятий на участке приносит токены от спонсоров
  • Staking land — залочить участок на период для yield (снижает оборот, дефляционный механизм)

Технический стек и инфраструктура

Компонент Технологии Назначение
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 Дешевые транзакции

Этапы и реалистичные сроки

Фаза Содержание Срок
Концепция и design World design, tokenomics, tech spec 4–6 нед
Smart contracts Land NFT, Economy, Governance 6–10 нед
3D движок (MVP) Basic world, movement, LOD 8–12 нед
Мультиплеер Colyseus интеграция, presence 4–6 нед
Content system Upload pipeline, IPFS, builder tools 6–8 нед
Marketplace P2P trading, auctions 4–6 нед
Аудит смарт-контрактов 4–8 нед
Alpha тестирование 4–6 нед
Launch 2 нед

Реалистичный timeline от нуля до alpha: 12–18 месяцев при команде 6–10 человек. Бюджет: $500k–$2M. Проекты, обещающие "метавёрс за 3 месяца", как правило, доставляют технологически несостоятельный продукт.

Главная ошибка при разработке метавёрсов — начинать с блокчейна. Начинайте с игры: если виртуальный мир интересен без NFT — блокчейн добавит ценности. Если без NFT в него никто не идёт — NFT это не исправят.