Skip to content

Commit bbaf00b

Browse files
committed
feat(eyes): add inertia to pupil tracking movement
Implemented smooth eye tracking with inertia to make eye movements appear more natural when following the fire. The eyes now gradually move toward their target position rather than instantly snapping to it. - Added pupil tracking properties to the Eye class - Added configuration parameters to control tracking speed and behavior - Modified pupil position calculations to use the tracked positions - Added speed reduction when eyes are touched - Limited the maximum distance pupils can move from centre
1 parent 8326d2d commit bbaf00b

1 file changed

Lines changed: 92 additions & 7 deletions

File tree

game/eyes/eyes.lua

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ local ResourceManager = require("eyes.utils.resource_manager")
1919
---@field reflection table {intensity=number, x=number, y=number} Reflection properties
2020
---@field pupilDilation number Current pupil dilation value (0-1)
2121
---@field isTouching boolean Whether the eye is currently being touched
22+
---@field pupilTarget table {x=number, y=number} Target position for the pupil
23+
---@field pupilCurrent table {x=number, y=number} Current position of the pupil
2224
local Eye = {}
2325
Eye.__index = Eye
2426

@@ -51,6 +53,11 @@ function Eye.new(id, x, y, size, phaseX, phaseY)
5153
}
5254
self.pupilDilation = 0
5355
self.isTouching = false
56+
57+
-- Initialize pupil tracking properties
58+
self.pupilTarget = {x = x, y = y}
59+
self.pupilCurrent = {x = x, y = y}
60+
5461
return self
5562
end
5663

@@ -178,6 +185,23 @@ function Eye:updatePupilDilation(targetDilation, fadeSpeed, dt)
178185
(targetDilation - self.pupilDilation) * dt * fadeSpeed
179186
end
180187

