mcpField
T
threejs-game

threejs-game

github.com/natea开发4415 次安装53 周前更新
代码仓库

关于 threejs-game

面向 Three.js 浏览器 3D 游戏开发的技能,涵盖渲染、玩法系统、资源加载与性能优化,并提供实现模式和分类参考资料。

主要功能

  1. 013D 场景与游戏循环搭建
  2. 02玩家控制与输入处理
  3. 03碰撞、物理和生命值系统
  4. 04模型加载与动画
  5. 05渲染和性能优化技术

适用场景

  1. 01开发浏览器 3D 游戏
  2. 02实现移动与碰撞行为
  3. 03加载并制作游戏角色动画
  4. 04提升渲染性能

安装

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

$npx skills add https://github.com/natea/fitfinder --skill threejs-game

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
animation.md
cameras.md
effects.md
game_development.md
geometry.md
getting_started.md
index.md
interactivity.md
lighting.md
loading.md
materials.md
scene_graph.md
textures.md

详细介绍

Three.js 游戏开发技能

使用 Three.js 开发游戏的全面支持,涵盖 WebGL、3D 渲染、游戏机制、物理、动画和基于浏览器的交互式游戏。

何时使用此技能

在以下情况下启用此技能:

  • 使用 Three.js 构建 3D 网页游戏
  • 实现游戏机制(玩家移动、碰撞、计分)
  • 设置摄像机、光照和场景管理
  • 加载 3D 模型(GLTF、OBJ、FBX)
  • 处理用户输入(键盘、鼠标、触摸、游戏手柄)
  • 创建动画和角色控制器
  • 集成物理引擎(Cannon.js、Ammo.js)
  • 优化 3D 游戏性能
  • 使用着色器和材质制作游戏视觉效果

快速参考

基础游戏设置
import * as THREE from 'three';

// Create scene, camera, renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Game loop
function animate(time) {
  requestAnimationFrame(animate);

  // Update game logic here
  updatePlayer(time);
  updateEnemies(time);
  checkCollisions();

  renderer.render(scene, camera);
}

animate();
玩家控制器(第三人称)
class PlayerController {
  constructor(camera, target) {
    this.camera = camera;
    this.target = target;
    this.distance = 10;
    this.height = 5;
    this.rotationSpeed = 0.005;
    this.moveSpeed = 0.1;
  }

  update(input) {
    // Movement
    const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(this.target.quaternion);
    const right = new THREE.Vector3(1, 0, 0).applyQuaternion(this.target.quaternion);

    if (input.forward) this.target.position.add(forward.multiplyScalar(this.moveSpeed));
    if (input.backward) this.target.position.add(forward.multiplyScalar(-this.moveSpeed));
    if (input.left) this.target.position.add(right.multiplyScalar(-this.moveSpeed));
    if (input.right) this.target.position.add(right.multiplyScalar(this.moveSpeed));

    // Rotation
    if (input.rotateLeft) this.target.rotation.y += this.rotationSpeed;
    if (input.rotateRight) this.target.rotation.y -= this.rotationSpeed;

    // Update camera position
    const offset = new THREE.Vector3(0, this.height, this.distance);
    offset.applyQuaternion(this.target.quaternion);
    this.camera.position.copy(this.target.position).add(offset);
    this.camera.lookAt(this.target.position);
  }
}
输入处理
class InputManager {
  constructor() {
    this.keys = {};
    this.mouse = { x: 0, y: 0, buttons: {} };

    window.addEventListener('keydown', (e) => this.keys[e.code] = true);
    window.addEventListener('keyup', (e) => this.keys[e.code] = false);
    window.addEventListener('mousemove', (e) => {
      this.mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      this.mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    });
  }

