ネズミスケッチのカスタムブラシ(param / script)を実装準拠で作るためのガイド
JSON単体の .mskp を作る場合、id (ルート) か metadata.id が必要です。
{
"format": "mskp",
"version": "1.0",
"id": "my-pack-id",
"metadata": {
"id": "my-pack-id",
"name": "My Brush Pack"
},
"brushes": []
}
src/js/mskp/mskp-loader.js / src/js/mskp/mskp-validator.js
mode: "param" では baseType が必須です。
{
"id": "water_soft",
"name": "Soft Watercolor",
"mode": "param",
"baseType": "watercolor",
"params": {
"size": 16,
"opacity": 65,
"wetness": 0.7
}
}
pen, ink, chalk, mosaic, watercolor, pastel, pencil, oilImpasto, texturePaint, smudge, wetMix, kaleidoscope, fire, lensFlare, foliage, rain, snow, haze
oil_impasto / lens_flare は実装上の正式名ではありません。oilImpasto / lensFlare です。
mode: "script" は script か scriptFile が必須です。
{
"id": "script_ink_bold",
"name": "Script Ink Bold",
"mode": "script",
"params": { "size": 14, "opacity": 80 },
"script": "api.setPenType('ink'); api.setPenSize(params.size || 12); api.setPenStrength(params.opacity || 70);"
}
{
"brushes": [
{
"id": "script_weather_advanced",
"name": "Advanced Weather",
"mode": "script",
"params": { "style": "storm", "power": 0.8, "size": 14, "angle": 30 },
"scriptFile": "scripts/advanced-weather.js"
}
],
"scripts": {
"scripts/advanced-weather.js": "/* script code */"
}
}
scriptFile は、scripts/... や .js 省略など複数候補で解決されます。
script 実行時は brush, params, api, drawGlobals が使えます。
| API | 用途 |
|---|---|
api.setPenType | ペンタイプ切り替え |
api.setPenSize | 太さ変更 |
api.setPenStrength | 強さ変更 |
api.setWatercolorWetness | 水彩の濡れ具合 |
api.setWeatherDropLength | 雨/雪の長さ |
api.setWeatherDropScatter | 雨/雪の散布 |
api.setOilImpastoHeight | 油絵の高さ |
api.setOilImpastoBlend | 油絵のブレンド |
api.setLensFlareColorTemp | レンズフレア色温度 |
api.setFoliageLeafShape | 葉の形 |
api.applyBaseType(baseType, params) | param適用を内部呼び出し |
api.getParam(key, def) | params取得ヘルパ |
入力状態や描画状態をリアルタイムで取得し、それに応じてブラシ動作を変更できます。
api.getPressure() → 現在の筆圧値(0~1 または 0.22~1.25)api.getVelocity() → ストロークの速度(0~1)api.getPenAngle() → ペンの傾き角度(-180~180度)api.getStrokeLength() → 現在のストロークサンプル数(ステップ数)api.noise(x, y, seed = 0) → Perlin風ノイズ値(0~1)api.random(seed = 0) → シード付き乱数(0~1)api.getPenDynamics() → 全ペン動力学状態 { speed, pressure, dirX, dirY, step, seed }// 例 1: 筆圧に応じてブラシ形状を変える
const pressure = api.getPressure();
api.setPenSize(12 + pressure * 8);
// 例 2: 速度に応じて不透明度を変える
const velocity = api.getVelocity();
api.setPenStrength(Math.round(40 + velocity * 60));
// 例 3: ノイズを使った有機的な散布
const step = api.getStrokeLength();
const noise = api.noise(step * 0.5, 0, 12345);
const scatter = 0.3 + noise * 0.4;
api.setWeatherDropScatter(scatter);
// 例 4: シード付き乱数で再現可能なランダムさ
const seed = api.getStrokeLength() + 42;
const rand = api.random(seed);
const density = Math.round(30 + rand * 70);
api.setWeatherDensity(density);
const clamp = (v, min, max) => Math.max(min, Math.min(max, Number(v) || 0));
const get = (k, d) =>
Object.prototype.hasOwnProperty.call(params, k) ? params[k] : d;
const style = String(get('style', 'storm'));
const power = clamp(get('power', 0.7), 0, 1);
const size = clamp(get('size', 12), 1, 100);
const angle = clamp(get('angle', 25), 0, 360);
if (style === 'storm') {
return {
baseType: 'rain',
params: {
size,
angle,
density: Math.round(40 + power * 60),
length: Math.round(8 + power * 30),
scatter: clamp(0.3 + power * 0.6, 0, 1)
}
};
}
if (style === 'blizzard') {
return {
baseType: 'snow',
params: {
size,
angle,
density: Math.round(20 + power * 50),
length: Math.round(3 + power * 12),
scatter: clamp(0.2 + power * 0.7, 0, 1)
}
};
}
api.applyBaseType('watercolor', {
size,
opacity: 60,
wetness: clamp(0.4 + power * 0.5, 0, 1)
});
api.set... と return { baseType, params } を混ぜると上書き順で混乱しやすいため、どちらかに統一を推奨します。
実ファイルの例は以下を参照してください。
docs/sample-brush-pack.mskp(param中心)docs/sample-script-brush-pack.mskp(inline + scriptFile + 高度分岐)docs/sample-api-phase4-pack.mskp(NEW Phase 4+ Getter API を使用したブラシ)script_ink_bold のような短い script から始め、慣れたら scriptFile 分離 + 分岐テンプレートへ進むのが安全です。
mode があるかmode: "script" の場合、script または scriptFile があるかscriptFile のパスが scripts マップと一致しているかbaseType 名が実装準拠か(例: oilImpasto)