188+
---Updates the tracking position of the pupil with inertia
189+
---@param dt number Delta time
190+
---@param targetX number Target X position
191+
---@param targetY number Target Y position
192+
---@param trackingSpeed number How quickly the pupil tracks the target
193+
function Eye:updatePupilTracking(dt, targetX, targetY, trackingSpeed)
194+
-- Update the target position
195+
self.pupilTarget.x = targetX
196+
self.pupilTarget.y = targetY
197+
198+
-- Move current position toward target with inertia
199+
self.pupilCurrent.x = self.pupilCurrent.x +
200+
(self.pupilTarget.x - self.pupilCurrent.x) * dt * trackingSpeed
201+
self.pupilCurrent.y = self.pupilCurrent.y +
202+
(self.pupilTarget.y - self.pupilCurrent.y) * dt * trackingSpeed
203+
end
204+
181205
-- The public module
182206
local eyes = {
183207
-- Configuration
@@ -208,6 +232,13 @@ local eyes = {
208232
dampingFactor = 0.85, -- How quickly attraction velocity decays
209233
},
210234

235+
-- Eye tracking configuration
236+
tracking = {
237+
speed = 15.0, -- Base speed of eye tracking (higher = faster tracking)
238+
touchedSpeedFactor = 0.3, -- When touched, tracking is this much slower
239+
maxTrackingDistance = 0.5, -- Max percentage of eye size the pupil can move from center
240+
},
241+
211242
-- Reflection state for fire effects
212243
reflection = {
213244
fadeSpeed = 3, -- How quickly reflection fades in/out
@@ -356,21 +387,28 @@ end
356387
---@param eyeY number Y position of the eye
357388
---@param eyeSize number Size of the eye
358389
---@param fadeValue number 0-1 fade value for oscillation
390+
---@param currentPupilX number Current X position of the pupil (for inertia)
391+
---@param currentPupilY number Current Y position of the pupil (for inertia)
359392
---@return number irisX X position of the iris
360393
---@return number irisY Y position of the iris
361394
---@return number pupilX X position of the pupil
362395
---@return number pupilY Y position of the pupil
363396
---@return number angle Direction angle
364-
local function calculatePupilPosition(eyeX, eyeY, eyeSize, fadeValue)
365-
-- Calculate tracking position (where pupil would be when tracking mouse)
397+
local function calculatePupilPosition(eyeX, eyeY, eyeSize, fadeValue, currentPupilX, currentPupilY)
366398
local mouseX, mouseY = love.mouse.getPosition()
367399
local distanceX = mouseX - eyeX
368400
local distanceY = mouseY - eyeY
369-
local distance = math.min(math.sqrt(distanceX^2 + distanceY^2), eyeSize / 2)
401+
402+
-- Calculate the maximum distance the pupil can move
403+
local maxDistance = eyeSize * eyes.tracking.maxTrackingDistance
404+
405+
-- Calculate tracking position
406+
local distance = math.min(math.sqrt(distanceX^2 + distanceY^2), maxDistance)
370407
local angle = math.atan2(distanceY, distanceX)
371408

372-
local trackingX = eyeX + (math.cos(angle) * distance)
373-
local trackingY = eyeY + (math.sin(angle) * distance)
409+
-- Use the current position from the eye's tracking system
410+
local trackingX = currentPupilX
411+
local trackingY = currentPupilY
374412

375413
-- Calculate oscillation position (where pupil would be when eye is touched)
376414
local oscillationRange = eyeSize / 16
@@ -546,8 +584,11 @@ local function drawEye(eye, isOnline)
546584
drawEyeBase(eyeX, eyeY, eyeSize, eyeColor, shadedEyeColor, dirX, dirY)
547585
drawBloodVeins(eyeX, eyeY, eyeSize, fadeValue)
548586

549-
-- Calculate iris and pupil positions
550-
local irisX, irisY, pupilX, pupilY = calculatePupilPosition(eyeX, eyeY, eyeSize, fadeValue)
587+
-- Calculate iris and pupil positions using current tracked position
588+
local irisX, irisY, pupilX, pupilY = calculatePupilPosition(
589+
eyeX, eyeY, eyeSize, fadeValue,
590+
eye.pupilCurrent.x, eye.pupilCurrent.y
591+
)
551592

552593
drawIris(irisX, irisY, eyeSize, pupilColor)
553594
drawPupil(pupilX, pupilY, eyeSize, dilationFactor)
@@ -743,6 +784,50 @@ function eyes.update(dt)
743784
eyes.shakeX = eyes.stateManager.shake.x
744785
eyes.shakeY = eyes.stateManager.shake.y
745786

787+
-- Update eye pupil tracking with inertia
788+
local leftEye = eyes.eyes.left
789+
local rightEye = eyes.eyes.right
790+
local leftEyeX, leftEyeY = leftEye:getPosition()
791+
local rightEyeX, rightEyeY = rightEye:getPosition()
792+
793+
-- Calculate target positions for both eyes
794+
local function calculateTargetPosition(eyeX, eyeY, eyeSize)
795+
local mouseX, mouseY = love.mouse.getPosition()
796+
local distanceX = mouseX - eyeX
797+
local distanceY = mouseY - eyeY
798+
799+
-- Limit how far the pupil can move from center
800+
local maxDistance = eyeSize * eyes.tracking.maxTrackingDistance
801+
local distance = math.sqrt(distanceX^2 + distanceY^2)
802+
803+
if distance > maxDistance then
804+
local factor = maxDistance / distance
805+
distanceX = distanceX * factor
806+
distanceY = distanceY * factor
807+
end
808+
809+
return eyeX + distanceX, eyeY + distanceY
810+
end
811+
812+
-- Calculate tracking speed based on touch state
813+
local leftTrackingSpeed = eyes.tracking.speed
814+
local rightTrackingSpeed = eyes.tracking.speed
815+
816+
if leftEye.isTouching then
817+
leftTrackingSpeed = leftTrackingSpeed * eyes.tracking.touchedSpeedFactor
818+
end
819+
820+
if rightEye.isTouching then
821+
rightTrackingSpeed = rightTrackingSpeed * eyes.tracking.touchedSpeedFactor
822+
end
823+
824+
-- Get target positions and update pupil tracking
825+
local leftTargetX, leftTargetY = calculateTargetPosition(leftEyeX, leftEyeY, leftEye.size)
826+
local rightTargetX, rightTargetY = calculateTargetPosition(rightEyeX, rightEyeY, rightEye.size)
827+
828+
leftEye:updatePupilTracking(dt, leftTargetX, leftTargetY, leftTrackingSpeed)
829+
rightEye:updatePupilTracking(dt, rightTargetX, rightTargetY, rightTrackingSpeed)
830+
746831
-- Update audio system with current state and cursor position
747832
audio:update(dt, {
748833
touching = eyes.stateManager.touching,

0 commit comments

Comments
 (0)