zeroed-some/dougk / a782e54

Browse files

lerp rotation towards target direction, turn before moving, move in facing direction

Authored by espadonne
SHA
a782e547317d120cc83604d386d3f22359b9c5f3
Parents
4b1b2b6
Tree
e509703

1 changed file

StatusFile+-
M src/renderers/three/duck.js 35 11
src/renderers/three/duck.jsmodified
@@ -140,6 +140,7 @@ export function createDoug(scene, gradientMap) {
140140
     position: new THREE.Vector3(0, 0, 0),
141141
     targetPosition: new THREE.Vector3(0, 0, 0),
142142
     rotation: 0,
143
+    targetRotation: 0,
143144
     mode: 'idle', // 'idle', 'waiting', 'swimming'
144145
     waitTimer: 0,
145146
     waitDuration: 0,
@@ -152,6 +153,16 @@ export function createDoug(scene, gradientMap) {
152153
   // Movement speeds
153154
   const idleSpeed = 0.5
154155
   const swimSpeed = 1.2
156
+  const turnSpeed = 4 // radians per second
157
+
158
+  // Helper to lerp angles properly (handles wraparound)
159
+  function lerpAngle(from, to, t) {
160
+    let diff = to - from
161
+    // Normalize to -PI to PI
162
+    while (diff > Math.PI) diff -= Math.PI * 2
163
+    while (diff < -Math.PI) diff += Math.PI * 2
164
+    return from + diff * t
165
+  }
155166
 
156167
   function pickIdleTarget(pond) {
157168
     const angle = Math.random() * Math.PI * 2
@@ -206,19 +217,32 @@ export function createDoug(scene, gradientMap) {
206217
     const dist = Math.sqrt(dx * dx + dz * dz)
207218
 
208219
     if (dist > 0.1) {
209
-      const speed = state.mode === 'swimming' ? swimSpeed : idleSpeed
210
-      const moveAmount = Math.min(speed * delta, dist)
211
-      const moveX = (dx / dist) * moveAmount
212
-      const moveZ = (dz / dist) * moveAmount
220
+      // Calculate desired rotation to face target
221
+      state.targetRotation = Math.atan2(dx, dz)
222
+
223
+      // Smoothly turn toward target direction
224
+      state.rotation = lerpAngle(state.rotation, state.targetRotation, turnSpeed * delta)
225
+
226
+      // Check if we're facing roughly the right direction (within ~30 degrees)
227
+      let angleDiff = Math.abs(state.targetRotation - state.rotation)
228
+      while (angleDiff > Math.PI) angleDiff -= Math.PI * 2
229
+      angleDiff = Math.abs(angleDiff)
213230
 
214
-      state.position.x += moveX
215
-      state.position.z += moveZ
231
+      // Only move forward if facing the right way
232
+      if (angleDiff < 0.5) {
233
+        const speed = state.mode === 'swimming' ? swimSpeed : idleSpeed
234
+        const moveAmount = Math.min(speed * delta, dist)
216235
 
217
-      // Face movement direction
218
-      state.rotation = Math.atan2(dz, dx)
236
+        // Move in the direction Doug is FACING (not toward target directly)
237
+        const moveX = Math.sin(state.rotation) * moveAmount
238
+        const moveZ = Math.cos(state.rotation) * moveAmount
219239
 
220
-      // Wobble animation while moving
221
-      state.wobble += delta * 8
240
+        state.position.x += moveX
241
+        state.position.z += moveZ
242
+
243
+        // Wobble animation while moving
244
+        state.wobble += delta * 8
245
+      }
222246
     } else {
223247
       // Arrived
224248
       if (state.mode === 'swimming') {
@@ -254,7 +278,7 @@ export function createDoug(scene, gradientMap) {
254278
     group.position.y = Math.sin(elapsed * 2) * 0.03
255279
 
256280
     // Rotation (face direction of movement)
257
-    group.rotation.y = -state.rotation + Math.PI / 2
281
+    group.rotation.y = state.rotation
258282
 
259283
     // Body wobble while swimming
260284
     const wobbleAmount = Math.sin(state.wobble) * 0.08