mcpField
B
blockbench-plugins

blockbench-plugins

github.com/jasonjgardner開発26661 回インストール5 週間前に更新
リポジトリ

blockbench-plugins について

BlockbenchのAPIを使ったプラグイン開発を支援するスキルです。UI、モデル編集、イベント、インポート/エクスポート、後処理、バージョンに応じた検証を案内します。

主な機能

  1. 01アクション・ダイアログ・サイドパネル
  2. 02Undo対応のモデル編集
  3. 03イベント購読と後処理
  4. 04カスタムインポート/エクスポート用Codec
  5. 05ホストのバージョンとAPIの確認

活用シーン

  1. 01メニューやツールバーへのコマンド追加
  2. 02プラグイン用サイドパネルの作成
  3. 03モデル形式のインポートまたはエクスポート実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jasonjgardner/blockbench-mcp-project --skill blockbench-plugins

インストール後、Claude Code で/blockbench-pluginsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
format_plugin.js
plugin_template.js
references/
api.md
elements.md
events.md

詳細

Blockbench プラグイン開発

概要

Blockbench はデスクトップとウェブで動作し、ホスト API と同梱のレンダリング/UI ライブラリをプラグインに公開します。API を選ぶ前に、対象となるホストのバージョンと型を確認してください。プラグイン変数が分離されていても、ネイティブモジュールやファイルシステムへ無制限にアクセスできるわけではありません。古い Wiki の例とホストの動作が異なる場合は、最新の生成された API リファレンスと対象のチェックアウトを優先してください。実際に検証した環境に合わせて、variant と min_version を設定してください。

モデル形式の選択、UV 単位、リグの違い、エクスポート時の受け渡しについては、形式と受け渡しのガイダンスを参照してください。形式の機能フラグはホストの動作を示すものであり、カスタムコーデックに不足している検証や実行時のシリアライズを実装するものではありません。

クイックリファレンス

タスク方法
クリック可能なコマンドを作成するnew Action() - メニュー/ツールバーに追加
フォーム/ダイアログを表示するフォームフィールドを指定して new Dialog() を使用
サイドバーパネルを追加するVue コンポーネントを使用して new Panel() を作成
モデル要素を変更するUndo.initEdit() → 変更 → Undo.finishEdit() を使用
カスタムのインポート/エクスポートnew Codec() + new ModelFormat()
変更に応答するBlockbench.on('event_name', callback)

プラグインのファイル構成

plugins/
└── my_plugin/
    ├── my_plugin.js      # Main file (required, ID must match filename)
    ├── about.md          # Extended docs (optional)
    └── icon.png          # 48x48 icon (optional)

プラグイン登録テンプレート

(function() {
    // Store references for cleanup
    let myAction, myPanel, myDialog, eventCallback;
    
    Plugin.register('my_plugin', {
        title: 'My Plugin',
        author: 'Author Name',
        description: 'Short description',
        icon: 'extension',            // Material icon name
        version: '1.0.0',
        variant: 'both',              // 'desktop', 'web', or 'both'
        min_version: '4.8.0',
        tags: ['Utility'],
        
        onload() {
            // Initialize all components here
        },
        
        onunload() {
            // CRITICAL: Delete ALL components here
        },
        
        oninstall() {
            Blockbench.showQuickMessage('Installed!');
        }
    });
})();

アクション

アクションは、メニュー、ツールバー、キー割り当てから実行できるクリック可能なコマンドです。

myAction = new Action('my_action_id', {
    name: 'Action Name',
    description: 'Tooltip text',
    icon: 'star',
    category: 'edit',                 // For keybind settings
    
    condition: () => Cube.selected.length > 0,
    
    keybind: new Keybind({ key: 'k', ctrl: true }),
    
    click(event) {
        // Action logic
    }
});

MenuBar.addAction(myAction, 'filter');  // Add to Filter menu

// Cleanup
myAction.delete();

メニューの配置先: 'file'、'edit'、'transform'、'filter'、'tools'、'view'、'help'

アクションのバリエーション:

// Toggle (on/off state)
new Toggle('toggle_id', {
    name: 'Feature',
    default: false,
    onChange(value) { /* handle */ }
});

// Tool (viewport interaction)
new Tool('tool_id', {
    name: 'My Tool',
    cursor: 'crosshair',
    onCanvasClick(data) { /* handle */ },
    onCanvasDrag(data) { /* handle */ }
});

ダイアログ

