mcpField
G
game-design

game-design

github.com/dcl-regenesislabs创意与媒体1744 次安装21 周前更新
代码仓库

关于 game-design

game-design 是面向 Decentraland 场景与游戏开发者的设计和优化技能,涵盖共享 3D 世界中的玩法约束、资源预算、性能和交互设计。它还提供 MVP 规划建议,并指向相关实现技能。

主要功能

  1. 01Decentraland 共享场景设计原则
  2. 02按地块数估算场景资源预算
  3. 03纹理与资源预加载建议
  4. 04对象复用、LOD 与绘制调用优化
  5. 05输入、状态和界面设计指导
  6. 06核心循环与 MVP 测试规划

适用场景

  1. 01规划适应玩家随时进入或离开的玩法
  2. 02估算目标地块规模下的场景资源
  3. 03减少场景的绘制调用与物理负担
  4. 04制定原型、测试和迭代计划

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dcl-regenesislabs/opendcl --skill game-design

安装后在 Claude Code 中输入/game-design确认已加载。

详细介绍

Decentraland 游戏设计与场景优化

1. DCL 游戏设计理念

Decentraland 是一个持续运行的共享 3D 世界。设计时请考虑以下限制:

  • 没有启动画面:场景始终处于运行状态。玩家会从相邻地块走进来——没有闪屏,也没有“按下开始”。玩家到达的瞬间,你的场景就必须有意义。
  • 没有强制结束:你无法强制进入“游戏结束”状态。玩家可以随时走开或传送离开。设计能够自然适应玩家随时加入或离开的循环。
  • 无法移除玩家:没有 API 可以将玩家逐出场景。你可以传送玩家,但必须征得他们同意(他们必须接受提示)。面对行为不当的玩家时,应通过游戏机制而非驱逐来处理。
  • 注意场景边界:站在地块外的玩家可以看到地块内部。你的场景始终展示在外。相邻场景也清晰可见——请考虑视觉协调性。
  • 共享空间:始终可能有多名玩家同时在场。即使是“单人”谜题,其他玩家也能看到。请善加利用,或将此纳入设计考虑。

2. 场景限制公式

所有限制都随地块数量 n 按比例变化。除部署时固定的 MB 文件大小限制外,其他限制都可以超出——场景不会崩溃,但性能会下降,在低端设备上甚至可能无法使用。请将下表作为参考,而非强制标准。

资源公式1 个地块2 个地块4 个地块9 个地块16 个地块
三角形n x 10,00010,00020,00040,00090,000160,000
实体n x 2002004008001,8003,200
物理体n x 3003006001,2002,7004,800
材质log2(n+1) x 202031466681
纹理log2(n+1) x 101015233340
高度限制log2(n+1) x 20m20m31m46m66m81m
绘制调用n x 300(目标值)3006001,2002,7004,800

**文件限制:**每个地块 15 MB,总计最多 300 MB,每个地块 200 个文件,单个文件最大 50 MB。

3. 纹理要求

  • 尺寸必须为 2 的幂次方:256、512、1024、2048
  • 建议尺寸:场景物体使用 1024x1024,可穿戴物品使用 512x512
  • 使用纹理图集将多个小纹理合并为一个,以减少绘制调用和材质数量
  • 尽可能优先使用压缩格式(WebP),而非原始 PNG
  • 在材质之间共享纹理引用——不要重复纹理文件

4. 资源预加载(AssetLoad 组件)

对于会造成明显渐显效果的大型资源,可使用 AssetLoad 在渲染前预下载:

import { engine, AssetLoad, LoadingState, GltfContainer, Transform } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const preloadEntity = engine.addEntity()
AssetLoad.create(preloadEntity, { src: 'models/large-model.glb' })

function assetLoadingSystem(dt: number) {
  for (const [entity] of engine.getEntitiesWith(AssetLoad)) {
    const state = AssetLoad.get(entity)
    if (state.loadingState === LoadingState.FINISHED) {
      GltfContainer.create(entity, { src: 'models/large-model.glb' })
      Transform.create(entity, { position: Vector3.create(8, 0, 8) })
      AssetLoad.deleteFrom(entity)
    }
  }
}
engine.addSystem(assetLoadingSystem)

对于任何大于约 1 MB 的模型,或应在游戏阶段开始前就绪的资源,都应使用此模式。

5. 性能优化模式

对象池

重复使用实体,而不是创建后再销毁:

const pool: Entity[] = []

function getFromPool(): Entity {
  const existing = pool.pop()
  if (existing) return existing
  return engine.addEntity()
}

function returnToPool(entity: Entity) {
  Transform.getMutable(entity).position = Vector3.create(0, -100, 0)
  pool.push(entity)
}
LOD(细节层次)

根据玩家的距离切换模型或隐藏实体:

function lodSystem() {
  const playerPos = Transform.get(engine.PlayerEntity).position
  for (const [entity, transform] of engine.getEntitiesWith(Transform, GltfContainer)) {
    const distance = Vector3.distance(playerPos, transform.position)
    VisibilityComponent.createOrReplace(entity, { visible: distance <= 30 })
  }
}
engine.addSystem(lodSystem)
减少绘制调用
  • 在导出前于 Blender 中合并网格
  • 使用纹理图集(多个物体共用一种材质)
  • 限制不同材质的数量——在多个实体之间重复使用
  • 尽可能避免透明效果(透明物体会额外增加绘制调用)
