@@ -188,9 +188,26 @@ export function createDonny(scene, gradientMap) { |
| 188 | 188 | group.rotation.y = angle + Math.PI / 2 // Face outward-ish |
| 189 | 189 | } |
| 190 | 190 | |
| 191 | | - function update(delta, elapsed, pond) { |
| 191 | + // Helper to smoothly interpolate angles |
| 192 | + function lerpAngle(from, to, t) { |
| 193 | + let diff = to - from |
| 194 | + while (diff > Math.PI) diff -= Math.PI * 2 |
| 195 | + while (diff < -Math.PI) diff += Math.PI * 2 |
| 196 | + return from + diff * t |
| 197 | + } |
| 198 | + |
| 199 | + function update(delta, elapsed, pond, doug) { |
| 192 | 200 | state.timer += delta |
| 193 | 201 | |
| 202 | + // Calculate angle to face Doug |
| 203 | + let angleToDoug = 0 |
| 204 | + if (doug) { |
| 205 | + const dougPos = doug.getPosition() |
| 206 | + const dx = dougPos.x - group.position.x |
| 207 | + const dz = dougPos.z - group.position.z |
| 208 | + angleToDoug = Math.atan2(dx, dz) |
| 209 | + } |
| 210 | + |
| 194 | 211 | switch (state.mode) { |
| 195 | 212 | case 'waiting': |
| 196 | 213 | if (state.timer >= 60) { |
@@ -211,6 +228,8 @@ export function createDonny(scene, gradientMap) { |
| 211 | 228 | state.timer = 0 |
| 212 | 229 | group.visible = true |
| 213 | 230 | group.position.y = -2 |
| 231 | + // Start facing Doug |
| 232 | + group.rotation.y = angleToDoug |
| 214 | 233 | } |
| 215 | 234 | break |
| 216 | 235 | |
@@ -220,6 +239,12 @@ export function createDonny(scene, gradientMap) { |
| 220 | 239 | const easeOut = 1 - Math.pow(1 - emergeProgress, 3) |
| 221 | 240 | group.position.y = -2 + easeOut * 2.3 // Rise to 0.3 above water |
| 222 | 241 | |
| 242 | + // Tilt upward as emerging (nose up!) |
| 243 | + group.rotation.x = -0.3 * easeOut |
| 244 | + |
| 245 | + // Slowly turn toward Doug - lugubrious, not laser tracking |
| 246 | + group.rotation.y = lerpAngle(group.rotation.y, angleToDoug, delta * 0.3) |
| 247 | + |
| 223 | 248 | // Gentle rocking as emerging |
| 224 | 249 | group.rotation.z = Math.sin(state.timer * 4) * 0.1 |
| 225 | 250 | |
@@ -235,6 +260,10 @@ export function createDonny(scene, gradientMap) { |
| 235 | 260 | group.position.y = 0.3 + Math.sin(elapsed * 2) * 0.08 |
| 236 | 261 | group.rotation.z = Math.sin(elapsed * 1.5) * 0.05 |
| 237 | 262 | |
| 263 | + // Keep tilted upward, slowly drifting gaze toward Doug |
| 264 | + group.rotation.x = -0.25 + Math.sin(elapsed * 1.5) * 0.05 |
| 265 | + group.rotation.y = lerpAngle(group.rotation.y, angleToDoug, delta * 0.2) |
| 266 | + |
| 238 | 267 | // Gentle flipper animation |
| 239 | 268 | leftFlipper.rotation.z = 2.2 + Math.sin(elapsed * 3) * 0.15 |
| 240 | 269 | rightFlipper.rotation.z = 2.2 + Math.sin(elapsed * 3 + 0.5) * 0.15 |
@@ -259,6 +288,9 @@ export function createDonny(scene, gradientMap) { |
| 259 | 288 | const easeIn = Math.pow(submergeProgress, 2) |
| 260 | 289 | group.position.y = 0.3 - easeIn * 2.5 |
| 261 | 290 | |
| 291 | + // Tilt nose down as submerging |
| 292 | + group.rotation.x = -0.25 + easeIn * 0.4 |
| 293 | + |
| 262 | 294 | // Add bubbles/ripples as submerging |
| 263 | 295 | if (Math.random() < delta * 4) { |
| 264 | 296 | pond.addRipple( |
@@ -272,6 +304,7 @@ export function createDonny(scene, gradientMap) { |
| 272 | 304 | state.timer = 0 |
| 273 | 305 | group.visible = false |
| 274 | 306 | group.position.y = -3 |
| 307 | + group.rotation.x = 0 |
| 275 | 308 | } |
| 276 | 309 | break |
| 277 | 310 | } |