myDialog = new Dialog({
    id: 'my_dialog',
    title: 'Dialog Title',
    width: 540,
    
    form: {
        name: { label: 'Name', type: 'text', value: 'default' },
        count: { label: 'Count', type: 'number', value: 10, min: 1, max: 100 },
        enabled: { label: 'Enabled', type: 'checkbox', value: true },
        mode: {
            label: 'Mode',
            type: 'select',
            options: { a: 'Option A', b: 'Option B' },
            value: 'a'
        },
        color: { label: 'Color', type: 'color', value: '#ff0000' },
        // Conditional field
        advanced: {
            label: 'Advanced',
            type: 'text',
            condition: (form) => form.enabled
        }
    },
    
    onConfirm(formData) {
        console.log(formData.name, formData.count);
        this.hide();
    }
});

myDialog.show();

// Quick dialogs
Blockbench.textPrompt('Enter Value', 'default', (text) => { });
Blockbench.showMessageBox({ title: 'Alert', message: 'Text', buttons: ['OK'] });

パネル

パネルは Vue コンポーネントを使用してサイドバーに表示されます。

myPanel = new Panel('my_panel', {
    name: 'My Panel',
    icon: 'dashboard',
    condition: () => Format.animation_mode,
    
    default_position: {
        slot: 'left_bar',             // 'left_bar', 'right_bar', 'bottom'
        height: 300
    },
    
    component: {
        template: `
            <div>
                <h3>{{ title }}</h3>
                <ul><li v-for="item in items">{{ item.name }}</li></ul>
                <button @click="refresh">Refresh</button>
            </div>
        `,
        data() {
            return { title: 'Items', items: [] };
        },
        methods: {
            refresh() {
                this.items = Cube.selected.map(c => ({ name: c.name }));
            }
        }
    }
});

// Cleanup
myPanel.delete();

モデルの操作(Undo 対応)

永続的なプロジェクトの編集は、影響を受けるすべての要素を含め、ネイティブの Undo に登録してください。トランザクションを開始する前に対象を検証し、非同期の入力を準備してください。失敗した場合は、トランザクションをキャンセルするか、状態を復元してください。ネイティブアクションやペイント API が独自のトランザクションを管理する場合は、競合するネストしたトランザクションを追加しないでください。読み取り専用の確認、カメラの移動、ファイルへの書き込みは、Undo 対応のモデル変更とは異なります。Undo のドキュメントを参照してください。

// Start tracking
Undo.initEdit({ elements: Cube.selected });

// Modify elements
Cube.selected.forEach(cube => {
    cube.from[0] += 5;
    cube.to[1] = 20;
    cube.rotation[1] = 45;
});

// Update view
Canvas.updateView({
    elements: Cube.selected,
    element_aspects: { geometry: true, transform: true }
});

// Commit
Undo.finishEdit('Move cubes');
要素の作成

作成した新しいオブジェクトは、空の初期リストだけでなく、トランザクションの完了時に記録する必要があります。

Undo.initEdit({ elements: [], outliner: true });
const createdCube = new Cube({ name: 'body_geo', from: [-4, 0, -2], to: [4, 12, 2] }).init();
Undo.finishEdit('Add body cube', { elements: [createdCube], outliner: true });
Canvas.updateAll();

以下のスニペットは、ホストの個別のコンストラクターの使用例です。既存のプロジェクトを変更する場合は、適切なトランザクション内に配置してください。グループの原点はピボットです。親子関係を設定しても、その原点がキューブの境界に移動量として加算されることはありません。

// Cube
let cube = new Cube({
    name: 'my_cube',
    from: [0, 0, 0],
    to: [16, 16, 16],
    origin: [8, 8, 8],
    rotation: [0, 45, 0]
}).init();
cube.addTo(Group.selected[0]);  // Add to group

// Group (bone)
let group = new Group({
    name: 'bone_arm',
    origin: [0, 12, 0]
}).init();
group.addTo();  // Add to root

// Texture
let texture = new Texture({ name: 'my_texture' });
texture.fromPath('/path/to/file.png');  // or .fromDataURL()
texture.add(true);  // true = add to undo
グローバルコレクション
コレクション説明
Cube.all / Cube.selectedすべてのキューブ/選択中のキューブ
Group.all / Group.selectedすべてのグループ/選択中のグループ
Mesh.all / Mesh.selectedすべてのメッシュ/選択中のメッシュ
Texture.all / Texture.selectedすべてのテクスチャ/選択中のテクスチャ
Animation.all / Animation.selectedすべてのアニメーション/選択中のアニメーション
Outliner.elementsアウトライナーのすべての要素

イベントシステム

// Subscribe
eventCallback = (data) => { /* handle */ };
Blockbench.on('update_selection', eventCallback);

// Unsubscribe (use SAME function reference)
Blockbench.removeListener('update_selection', eventCallback);

一般的なイベント: update_selection、select_project、new_project、load_project、save_project、close_project、add_cube、add_group、add_texture、add_animation、select_animation、render_frame、undo、redo、finish_edit