  getInput() {
    return {
      forward: this.keys['KeyW'] || this.keys['ArrowUp'],
      backward: this.keys['KeyS'] || this.keys['ArrowDown'],
      left: this.keys['KeyA'] || this.keys['ArrowLeft'],
      right: this.keys['KeyD'] || this.keys['ArrowRight'],
      jump: this.keys['Space'],
      action: this.keys['KeyE'],
      rotateLeft: this.keys['KeyQ'],
      rotateRight: this.keys['KeyE']
    };
  }
}
碰撞检测(射线投射)
function checkCollisions(player, obstacles) {
  const raycaster = new THREE.Raycaster();
  const directions = [
    new THREE.Vector3(1, 0, 0),   // right
    new THREE.Vector3(-1, 0, 0),  // left
    new THREE.Vector3(0, 0, 1),   // forward
    new THREE.Vector3(0, 0, -1),  // backward
  ];

  for (const direction of directions) {
    raycaster.set(player.position, direction);
    const intersects = raycaster.intersectObjects(obstacles);

    if (intersects.length > 0 && intersects[0].distance < 1.0) {
      return {
        collision: true,
        object: intersects[0].object,
        distance: intersects[0].distance,
        point: intersects[0].point
      };
    }
  }

  return { collision: false };
}
加载 3D 模型(GLTF)
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

const loader = new GLTFLoader();

function loadCharacter(path) {
  return new Promise((resolve, reject) => {
    loader.load(
      path,
      (gltf) => {
        const model = gltf.scene;
        model.scale.set(1, 1, 1);
        scene.add(model);

        // Setup animations if available
        const mixer = new THREE.AnimationMixer(model);
        const animations = {};
        gltf.animations.forEach(clip => {
          animations[clip.name] = mixer.clipAction(clip);
        });

        resolve({ model, mixer, animations });
      },
      (progress) => {
        console.log(`Loading: ${(progress.loaded / progress.total * 100).toFixed(2)}%`);
      },
      (error) => reject(error)
    );
  });
}

// Usage
const character = await loadCharacter('/models/character.glb');
character.animations.idle.play();
基础物理(重力和跳跃)
class PhysicsBody {
  constructor(mesh) {
    this.mesh = mesh;
    this.velocity = new THREE.Vector3();
    this.onGround = false;
    this.gravity = -9.8;
    this.jumpPower = 5;
  }

  update(deltaTime) {
    // Apply gravity
    if (!this.onGround) {
      this.velocity.y += this.gravity * deltaTime;
    }

    // Apply velocity
    this.mesh.position.add(this.velocity.clone().multiplyScalar(deltaTime));

    // Ground check
    if (this.mesh.position.y <= 0) {
      this.mesh.position.y = 0;
      this.velocity.y = 0;
      this.onGround = true;
    }
  }

  jump() {
    if (this.onGround) {
      this.velocity.y = this.jumpPower;
      this.onGround = false;
    }
  }
}
交互式对象(拾取)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

function onMouseClick(event) {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(interactableObjects);

  if (intersects.length > 0) {
    const object = intersects[0].object;
    object.userData.onInteract?.();
  }
}

window.addEventListener('click', onMouseClick);
生命值和伤害系统
class Entity {
  constructor(mesh, maxHealth) {
    this.mesh = mesh;
    this.maxHealth = maxHealth;
    this.health = maxHealth;
    this.isDead = false;
  }

  takeDamage(amount) {
    if (this.isDead) return;

    this.health = Math.max(0, this.health - amount);

    if (this.health === 0) {
      this.die();
    }

    return this.health;
  }

  heal(amount) {
    this.health = Math.min(this.maxHealth, this.health + amount);
    return this.health;
  }

  die() {
    this.isDead = true;
    this.mesh.visible = false;
    // Trigger death animation, effects, etc.
  }
}

核心概念

场景图
  • 按层级组织游戏对象
  • 使用组管理复杂对象
  • 父子变换
游戏循环
  • 使用 requestAnimationFrame 实现 60 fps
  • 计算时间增量,实现与帧率无关的移动
  • 将更新逻辑与渲染分开
