# Vtj Resource Management

> Loads and manages 3D assets via the Resources system. Use when adding models, textures, or fonts; declare in sources.js and access via core:ready event. Use when this capability is needed.

- Skill: `tomevault-io/vtj-resource-management` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/vtj-resource-management`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/vtj-resource-management/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/vtj-resource-management

---


# vite-threejs Resource Management

## Overview

所有资源（模型、纹理、字体等）通过 **Resources** 类统一加载。资源在 `sources.js` 中声明，加载完成后通过 `this.experience.resources.items` 访问。

**核心原则**：在 sources.js 声明，通过 items 访问，在 `core:ready` 事件后使用。

## When to Use

- 添加新模型（GLB/GLTF/FBX/OBJ）
- 添加新纹理（PNG/JPG/HDR/EXR/KTX2）
- 添加字体、音频、视频等资源
- 需要等待资源加载完成后执行逻辑

## 资源声明 (sources.js)

所有资源在 `src/js/sources.js` 中统一声明：

```javascript
// src/js/sources.js
export default [
  {
    name: 'playerModel',        // 资源名称（用于访问）
    type: 'gltfModel',          // 资源类型
    path: 'models/character/player.glb',  // 相对于 public/ 的路径
  },
  {
    name: 'grass_block_top_texture',
    type: 'texture',
    path: 'textures/blocks/grass_block_top.png',
  },
  // ...
]
```

## 支持的资源类型

| type | 加载器 | 用途 | 示例路径 |
|------|--------|------|----------|
| `gltfModel` | GLTFLoader | GLB/GLTF 模型 | `models/xxx.glb` |
| `fbxModel` | FBXLoader | FBX 模型 | `models/xxx.fbx` |
| `objModel` | OBJLoader | OBJ 模型 | `models/xxx.obj` |
| `texture` | TextureLoader | 普通纹理 | `textures/xxx.png` |
| `hdrTexture` | RGBELoader | HDR 环境贴图 | `textures/xxx.hdr` |
| `exrTexture` | EXRLoader | EXR 高动态范围 | `textures/xxx.exr` |
| `ktx2Texture` | KTX2Loader | KTX2 压缩纹理 | `textures/xxx.ktx2` |
| `cubeTexture` | CubeTextureLoader | 立方体贴图 | `['px.jpg', 'nx.jpg', ...]` |
| `font` | FontLoader | 字体文件 | `fonts/xxx.json` |
| `audio` | AudioLoader | 音频文件 | `audio/xxx.mp3` |
| `svg` | SVGLoader | SVG 文件 | `images/xxx.svg` |
| `video` | VideoTexture | 视频纹理 | `videos/xxx.mp4` |

## 访问已加载资源

```javascript
// 在组件中访问资源
constructor() {
  this.experience = new Experience()
  this.resources = this.experience.resources
}

// 通过 items[name] 访问
const texture = this.resources.items['grass_block_top_texture']
const model = this.resources.items['playerModel']
```

### GLTF 模型结构

```javascript
const gltf = this.resources.items['playerModel']

// 完整 GLTF 对象
gltf.scene        // THREE.Group - 场景根节点
gltf.animations   // AnimationClip[] - 动画列表
gltf.cameras      // Camera[] - 相机（如有）
gltf.asset        // 元数据

// 添加到场景
this.scene.add(gltf.scene)

// 克隆模型（多实例）
const clone = gltf.scene.clone()
```

### 纹理配置

```javascript
const texture = this.resources.items['grass_block_top_texture']

// 常见纹理配置
texture.colorSpace = THREE.SRGBColorSpace  // 颜色空间
texture.wrapS = THREE.RepeatWrapping       // 水平重复
texture.wrapT = THREE.RepeatWrapping       // 垂直重复
texture.minFilter = THREE.NearestFilter    // 像素风格
texture.magFilter = THREE.NearestFilter
texture.generateMipmaps = false            // 像素风格关闭 mipmap
```

## 等待资源加载完成

资源全部加载完成后触发 `core:ready` 事件：

```javascript
import emitter from './utils/event-bus.js'

