From 09d8514b99e45c058425d55b13d861e7f00b4da8 Mon Sep 17 00:00:00 2001 From: gm Date: Sat, 25 Jul 2026 22:25:31 +0900 Subject: [PATCH] =?UTF-8?q?=EC=82=AC=EA=B3=BC=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=EB=A5=BC=20=EB=82=98=EB=AD=87=EC=9E=8E=20?= =?UTF-8?q?=ED=91=9C=EB=A9=B4=20=EB=9E=9C=EB=8D=A4=20=EC=A7=80=EC=A0=90?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C,=20=EB=8B=A4=EB=9E=8C=EC=A5=90=20=EB=8F=84?= =?UTF-8?q?=EC=A3=BC=20=EB=B0=A9=ED=96=A5=EC=9D=84=20=EC=A2=8C=EC=9A=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EA=B3=A0=20=EB=82=98?= =?UTF-8?q?=EB=AD=87=EC=9E=8E=20=EB=AA=A8=EC=96=91=EC=9D=84=20=EB=8B=A8?= =?UTF-8?q?=EC=88=9C=20=EC=82=AC=EA=B0=81=ED=98=95=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=9E=8E=20=EC=8B=A4=EB=A3=A8=EC=97=A3=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- index.html | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/index.html b/index.html index 851ba57..1de4f62 100644 --- a/index.html +++ b/index.html @@ -243,16 +243,12 @@ basketShadow.position.set(BASKET_POS.x, 0.005, BASKET_POS.z); scene.add(basketShadow); // ---------- apples ---------- -const appleHomes = [ - new THREE.Vector3(1.55, 2.75, 0.65), - new THREE.Vector3(-1.5, 2.65, -0.55), - new THREE.Vector3(0.25, 3.75, 0.85), - new THREE.Vector3(0.95, 1.95, -1.25), - new THREE.Vector3(-0.85, 1.9, 1.15), - new THREE.Vector3(1.1, 3.15, -0.55), - new THREE.Vector3(-1.05, 3.05, 0.3), - new THREE.Vector3(0.15, 2.4, 1.35), -]; +const APPLE_COUNT = 8; +function randomAppleHome(){ + const spec = foliageSpecs[Math.floor(Math.random() * foliageSpecs.length)]; + const dir = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.2, Math.random() - 0.5).normalize(); + return new THREE.Vector3(spec.pos[0], spec.pos[1], spec.pos[2]).addScaledVector(dir, spec.r * 0.92); +} const appleGeo = new THREE.SphereGeometry(0.16, 10, 8); const appleMat = new THREE.MeshStandardMaterial({ color: 0xd1453b, roughness: 0.5, flatShading:true }); const stemGeo = new THREE.CylinderGeometry(0.015, 0.02, 0.14, 5); @@ -274,8 +270,10 @@ const LEAF_LINGER = 1.8; // how long a fallen leaf rests before fading away const SQUIRREL_COOLDOWN = 6; // min seconds between squirrel appearances // a tree "slot" holds at most one apple at a time and regrows independently of -// whatever happens to the apple once it falls (caught in the basket or not) -const treeSlots = appleHomes.map((home) => { +// whatever happens to the apple once it falls (caught in the basket or not); +// each grow picks a fresh random spot on the foliage rather than a fixed home +const treeSlots = Array.from({ length: APPLE_COUNT }, () => { + const home = randomAppleHome(); const mesh = createAppleMesh(); mesh.position.copy(home); treeGroup.add(mesh); @@ -287,12 +285,21 @@ const basketApples = []; // apples caught and resting in the basket let basketCount = 0; // ---------- leaves ---------- -const leafGeo = new THREE.PlaneGeometry(0.14, 0.18); +const leafShape = new THREE.Shape(); +leafShape.moveTo(0, -0.09); +leafShape.quadraticCurveTo(0.07, -0.04, 0.065, 0.03); +leafShape.quadraticCurveTo(0.05, 0.08, 0, 0.11); +leafShape.quadraticCurveTo(-0.05, 0.08, -0.065, 0.03); +leafShape.quadraticCurveTo(-0.07, -0.04, 0, -0.09); +const leafGeo = new THREE.ShapeGeometry(leafShape); const leafMats = [0x4f9d6e, 0x3f8a5d, 0x5aab77].map( (c) => new THREE.MeshStandardMaterial({ color: c, roughness: 0.85, side: THREE.DoubleSide }) ); function createLeafMesh(){ - return new THREE.Mesh(leafGeo, leafMats[Math.floor(Math.random() * leafMats.length)]); + const mesh = new THREE.Mesh(leafGeo, leafMats[Math.floor(Math.random() * leafMats.length)]); + const s = 0.8 + Math.random() * 0.35; + mesh.scale.set(s, s, s); + return mesh; } const fallingLeaves = []; // falling | resting | vanishing @@ -379,7 +386,9 @@ function knockOffSquirrel(){ const shadow = makeShadowBlob(0.55); scene.add(shadow); - const fleeAngle = Math.random() * Math.PI * 2; + // camera looks down roughly -Z, so world X is screen left/right — flee sideways + const fleeSign = Math.random() < 0.5 ? -1 : 1; + const fleeDir = new THREE.Vector3(fleeSign, 0, (Math.random() - 0.5) * 0.35).normalize(); fallingSquirrels.push({ mesh, shadow, @@ -388,7 +397,7 @@ function knockOffSquirrel(){ 0.6 + Math.random() * 0.4, (Math.random() - 0.5) * 1.2 ), - fleeDir: new THREE.Vector3(Math.sin(fleeAngle), 0, Math.cos(fleeAngle)), + fleeDir, state: 'falling', timer: 0, }); @@ -485,6 +494,7 @@ function knockOffRandomApple(){ } function growSlotApple(slot){ + slot.home = randomAppleHome(); const mesh = createAppleMesh(); mesh.position.copy(slot.home); mesh.scale.setScalar(0.001);