摄像机系统
  • PerspectiveCamera:第一人称/第三人称游戏
  • OrthographicCamera:2D/等距游戏
  • 实现摄像机跟随和平滑过渡
光照
  • AmbientLight:基础照明
  • DirectionalLight:带阴影的日光/月光
  • PointLight:火把、爆炸
  • SpotLight:手电筒、舞台灯
性能优化
  • 对重复对象使用实例化
  • 实现视锥体剔除
  • 对远处对象使用 LOD(细节层次)
  • 尽量减少绘制调用
  • 使用纹理图集
  • 启用阴影贴图优化
资源加载
  • 在游戏开始前预加载所有资源
  • 显示加载进度条
  • 使用 LoadingManager 进行协调
  • 缓存已加载的资源

常见游戏模式

状态机(游戏状态)
class GameStateMachine {
  constructor() {
    this.states = {
      menu: new MenuState(),
      playing: new PlayingState(),
      paused: new PausedState(),
      gameOver: new GameOverState()
    };
    this.currentState = this.states.menu;
  }

  changeState(stateName) {
    this.currentState.exit();
    this.currentState = this.states[stateName];
    this.currentState.enter();
  }

  update(deltaTime) {
    this.currentState.update(deltaTime);
  }
}
对象池
class ObjectPool {
  constructor(factory, initialSize = 10) {
    this.factory = factory;
    this.available = [];
    this.inUse = [];

    for (let i = 0; i < initialSize; i++) {
      this.available.push(factory());
    }
  }

  acquire() {
    let obj = this.available.pop();
    if (!obj) obj = this.factory();
    this.inUse.push(obj);
    return obj;
  }

  release(obj) {
    const index = this.inUse.indexOf(obj);
    if (index > -1) {
      this.inUse.splice(index, 1);
      this.available.push(obj);
    }
  }
}

// Usage
const bulletPool = new ObjectPool(() => createBullet(), 20);
const bullet = bulletPool.acquire();
// ... use bullet
bulletPool.release(bullet);

参考文件

按主题整理的详细文档:

  • getting_started.md - Three.js 基础、设置和基本概念
  • game_development.md - 游戏循环、玩家控制器、游戏机制
  • scene_graph.md - 场景组织、层级、变换
  • materials.md - 材质类型、着色器、视觉效果
  • textures.md - 纹理加载、UV 映射、图集
  • lighting.md - 光源类型、阴影、HDR
  • cameras.md - 摄像机类型、控制、视口管理
  • geometry.md - 内置几何体、自定义几何体、缓冲区
  • loading.md - 资源加载(模型、纹理、音频)
  • animation.md - 动画系统、骨骼动画、补间动画
  • interactivity.md - 射线投射、拾取、UI 集成
  • effects.md - 后期处理、粒子、雾效

资源

官方文档
物理集成
  • Cannon.js:轻量级 3D 物理引擎
  • Ammo.js:完整的 Bullet 物理引擎移植版
  • Rapier:高性能物理引擎
实用库
  • three-mesh-bvh:快速射线投射
  • three-pathfinding:导航网格
  • postprocessing:高级效果

使用此技能

对于初学者
  1. 从基础场景设置开始
  2. 学习坐标系
  3. 理解游戏循环
  4. 先用简单形状练习,再使用模型
对于游戏开发
  1. 规划游戏架构
  2. 首先实现输入处理
  3. 构建简单的玩家控制器
  4. 逐步添加游戏机制
  5. 全程优化性能
对于高级功能
  1. 集成物理引擎
  2. 实现高级着色器
  3. 添加后期处理效果
  4. 构建多人游戏网络

备注

  • Three.js 使用右手坐标系(X 向右,Y 向上,Z 向外)
  • 尽早优化:定期进行性能分析,尽量减少绘制调用
  • 调试时使用开发版本,发布时使用生产版本
  • 为现代浏览器考虑使用 WebGL 2 功能
  • 移动设备的性能需要仔细优化

评论

支持 Markdown