// 在需要等待资源的地方
emitter.on('core:ready', () => {
  // 此时所有资源已加载完成
  this.initWithResources()
})
```

### 典型模式：World 初始化

```javascript
// src/js/world/world.js
export default class World {
  constructor() {
    this.experience = new Experience()
    this.resources = this.experience.resources

    // 等待资源加载完成
    emitter.on('core:ready', () => {
      // 初始化需要资源的组件
      this.player = new Player()
      this.environment = new Environment()
      this.chunkManager = new ChunkManager()
    })
  }
}
```

## 添加新资源步骤

### 1. 放置文件

```
public/
├── models/
│   └── character/
│       └── my-model.glb      ← 新模型
├── textures/
│   └── blocks/
│       └── my-texture.png    ← 新纹理
```

### 2. 声明资源

```javascript
// src/js/sources.js
export default [
  // ... 现有资源
  
  // 添加新资源
  {
    name: 'myModel',
    type: 'gltfModel',
    path: 'models/character/my-model.glb',
  },
  {
    name: 'myTexture',
    type: 'texture',
    path: 'textures/blocks/my-texture.png',
  },
]
```

### 3. 使用资源

```javascript
// 在组件中（确保在 core:ready 后）
const model = this.resources.items['myModel']
const texture = this.resources.items['myTexture']
```

## 资源命名规范

| 类型 | 命名模式 | 示例 |
|------|----------|------|
| 模型 | `xxxModel` | `playerModel`, `steveModel` |
| 纹理 | `xxx_texture` 或描述性 | `grass_block_top_texture`, `dirt` |
| HDR | `xxxHDRTexture` | `environmentMapHDRTexture` |
| 天空盒 | `sky_xxxTexture` | `sky_sunriseTexture` |
| 植物 | `xxx_plant_Texture` | `dandelion_plant_Texture` |
| 破坏阶段 | `destroy_stage_N` | `destroy_stage_0` ~ `destroy_stage_9` |

## 加载进度

```javascript
// Resources 提供加载进度
this.resources.loadProgress  // 0.0 ~ 1.0
this.resources.isLoaded      // boolean
this.resources.loaded        // 已加载数量
this.resources.toLoad        // 总数量
```

## Common Mistakes

### ❌ 在 core:ready 之前访问资源

```javascript
// BAD: 构造函数中直接访问（资源可能未加载）
constructor() {
  this.model = this.resources.items['playerModel']  // ❌ 可能是 undefined
}

// GOOD: 在 core:ready 回调中访问
constructor() {
  emitter.on('core:ready', () => {
    this.model = this.resources.items['playerModel']  // ✅
  })
}
```

### ❌ 忘记在 sources.js 声明

```javascript
// BAD: 直接使用加载器（绕过资源系统）
const loader = new GLTFLoader()
loader.load('models/xxx.glb', (gltf) => { ... })

// GOOD: 在 sources.js 声明
// sources.js
{ name: 'xxx', type: 'gltfModel', path: 'models/xxx.glb' }

// 组件中
const gltf = this.resources.items['xxx']
```

### ❌ 资源名称拼写错误

```javascript
// BAD: 名称不匹配
// sources.js 中是 'playerModel'
const model = this.resources.items['player_model']  // ❌ undefined

// GOOD: 名称完全一致
const model = this.resources.items['playerModel']   // ✅
```

### ❌ 路径不以 public/ 为根

```javascript
// BAD: 使用绝对路径或 src/ 路径
{ path: '/public/models/xxx.glb' }     // ❌
{ path: 'src/assets/models/xxx.glb' }  // ❌

// GOOD: 相对于 public/ 目录
{ path: 'models/xxx.glb' }             // ✅
```

### ❌ 忘记配置像素风格纹理

```javascript
// BAD: 模糊的像素纹理（Minecraft 风格需要锐利边缘）
const texture = this.resources.items['grass']
// 使用默认 filter，导致模糊

// GOOD: 配置 NearestFilter
const texture = this.resources.items['grass']
texture.minFilter = THREE.NearestFilter
texture.magFilter = THREE.NearestFilter
texture.generateMipmaps = false
```

## Quick Reference

| 操作 | 代码 |
|------|------|
| 声明资源 | `sources.js`: `{ name, type, path }` |
| 访问资源 | `this.resources.items['name']` |
| 等待加载 | `emitter.on('core:ready', () => {})` |
| 检查进度 | `this.resources.loadProgress` |
| GLTF 场景 | `gltf.scene` |
| GLTF 动画 | `gltf.animations` |

| 资源类型 | type 值 |
|----------|---------|
| GLB/GLTF | `gltfModel` |
| 普通纹理 | `texture` |
| HDR | `hdrTexture` |
| 字体 | `font` |
| 音频 | `audio` |

---
> Source: [hexianWeb/Third-Person-MC](https://github.com/hexianWeb/Third-Person-MC) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-24 -->

