פיתוח עולם וירטואלי מבוסס בלוקצ'יין: ארכיטקטורה וטכנולוגיות
אנחנו יודעים לבנות עולם וירטואלי מתמשך שבו כל זכות קניין מובטחת קריפטוגרפית. עשינו זאת עבור פרויקטים עם אלפי משתמשים. בפרויקט אחד, ניסיון לאחסן מיקומי שחקנים על השרשרת גרם לעיכובים של 2 שניות לכל תנועה—משתמשים התלוננו על חוויה לא ניתנת למשחק. הפתרון: העברת סנכרון לשרת WebSocket, והשארת רק רישום זכויות על הבלוקצ'יין. זה הפחית את זמן ההשהיה ל-50 אלפיות השנייה. כך נולדה הארכיטקטורה ההיברידית—הבסיס של כל המטאוורסים המודרניים.
למה בלוקצ'יין אינו מנוע משחק
הבלוקצ'יין מאשר עסקאות בשניות, אבל משחק מרובה משתתפים דורש אלפיות שנייה. לכן, המחסנית שלנו כוללת שתי שכבות נפרדות: חוזים חכמים (Solidity על Polygon) לבעלות וטוקנומיקה, ושירותים מחוץ לשרשרת (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 │
└─────────────────────┘ └──────────────────────┘עיקרון מפתח: בלוקצ'יין אינו מנוע משחק. כל מה שדורש זמן השהיה נמוך (מיקומים, אנימציות, תנועה) נשאר מחוץ לשרשרת. הבלוקצ'יין מנהל בעלות וכלכלה. ארכיטקטורה היברידית זו מתקפלת פי 3 טוב יותר מגישות מלאות על השרשרת ומפחיתה עלויות עסקה פי 10.
קרקע NFT: מערכת קרקע וירטואלית
קרקע היא הנכס הבסיסי של רוב המטאוורסים. The Sandbox, Decentraland, Otherside—כולם משתמשים בקרקע NFT כמנגנון הבעלות העיקרי. אנו מיישמים מערכת מבוססת קואורדינטות עם בדיקות גבולות ותמיכה בהשכרה.
איך פועלת המערכת מבוססת הקואורדינטות
כל חלקת קרקע מיוצגת על ידי קואורדינטות ייחודיות (x, y) על מפה בגודל קבוע. החוזה החכם מאחסן מיפוי של קואורדינטות ל-tokenId, ומבטיח ייחודיות. כדי לטעון תוכן, הבעלים מגדיר URI של IPFS על החלקה שלו.
// 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, שמוסיף תפקיד משתמש זמני. הבעלים יכול להעביר זכויות שימוש בחלקה לתקופה קבועה מבלי להעביר בעלות. זה אידיאלי לאירועים וחנויות.
מנוע תלת-ממד ועיבוד
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} />;
} רמת פירוט (LOD) וטעינת נתחים
מטאוורס עם אלפי חלקות לא ניתן לעיבוד מלא. אנו משתמשים בארכיטקטורת נתחים עם ארבע רמות פירוט: אזור מיידי (0–50 מ', פירוט מלא), קרוב (50–200 מ', LOD1), רחוק (200–500 מ', LOD2), רחוק מאוד (שלט חוצות). נתח של 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 לסנכרון מצב. חדר מארח עד 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 ומקשר את החשבון לזהות על השרשרת.
אחסון תוכן מבוזר
משתמשים מעלים מודלים תלת-ממדיים לחלקות שלהם. אנו משתמשים ב-IPFS + Pinata לקבצים וב-Arweave לנכסים יקרי ערך. רק ה-CID נכתב על הבלוקצ'יין. בנינו צינור עם מטמון שער HTTP להעלאה ושליפת תוכן. Arweave היא אלטרנטיבה לאחסון קבוע: משלמים פעם אחת על אחסון נצחי. היא מתאימה לנכסים יקרי ערך שבהם המשכיות קריטית.
כלכלה וטוקנומיקה
מודל דו-טוקני
רוב המטאוורסים משתמשים בשני טוקנים: ממשל (ERC-20, היצע מוגבל) ל-DAO, ושירות (אינפלציוני, עם מנגנוני כיור) לרכישות בעולם. אנו מיישמים שריפת טוקנים על בנייה והחזקת קרקע כדי להפחית את ההיצע במחזור.
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 על שירות: יוצרי תוכן פופולרי מרוויחים חלק מההכנסות מאזורים, מארחי אירועים מקבלים טוקני חסות, החזקת קרקע מניבה תשואה. ללא מנגנוני כיור אלה, הכלכלה מתנפחת במהירות.
עוד על מנגנוני כיור
מנגנוני שריפת טוקנים אופייניים
- בנייה: כל אובייקט חדש שורף טוקני שירות. - החזקת קרקע: טוקנים ננעלים לתקופה קבועה. - עמלות פעולה: חלק מהעמלות נשרף. - אירועים: רכישת כרטיסים בטוקנים עם שריפה לאחר מכן.מחסנית טכנית ותשתית
| רכיב | טכנולוגיות | מטרה |
|---|---|---|
| מנוע תלת-ממד | Three.js + R3F, או Babylon.js | עיבוד עולם |
| פיזיקה | Rapier (Rust/WASM) | התנגשויות, פיזיקה |
| מרובה משתתפים | Colyseus או Nakama | סנכרון שחקנים |
| חוזים חכמים | Solidity + Foundry | קרקע NFT, כלכלה |
| אחסון | IPFS + Pinata, Arweave | תוכן תלת-ממדי, מטא-דאטה |
| אינדוקס | The Graph | מפת עולם, בעלות |
| בקאנד | Node.js / Go | לוגיקת משחק, API |
| מסד נתונים | PostgreSQL + Redis | אנליטיקה, מטמון |
| רשת | Polygon PoS או Ethereum L2 | עסקאות זולות |
תהליך פיתוח
- קונספט וטוקנומיקה: 4–6 שבועות.
- חוזים חכמים: 6–10 שבועות.
- מנוע תלת-ממד (MVP): 8–12 שבועות.
- מרובה משתתפים: 4–6 שבועות.
- מערכת תוכן: 6–8 שבועות.
- שוק: 4–6 שבועות.
- ביקורת: 4–8 שבועות.
- בדיקות אלפא: 4–6 שבועות.
- השקה: 2 שבועות.
ציר זמן ריאלי מאפס לאלפא: 12–18 חודשים עם צוות של 6–10 אנשים. הסטייה הממוצעת מהתוכנית אינה עולה על 20%. פרויקטים שמבטיחים "מטאוורס ב-3 חודשים" בדרך כלל מספקים מוצר לא תקין טכנולוגית.
מה כלול
- ארכיטקטורה מלאה ותיעוד API
- קוד מקור של חוזים חכמים עם בדיקות
- קוד לקוח ב-TypeScript (R3F, Colyseus)
- פריסה והקמת תשתית (AWS, Pinata, The Graph)
- הדרכה לצוות שלך (שבועיים)
- 3 חודשים של תמיכה לאחר השקה
אנו עובדים במודל זריז: אתה מקבל הדגמה כל שבועיים ויכול להתאים עדיפויות.
הטעות העיקרית בפיתוח מטאוורס היא להתחיל עם הבלוקצ'יין. התחל עם המשחק: אם העולם הווירטואלי מעניין ללא NFTs, הבלוקצ'יין יוסיף ערך. אם אף אחד לא נכנס ללא NFTs, NFTs לא יתקנו זאת.
הזמן ייעוץ—נעריך את הפרויקט שלך תוך יומיים ונציע תוכנית פיתוח שקופה. צור קשר כדי לדון בפרטים.







