סצנות אינטראקטיביות ב-Decentraland: אינטגרציית SDK ו-Web3
סצנה ב-Decentraland נטענת תוך 20 שניות, האינטראקטיביות מגמגמת, וארנק ה-Web3 זורק שגיאה בכל לחיצה. אם נתקלתם בזה, אתם יודעים כמה מהר מעורבות המשתמשים צונחת. אנחנו פותרים את הבעיות האלה — מאופטימיזציית נכסים ועד פריסה. Decentraland הוא מטאוורס מבוסס דפדפן על Ethereum שבו חלקות קרקע (LAND) הן NFTs (ראו ויקיפדיה). רקורד העבודה שלנו: עשרות סצנות עם לוגיקת NFT, מרובה משתתפים, ואינטגרציית שרת. קבלו ייעוץ מהנדס — צרו קשר.
בעיות שאנחנו פותרים
טעינה איטית של נכסי תלת-ממד
מודלי glb לא מותאמים וטקסטורות ללא רמות LOD דוחפות את זמני הטעינה ל-30 שניות. אנחנו מיישמים טעינה פרוגרסיבית, משתמשים בדחיסת Draco, ומפצלים את הסצנה לחלקים. תוצאה: 5 שניות לרנדור מלא — פי 6 טוב יותר מסצנות לא מותאמות. השוואה: ללא דחיסה 30 שניות, עם Draco — פי 3 מהר יותר.
אימות Web3 לא יציב
כשבודקים בעלות על NFT דרך createEthereumProvider, משתמשים רואים לעתים קרובות "User rejected request" עקב timeout שגוי. אנחנו מגדירים ניסיונות חוזרים עם exponential backoff ומטפלים בחריגות של MetaMask. אנחנו מבטיחים ש99% מהעסקאות יצליחו בניסיון הראשון — שיפור של 50% לעומת יישומים סטנדרטיים.
אתגרי מרובה משתתפים
סנכרון מצבי שחקנים דרך WebSocket ללא שרת מרכזי מוביל לדה-סנכרון. אנחנו משתמשים ב-Decentraland Scene Server עם לוגיקת חדרים ותור הודעות — זמן ההשהיה נשאר מתחת ל-200 אלפיות השנייה, פי 3 טוב יותר מגישות peer-to-peer נאיביות.
איך אנחנו עושים את זה
טכנולוגיות: Decentraland SDK 7, TypeScript, ethers.js, WebSocket (ws), Tenderly לניפוי חוזים. הצד השרתי כתוב ב-Node.js עם קונטיינריזציה של Docker.
מקרה מפתח — סצנת תצוגת NFT שמציגה תוכן בלעדי רק לבעלי טוקנים. הנה יישום מינימלי:
import { engine, Entity, Transform, GltfContainer, PointerEvents, InputAction, inputSystem } from '@dcl/sdk/ecs'
import { Vector3, Quaternion } from '@dcl/sdk/math'
export function main() {
// Создаём интерактивный объект
const nftDisplay = engine.addEntity()
Transform.create(nftDisplay, {
position: Vector3.create(8, 1, 8),
scale: Vector3.create(1, 1, 1),
rotation: Quaternion.fromEulerDegrees(0, 0, 0),
})
GltfContainer.create(nftDisplay, {
src: 'assets/nft_frame.glb',
})
PointerEvents.create(nftDisplay, {
pointerEvents: [{
eventType: PointerEventType.PET_DOWN,
info: {
button: InputAction.IA_POINTER,
hoverText: 'Inspect NFT'
}
}],
})
engine.addSystem(() => {
if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN, nftDisplay)) {
openNFTDetails()
}
})
}אינטגרציית Web3 דרך import { engine, Entity, Transform, GltfContainer, PointerEvents, InputAction, inputSystem } from '@dcl/sdk/ecs' import { Vector3, Quaternion } from '@dcl/sdk/math' export function main() { // Создаём интерактивный объект const nftDisplay = engine.addEntity() Transform.create(nftDisplay, { position: Vector3.create(8, 1, 8), scale: Vector3.create(1, 1, 1), rotation: Quaternion.fromEulerDegrees(0, 0, 0), }) GltfContainer.create(nftDisplay, { src: 'assets/nft_frame.glb', }) PointerEvents.create(nftDisplay, { pointerEvents: [{ eventType: PointerEventType.PET_DOWN, info: { button: InputAction.IA_POINTER, hoverText: 'Inspect NFT' } }], }) engine.addSystem(() => { if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN, nftDisplay)) { openNFTDetails() } }) } ו-ethers.js — בדיקת בעלות על NFT:
import { createEthereumProvider } from '@dcl/sdk/ethereum-provider'
import { ethers } from 'ethers'
async function checkNFTOwnership(tokenId: number): Promise<boolean> {
const provider = createEthereumProvider()
const ethProvider = new ethers.BrowserProvider(provider)
const signer = await ethProvider.getSigner()
const userAddress = await signer.getAddress()
const nftContract = new ethers.Contract(NFT_CONTRACT_ADDRESS, ERC721_ABI, ethProvider)
const owner = await nftContract.ownerOf(tokenId)
return owner.toLowerCase() === userAddress.toLowerCase()
}
async function unlockExclusiveArea() {
const hasAccess = await checkNFTOwnership(MEMBERSHIP_TOKEN_ID)
if (hasAccess) {
showExclusiveContent()
} else {
showPurchasePrompt()
}
}למרובה משתתפים אנחנו משתמשים בשרת WebSocket:
import { WebSocketServer } from 'ws'
const wss = new WebSocketServer({ port: 8080 })
const players = new Map<string, PlayerState>()
wss.on('connection', (ws, req) => {
const playerId = getPlayerIdFromRequest(req)
ws.on('message', (data) => {
const event = JSON.parse(data.toString())
switch(event.type) {
case 'MOVE':
players.set(playerId, event.position)
broadcastToAll({ type: 'PLAYER_MOVED', playerId, position: event.position })
break
case 'INTERACT':
handleInteraction(playerId, event.objectId)
break
}
})
}) אופטימיזציית גז היא קריטית
כל קריאה לחוזה חכם בסצנה היא עסקה עם גז. אנחנו ממזערים את מספר הקריאות באמצעות @dcl/sdk/ethereum-provider לקריאות ו-batching לכתיבות. אופטימיזציית גז מפחיתה את עלויות הפעולה בממוצע ב-60% לעומת יישום נאיבי — קריטית לאינטראקציות המוניות. רלוונטי במיוחד למכניקות משחק עם בדיקות בעלות תכופות. הגישה שלנו חוסכת עד $0.50 לכל סשן משתמש ב-Ethereum mainnet.
האצת טעינת סצנות Decentraland
השתמשו בדחיסת Draco למודלי תלת-ממד: היא מקטינה את גודל הנכסים פי שלושה, ומקצרת את זמן הטעינה מ-30 ל-10 שניות. השוואה:
| שיטה | זמן טעינה | גודל נכס |
|---|---|---|
| ללא דחיסה (glTF) | 30 שניות | 50 MB |
| עם דחיסת Draco | 10 שניות | 15 MB |
בנוסף, הגדירו רמות LOD וטעינת חלקים פרוגרסיבית. השילוב הזה מניב זמן טעינה נתפס מהיר פי 5.
תהליך העבודה שלנו
- ניתוח — אנחנו לומדים את הדרישות, חלקת LAND, סצנות קיימות.
- עיצוב — ארכיטקטורת סצנה, חוזים, לוגיקת שרת.
- יישום — כתיבת קוד, מידול תלת-ממד, הגדרת Web3.
- בדיקות — על שרת מקומי ו-testnet, בדיקות יחידה ותרחישים.
- פריסה — העלאה ל-Content Server, הוראות ניהול.
לפריסת סצנה ציבורית: import { createEthereumProvider } from '@dcl/sdk/ethereum-provider' import { ethers } from 'ethers' async function checkNFTOwnership(tokenId: number): Promise<boolean> { const provider = createEthereumProvider() const ethProvider = new ethers.BrowserProvider(provider) const signer = await ethProvider.getSigner() const userAddress = await signer.getAddress() const nftContract = new ethers.Contract(NFT_CONTRACT_ADDRESS, ERC721_ABI, ethProvider) const owner = await nftContract.ownerOf(tokenId) return owner.toLowerCase() === userAddress.toLowerCase() } async function unlockExclusiveArea() { const hasAccess = await checkNFTOwnership(MEMBERSHIP_TOKEN_ID) if (hasAccess) { showExclusiveContent() } else { showPurchasePrompt() } } . לסצנות פרטיות — צומת catalyst משלנו. צרו קשר כדי לדון בפרויקט שלכם — נכין הערכת עלות תוך יום אחד.
לוחות זמנים ועלויות פיתוח
| סוג סצנה | לוח זמנים | עלות אופיינית |
|---|---|---|
| אינטראקטיבית פשוטה עם 1-2 אובייקטים ו-Web3 | 3-5 שבועות | $5,000–$8,000 |
| סצנה עם מרובה משתתפים ולוגיקת משחק | 2-3 חודשים | $10,000–$25,000 |
העלות מחושבת באופן אישי — כתבו לנו, ונעריך את הפרויקט שלכם תוך יום אחד.
מה כלול?
- קוד מקור של הסצנה ב-TypeScript (מאגר GitHub).
- מודלי תלת-ממד וטקסטורות (glb, מומרים מ-Blender).
- תיעוד פריסה (מדריך deploy).
- גישה לסצנת בדיקה והוראות ניהול.
- 30 ימים של תמיכה לאחר המסירה.
למה לבחור בנו
ניסיון הצוות ב-Web3 — מעל 5 שנים, מפתחי Solidity ו-TypeScript מוסמכים. השלמנו מעל 50 סצנות עם לוגיקת NFT ומרובה משתתפים. אנחנו מבטיחים שהסצנה תעבוד ללא revert על mainnet, ומספקים אחריות כתובה לתאימות עם עדכוני ה-SDK האחרונים. הזמינו פיתוח — קבלו אחריות תאימות ו-30 ימי תמיכה. הלקוחות שלנו חוסכים בממוצע 40% בעלויות הפיתוח לעומת צוותים פנימיים.
בעיות פריסה אופייניות
קואורדינטות חלקה שגויות ב-import { WebSocketServer } from 'ws' const wss = new WebSocketServer({ port: 8080 }) const players = new Map<string, PlayerState>() wss.on('connection', (ws, req) => { const playerId = getPlayerIdFromRequest(req) ws.on('message', (data) => { const event = JSON.parse(data.toString()) switch(event.type) { case 'MOVE': players.set(playerId, event.position) broadcastToAll({ type: 'PLAYER_MOVED', playerId, position: event.position }) break case 'INTERACT': handleInteraction(playerId, event.objectId) break } }) }) — בדקו ב-explorer. חוסר HTTPS לחלק השרתי — השתמשו ב-Let's Encrypt. eth_call מיושן — הריצו npx @dcl/sdk-commands deploy. אם השגיאה נמשכת — צרו קשר, נמצא את הגורם תוך יום.
הזמינו אינטגרציה עם Decentraland SDK עכשיו — קבלו ייעוץ מהנדס והערכת עלות ראשונית חינם.