一覧については references/events.md を参照してください。

カスタムメニュー

let menu = new Menu([
    'existing_action_id',
    myAction,
    '_',  // Separator
    {
        name: 'Custom Item',
        icon: 'star',
        click() { /* handle */ }
    },
    {
        name: 'Submenu',
        children: [ /* more items */ ]
    }
]);

menu.open(event);  // Open at mouse position

形式とコーデック(インポート/エクスポート)

const myCodec = new Codec('my_codec', {
    name: 'My Format',
    extension: 'mymodel',
    
    compile(options) {
        // Model → file content
        let data = { bones: [] };
        Group.all.forEach(g => {
            data.bones.push({
                name: g.name,
                pivot: g.origin,
                cubes: g.children.filter(c => c instanceof Cube).map(c => ({
                    from: c.from, to: c.to
                }))
            });
        });
        return JSON.stringify(data, null, 2);
    },
    
    parse(content, path) {
        // File content → model
        let data = JSON.parse(content);
        newProject(myFormat);
        data.bones.forEach(b => {
            let group = new Group({ name: b.name, origin: b.pivot }).init();
            b.cubes.forEach(c => {
                new Cube({ from: c.from, to: c.to }).init().addTo(group);
            });
        });
        Canvas.updateAll();
    }
});

const myFormat = new ModelFormat('my_format', {
    id: 'my_format',
    name: 'My Format',
    icon: 'icon_name',
    codec: myCodec,
    box_uv: true,
    bone_rig: true,
    animation_mode: true
});

// Cleanup
myFormat.delete();
myCodec.delete();

条件パターン

// Function
condition: () => Format.id === 'bedrock' && Cube.selected.length > 0

// Object (combined with AND)
condition: {
    formats: ['bedrock', 'java_block'],
    modes: ['edit', 'paint'],
    project: true,
    selected: { cube: 1 },
    method: () => customCheck()
}

コードスタイルの規約

種類規約例
クラスPascalCaseOutlinerElement、Animation
メソッドcamelCaseupdateTransform、getSelectedFaces
プロパティsnake_casedata_points、uv_offset
イベント/アクション IDsnake_caseselect_project、my_action

重要なクリーンアップパターン

メモリリークを防ぐため、onunload() ですべてのコンポーネントを必ず削除してください。

let action, panel, dialog, toolbar, format, codec, eventCallback, css;

Plugin.register('my_plugin', {
    onload() {
        action = new Action('id', { /* ... */ });
        panel = new Panel('id', { /* ... */ });
        format = new ModelFormat('id', { /* ... */ });
        codec = new Codec('id', { /* ... */ });
        
        eventCallback = (data) => { };
        Blockbench.on('update_selection', eventCallback);
        
        css = Blockbench.addCSS('.my-class { color: blue; }');
    },
    
    onunload() {
        action.delete();
        panel.delete();
        format.delete();
        codec.delete();
        Blockbench.removeListener('update_selection', eventCallback);
        css.delete();
    }
});

アンチパターン

// ❌ No stored reference = memory leak
onload() { new Action('leaked', { }); }

// ❌ No Undo = users can't reverse
Cube.selected.forEach(c => c.from[0] += 1);

// ❌ Bundling THREE.js (already global)
import * as THREE from 'three';

// ❌ Global pollution
window.myData = {};

組み込みライブラリ(バンドルしないでください)

  • THREE — Three.js の 3D レンダリング
  • Vue — ホストが提供するリアクティブ UI。ホストがサポートするコンポーネント API に合わせてください
  • JSZip — ZIP の処理
  • marked — Markdown の解析
  • Molang — Molang 式パーサー

アイコン

icon: 'star'              // Material icon
icon: 'fa-bone'           // Font Awesome
icon: 'icon-player'       // Blockbench custom
icon: 'data:image/...'    // Base64 image

TypeScript のセットアップ

npm i --save-dev blockbench-types
/// <reference types="blockbench-types" />

テンプレートファイル

  • assets/plugin_template.js — プラグインの完全なスターター
  • assets/format_plugin.js — カスタム形式/コーデックの例

参考資料

代表的なプロジェクトを使ってプラグインの編集内容を検証してください。期待されるデータ/プレビュー、元に戻す操作の後にやり直す操作を行った結果、アンロード/再読み込み時のクリーンアップを確認します。形式/コーデックでは、代表的なエクスポートと再インポートを確認してください。サポートされている場合は、UV 寸法、階層、名前、テクスチャ参照、アニメーションの動作も確認します。コンパイルが成功しても、ネイティブのエディター状態やシリアライズされた出力は検証されません。

コメント