系统优化
  • 不要每帧都运行繁重逻辑。请使用计时器:
    let timer = 0
    function heavySystem(dt: number) {
      timer += dt
      if (timer < 0.5) return // Run every 500ms, not every frame
      timer = 0
      // ... expensive work here
    }
    
  • 尽量减少 engine.getEntitiesWith() 查询——实体集合稳定时缓存查询结果
  • 避免在每帧运行的系统中分配新对象(Vector3.create、数组)
禁用未使用的碰撞体

移除玩家永远不会与之交互的装饰物体上的碰撞网格。这可以显著减少物理体数量。

6. 输入系统设计

输入操作备注
E key主要操作(IA_PRIMARY)主要交互
F key次要操作(IA_SECONDARY)备用交互
Pointer clickIA_POINTER鼠标左键单击 / 点按
Keys 1-4IA_ACTION_3 至 IA_ACTION_6操作栏槽位
设计注意事项
  • 不能使用鼠标滚轮作为输入
  • 始终兼顾 桌面端和移动端。移动端没有键盘,因此应依赖指针和屏幕按钮
  • 为指针事件设置 maxDistance(通常为 8-10 米),以防玩家从场景另一端进行交互
  • 使用 hoverText,在玩家确认交互之前说明交互的作用

7. 状态管理模式

模块级状态(简单游戏)
// game-state.ts
export let score = 0
export let gamePhase: 'waiting' | 'playing' | 'ended' = 'waiting'
export function addScore(points: number) { score += points }
基于组件的状态(复杂游戏)

使用自定义组件作为结构化数据容器:

import { engine, Schemas } from '@dcl/sdk/ecs'

const EnemyState = engine.defineComponent('EnemyState', {
  health: Schemas.Number,
  speed: Schemas.Number,
  target: Schemas.Entity
})
状态机

将游戏阶段建模为具有明确转换规则的显式状态:

type GameState = 'lobby' | 'countdown' | 'active' | 'cooldown'
let currentState: GameState = 'lobby'

function gameStateSystem(dt: number) {
  switch (currentState) {
    case 'lobby': handleLobby(dt); break
    case 'countdown': handleCountdown(dt); break
    case 'active': handleActive(dt); break
    case 'cooldown': handleCooldown(dt); break
  }
}

8. 用户体验 / UI 指南

  • 保持 UI 简洁:元宇宙注重 3D 临场感,而非 2D 覆盖层。避免使用遮挡世界的大型 HUD。
  • 优先使用空间 UI:如果信息与某个地点或对象相关,优先使用实体上的 TextShape 和 3D 标牌,而非屏幕空间 UI。
  • 清晰的可操作提示:交互对象应让人一看便知可以交互。使用发光效果、轮廓、浮动指示器或细微动画,提示“你可以点击这里”。
  • 声音反馈:玩家的每个重要操作都应产生音频反馈。这能确认操作已生效,也能提升体验质感。
  • 渐进式呈现:不要一次性展示所有信息。随着玩家参与,逐步揭示玩法机制和故事。从简单开始,逐步增加复杂度。
  • 即时反馈:玩家交互时,应在同一帧内作出响应。使用补间动画、声音或 UI 弹窗,让玩家明确知道“操作是否生效”。
  • 无障碍性:使用高对比度文本、易读的字号(屏幕 UI 的 fontSize >= 16),并搭配音频提示。

9. MVP 规划

从核心循环开始

思考:玩家要做什么? 答案应当是一个简单句:

  • “玩家探索房间并寻找隐藏物品。”
  • “玩家与其他玩家在障碍赛道上竞速。”
  • “玩家收集资源并建造结构。”
快速制作原型
  • 先在 1-2 个地块中搭建,即使最终场景会更大
  • 使用基本形状(方块、球体)作为占位符——不要等待最终美术资源
  • 先让核心循环正常运行,再添加任何次要功能
尽早测试
  • 部署到测试世界,亲自走一遍
  • 邀请 2-3 名真实玩家并观察他们(不要解释游戏,看看他们能否自行理解)
  • 衡量:玩家能否在 30 秒内明白该做什么?
迭代打磨乐趣
  • 最后再打磨。如果使用占位美术资源时核心循环都不好玩,更好的美术也无法解决问题
  • 果断删减功能。紧凑的小型体验胜过庞大却未完成的体验
  • 在 DCL 中,可重玩性比内容量更重要(玩家会回到自己喜欢的场景)
MVP 检查清单
  • 核心循环可在 60 秒内上手游玩
  • 支持 1 名玩家游玩,也支持 5 名以上玩家同时游玩
  • 符合目标地块数量对应的场景限制
  • 所有交互都有清晰的视觉 / 音频反馈
  • 玩家无需外部说明即可理解目标

从零开始? 请先查看 create-scene 技能,在设计游戏之前搭建项目框架。

10. 交叉引用

主题技能适用场景
交互性、输入处理、射线投射add-interactivity实现点击处理器、触发器、输入
多人同步、服务器通信multiplayer-sync网络游戏状态、实时同步
屏幕 UI、React-ECS、HUD 元素build-ui构建菜单、记分板、对话框
性能优化、实体/三角形预算optimize-scene详细的优化技巧

此技能重点关注影响实现方案的设计决策和优化约束。有关详细的代码模式,请参阅引用的技能。

评论

支持 Markdown