playcanvas-engine
ブラウザベースのゲームやインタラクティブな3Dアプリケーション、高パフォーマンスが求められるWeb体験を構築する際に活用できる、エンティティ・コンポーネントアーキテクチャとビジュアルエディタ統合を備えた軽量WebGL/WebGPUゲームエンジンです。PlayCanvas・エンティティコンポーネントシステム・WebGLゲーム・3Dブラウザアプリ・リアルタイム3Dレンダリングに関するタスクでトリガーされ、ゲーム特化機能と統合開発環境を備えたThree.jsの代替として利用できます。
description の原文を見る
Lightweight WebGL/WebGPU game engine with entity-component architecture and visual editor integration. Use this skill when building browser-based games, interactive 3D applications, or performance-critical web experiences. Triggers on tasks involving PlayCanvas, entity-component systems, game engine development, WebGL games, 3D browser applications, editor-first workflows, or real-time 3D rendering. Alternative to Three.js with game-specific features and integrated development environment.
SKILL.md 本文
PlayCanvas Engine スキル
Entity-component アーキテクチャ、ビジュアルエディタ統合、パフォーマンス重視設計を備えた軽量 WebGL/WebGPU ゲームエンジン。
このスキルを使うべき時
以下のようなシチュエーションでこのスキルをトリガーします:
- 「PlayCanvas エンジン」
- 「WebGL ゲームエンジン」
- 「entity component system」
- 「PlayCanvas アプリケーション」
- 「3D ブラウザゲーム」
- 「オンライン 3D エディタ」
- 「軽量 3D エンジン」
- エディタファースト ワークフローの必要性
比較対象:
- Three.js: 低レベル、より柔軟だが初期設定が必要
- Babylon.js: 機能豊富だがより重い、エディタはあるが成熟度が低い
- A-Frame: VR フォーカス、宣言的 HTML アプローチ
- PlayCanvas を使う場面: ゲームプロジェクト、エディタファースト ワークフロー、パフォーマンス重視アプリ
コアコンセプト
1. Application
PlayCanvas アプリケーション (Application) はレンダリングループを管理するルートです。
import * as pc from 'playcanvas';
// キャンバスを作成
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
// アプリケーションを作成
const app = new pc.Application(canvas, {
keyboard: new pc.Keyboard(window),
mouse: new pc.Mouse(canvas),
touch: new pc.TouchDevice(canvas),
gamepads: new pc.GamePads()
});
// キャンバスを設定
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
// リサイズを処理
window.addEventListener('resize', () => app.resizeCanvas());
// アプリケーションを開始
app.start();
2. Entity-Component System
PlayCanvas は ECS アーキテクチャを使用します:Entity は Component を含みます。
// Entity を作成
const entity = new pc.Entity('myEntity');
// シーン階層に追加
app.root.addChild(entity);
// Component を追加
entity.addComponent('model', {
type: 'box'
});
entity.addComponent('script');
// トランスフォーム
entity.setPosition(0, 1, 0);
entity.setEulerAngles(0, 45, 0);
entity.setLocalScale(2, 2, 2);
// 親子階層
const parent = new pc.Entity('parent');
const child = new pc.Entity('child');
parent.addChild(child);
3. Update Loop
アプリケーションはアップデートループ中にイベントを発火します。
app.on('update', (dt) => {
// dt はデルタタイム(秒単位)
entity.rotate(0, 10 * dt, 0);
});
app.on('prerender', () => {
// レンダリング前
});
app.on('postrender', () => {
// レンダリング後
});
4. Components
コアコンポーネントは entity の機能を拡張します:
Model Component:
entity.addComponent('model', {
type: 'box', // 'box', 'sphere', 'cylinder', 'cone', 'capsule', 'asset'
material: material,
castShadows: true,
receiveShadows: true
});
Camera Component:
entity.addComponent('camera', {
clearColor: new pc.Color(0.1, 0.2, 0.3),
fov: 45,
nearClip: 0.1,
farClip: 1000,
projection: pc.PROJECTION_PERSPECTIVE // or PROJECTION_ORTHOGRAPHIC
});
Light Component:
entity.addComponent('light', {
type: pc.LIGHTTYPE_DIRECTIONAL, // DIRECTIONAL, POINT, SPOT
color: new pc.Color(1, 1, 1),
intensity: 1,
castShadows: true,
shadowDistance: 50
});
Rigidbody Component (物理演算が必要):
entity.addComponent('rigidbody', {
type: pc.BODYTYPE_DYNAMIC, // STATIC, DYNAMIC, KINEMATIC
mass: 1,
friction: 0.5,
restitution: 0.3
});
entity.addComponent('collision', {
type: 'box',
halfExtents: new pc.Vec3(0.5, 0.5, 0.5)
});
よくあるパターン
パターン 1: 基本シーン設定
カメラ、ライト、モデルを備えた完全なシーンを作成します。
import * as pc from 'playcanvas';
// アプリケーションを初期化
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
const app = new pc.Application(canvas);
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
window.addEventListener('resize', () => app.resizeCanvas());
// カメラを作成
const camera = new pc.Entity('camera');
camera.addComponent('camera', {
clearColor: new pc.Color(0.2, 0.3, 0.4)
});
camera.setPosition(0, 2, 5);
camera.lookAt(0, 0, 0);
app.root.addChild(camera);
// 平行光源を作成
const light = new pc.Entity('light');
light.addComponent('light', {
type: pc.LIGHTTYPE_DIRECTIONAL,
castShadows: true
});
light.setEulerAngles(45, 30, 0);
app.root.addChild(light);
// 地面を作成
const ground = new pc.Entity('ground');
ground.addComponent('model', {
type: 'plane'
});
ground.setLocalScale(10, 1, 10);
app.root.addChild(ground);
// キューブを作成
const cube = new pc.Entity('cube');
cube.addComponent('model', {
type: 'box',
castShadows: true
});
cube.setPosition(0, 1, 0);
app.root.addChild(cube);
// キューブをアニメーション
app.on('update', (dt) => {
cube.rotate(10 * dt, 20 * dt, 30 * dt);
});
app.start();
パターン 2: GLTF モデルの読み込み
アセット管理で外部 3D モデルを読み込みます。
// モデル用アセットを作成
const modelAsset = new pc.Asset('model', 'container', {
url: '/models/character.glb'
});
// アセットレジストリに追加
app.assets.add(modelAsset);
// アセットを読み込み
modelAsset.ready((asset) => {
// 読み込まれたモデルから entity を作成
const entity = asset.resource.instantiateRenderEntity();
app.root.addChild(entity);
// スケールと位置を設定
entity.setLocalScale(2, 2, 2);
entity.setPosition(0, 0, 0);
});
app.assets.load(modelAsset);
エラーハンドリング付き:
modelAsset.ready((asset) => {
console.log('Model loaded:', asset.name);
const entity = asset.resource.instantiateRenderEntity();
app.root.addChild(entity);
});
modelAsset.on('error', (err) => {
console.error('Failed to load model:', err);
});
app.assets.load(modelAsset);
パターン 3: マテリアルとテクスチャ
PBR ワークフローでカスタムマテリアルを作成します。
// マテリアルを作成
const material = new pc.StandardMaterial();
material.diffuse = new pc.Color(1, 0, 0); // 赤
material.metalness = 0.5;
material.gloss = 0.8;
material.update();
// entity に適用
entity.model.material = material;
// テクスチャ付き
const textureAsset = new pc.Asset('diffuse', 'texture', {
url: '/textures/brick_diffuse.jpg'
});
app.assets.add(textureAsset);
app.assets.load(textureAsset);
textureAsset.ready((asset) => {
material.diffuseMap = asset.resource;
material.update();
});
// すべてのマップを備えた PBR マテリアル
const pbrMaterial = new pc.StandardMaterial();
// すべてのテクスチャを読み込み
const textures = {
diffuse: '/textures/albedo.jpg',
normal: '/textures/normal.jpg',
metalness: '/textures/metalness.jpg',
gloss: '/textures/roughness.jpg',
ao: '/textures/ao.jpg'
};
Object.keys(textures).forEach(key => {
const asset = new pc.Asset(key, 'texture', { url: textures[key] });
app.assets.add(asset);
asset.ready((loadedAsset) => {
switch(key) {
case 'diffuse':
pbrMaterial.diffuseMap = loadedAsset.resource;
break;
case 'normal':
pbrMaterial.normalMap = loadedAsset.resource;
break;
case 'metalness':
pbrMaterial.metalnessMap = loadedAsset.resource;
break;
case 'gloss':
pbrMaterial.glossMap = loadedAsset.resource;
break;
case 'ao':
pbrMaterial.aoMap = loadedAsset.resource;
break;
}
pbrMaterial.update();
});
app.assets.load(asset);
});
パターン 4: 物理演算統合
物理シミュレーション用に Ammo.js を使用します。
import * as pc from 'playcanvas';
// Ammo.js で初期化
const app = new pc.Application(canvas, {
keyboard: new pc.Keyboard(window),
mouse: new pc.Mouse(canvas)
});
// Ammo.js を読み込み
const ammoScript = document.createElement('script');
ammoScript.src = 'https://cdn.jsdelivr.net/npm/ammo.js@0.0.10/ammo.js';
document.body.appendChild(ammoScript);
ammoScript.onload = () => {
Ammo().then((AmmoLib) => {
window.Ammo = AmmoLib;
// 静的な地面を作成
const ground = new pc.Entity('ground');
ground.addComponent('model', { type: 'plane' });
ground.setLocalScale(10, 1, 10);
ground.addComponent('rigidbody', {
type: pc.BODYTYPE_STATIC
});
ground.addComponent('collision', {
type: 'box',
halfExtents: new pc.Vec3(5, 0.1, 5)
});
app.root.addChild(ground);
// 動的なキューブを作成
const cube = new pc.Entity('cube');
cube.addComponent('model', { type: 'box' });
cube.setPosition(0, 5, 0);
cube.addComponent('rigidbody', {
type: pc.BODYTYPE_DYNAMIC,
mass: 1,
friction: 0.5,
restitution: 0.5
});
cube.addComponent('collision', {
type: 'box',
halfExtents: new pc.Vec3(0.5, 0.5, 0.5)
});
app.root.addChild(cube);
// 力を適用
cube.rigidbody.applyForce(10, 0, 0);
cube.rigidbody.applyTorque(0, 10, 0);
app.start();
});
};
パターン 5: カスタムスクリプト
再利用可能なスクリプトコンポーネントを作成します。
// スクリプトクラスを定義
const RotateScript = pc.createScript('rotate');
// スクリプト属性 (エディタに公開)
RotateScript.attributes.add('speed', {
type: 'number',
default: 10,
title: 'Rotation Speed'
});
RotateScript.attributes.add('axis', {
type: 'vec3',
default: [0, 1, 0],
title: 'Rotation Axis'
});
// Initialize メソッド
RotateScript.prototype.initialize = function() {
console.log('RotateScript initialized');
};
// Update メソッド (フレームごとに呼び出される)
RotateScript.prototype.update = function(dt) {
this.entity.rotate(
this.axis.x * this.speed * dt,
this.axis.y * this.speed * dt,
this.axis.z * this.speed * dt
);
};
// クリーンアップ
RotateScript.prototype.destroy = function() {
console.log('RotateScript destroyed');
};
// 使用法
const entity = new pc.Entity('rotatingCube');
entity.addComponent('model', { type: 'box' });
entity.addComponent('script');
entity.script.create('rotate', {
attributes: {
speed: 20,
axis: new pc.Vec3(0, 1, 0)
}
});
app.root.addChild(entity);
スクリプトライフサイクルメソッド:
const MyScript = pc.createScript('myScript');
MyScript.prototype.initialize = function() {
// すべてのリソースが読み込まれた後に1回呼ばれる
};
MyScript.prototype.postInitialize = function() {
// すべての entity が初期化された後に呼ばれる
};
MyScript.prototype.update = function(dt) {
// レンダリング前のフレームごとに呼ばれる
};
MyScript.prototype.postUpdate = function(dt) {
// update 後のフレームごとに呼ばれる
};
MyScript.prototype.swap = function(old) {
// ホットリロードのサポート
};
MyScript.prototype.destroy = function() {
// entity が削除される時にクリーンアップ
};
パターン 6: 入力ハンドリング
キーボード、マウス、タッチ入力を処理します。
// キーボード
if (app.keyboard.isPressed(pc.KEY_W)) {
entity.translate(0, 0, -speed * dt);
}
if (app.keyboard.wasPressed(pc.KEY_SPACE)) {
entity.rigidbody.applyImpulse(0, 10, 0);
}
// マウス
app.mouse.on(pc.EVENT_MOUSEDOWN, (event) => {
if (event.button === pc.MOUSEBUTTON_LEFT) {
console.log('Left click at', event.x, event.y);
}
});
app.mouse.on(pc.EVENT_MOUSEMOVE, (event) => {
const dx = event.dx;
const dy = event.dy;
camera.rotate(-dy * 0.2, -dx * 0.2, 0);
});
// タッチ
app.touch.on(pc.EVENT_TOUCHSTART, (event) => {
event.touches.forEach((touch) => {
console.log('Touch at', touch.x, touch.y);
});
});
// レイキャスト (マウスピッキング)
app.mouse.on(pc.EVENT_MOUSEDOWN, (event) => {
const camera = app.root.findByName('camera');
const cameraComponent = camera.camera;
const from = cameraComponent.screenToWorld(
event.x,
event.y,
cameraComponent.nearClip
);
const to = cameraComponent.screenToWorld(
event.x,
event.y,
cameraComponent.farClip
);
const result = app.systems.rigidbody.raycastFirst(from, to);
if (result) {
console.log('Hit:', result.entity.name);
result.entity.model.material.emissive = new pc.Color(1, 0, 0);
}
});
パターン 7: アニメーション
スケルタルアニメーションとトゥイーンを再生します。
スケルタルアニメーション:
// アニメーション付きモデルを読み込み
const modelAsset = new pc.Asset('character', 'container', {
url: '/models/character.glb'
});
app.assets.add(modelAsset);
modelAsset.ready((asset) => {
const entity = asset.resource.instantiateRenderEntity();
app.root.addChild(entity);
// アニメーションコンポーネントを取得
entity.addComponent('animation', {
assets: [asset],
speed: 1.0,
loop: true,
activate: true
});
// 特定のアニメーションを再生
entity.animation.play('Walk', 0.2); // 0.2s ブレンド時間
// 後で Run に遷移
entity.animation.play('Run', 0.5);
});
app.assets.load(modelAsset);
プロパティトゥイーン:
// 位置をアニメーション
entity.tween(entity.getLocalPosition())
.to({ x: 5, y: 2, z: 0 }, 2.0, pc.SineInOut)
.start();
// 回転をアニメーション
entity.tween(entity.getLocalEulerAngles())
.to({ x: 0, y: 180, z: 0 }, 1.0, pc.Linear)
.loop(true)
.yoyo(true)
.start();
// マテリアルカラーをアニメーション
const color = material.emissive;
app.tween(color)
.to(new pc.Color(1, 0, 0), 1.0, pc.SineInOut)
.yoyo(true)
.loop(true)
.start();
// トゥイーンをチェーン
entity.tween(entity.getLocalPosition())
.to({ y: 2 }, 1.0)
.to({ y: 0 }, 1.0)
.delay(0.5)
.repeat(3)
.start();
統合パターン
統合パターン 1: React 統合
PlayCanvas を React コンポーネントでラップします。
import React, { useEffect, useRef } from 'react';
import * as pc from 'playcanvas';
function PlayCanvasScene() {
const canvasRef = useRef(null);
const appRef = useRef(null);
useEffect(() => {
// 初期化
const app = new pc.Application(canvasRef.current);
appRef.current = app;
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
// シーンを作成
const camera = new pc.Entity('camera');
camera.addComponent('camera', {
clearColor: new pc.Color(0.1, 0.2, 0.3)
});
camera.setPosition(0, 0, 5);
app.root.addChild(camera);
const cube = new pc.Entity('cube');
cube.addComponent('model', { type: 'box' });
app.root.addChild(cube);
const light = new pc.Entity('light');
light.addComponent('light');
light.setEulerAngles(45, 0, 0);
app.root.addChild(light);
app.on('update', (dt) => {
cube.rotate(10 * dt, 20 * dt, 30 * dt);
});
app.start();
// クリーンアップ
return () => {
app.destroy();
};
}, []);
return (
<canvas
ref={canvasRef}
style={{ width: '100%', height: '100vh' }}
/>
);
}
export default PlayCanvasScene;
統合パターン 2: エディタエクスポート
PlayCanvas Editor プロジェクトで作業します。
// PlayCanvas Editor からエクスポート
// ビルドファイルをダウンロードしてコードで読み込みます:
import * as pc from 'playcanvas';
const app = new pc.Application(canvas);
// エクスポートされたプロジェクト設定を読み込み
fetch('/config.json')
.then(response => response.json())
.then(config => {
// シーンを読み込み
app.scenes.loadSceneHierarchy(config.scene_url, (err, parent) => {
if (err) {
console.error('Failed to load scene:', err);
return;
}
// アプリケーションを開始
app.start();
// 名前で entity を検索
const player = app.root.findByName('Player');
const enemy = app.root.findByName('Enemy');
// スクリプトにアクセス
player.script.myScript.doSomething();
});
});
パフォーマンス最適化
1. オブジェクトプーリング
entity を作成/削除する代わりに再利用します。
class EntityPool {
constructor(app, count) {
this.app = app;
this.pool = [];
this.active = [];
for (let i = 0; i < count; i++) {
const entity = new pc.Entity('pooled');
entity.addComponent('model', { type: 'box' });
entity.enabled = false;
app.root.addChild(entity);
this.pool.push(entity);
}
}
spawn(position) {
let entity = this.pool.pop();
if (!entity) {
// プールが尽きた、新規作成
entity = new pc.Entity('pooled');
entity.addComponent('model', { type: 'box' });
this.app.root.addChild(entity);
}
entity.enabled = true;
entity.setPosition(position);
this.active.push(entity);
return entity;
}
despawn(entity) {
entity.enabled = false;
const index = this.active.indexOf(entity);
if (index > -1) {
this.active.splice(index, 1);
this.pool.push(entity);
}
}
}
// 使用法
const pool = new EntityPool(app, 100);
const bullet = pool.spawn(new pc.Vec3(0, 0, 0));
// 後で
pool.despawn(bullet);
2. LOD (Level of Detail)
遠くのオブジェクトのジオメトリを減らします。
// 手動 LOD 切り替え
app.on('update', () => {
const distance = camera.getPosition().distance(entity.getPosition());
if (distance < 10) {
entity.model.asset = highResModel;
} else if (distance < 50) {
entity.model.asset = mediumResModel;
} else {
entity.model.asset = lowResModel;
}
});
// または遠い entity を無効化
app.on('update', () => {
entities.forEach(entity => {
const distance = camera.getPosition().distance(entity.getPosition());
entity.enabled = distance < 100;
});
});
3. バッチング
静的メッシュを結合してドローコールを減らします。
// entity に対して静的バッチングを有効化
entity.model.batchGroupId = 1;
// 同じグループ ID を持つすべての entity をバッチ処理
app.batcher.generate([entity1, entity2, entity3]);
4. テクスチャ圧縮
圧縮テクスチャフォーマットを使用します。
// テクスチャを作成する際、圧縮フォーマットを使用
const texture = new pc.Texture(app.graphicsDevice, {
width: 512,
height: 512,
format: pc.PIXELFORMAT_DXT5, // GPU 圧縮
minFilter: pc.FILTER_LINEAR_MIPMAP_LINEAR,
magFilter: pc.FILTER_LINEAR,
mipmaps: true
});
よくある落とし穴
落とし穴 1: アプリケーション開始を忘れた
問題: シーンはレンダリングされるが何も起こりません。
// ❌ 間違い - 開始を忘れた
const app = new pc.Application(canvas);
// ... entity を作成 ...
// 何も起こりません!
// ✅ 正しい
const app = new pc.Application(canvas);
// ... entity を作成 ...
app.start(); // 重要!
落とし穴 2: Update 中に Entity を変更
問題: イテレーション中にシーングラフを変更します。
// ❌ 間違い - イテレーション中に配列を変更
app.on('update', () => {
entities.forEach(entity => {
if (entity.shouldDestroy) {
entity.destroy(); // 配列を変更!
}
});
});
// ✅ 正しい - 削除対象を記録し、後でクリーンアップ
const toDestroy = [];
app.on('update', () => {
entities.forEach(entity => {
if (entity.shouldDestroy) {
toDestroy.push(entity);
}
});
});
app.on('postUpdate', () => {
toDestroy.forEach(entity => entity.destroy());
toDestroy.length = 0;
});
落とし穴 3: アセットのメモリリーク
問題: 読み込まれたアセットがクリーンアップされません。
// ❌ 間違い - アセットが永遠にメモリに残る
function loadModel() {
const asset = new pc.Asset('model', 'container', { url: '/model.glb' });
app.assets.add(asset);
app.assets.load(asset);
// アセットは永遠にメモリに残ります
}
// ✅ 正しい - 使い終わったらクリーンアップ
function loadModel() {
const asset = new pc.Asset('model', 'container', { url: '/model.glb' });
app.assets.add(asset);
asset.ready(() => {
// モデルを使用
});
app.assets.load(asset);
// 後でクリーンアップ
return () => {
app.assets.remove(asset);
asset.unload();
};
}
const cleanup = loadModel();
// 後で: cleanup();
落とし穴 4: 不正なトランスフォーム階層
問題: トランスフォームが正しく伝播しません。
// ❌ 間違い - 子に対してワールドトランスフォームを設定
const parent = new pc.Entity();
const child = new pc.Entity();
parent.addChild(child);
child.setPosition(5, 0, 0); // ローカル位置
parent.setPosition(10, 0, 0);
// 子は世界座標系で (15, 0, 0) にあります
// ✅ 正しい - ローカルとワールドの違いを理解
child.setLocalPosition(5, 0, 0); // 明確にローカル
// または
const worldPos = new pc.Vec3(15, 0, 0);
child.setPosition(worldPos); // 明確にワールド
落とし穴 5: 物理演算が初期化されていない
問題: 物理演算コンポーネントが機能しません。
// ❌ 間違い - Ammo.js が読み込まれていない
const entity = new pc.Entity();
entity.addComponent('rigidbody', { type: pc.BODYTYPE_DYNAMIC });
// エラー: Ammo is not defined
// ✅ 正しい - Ammo.js が読み込まれていることを確認
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/ammo.js@0.0.10/ammo.js';
document.body.appendChild(script);
script.onload = () => {
Ammo().then((AmmoLib) => {
window.Ammo = AmmoLib;
// 今、物理演算が機能します
const entity = new pc.Entity();
entity.addComponent('rigidbody', { type: pc.BODYTYPE_DYNAMIC });
entity.addComponent('collision', { type: 'box' });
});
};
落とし穴 6: キャンバスサイズの問題
問題: キャンバスがコンテナを埋めたり、リサイズに対応しません。
// ❌ 間違い - 固定サイズのキャンバス
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
// ✅ 正しい - レスポンシブキャンバス
const canvas = document.createElement('canvas');
const app = new pc.Application(canvas);
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
window.addEventListener('resize', () => app.resizeCanvas());
リソース
- Official API: https://api.playcanvas.com/
- Developer Docs: https://developer.playcanvas.com/
- Examples: https://playcanvas.github.io/
- Editor: https://playcanvas.com/
- GitHub: https://github.com/playcanvas/engine
- Forum: https://forum.playcanvas.com/
クイックリファレンス
Application の設定
const app = new pc.Application(canvas);
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);
app.start();
Entity の作成
const entity = new pc.Entity('name');
entity.addComponent('model', { type: 'box' });
entity.setPosition(x, y, z);
app.root.addChild(entity);
Update ループ
app.on('update', (dt) => {
// ロジックをここに記述
});
アセットの読み込み
const asset = new pc.Asset('name', 'type', { url: '/path' });
app.assets.add(asset);
asset.ready(() => { /* アセットを使用 */ });
app.assets.load(asset);
関連スキル: より低レベルの WebGL コントロールについては threejs-webgl を参照してください。React 統合パターンについては react-three-fiber を参照してください。物理演算が重要なシミュレーションについては babylonjs-engine を参照してください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- freshtechbro
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/freshtechbro/claudedesignskills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。