Code edit (17 edits merged)
Please save this source code
User prompt
switch bypassTracking to false
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
switch bypassTracking to true
Code edit (11 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'head')' in or related to this line: 'self.scales = {' Line Number: 238
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'head')' in or related to this line: 'self.scales = {' Line Number: 224
Code edit (1 edits merged)
Please save this source code
Code edit (8 edits merged)
Please save this source code
User prompt
switch bypassTracking to false
User prompt
switch bypassTracking to true
Code edit (1 edits merged)
Please save this source code
Code edit (18 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'parse')' in or related to this line: 'previousFacekit = JSON.parse(JSON.stringify(currentKit));' Line Number: 609
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'parse')' in or related to this line: 'previousFacekit = JSON.parse(JSON.stringify(currentKit));' Line Number: 600
Code edit (1 edits merged)
Please save this source code
Code edit (18 edits merged)
Please save this source code
User prompt
in debugMode, console log y, elementOffset.minY * eyeDistance, elementOffset.maxY * eyeDistance when element is lowerLip every second
User prompt
in debugMode, console log y, elementOffset.minY * eyeDistance, elementOffset.maxY * eyeDistance when element is lowerLip
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
User prompt
along with console log of facekit, write in instructionText coordinates of main elements concisely: le: x,y / re: x,y / ul: x,y / ll: x/y (don't write decimals)
Code edit (4 edits merged)
Please save this source code
User prompt
in debugMode call console.log(facekit) every second
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var DebugPoints = Container.expand(function () {
var self = Container.call(this);
// Create points for face tracking debugging
self.points = {
leftEye: self.attachAsset('debugFacePoints', {
anchorX: 0.5,
anchorY: 0.5
}),
rightEye: self.attachAsset('debugFacePoints', {
anchorX: 0.5,
anchorY: 0.5
}),
noseTip: self.attachAsset('debugFacePoints', {
anchorX: 0.5,
anchorY: 0.5
}),
mouthCenter: self.attachAsset('debugFacePoints', {
anchorX: 0.5,
anchorY: 0.5
}),
upperLip: self.attachAsset('debugFacePoints', {
anchorX: 0.5,
anchorY: 0.5
}),
lowerLip: self.attachAsset('debugFacePoints', {
anchorX: 0.5,
anchorY: 0.5
}),
chin: self.attachAsset('debugFacePoints', {
anchorX: 0.5,
anchorY: 0.5
})
};
// Update debug points to match face points
self.update = function (kit) {
if (!kit) {
return;
}
if (kit.leftEye) {
self.points.leftEye.x = kit.leftEye.x;
self.points.leftEye.y = kit.leftEye.y;
}
if (kit.rightEye) {
self.points.rightEye.x = kit.rightEye.x;
self.points.rightEye.y = kit.rightEye.y;
}
if (kit.noseTip) {
self.points.noseTip.x = kit.noseTip.x;
self.points.noseTip.y = kit.noseTip.y;
}
if (kit.mouthCenter) {
self.points.mouthCenter.x = kit.mouthCenter.x;
self.points.mouthCenter.y = kit.mouthCenter.y;
}
if (kit.upperLip) {
self.points.upperLip.x = kit.upperLip.x;
self.points.upperLip.y = kit.upperLip.y;
}
if (kit.lowerLip) {
self.points.lowerLip.x = kit.lowerLip.x;
self.points.lowerLip.y = kit.lowerLip.y;
}
if (kit.chin) {
self.points.chin.x = kit.chin.x;
self.points.chin.y = kit.chin.y;
}
};
return self;
});
var TrollFace = Container.expand(function () {
var self = Container.call(this);
self.scales = {
head: {
x: 1,
y: 1
},
eye: {
x: 1,
y: 1
},
lip: {
x: 1,
y: 1
}
};
// Properties
self.currentStyle = 1;
self.currentOffsets = trollFaceOffsets[self.currentStyle - 1];
self.elements = {};
// Initialize cached position objects to reuse
self.positionCache = {
head: {
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
},
leftEye: {
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
},
rightEye: {
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
},
upperLip: {
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
},
lowerLip: {
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
}
};
// Initialize the troll face elements
self.initialize = function (style) {
// Clear previous elements
self.removeAllElements();
// Create new elements for the selected style
self.createFaceElements(style || self.currentStyle);
};
// Create face elements for a specific style
self.createFaceElements = function (style) {
// Create head
self.elements.head = self.attachAsset('trollHead' + style, {
anchorX: 0.5,
anchorY: 0.5,
scale: 1,
alpha: 1 // DEBUG
});
// Create eyes
self.elements.leftEye = self.attachAsset('trollLeftEye' + style, {
anchorX: 0.5,
anchorY: 0.5,
scale: 1
});
self.elements.rightEye = self.attachAsset('trollRightEye' + style, {
anchorX: 0.5,
anchorY: 0.5,
scale: 1
});
// Create upper lip
self.elements.upperLip = self.attachAsset('trollUpperLip' + style, {
anchorX: 0.5,
anchorY: 0.5,
scale: 1
});
// Create lower lip
self.elements.lowerLip = self.attachAsset('trollLowerLip' + style, {
anchorX: 0.5,
anchorY: 0.5,
scale: 1
});
};
// Remove all face elements
self.removeAllElements = function () {
if (self.elements.head) {
self.removeChild(self.elements.head);
}
if (self.elements.leftEye) {
self.removeChild(self.elements.leftEye);
}
if (self.elements.rightEye) {
self.removeChild(self.elements.rightEye);
}
if (self.elements.upperLip) {
self.removeChild(self.elements.upperLip);
}
if (self.elements.lowerLip) {
self.removeChild(self.elements.lowerLip);
}
};
// Change to the next troll face style
self.nextStyle = function (forcedStyle) {
self.currentStyle = forcedStyle || self.currentStyle % 3 + 1;
self.initialize();
self.currentOffsets = trollFaceOffsets[self.currentStyle - 1];
// Calculate face scale once and reuse
var baseScale = self.calculateFaceScale(facekit);
// Calculate all scales upfront
self.scales = {
head: {
x: baseScale * self.currentOffsets.head.sx,
y: baseScale * self.currentOffsets.head.sy
},
eye: {
x: baseScale * self.currentOffsets.leftEye.sx,
y: baseScale * self.currentOffsets.leftEye.sy
},
lip: {
x: baseScale * self.currentOffsets.upperLip.sx,
y: baseScale * self.currentOffsets.upperLip.sy
}
};
return self.currentStyle;
};
// Helper function to clamp a value between min and max
self.clampPosition = function (value, min, max) {
return Math.min(Math.max(value, min), max);
};
// Helper function to ensure scale is an object
self.ensureScaleIsObject = function (element) {
if (_typeof(element.scale) !== 'object') {
element.scale = {
x: 1,
y: 1
};
}
};
// Helper function to update a face element
self.updateFaceElement = function (elementName, x, y, scaleX, scaleY, makeVisible) {
var element = self.elements[elementName];
if (!element) {
return;
}
// Ensure scale is an object
self.ensureScaleIsObject(element);
// Apply position with clamping using scaled boundaries
var elementOffset = self.currentOffsets[elementName];
element.x = self.clampPosition(x, elementOffset.minX * currentEyeDistance, elementOffset.maxX * currentEyeDistance);
element.y = self.clampPosition(y, elementOffset.minY * currentEyeDistance, elementOffset.maxY * currentEyeDistance);
// Apply scale
element.scale.x = scaleX;
element.scale.y = scaleY;
// Set visibility if needed
if (makeVisible) {
element.visible = true;
}
};
// Helper function to update all face elements
self.updateAllFaceElements = function (kit, makeVisible, useDefaultScales) {
var elementNames = ['head', 'leftEye', 'rightEye', 'upperLip', 'lowerLip'];
var positions = self.positionCache;
// Calculate positions for all elements
positions.head.x = 0;
positions.head.y = 0;
positions.head.scaleX = useDefaultScales ? 1 : self.scales.head.x;
positions.head.scaleY = useDefaultScales ? 1 : self.scales.head.y;
// For other elements, calculate based on kit positions
for (var i = 1; i < elementNames.length; i++) {
var name = elementNames[i];
var kitElement = kit[name];
if (kitElement) {
var scaleX, scaleY;
// Determine which scale to use based on element type and useDefaultScales flag
if (useDefaultScales) {
scaleX = 1 * self.currentOffsets[name].sx;
scaleY = 1 * self.currentOffsets[name].sy;
} else {
if (name === 'leftEye' || name === 'rightEye') {
scaleX = self.scales.eye.x;
scaleY = self.scales.eye.y;
} else {
scaleX = self.scales.lip.x;
scaleY = self.scales.lip.y;
}
}
// Calculate position using relative offsets scaled by eye distance
positions[name].x = kitElement.x - self.x + self.currentOffsets[name].x * currentEyeDistance;
positions[name].y = kitElement.y - self.y + self.currentOffsets[name].y * currentEyeDistance;
positions[name].scaleX = scaleX;
positions[name].scaleY = scaleY;
}
}
// Update each element with calculated positions
for (var j = 0; j < elementNames.length; j++) {
var elemName = elementNames[j];
if (self.elements[elemName] && positions[elemName]) {
var pos = positions[elemName];
self.updateFaceElement(elemName, pos.x, pos.y, pos.scaleX, pos.scaleY, makeVisible);
}
}
// Handle mouth open adjustment
if (kit.mouthOpen && self.elements.lowerLip) {
//self.elements.lowerLip.scale.y = self.scales.lip.y * 1.5;
}
};
// Update face elements to match real face
self.updateFacePosition = function () {
if (!facekit) {
return;
}
// Recalculate scales based on the current kit
var kit = bypassTracking ? fakeCamera : facekit;
// Update global eye distance once per frame
currentEyeDistance = self.getEyeDistance(kit);
var baseScale = self.calculateFaceScale(kit);
// Update scales
self.scales = {
head: {
x: baseScale * self.currentOffsets.head.sx,
y: baseScale * self.currentOffsets.head.sy
},
eye: {
x: baseScale * self.currentOffsets.leftEye.sx,
y: baseScale * self.currentOffsets.leftEye.sy
},
lip: {
x: baseScale * self.currentOffsets.upperLip.sx,
y: baseScale * self.currentOffsets.upperLip.sy
}
};
if (bypassTracking) {
self.x = 2048 / 2;
self.y = 2732 / 2;
// Use the global fakeCamera with dynamic scales
self.updateAllFaceElements(fakeCamera, true, false);
return;
}
// Update all elements with facekit - smoothing is now handled in the update function
};
// Calculate scale based on eye distance
self.calculateFaceScale = function (kit) {
return currentEyeDistance * 0.005;
};
// Get eye distance from kit
self.getEyeDistance = function (kit) {
if (kit && kit.leftEye && kit.rightEye) {
var dx = kit.leftEye.x - kit.rightEye.x;
var dy = kit.leftEye.y - kit.rightEye.y;
return Math.sqrt(dx * dx + dy * dy);
}
return 200; // Default eye distance
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFFFFF
});
/****
* Game Code
****/
/****
* Global Variables
****/
function _typeof3(o) {
"@babel/helpers - typeof";
return _typeof3 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof3(o);
}
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
var debugMode = false; // DEBUG MODE DEBUG MODE DEBUG MODE
var bypassTracking = false; // Global variable to bypass face tracking
var currentEyeDistance = 200; // Global variable to store current eye distance
var smoothingFactor = 0.3; // Controls how smooth the transitions are (0-1, lower = smoother)
var smoothedFacekit = null; // Smoothed version of the facekit
var trollFaceOffsets = [{
head: {
x: 0,
y: 0,
sx: 1,
sy: 1
},
leftEye: {
x: 0.75,
// 150/200 = 0.75
y: 1.0,
// 200/200 = 1.0
sx: 0.6,
sy: 0.6,
minX: -1.0,
// -200/200 = -1.0
maxX: 1.0,
// 200/200 = 1.0
minY: -1.0,
maxY: 1.0
},
rightEye: {
x: 0,
// 0/200 = 0
y: 1.0,
// 200/200 = 1.0
sx: 0.6,
sy: 0.6,
minX: -1.0,
maxX: 1.0,
minY: -1.0,
maxY: 1.0
},
upperLip: {
x: 0,
// 0/200 = 0
y: 0.15,
// 30/200 = 0.15
sx: 1,
sy: 1,
minX: -1.0,
maxX: 1.0,
minY: -1.0,
maxY: 1.0
},
lowerLip: {
x: 0,
// 0/200 = 0
y: 0.65,
// 130/200 = 0.65
sx: 1,
sy: 1,
minX: -1.0,
maxX: 1.0,
minY: -1.0,
maxY: 1.0
}
}, {
head: {
x: 0,
y: 0,
sx: 1,
sy: 1
},
leftEye: {
x: 0,
y: 0.5,
sx: 0.6,
sy: 0.6,
minX: 0.2,
maxX: 0.7,
minY: 0,
maxY: 0.6
},
rightEye: {
x: 0.1,
y: 0.3,
sx: 0.6,
sy: 0.6,
minX: -0.7,
maxX: 0.1,
minY: 0,
maxY: 0.4
},
upperLip: {
x: -0.2,
y: 0.1,
sx: 0.5,
sy: 0.5,
minX: -0.3,
maxX: 0,
minY: -1,
maxY: 1
},
lowerLip: {
x: -0.10,
y: 0.12,
sx: 0.2,
sy: 0.2,
minX: -0.2,
maxX: 0,
minY: 0,
maxY: 0.8
}
}, {
head: {
x: 0,
y: 0,
sx: 1,
sy: 1
},
leftEye: {
x: -0.275,
// -55/200 = -0.275
y: -1.0,
// -200/200 = -1.0
sx: 1,
sy: 1,
minX: -1.0,
maxX: 1.0,
minY: -1.0,
maxY: 1.0
},
rightEye: {
x: -0.775,
// -155/200 = -0.775
y: -1.0,
// -200/200 = -1.0
sx: 1,
sy: 1,
minX: -1.0,
maxX: 1.0,
minY: -1.0,
maxY: 1.0
},
upperLip: {
x: 0,
// 0/200 = 0
y: 0.16,
// 32/200 = 0.16
sx: 1,
sy: 1.5,
minX: -1.0,
maxX: 1.0,
minY: -1.0,
maxY: 1.0
},
lowerLip: {
x: 0,
// 0/200 = 0
y: 0.26,
// 52/200 = 0.26
sx: 1,
sy: 1.5,
minX: -1.0,
maxX: 1.0,
minY: -1.0,
maxY: 1.0
}
}];
// Define fakeCamera globally with fixed positions for testing
var fakeCamera = {
leftEye: {
x: 1380,
y: 958
},
rightEye: {
x: 673,
y: 970
},
upperLip: {
x: 1027,
y: 1610
},
lowerLip: {
x: 1030,
y: 1613
},
mouthOpen: false
}; // Global object for bypass tracking mode
var background;
var instructionText;
var styleText;
var trollFace;
var debugPoints;
var backgroundContainer;
var middlegroundContainer;
var foregroundContainer;
var isTrackingFace = false; // Global flag to track face detection state
var targetPosition;
var lastNosePosition = null; // Global variable to store last facekit.noseTip.x
var trackingStoppedDelay = 100;
var trackingStoppedCounter = trackingStoppedDelay;
// Create a cache for smoothing face element positions
var previousFacekit = null;
// Helper function to smooth face tracking data
function smoothFaceTracking(currentKit) {
if (!currentKit || _typeof3(currentKit) !== 'object') {
return null;
}
// If no previous kit, just use the current one
if (!previousFacekit) {
previousFacekit = JSON.parse(JSON.stringify(currentKit));
return currentKit;
}
// Create a smoothed copy
var result = JSON.parse(JSON.stringify(currentKit));
// Only smooth the x,y positions, not any other properties
for (var key in result) {
if (result[key] && _typeof2(result[key]) === 'object' && previousFacekit[key] && _typeof2(previousFacekit[key]) === 'object') {
if ('x' in result[key] && 'x' in previousFacekit[key]) {
result[key].x = previousFacekit[key].x + (currentKit[key].x - previousFacekit[key].x) * smoothingFactor;
}
if ('y' in result[key] && 'y' in previousFacekit[key]) {
result[key].y = previousFacekit[key].y + (currentKit[key].y - previousFacekit[key].y) * smoothingFactor;
}
}
}
// Update previous kit for next frame
previousFacekit = JSON.parse(JSON.stringify(result));
return result;
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
/****
* Game Functions
****/
// Handle tap anywhere on the screen to change face
game.down = function (x, y, obj) {
// Switch to the next troll face style
var newStyle = trollFace.nextStyle();
// Save the current style to storage
storage.lastTrollStyle = newStyle;
// Update the style text
styleText.setText('Style: ' + newStyle + '/3');
// Play switch sound
LK.getSound('switchTroll').play();
};
// Update function called every frame
game.update = function () {
// If bypassing tracking
if (bypassTracking) {
// When bypassing tracking, position the troll face at the center and update its elements
trollFace.updateFacePosition();
return;
}
// If face tracking is working
if (facekit && facekit.leftEye && facekit.rightEye && facekit.upperLip && facekit.lowerLip) {
// Reset tracking stopped counter
trackingStoppedCounter = trackingStoppedDelay;
// Update face tracking state
isTrackingFace = true;
// Apply smoothing to facekit
smoothedFacekit = smoothFaceTracking(facekit);
// Update face position
trollFace.updateFacePosition();
// Update all face elements with the smoothed data
trollFace.updateAllFaceElements(smoothedFacekit, true, false);
trollFace.isCentered = false;
trollFace.isCentering = false;
// Update debug points if in debug mode
if (debugMode && debugPoints) {
debugPoints.update(facekit);
}
return;
}
if (!facekit || !facekit.noseTip) {
return;
}
// Check if lastNosePosition is the same after trackingStoppedDelay ticks
if (lastNosePosition === facekit.noseTip.x) {
trackingStoppedCounter--;
if (trackingStoppedCounter <= 0) {
isTrackingFace = false;
instructionText.setText("Stopped tracking");
trackingStoppedCounter = trackingStoppedDelay; // Reset delay
}
} else {
isTrackingFace = true;
lastNosePosition = facekit.noseTip.x;
instructionText.setText("Tracking...");
trackingStoppedCounter = trackingStoppedDelay; // Reset delay
}
// Update troll face position to match real face
if (isTrackingFace) {
// Use the nose tip position for the face with easing
targetPosition.x = facekit.noseTip.x;
targetPosition.y = facekit.noseTip.y;
trollFace.x = targetPosition.x;
trollFace.y = targetPosition.y;
trollFace.isTweening = true; // TEMP DEBUG
// Apply tweening for smooth movement and wait for it to finish before starting the next
// if (!trollFace.isTweening) {
// trollFace.isTweening = true;
// tween(trollFace, {
// x: targetPosition.x,
// y: targetPosition.y
// }, {
// duration: 300,
// easing: tween.easeInOut,
// onFinish: function onFinish() {
// trollFace.isTweening = false;
// }
// });
// }
// Update face elements to match real face features
var smoothedFacekit = smoothFaceTracking(facekit);
trollFace.updateAllFaceElements(smoothedFacekit, true, false);
trollFace.isCentered = false;
trollFace.isCentering = false;
// Update face tracking state
if (!isTrackingFace) {
isTrackingFace = true;
instructionText.setText("Tracking..." + new Date());
console.log("facekit", facekit);
}
//console.log("noseTip x=", facekit.noseTip.x);
} else {
// If face is not detected, return the face to the center
if (!trollFace.isCentered) {
if (trollFace.isCentering) {
// Don't exit the update function, just skip starting a new tween
// This allows other updates to continue
// Continue updating face elements during centering
trollFace.updateFacePosition();
} else {
trollFace.isCentering = true;
LK.effects.flashScreen(0xFFFFFF, 1000); // Flash screen
tween(trollFace, {
x: 2048 / 2,
y: 2732 / 2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
trollFace.isCentered = true;
trollFace.isCentering = false;
trollFace.updateFacePosition();
}
});
}
}
// Update face tracking state
if (isTrackingFace) {
isTrackingFace = false;
instructionText.setText("No Face found");
}
}
};
function initializeGame() {
// Initialize game
// Create containers for layering
backgroundContainer = new Container();
middlegroundContainer = new Container();
foregroundContainer = new Container();
// Add containers to game
game.addChild(backgroundContainer);
game.addChild(middlegroundContainer);
game.addChild(foregroundContainer);
// Global target position for the troll face
targetPosition = {
x: 2048 / 2,
y: 2732 / 2
};
// Setup background
background = LK.getAsset('whiteBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
visible: false
});
backgroundContainer.addChild(background);
// Setup UI text
instructionText = new Text2('Tap anywhere to change troll face', {
size: 50,
fill: 0xFF0000
});
instructionText.anchor.set(0.5, 0);
LK.gui.top.addChild(instructionText);
instructionText.y = 1700;
// Load the last used style from storage
var lastStyle = storage.lastTrollStyle || 1;
// Create the troll face
trollFace = new TrollFace();
trollFace.currentStyle = lastStyle;
trollFace.initialize();
trollFace.nextStyle(lastStyle);
middlegroundContainer.addChild(trollFace);
// Set the troll face position to the center of the screen
trollFace.x = 2048 / 2;
trollFace.y = 2732 / 2;
// Initialize tracking state
isTrackingFace = false;
// Add style text
styleText = new Text2('Style: ' + lastStyle + '/3', {
size: 50,
fill: 0xFF0000
});
styleText.anchor.set(0.5, 0);
LK.gui.top.addChild(styleText);
styleText.y = 120;
// Debug mode (turn on for development, off for production)
debugPoints = null;
if (debugMode) {
debugPoints = new DebugPoints();
foregroundContainer.addChild(debugPoints);
// Log facekit to console every second
LK.setInterval(function () {
console.log(facekit);
if (facekit.lowerLip) {
var elementOffset = trollFace.currentOffsets.lowerLip;
var eyeDistance = trollFace.getEyeDistance(facekit);
console.log("lowerLip y:", facekit.lowerLip.y, "minY:", elementOffset.minY * eyeDistance, "maxY:", elementOffset.maxY * eyeDistance);
}
instructionText.setText("le:".concat(Math.round(facekit.leftEye.x), ",").concat(Math.round(facekit.leftEye.y), " / ") + "re:".concat(Math.round(facekit.rightEye.x), ",").concat(Math.round(facekit.rightEye.y), " / ") + "ul:".concat(Math.round(facekit.upperLip.x), ",").concat(Math.round(facekit.upperLip.y), " / ") + "ll:".concat(Math.round(facekit.lowerLip.x), ",").concat(Math.round(facekit.lowerLip.y)));
}, 1000);
}
}
// Initialize the game
initializeGame(); ===================================================================
--- original.js
+++ change.js
@@ -41,39 +41,39 @@
anchorY: 0.5
})
};
// Update debug points to match face points
- self.update = function () {
- if (!facekit) {
+ self.update = function (kit) {
+ if (!kit) {
return;
}
- if (facekit.leftEye) {
- self.points.leftEye.x = facekit.leftEye.x;
- self.points.leftEye.y = facekit.leftEye.y;
+ if (kit.leftEye) {
+ self.points.leftEye.x = kit.leftEye.x;
+ self.points.leftEye.y = kit.leftEye.y;
}
- if (facekit.rightEye) {
- self.points.rightEye.x = facekit.rightEye.x;
- self.points.rightEye.y = facekit.rightEye.y;
+ if (kit.rightEye) {
+ self.points.rightEye.x = kit.rightEye.x;
+ self.points.rightEye.y = kit.rightEye.y;
}
- if (facekit.noseTip) {
- self.points.noseTip.x = facekit.noseTip.x;
- self.points.noseTip.y = facekit.noseTip.y;
+ if (kit.noseTip) {
+ self.points.noseTip.x = kit.noseTip.x;
+ self.points.noseTip.y = kit.noseTip.y;
}
- if (facekit.mouthCenter) {
- self.points.mouthCenter.x = facekit.mouthCenter.x;
- self.points.mouthCenter.y = facekit.mouthCenter.y;
+ if (kit.mouthCenter) {
+ self.points.mouthCenter.x = kit.mouthCenter.x;
+ self.points.mouthCenter.y = kit.mouthCenter.y;
}
- if (facekit.upperLip) {
- self.points.upperLip.x = facekit.upperLip.x;
- self.points.upperLip.y = facekit.upperLip.y;
+ if (kit.upperLip) {
+ self.points.upperLip.x = kit.upperLip.x;
+ self.points.upperLip.y = kit.upperLip.y;
}
- if (facekit.lowerLip) {
- self.points.lowerLip.x = facekit.lowerLip.x;
- self.points.lowerLip.y = facekit.lowerLip.y;
+ if (kit.lowerLip) {
+ self.points.lowerLip.x = kit.lowerLip.x;
+ self.points.lowerLip.y = kit.lowerLip.y;
}
- if (facekit.chin) {
- self.points.chin.x = facekit.chin.x;
- self.points.chin.y = facekit.chin.y;
+ if (kit.chin) {
+ self.points.chin.x = kit.chin.x;
+ self.points.chin.y = kit.chin.y;
}
};
return self;
});
@@ -96,8 +96,41 @@
// Properties
self.currentStyle = 1;
self.currentOffsets = trollFaceOffsets[self.currentStyle - 1];
self.elements = {};
+ // Initialize cached position objects to reuse
+ self.positionCache = {
+ head: {
+ x: 0,
+ y: 0,
+ scaleX: 1,
+ scaleY: 1
+ },
+ leftEye: {
+ x: 0,
+ y: 0,
+ scaleX: 1,
+ scaleY: 1
+ },
+ rightEye: {
+ x: 0,
+ y: 0,
+ scaleX: 1,
+ scaleY: 1
+ },
+ upperLip: {
+ x: 0,
+ y: 0,
+ scaleX: 1,
+ scaleY: 1
+ },
+ lowerLip: {
+ x: 0,
+ y: 0,
+ scaleX: 1,
+ scaleY: 1
+ }
+ };
// Initialize the troll face elements
self.initialize = function (style) {
// Clear previous elements
self.removeAllElements();
@@ -199,15 +232,12 @@
return;
}
// Ensure scale is an object
self.ensureScaleIsObject(element);
- // Get eye distance for relative positioning
- var kit = bypassTracking ? fakeCamera : facekit;
- var eyeDistance = self.getEyeDistance(kit);
// Apply position with clamping using scaled boundaries
var elementOffset = self.currentOffsets[elementName];
- element.x = self.clampPosition(x, elementOffset.minX * eyeDistance, elementOffset.maxX * eyeDistance);
- element.y = self.clampPosition(y, elementOffset.minY * eyeDistance, elementOffset.maxY * eyeDistance);
+ element.x = self.clampPosition(x, elementOffset.minX * currentEyeDistance, elementOffset.maxX * currentEyeDistance);
+ element.y = self.clampPosition(y, elementOffset.minY * currentEyeDistance, elementOffset.maxY * currentEyeDistance);
// Apply scale
element.scale.x = scaleX;
element.scale.y = scaleY;
// Set visibility if needed
@@ -217,18 +247,14 @@
};
// Helper function to update all face elements
self.updateAllFaceElements = function (kit, makeVisible, useDefaultScales) {
var elementNames = ['head', 'leftEye', 'rightEye', 'upperLip', 'lowerLip'];
- var positions = {};
- // Get eye distance for relative positioning
- var eyeDistance = self.getEyeDistance(kit);
+ var positions = self.positionCache;
// Calculate positions for all elements
- positions.head = {
- x: 0,
- y: 0,
- scaleX: useDefaultScales ? 1 : self.scales.head.x,
- scaleY: useDefaultScales ? 1 : self.scales.head.y
- };
+ positions.head.x = 0;
+ positions.head.y = 0;
+ positions.head.scaleX = useDefaultScales ? 1 : self.scales.head.x;
+ positions.head.scaleY = useDefaultScales ? 1 : self.scales.head.y;
// For other elements, calculate based on kit positions
for (var i = 1; i < elementNames.length; i++) {
var name = elementNames[i];
var kitElement = kit[name];
@@ -247,14 +273,12 @@
scaleY = self.scales.lip.y;
}
}
// Calculate position using relative offsets scaled by eye distance
- positions[name] = {
- x: kitElement.x - self.x + self.currentOffsets[name].x * eyeDistance,
- y: kitElement.y - self.y + self.currentOffsets[name].y * eyeDistance,
- scaleX: scaleX,
- scaleY: scaleY
- };
+ positions[name].x = kitElement.x - self.x + self.currentOffsets[name].x * currentEyeDistance;
+ positions[name].y = kitElement.y - self.y + self.currentOffsets[name].y * currentEyeDistance;
+ positions[name].scaleX = scaleX;
+ positions[name].scaleY = scaleY;
}
}
// Update each element with calculated positions
for (var j = 0; j < elementNames.length; j++) {
@@ -275,8 +299,10 @@
return;
}
// Recalculate scales based on the current kit
var kit = bypassTracking ? fakeCamera : facekit;
+ // Update global eye distance once per frame
+ currentEyeDistance = self.getEyeDistance(kit);
var baseScale = self.calculateFaceScale(kit);
// Update scales
self.scales = {
head: {
@@ -298,20 +324,20 @@
// Use the global fakeCamera with dynamic scales
self.updateAllFaceElements(fakeCamera, true, false);
return;
}
- // Update all elements with facekit
- self.updateAllFaceElements(facekit, true, false);
+ // Update all elements with facekit - smoothing is now handled in the update function
};
// Calculate scale based on eye distance
self.calculateFaceScale = function (kit) {
- var eyeDistance = self.getEyeDistance(kit);
- return eyeDistance / 200;
+ return currentEyeDistance * 0.005;
};
// Get eye distance from kit
self.getEyeDistance = function (kit) {
if (kit && kit.leftEye && kit.rightEye) {
- return Math.sqrt(Math.pow(kit.leftEye.x - kit.rightEye.x, 2) + Math.pow(kit.leftEye.y - kit.rightEye.y, 2));
+ var dx = kit.leftEye.x - kit.rightEye.x;
+ var dy = kit.leftEye.y - kit.rightEye.y;
+ return Math.sqrt(dx * dx + dy * dy);
}
return 200; // Default eye distance
};
return self;
@@ -329,10 +355,29 @@
****/
/****
* Global Variables
****/
-var debugMode = true; // DEBUG MODE DEBUG MODE DEBUG MODE
+function _typeof3(o) {
+ "@babel/helpers - typeof";
+ return _typeof3 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
+ return typeof o;
+ } : function (o) {
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
+ }, _typeof3(o);
+}
+function _typeof2(o) {
+ "@babel/helpers - typeof";
+ return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
+ return typeof o;
+ } : function (o) {
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
+ }, _typeof2(o);
+}
+var debugMode = false; // DEBUG MODE DEBUG MODE DEBUG MODE
var bypassTracking = false; // Global variable to bypass face tracking
+var currentEyeDistance = 200; // Global variable to store current eye distance
+var smoothingFactor = 0.3; // Controls how smooth the transitions are (0-1, lower = smoother)
+var smoothedFacekit = null; // Smoothed version of the facekit
var trollFaceOffsets = [{
head: {
x: 0,
y: 0,
@@ -411,31 +456,31 @@
y: 0.3,
sx: 0.6,
sy: 0.6,
minX: -0.7,
- maxX: 0,
+ maxX: 0.1,
minY: 0,
maxY: 0.4
},
upperLip: {
x: -0.2,
- y: 0.25,
+ y: 0.1,
sx: 0.5,
sy: 0.5,
minX: -0.3,
- maxX: -0.1,
+ maxX: 0,
minY: -1,
maxY: 1
},
lowerLip: {
x: -0.10,
- y: 0.27,
+ y: 0.12,
sx: 0.2,
sy: 0.2,
minX: -0.2,
maxX: 0,
- minY: -1,
- maxY: 1
+ minY: 0,
+ maxY: 0.8
}
}, {
head: {
x: 0,
@@ -524,8 +569,37 @@
var targetPosition;
var lastNosePosition = null; // Global variable to store last facekit.noseTip.x
var trackingStoppedDelay = 100;
var trackingStoppedCounter = trackingStoppedDelay;
+// Create a cache for smoothing face element positions
+var previousFacekit = null;
+// Helper function to smooth face tracking data
+function smoothFaceTracking(currentKit) {
+ if (!currentKit || _typeof3(currentKit) !== 'object') {
+ return null;
+ }
+ // If no previous kit, just use the current one
+ if (!previousFacekit) {
+ previousFacekit = JSON.parse(JSON.stringify(currentKit));
+ return currentKit;
+ }
+ // Create a smoothed copy
+ var result = JSON.parse(JSON.stringify(currentKit));
+ // Only smooth the x,y positions, not any other properties
+ for (var key in result) {
+ if (result[key] && _typeof2(result[key]) === 'object' && previousFacekit[key] && _typeof2(previousFacekit[key]) === 'object') {
+ if ('x' in result[key] && 'x' in previousFacekit[key]) {
+ result[key].x = previousFacekit[key].x + (currentKit[key].x - previousFacekit[key].x) * smoothingFactor;
+ }
+ if ('y' in result[key] && 'y' in previousFacekit[key]) {
+ result[key].y = previousFacekit[key].y + (currentKit[key].y - previousFacekit[key].y) * smoothingFactor;
+ }
+ }
+ }
+ // Update previous kit for next frame
+ previousFacekit = JSON.parse(JSON.stringify(result));
+ return result;
+}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
@@ -548,13 +622,34 @@
LK.getSound('switchTroll').play();
};
// Update function called every frame
game.update = function () {
+ // If bypassing tracking
if (bypassTracking) {
// When bypassing tracking, position the troll face at the center and update its elements
trollFace.updateFacePosition();
return;
}
+ // If face tracking is working
+ if (facekit && facekit.leftEye && facekit.rightEye && facekit.upperLip && facekit.lowerLip) {
+ // Reset tracking stopped counter
+ trackingStoppedCounter = trackingStoppedDelay;
+ // Update face tracking state
+ isTrackingFace = true;
+ // Apply smoothing to facekit
+ smoothedFacekit = smoothFaceTracking(facekit);
+ // Update face position
+ trollFace.updateFacePosition();
+ // Update all face elements with the smoothed data
+ trollFace.updateAllFaceElements(smoothedFacekit, true, false);
+ trollFace.isCentered = false;
+ trollFace.isCentering = false;
+ // Update debug points if in debug mode
+ if (debugMode && debugPoints) {
+ debugPoints.update(facekit);
+ }
+ return;
+ }
if (!facekit || !facekit.noseTip) {
return;
}
// Check if lastNosePosition is the same after trackingStoppedDelay ticks
@@ -593,9 +688,10 @@
// }
// });
// }
// Update face elements to match real face features
- trollFace.updateFacePosition();
+ var smoothedFacekit = smoothFaceTracking(facekit);
+ trollFace.updateAllFaceElements(smoothedFacekit, true, false);
trollFace.isCentered = false;
trollFace.isCentering = false;
// Update face tracking state
if (!isTrackingFace) {