aboutsummaryrefslogtreecommitdiffhomepage
path: root/public/bluebey-studio/src/panel.js
diff options
context:
space:
mode:
authorYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-10-03 15:16:11 +0900
committerYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-10-03 15:16:11 +0900
commit20e722e5ab241d0a2153a3fdcff8fc8fddf1361e (patch)
treeecc8ddeb0b7ce4e47f30d43c5b77d30c7540eb02 /public/bluebey-studio/src/panel.js
parentc6d1df09c66bcfe8388651866d26ed5d05b4d2d2 (diff)
public/bluebey-studio: 見えない壁を2枚にし、ぶるべーに追従させる
見えない壁は2枚まで置けるようにした(壁1・壁2)。板の位置はぶるべーからの相対で持ち、ぶるべーを動かすと壁も一緒に動くようにした。 地面(影を受ける板)が深度を書かないようにし、ぶるべーが地面より下に来ても体やテクスチャが隠れないようにした。
Diffstat (limited to 'public/bluebey-studio/src/panel.js')
-rw-r--r--public/bluebey-studio/src/panel.js81
1 files changed, 48 insertions, 33 deletions
diff --git a/public/bluebey-studio/src/panel.js b/public/bluebey-studio/src/panel.js
index 72718bc..e9764c1 100644
--- a/public/bluebey-studio/src/panel.js
+++ b/public/bluebey-studio/src/panel.js
@@ -2066,50 +2066,63 @@ export function buildPanel(app, root) {
/* ------------------------------------------------------------- 見えない壁 */
- // 見えない壁: place a clip plane and bury the character in it, so only the
- // part in front of the wall still shows. It is the same cut the camera's own
- // far plane makes, put where you want it (see src/clip.js).
+ // 見えない壁: place up to two finite "boards" and bury the character in them, so
+ // only the part in front still shows. A wall's position is kept *relative to the
+ // character*, so moving ぶるべー carries its walls along (see src/clip.js).
const wallSection = details(stageSection.body, '見えない壁');
- const wallConfig = () => (state.render.wall = state.render.wall ?? defaultState().render.wall);
wallSection.add(hint('見えない板(有限の壁)を置くと、その向こう側が隠れます。'
+ 'ぶるべーを壁に埋め込むと、体の一部だけが見えるようになります。'
+ + '板は2枚まで置けます。板の位置はぶるべーからの相対なので、ぶるべーを動かすと壁も一緒に動きます。'
+ '板そのものは透明です(下の「板を表示」をオンにしたときだけ見えます)。'));
- const wallToggle = check({
- label: '見えない壁で一部を隠す',
- value: false,
- onChange: (value) => { wallConfig().on = value; app.actions.refresh('render'); sync(); },
- });
- const wallSliders = {
- x: slider({ label: 'よこ(X)', min: -4, max: 4, step: 0.05, value: 0, format: (v) => v.toFixed(2), onInput: (v) => { wallConfig().x = v; app.actions.refresh('render'); sync(); } }),
- y: slider({ label: 'たかさ(Y)', min: -2, max: 4, step: 0.05, value: 0, format: (v) => v.toFixed(2), onInput: (v) => { wallConfig().y = v; app.actions.refresh('render'); sync(); } }),
- z: slider({ label: 'おくゆき(Z)', min: -4, max: 4, step: 0.05, value: 0, format: (v) => v.toFixed(2), onInput: (v) => { wallConfig().z = v; app.actions.refresh('render'); sync(); } }),
- yaw: slider({ label: '向き(左右)', min: -180, max: 180, step: 1, value: 0, format: (v) => `${Math.round(v)}°`, onInput: (v) => { wallConfig().yaw = v; app.actions.refresh('render'); sync(); } }),
- tilt: slider({ label: '傾き(前後)', min: -90, max: 90, step: 1, value: 0, format: (v) => `${Math.round(v)}°`, onInput: (v) => { wallConfig().tilt = v; app.actions.refresh('render'); sync(); } }),
- size: slider({ label: '大きさ', min: 0.2, max: 8, step: 0.1, value: 1, format: (v) => v.toFixed(1), onInput: (v) => { wallConfig().size = v; app.actions.refresh('render'); sync(); } }),
- };
- wallSection.add(wallToggle.el);
- for (const widget of Object.values(wallSliders)) wallSection.add(widget.el);
+ const WALL_SLOTS = [
+ { key: 'wall', label: '壁 1' },
+ { key: 'wall2', label: '壁 2' },
+ ];
- // The wall writes depth but no colour, so it hides what is behind it without
- // being visible. The guide is the tinted copy of that same rectangle, shown
- // only while placing - and it is also the switch that says "I am placing the
- // wall now", so with it off the wall cannot swallow an orbit drag.
- const wallGuideToggle = check({
- label: '板を表示(ビューポートでドラッグして動かせます)',
- value: false,
- onChange: (value) => { wallConfig().guide = value; app.actions.refresh('render'); sync(); },
+ const wallGroups = WALL_SLOTS.map(({ key, label }) => {
+ const box = details(wallSection.body, label);
+ const cfg = () => (state.render[key] = state.render[key] ?? defaultState().render[key]);
+ const toggle = check({
+ label: `${label}で一部を隠す`,
+ value: false,
+ onChange: (value) => { cfg().on = value; app.actions.refresh('render'); sync(); },
+ });
+ box.add(controlRow(null, toggle.el, { wide: true }));
+ const sliders = {
+ x: slider({ label: 'よこ(X)', min: -4, max: 4, step: 0.05, value: 0, format: (v) => v.toFixed(2), onInput: (v) => { cfg().x = v; app.actions.refresh('render'); sync(); } }),
+ y: slider({ label: 'たかさ(Y)', min: -2, max: 4, step: 0.05, value: 0, format: (v) => v.toFixed(2), onInput: (v) => { cfg().y = v; app.actions.refresh('render'); sync(); } }),
+ z: slider({ label: 'おくゆき(Z)', min: -4, max: 4, step: 0.05, value: 0, format: (v) => v.toFixed(2), onInput: (v) => { cfg().z = v; app.actions.refresh('render'); sync(); } }),
+ yaw: slider({ label: '向き(左右)', min: -180, max: 180, step: 1, value: 0, format: (v) => `${Math.round(v)}°`, onInput: (v) => { cfg().yaw = v; app.actions.refresh('render'); sync(); } }),
+ tilt: slider({ label: '傾き(前後)', min: -90, max: 90, step: 1, value: 0, format: (v) => `${Math.round(v)}°`, onInput: (v) => { cfg().tilt = v; app.actions.refresh('render'); sync(); } }),
+ size: slider({ label: '大きさ', min: 0.2, max: 8, step: 0.1, value: 1, format: (v) => v.toFixed(1), onInput: (v) => { cfg().size = v; app.actions.refresh('render'); sync(); } }),
+ };
+ for (const widget of Object.values(sliders)) box.add(widget.el);
+
+ // The wall writes depth but no colour, so it hides what is behind it without
+ // being visible. The guide is the tinted copy of that same rectangle, shown
+ // only while placing - and it is also the switch that says "I am placing the
+ // wall now", so with it off the wall cannot swallow an orbit drag.
+ const guide = check({
+ label: '板を表示(ビューポートでドラッグして動かせます)',
+ value: false,
+ onChange: (value) => { cfg().guide = value; app.actions.refresh('render'); sync(); },
+ });
+ box.add(controlRow(null, guide.el, { wide: true }));
+ return { key, label, toggle, sliders, guide };
});
- wallSection.add(controlRow(null, wallGuideToggle.el, { wide: true }));
+
wallSection.add(hint('板は「大きさ」の四角い壁そのもので、**その後ろ側が隠れます**'
+ '(カメラから見て板の後ろになる部分が隠れるので、カメラを回すと見え方も変わります)。'
- + '「板を表示」を切ると板は完全に透明になり、カメラ操作のじゃまもしません。'));
+ + '「板を表示」を切ると板は完全に透明になり、カメラ操作のじゃまもしません。'
+ + '「板を表示」した板は、ビューポートでドラッグしても動かせます。'));
wallSection.add(buttons({
items: [{
id: 'wallReset',
label: '見えない壁をリセット',
onClick: () => {
state.render.wall = defaultState().render.wall;
+ state.render.wall2 = defaultState().render.wall2;
app.actions.refresh('render');
sync();
},
@@ -2622,10 +2635,12 @@ export function buildPanel(app, root) {
});
syncers.push(() => {
- const wall = state.render?.wall ?? defaultState().render.wall;
- wallToggle.set(wall.on === true);
- for (const [key, widget] of Object.entries(wallSliders)) widget.set(wall[key] ?? (key === 'size' ? 1 : 0));
- wallGuideToggle.set(wall.guide === true);
+ for (const group of wallGroups) {
+ const wall = state.render?.[group.key] ?? defaultState().render[group.key];
+ group.toggle.set(wall.on === true);
+ for (const [key, widget] of Object.entries(group.sliders)) widget.set(wall[key] ?? (key === 'size' ? 1 : 0));
+ group.guide.set(wall.guide === true);
+ }
});
syncers.push(() => {