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

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску 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

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

Blockchain метавселенна—це не просто «3D гра з NFT». Це постійний віртуальний світ, де право власності на цифрові активи (землі, будівлі, предмети, аватари) криптографічно гарантовано, а економіка керується кодом, а не централізованим оператором. Технічно один з найбільш складних продуктів у Web3: перетинаються real-time 3D рендеринг, мережа мультиплеєра, системи смарт-контрактів, децентралізоване сховище даних та складна токеноміка. Розберемо кожний шар.

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

┌─────────────────────────────────────────────┐
│             Клієнт (браузер/десктоп)        │
│        Three.js / Babylon.js / Unity WebGL  │
└─────────────────┬───────────────────────────┘
                  │ WebSocket / WebRTC
┌─────────────────▼───────────────────────────┐
│            Шар мультиплеєра                 │
│     Colyseus / Photon / користувацький     │
│     Позиції, рух, синхронізація             │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│         Шар логіки гри/світу                │
│    Node.js / Go сервіси                    │
│    Управління сценами, завантаження чанків │
└──────────┬──────────────────────┬────────────┘
           │                      │
┌──────────▼──────────┐  ┌────────▼─────────────┐
│  Смарт-контракти    │  │  Децентр. сховище   │
│  Власність, економіка│  │  IPFS / Arweave     │
│  Управління         │  │  3D активи, метадані│
└─────────────────────┘  └──────────────────────┘

Ключовий принцип: блокчейн—не ігровий рушій. Усе, що вимагає низької затримки (позиції, анімації, рух), залишається off-chain. Блокчейн керує власністю та економікою.

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

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

Система земель на основі координат

// 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>
        {/* Терен */}
        <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 метрів)—повна деталізація, фізика активна
  • Near zone (50–200 метрів)—LOD1 моделі (50% полігонів)
  • Far zone (200–500 метрів)—LOD2 (10% полігонів), без фізики
  • Very far—тільки 2D billboard спрайти
  • 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: синхронізація стану

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

// Сервер: 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);
  }
}
// Клієнт: підключитися до 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))
  );

  // Створюємо 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—альтернатива для постійного сховища. Платиш один раз за вічне сховище. Підходить для цінних активів, де безперервність критична. Інтеграція через Bundlr/Irys.

Економіка та токеноміка

Модель з двома токенами

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

Токен управління (ERC-20)—обмежений supply, для DAO голосувань, staking. Наприклад, MANA в Decentraland, SAND у The Sandbox.

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

contract MetaverseEconomy {
    // Створення контенту на ділянці вимагає спалювання utility токена
    function buildOnLand(uint256 landTokenId, uint256 buildingType) external {
        uint256 buildCost = buildingCosts[buildingType];
        utilityToken.burnFrom(msg.sender, buildCost); // sink механізм

        // Верифікуємо власність або оренду
        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 за кожною награєю:

  • Створення контенту—користувачі, які створюють популярні ділянки, отримують частку трафіку/продаж
  • Хостинг подій—проведення подій на ділянці приносить токени від спонсорів
  • Staking землі—залочити ділянку на період для yield (зменшує циркуляцію, дефляційний)

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

Компонент Технології Призначення
3D рушій Three.js + R3F, або Babylon.js Рендеринг світу
Фізика Rapier (Rust/WASM) Колізії, фізика
Мультиплеєр Colyseus або Nakama Синхронізація гравців
Смарт-контракти Solidity + Foundry Land NFT, Economy
Сховище IPFS + Pinata, Arweave 3D контент, метаданні
Індексування The Graph Карта, власність
Бекенд Node.js / Go Логіка гри, API
БД PostgreSQL + Redis Аналітика, кеш
Мережа Polygon PoS або Ethereum L2 Дешеві транзакції

Етапи та реалістичні сроки

Фаза Вміст Сроки
Концепція та дизайн Дизайн світу, токеноміка, tech spec 4–6 тиж
Смарт-контракти Land NFT, Economy, Governance 6–10 тиж
3D рушій (MVP) Базовий світ, рух, LOD 8–12 тиж
Мультиплеєр Colyseus інтеграція, presence 4–6 тиж
Content system Upload pipeline, IPFS, builder tools 6–8 тиж
Marketplace P2P торговля, аукціони 4–6 тиж
Аудит смарт-контрактів 4–8 тиж
Альфа-тестування 4–6 тиж
Launch 2 тиж

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

Головна помилка при розробці метавселень—почнеяти з блокчейну. Почніть з гри: якщо віртуальний світ цікавий без NFT—блокчейн додає цінність. Якщо без NFT ніхто не входить—NFT це не виправить.