From 6ac091096b485d0cb277cb7cbb50b44c4b059e8c Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Wed, 11 Feb 2026 09:50:12 +0900 Subject: [PATCH] wip --- packages/frontend/assets/room/sfx/grab.mp3 | Bin 0 -> 21941 bytes packages/frontend/assets/room/sfx/put.mp3 | Bin 0 -> 26496 bytes packages/frontend/src/pages/room.vue | 7 ++- packages/frontend/src/utility/room/engine.ts | 52 ++++++++++++++++++- 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 packages/frontend/assets/room/sfx/grab.mp3 create mode 100644 packages/frontend/assets/room/sfx/put.mp3 diff --git a/packages/frontend/assets/room/sfx/grab.mp3 b/packages/frontend/assets/room/sfx/grab.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f064c976d3b91b18fafe5efc68ca633a9cb334f1 GIT binary patch literal 21941 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
}u?)NOM&;Ad$0Y%6 zFqeVu*DBunQ<2_ zcda}&o2Ht@>Nci^fKax{hgS!vPUBZ%A025A+TIL>iUB|tqn?~DVPFH0uhumiq3m=E zI8O&8s-n@~n8%H?uuj-v7V=-~G<;pR;@H0N2>n)$H5^nX*!#%?0CHpdvqp>oI&cd0 zrAmNgnZ-e<{tGbb7sXa~*}~^%Pg8j+d7fhB<0{7b-iz1Mg&CsS3o_Cx&sxM&jf{fg zr%TeVI_y~nB<2RaEU(_O>C4!%SFNk7*Fnhf_f}S?#?q67n={2t@6QD&?q5l29Xg8_ zAxvlYx~my0BC}KpoerM+m^m8Qsg=-E>tMpVR7b~}L{)dTi`rCz=Vg~qsz_S~L*>FOkY{ zH+sP>)gaYiHPghA|Gr{cKsN4I|1dfWAK zc4lTPM9aFnyL3ZDqwrAj&4I@j(w}dy$KQ?i@+0F{UkPPdD_{glcyyD(AQfs}k)s7A z2WoYKkcNbAwZPJ#yQJaKUMR9eYNx7|{@1+DVM=a|dtdC-Vp6W%xBwZ4PBa$Tl+!5o zkIv85@l}Guwk9@>vb}>xuOBrX-D-`u_fLu!1>)$#=K0jlmI&G8uPT4vm}rf4&0)en z+bjtyVB!3H9c4-?hCs})sJgY>q$u(m@n=9ITB_R6&HOYBMslCE^XIAd>9g0hlLh#$ z4cK!dW%##wt89+hDnytmD8u&i45xM3gvnU~+5Wn)()9ckP>ocO6bsS(!D-Kpr2(~c z^DPZ`HKoqMu{6d?oYoU1W){!E5#<&*8cZ>d11~o6k%HL%ZWi?=G!H!`2gM_rFIH^E zf$+Sy=Fc-v1=J{trifpqd|OEQq;JVLoFB&0NwWr+)dL=qj8)kCv<10V2SPmX;s9)q zka1McWC;I5tUnD8WrMtV2BnYrj}da@us|xgZd4J!e~O)1d@3Y7JqXRMrW5V*fZAvK zD^djxO=4mDDlm*90O%BsTVNvFZBb7 z8{svR-(Viq49^ccaXcO!CJzIFc>gAS&bfDz0|e z%-Fhu*;$ZZm{5cwxiC8TV?ln7^wZ!Tn3-t)@iV+Jz)h~1@L?&3Kh74ds>#o8ZNfW@ z+#7Lwt7}{qrO#qjNc9v)6&lyL=1U&247sI-yyajanmWxLu-OCwyH8uK8Q7-?4q~;d z1XpW4pf8H_wH}5e~xqtHk_~)f3 zD)-Mj=KtnBCMx%DJ^=r`^nX1Qe6qs_;^^U@r`aESO#G4i0|qfX2?9jrPJ%Kq#~%cU z%KZU@7@h { type: 'color-box', position: [-135, 0, -5], rotation: [0, 0, 0], + }, { + id: 'r', + type: 'plant2', + position: [135, 0, -135], + rotation: [0, 0, 0], }], }, { canvas: canvas.value!, @@ -172,7 +177,7 @@ function onKeyup(ev: KeyboardEvent) { } function grab() { - engine.grab(); + engine.toggleGrab(); canvas.value!.focus(); } diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index 28c3291063..e1da699362 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -14,6 +14,7 @@ import * as BABYLON from '@babylonjs/core'; import { AxesViewer } from '@babylonjs/core/Debug/axesViewer'; import { registerBuiltInLoaders } from '@babylonjs/loaders/dynamic'; +import * as sound from '@/utility/sound.js'; type RoomDef = { roomType: 'default'; @@ -129,6 +130,7 @@ const OBJECTS = { const ps = new BABYLON.ParticleSystem('', 32, room.scene); ps.particleTexture = new BABYLON.Texture('/client-assets/room/objects/lava-lamp/bubble.png'); ps.emitter = emitter; + ps.isLocal = true; ps.minEmitBox = new BABYLON.Vector3(-1/*cm*/, 0, -1/*cm*/); ps.maxEmitBox = new BABYLON.Vector3(1/*cm*/, 0, 1/*cm*/); ps.minEmitPower = 2; @@ -301,6 +303,8 @@ export class RoomEngine { private time: 0 | 1 | 2 = 2; // 0: 昼, 1: 夕, 2: 夜 private roomCollisionMeshes: BABYLON.AbstractMesh[] = []; private def: RoomDef; + public enableGridSnapping = true; + private putParticleSystem: BABYLON.ParticleSystem; constructor(def: RoomDef, options: { canvas: HTMLCanvasElement; @@ -417,6 +421,26 @@ export class RoomEngine { //}, this.scene, 1, [this.camera]); } + this.putParticleSystem = new BABYLON.ParticleSystem('', 64, this.scene); + this.putParticleSystem.particleTexture = new BABYLON.Texture('/client-assets/room/steam.png'); + this.putParticleSystem.createCylinderEmitter(5/*cm*/, 1/*cm*/, 5/*cm*/); + this.putParticleSystem.minEmitBox = new BABYLON.Vector3(-3/*cm*/, 0, -3/*cm*/); + this.putParticleSystem.maxEmitBox = new BABYLON.Vector3(3/*cm*/, 0, 3/*cm*/); + this.putParticleSystem.minEmitPower = 700; + this.putParticleSystem.maxEmitPower = 1000; + this.putParticleSystem.addVelocityGradient(0, 1); + this.putParticleSystem.addVelocityGradient(1, 0); + this.putParticleSystem.minLifeTime = 0.2; + this.putParticleSystem.maxLifeTime = 0.2; + this.putParticleSystem.minSize = 1/*cm*/; + this.putParticleSystem.maxSize = 4/*cm*/; + this.putParticleSystem.emitRate = 256; + this.putParticleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; + this.putParticleSystem.color1 = new BABYLON.Color4(1, 1, 1, 0.3); + this.putParticleSystem.color2 = new BABYLON.Color4(1, 1, 1, 0.2); + this.putParticleSystem.colorDead = new BABYLON.Color4(1, 1, 1, 0); + this.putParticleSystem.targetStopDuration = 0.05; + let isDragging = false; this.canvas.addEventListener('pointerdown', (ev) => { @@ -454,7 +478,7 @@ export class RoomEngine { if (ev.code === 'KeyE') { ev.preventDefault(); ev.stopPropagation(); - this.grab(); + this.toggleGrab(); } }); @@ -570,6 +594,14 @@ export class RoomEngine { this.grabbing.ghost.position = this.camera.position.add(dir.scale(this.grabbing.startDistance)).add(this.grabbing.startOffset); this.grabbing.ghost.rotation = new BABYLON.Vector3(0, this.camera.rotation.y + this.grabbing.startRotationY, 0); + if (this.enableGridSnapping) { + const scale = 10/*cm*/; + this.grabbing.ghost.position.x = Math.round(this.grabbing.ghost.position.x / scale) * scale; + this.grabbing.ghost.position.y = Math.round(this.grabbing.ghost.position.y / scale) * scale; + this.grabbing.ghost.position.z = Math.round(this.grabbing.ghost.position.z / scale) * scale; + this.grabbing.ghost.rotation.y = Math.round(this.grabbing.ghost.rotation.y / (Math.PI / 4)) * (Math.PI / 4); + } + const stickyObjectIds = Array.from(this.def.objects.filter(o => o.sticky === this.grabbing.mesh.metadata.objectId)).map(o => o.id); let y = 0; @@ -739,7 +771,7 @@ export class RoomEngine { } } - public grab() { + public toggleGrab() { if (this.grabbing != null) { // 親から先に外していく const removeStickyParentRecursively = (mesh: BABYLON.AbstractMesh) => { @@ -753,11 +785,22 @@ export class RoomEngine { } }; removeStickyParentRecursively(this.grabbing.mesh); + const pos = this.grabbing.mesh.position.clone(); this.grabbing.ghost.dispose(false, true); this.grabbing = null; + + sound.playUrl('/client-assets/room/sfx/put.mp3', { + volume: 1, + playbackRate: 1, + }); + + this.putParticleSystem.emitter = pos; + this.putParticleSystem.start(); return; } + if (this.highlightedObjectId == null) return; + const highlightedObject = this.objectMeshs.get(this.highlightedObjectId)!; for (const om of highlightedObject.getChildMeshes()) { om.renderOutline = false; @@ -794,6 +837,11 @@ export class RoomEngine { startDistance: startDistance, ghost: ghost, }; + + sound.playUrl('/client-assets/room/sfx/grab.mp3', { + volume: 1, + playbackRate: 1, + }); } public destroy() {