Code edit (6 edits merged)
Please save this source code
User prompt
switch bypassTracking to true
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
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
/****
* 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 () {
if (!facekit) {
return;
}
if (facekit.leftEye) {
self.points.leftEye.x = facekit.leftEye.x;
self.points.leftEye.y = facekit.leftEye.y;
}
if (facekit.rightEye) {
self.points.rightEye.x = facekit.rightEye.x;
self.points.rightEye.y = facekit.rightEye.y;
}
if (facekit.noseTip) {
self.points.noseTip.x = facekit.noseTip.x;
self.points.noseTip.y = facekit.noseTip.y;
}
if (facekit.mouthCenter) {
self.points.mouthCenter.x = facekit.mouthCenter.x;
self.points.mouthCenter.y = facekit.mouthCenter.y;
}
if (facekit.upperLip) {
self.points.upperLip.x = facekit.upperLip.x;
self.points.upperLip.y = facekit.upperLip.y;
}
if (facekit.lowerLip) {
self.points.lowerLip.x = facekit.lowerLip.x;
self.points.lowerLip.y = facekit.lowerLip.y;
}
if (facekit.chin) {
self.points.chin.x = facekit.chin.x;
self.points.chin.y = facekit.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
},
upperLip: {
x: 1,
y: 1
},
lowerLip: {
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
},
upperLip: {
x: baseScale * self.currentOffsets.upperLip.sx,
y: baseScale * self.currentOffsets.upperLip.sy
},
lowerLip: {
x: baseScale * self.currentOffsets.lowerLip.sx,
y: baseScale * self.currentOffsets.lowerLip.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;
// Get the rotation angle for constraint calculations
var rotationAngle = self.rotation;
var cosAngle = Math.cos(-rotationAngle); // Negative to counter-rotate
var sinAngle = Math.sin(-rotationAngle);
// 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 if (name === 'upperLip') {
scaleX = self.scales.upperLip.x;
scaleY = self.scales.upperLip.y;
} else if (name === 'lowerLip') {
scaleX = self.scales.lowerLip.x;
scaleY = self.scales.lowerLip.y;
}
}
// Calculate position using relative offsets scaled by eye distance
var rawX = kitElement.x - self.x + self.currentOffsets[name].x * currentEyeDistance;
var rawY = kitElement.y - self.y + self.currentOffsets[name].y * currentEyeDistance;
// Apply rotation constraints to maintain relative positions
// This counter-rotates the positions to keep elements in proper alignment
positions[name].x = rawX * cosAngle - rawY * sinAngle;
positions[name].y = rawX * sinAngle + rawY * cosAngle;
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;
}
// Get kit based on tracking state
var kit = bypassTracking ? fakeCamera : facekitMgr.isTracking ? facekitMgr.currentFacekit : facekit;
// If re-centering, use fakeCamera data for positioning without changing bypassTracking
if (self.isRecentering) {
kit = fakeCamera;
}
// Update global eye distance once per frame
currentEyeDistance = self.getEyeDistance(kit);
var baseScale = self.calculateFaceScale(kit);
// Calculate face rotation - use FacekitManager if tracking, otherwise use local calculation
var rotation = facekitMgr.isTracking ? facekitMgr.currentRotation + Math.PI : self.calculateFaceRotation(kit);
// If re-centering, reset rotation to 0
if (self.isRecentering) {
rotation = 0;
}
// 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
},
upperLip: {
x: baseScale * self.currentOffsets.upperLip.sx,
y: baseScale * self.currentOffsets.upperLip.sy
},
lowerLip: {
x: baseScale * self.currentOffsets.lowerLip.sx,
y: baseScale * self.currentOffsets.lowerLip.sy
}
};
if (bypassTracking) {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.rotation = 0; // Reset rotation in bypass mode
// Use the global fakeCamera with dynamic scales
self.updateAllFaceElements(fakeCamera, true, false);
return;
}
// Apply rotation to the entire troll face container
self.rotation = rotation;
// Update all elements with kit
self.updateAllFaceElements(kit, true, false);
};
// Calculate scale based on eye distance
self.calculateFaceScale = function (kit) {
return currentEyeDistance * 0.005;
};
// Calculate face rotation angle based on eye positions
self.calculateFaceRotation = function (kit) {
if (kit && kit.leftEye && kit.rightEye) {
// Get eye positions
var leftEye = kit.leftEye;
var rightEye = kit.rightEye;
// Calculate angle - simpler approach
// This gives us the angle of the line connecting the eyes
var angle = Math.atan2(rightEye.y - leftEye.y, rightEye.x - leftEye.x);
// More efficient angle wrapping using modulo
var angleDiff = angle - currentRotation;
// Normalize the difference to [-PI, PI] range
angleDiff = (angleDiff + Math.PI) % (2 * Math.PI) - Math.PI;
// Apply smoothing to the rotation
currentRotation = currentRotation + angleDiff * 0.2;
return currentRotation + Math.PI;
}
return 0; // Default rotation (no rotation)
};
// 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
};
// Add a property to track if we're in re-centering mode
self.isRecentering = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFFFFF
});
/****
* Game Code
****/
/****
* Global Variables
****/
var debugMode = false; // DEBUG MODE DEBUG MODE DEBUG MODE
var bypassTracking = true; // Global variable to bypass face tracking
var currentEyeDistance = 200; // Global variable to store current eye distance
var currentRotation = 0; // Current smoothed rotation value
// FacekitManager: handles face tracking, smoothing, and fallback
var FacekitManager = function FacekitManager() {
var self = new Container();
// Public properties
self.currentFacekit = null; // Current smoothed face data
self.isTracking = false; // Current tracking state
self.currentRotation = 0; // Current smoothed rotation (accessible directly for performance)
self.smoothingFactor = 0.2; // Default smoothing factor
self.lastNosePosition = null; // Last nose position for tracking detection
self.trackingStoppedCounter = 100; // Counter for tracking detection
self.trackingStoppedDelay = 100; // Delay threshold for tracking detection
// Default positions - defined inline
var defaultFacekit = {
leftEye: {
x: 1380,
y: 958
},
rightEye: {
x: 673,
y: 970
},
upperLip: {
x: 1027,
y: 1610
},
lowerLip: {
x: 1030,
y: 1613
},
noseTip: {
x: 1024,
y: 1366
},
mouthOpen: false
};
// Initialize manager
self.initialize = function (fakeFacekit) {
// Use provided fallback or default
self.fallbackFacekit = fakeFacekit || defaultFacekit;
// Initialize current data as reference to fallback
self.currentFacekit = self.fallbackFacekit;
// Create a reusable object for smoothing calculations
self.smoothFacekit = {
leftEye: {
x: 0,
y: 0
},
rightEye: {
x: 0,
y: 0
},
upperLip: {
x: 0,
y: 0
},
lowerLip: {
x: 0,
y: 0
},
noseTip: {
x: 0,
y: 0
},
mouthOpen: false
};
return self;
};
// Process new tracking data and update tracking status
self.updateTrackingStatus = function (rawFacekit) {
// Check if there's valid face data
var hasFaceData = !!(rawFacekit && rawFacekit.noseTip);
if (!hasFaceData) {
self.isTracking = false;
return false;
}
// Check if nose position has changed using existing tracking mechanism
if (self.lastNosePosition === rawFacekit.noseTip.x) {
self.trackingStoppedCounter--;
if (self.trackingStoppedCounter <= 0) {
self.isTracking = false;
self.trackingStoppedCounter = self.trackingStoppedDelay; // Reset delay
}
} else {
self.isTracking = true;
self.lastNosePosition = rawFacekit.noseTip.x;
self.trackingStoppedCounter = self.trackingStoppedDelay; // Reset delay
}
// If tracking, process the face data
if (self.isTracking) {
// If tracking just started, initialize smooth values
if (self.currentFacekit === self.fallbackFacekit) {
self._initSmoothValues(rawFacekit);
}
// Apply smoothing to each facial feature
self._smoothValues(rawFacekit);
// Set currentFacekit to the smoothed values
self.currentFacekit = self.smoothFacekit;
} else {
// Use fallback when not tracking
self.currentFacekit = self.fallbackFacekit;
}
// Return tracking state
return self.isTracking;
};
// Initialize smooth values with raw data
self._initSmoothValues = function (rawFacekit) {
// Directly set initial values from raw data
for (var key in rawFacekit) {
if (rawFacekit[key] && self.smoothFacekit[key] && typeof rawFacekit[key].x !== 'undefined') {
self.smoothFacekit[key].x = rawFacekit[key].x;
self.smoothFacekit[key].y = rawFacekit[key].y;
}
}
// Initialize rotation directly
if (rawFacekit.leftEye && rawFacekit.rightEye) {
self.currentRotation = Math.atan2(rawFacekit.rightEye.y - rawFacekit.leftEye.y, rawFacekit.rightEye.x - rawFacekit.leftEye.x);
}
};
// Apply smoothing to all values
self._smoothValues = function (rawFacekit) {
// Smooth positions (reuse existing objects)
for (var key in rawFacekit) {
if (rawFacekit[key] && self.smoothFacekit[key] && typeof rawFacekit[key].x !== 'undefined') {
// Apply position smoothing directly
self.smoothFacekit[key].x += (rawFacekit[key].x - self.smoothFacekit[key].x) * self.smoothingFactor;
self.smoothFacekit[key].y += (rawFacekit[key].y - self.smoothFacekit[key].y) * self.smoothingFactor;
}
}
// Calculate and smooth rotation
if (rawFacekit.leftEye && rawFacekit.rightEye) {
var newAngle = Math.atan2(rawFacekit.rightEye.y - rawFacekit.leftEye.y, rawFacekit.rightEye.x - rawFacekit.leftEye.x);
// Improved angle normalization to prevent full rotations - no while loops
var angleDiff = newAngle - self.currentRotation;
// Efficiently handle -PI/PI boundary crossing (replaces the while loops)
if (angleDiff > Math.PI) {
angleDiff -= 2 * Math.PI;
} else if (angleDiff < -Math.PI) {
angleDiff += 2 * Math.PI;
}
// Apply safeguard against large changes
var maxRotationPerFrame = Math.PI / 10; // Limit rotation change
// Clamp the rotation change to prevent sudden large rotations
if (angleDiff > maxRotationPerFrame) {
angleDiff = maxRotationPerFrame;
}
if (angleDiff < -maxRotationPerFrame) {
angleDiff = -maxRotationPerFrame;
}
// Apply smoothing
self.currentRotation += angleDiff * self.smoothingFactor;
// Normalize the result angle to ensure it stays in -PI to PI range
// Using efficient modulo approach rather than while loops
self.currentRotation = (self.currentRotation + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
}
// Copy mouth state
self.smoothFacekit.mouthOpen = rawFacekit.mouthOpen;
};
return self;
};
var trollFaceOffsets = [{
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,
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.13,
y: -0.02,
sx: 0.8,
sy: 0.8,
minX: 0.6,
maxX: 0.72,
minY: -0.75,
maxY: -0.6
},
rightEye: {
x: 0.15,
y: -0.02,
sx: 0.8,
sy: 0.8,
minX: -0.6,
maxX: -0.3,
minY: -0.75,
maxY: -0.6
},
upperLip: {
x: 0.1,
y: -0.3,
sx: 1,
sy: 1,
minX: -0.5,
maxX: 0.5,
minY: -1.0,
maxY: 1.0
},
lowerLip: {
x: 0.15,
y: -0.10,
sx: 0.93,
sy: 0.93,
minX: -0.5,
maxX: 0.5,
minY: -1.0,
maxY: 0.55
}
}];
// 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
// Initialize FacekitManager
var facekitMgr = new FacekitManager();
facekitMgr.initialize(fakeCamera);
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;
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 (bypassTracking) {
// When bypassing tracking, position the troll face at the center and update its elements
trollFace.updateFacePosition();
return;
}
if (!facekit || !facekit.noseTip) {
return;
}
// Update tracking status and get smoothed face data
var isCurrentlyTracking = facekitMgr.updateTrackingStatus(facekit);
// Update tracking state UI if changed
if (isCurrentlyTracking !== isTrackingFace) {
isTrackingFace = isCurrentlyTracking;
instructionText.setText(isCurrentlyTracking ? "Tracking..." : "No Face found");
}
// Update troll face position to match real face
if (isTrackingFace) {
// Reset isRecentering flag when face tracking is detected
trollFace.isRecentering = false;
// Use the smoothed nose tip position
trollFace.x = facekitMgr.currentFacekit.noseTip.x;
trollFace.y = facekitMgr.currentFacekit.noseTip.y;
// Use the original updateFacePosition with smoothed data
trollFace.updateFacePosition();
trollFace.isCentered = false;
trollFace.isCentering = false;
} 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;
// Set isRecentering flag to use fakeCamera data for positioning
trollFace.isRecentering = 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;
// Keep isRecentering true to maintain centered elements
trollFace.updateFacePosition();
}
});
}
}
// Update face tracking state
if (isTrackingFace) {
isTrackingFace = false;
instructionText.setText("No Face found");
}
}
// If in debug mode, display the face elements positions
if (debugMode) {
// Clear existing debug points
if (debugPoints) {
for (var i = 0; i < debugPoints.children.length; i++) {
debugPoints.children[i].alpha = 0;
}
}
// Draw debug points for each face element
if (facekit) {
var pointIndex = 0;
// Draw a point for each facial feature
for (var key in facekit) {
if (facekit[key] && typeof facekit[key].x !== 'undefined') {
var point = debugPoints.children[pointIndex++];
if (point) {
point.x = facekit[key].x;
point.y = facekit[key].y;
point.alpha = 1;
}
}
}
// Display rotation in degrees
instructionText.setText("Rotation: " + Math.round(trollFace.rotation * 180 / Math.PI) + "°");
}
}
};
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: true
});
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 || 3;
// 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);
}
// Display rotation angle in degrees for debugging
var rotationDegrees = Math.round(trollFace.rotation * (180 / Math.PI));
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), " / ") + "rot:".concat(rotationDegrees, "°"));
}, 1000);
}
}
// Initialize the game
initializeGame(); ===================================================================
--- original.js
+++ change.js
@@ -414,9 +414,9 @@
/****
* Global Variables
****/
var debugMode = false; // DEBUG MODE DEBUG MODE DEBUG MODE
-var bypassTracking = false; // Global variable to bypass face tracking
+var bypassTracking = true; // Global variable to bypass face tracking
var currentEyeDistance = 200; // Global variable to store current eye distance
var currentRotation = 0; // Current smoothed rotation value
// FacekitManager: handles face tracking, smoothing, and fallback
var FacekitManager = function FacekitManager() {
@@ -584,56 +584,46 @@
sx: 1,
sy: 1
},
leftEye: {
- x: 0.75,
- // 150/200 = 0.75
- y: 1.0,
- // 200/200 = 1.0
+ x: 0,
+ y: 0.5,
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
+ minX: 0.2,
+ maxX: 0.7,
+ minY: 0,
+ maxY: 0.6
},
rightEye: {
- x: 0,
- // 0/200 = 0
- y: 1.0,
- // 200/200 = 1.0
+ x: 0.1,
+ y: 0.3,
sx: 0.6,
sy: 0.6,
- minX: -1.0,
- maxX: 1.0,
- minY: -1.0,
- maxY: 1.0
+ minX: -0.7,
+ maxX: 0.1,
+ minY: 0,
+ maxY: 0.4
},
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
+ x: -0.2,
+ y: 0.1,
+ sx: 0.5,
+ sy: 0.5,
+ minX: -0.3,
+ maxX: 0,
+ minY: -1,
+ maxY: 1
},
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
+ 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,
@@ -692,19 +682,19 @@
x: 0.13,
y: -0.02,
sx: 0.8,
sy: 0.8,
- minX: 0.5,
- maxX: 0.7,
+ minX: 0.6,
+ maxX: 0.72,
minY: -0.75,
maxY: -0.6
},
rightEye: {
x: 0.15,
y: -0.02,
sx: 0.8,
sy: 0.8,
- minX: -0.65,
+ minX: -0.6,
maxX: -0.3,
minY: -0.75,
maxY: -0.6
},
@@ -908,9 +898,9 @@
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;
+ var lastStyle = storage.lastTrollStyle || 3;
// Create the troll face
trollFace = new TrollFace();
trollFace.currentStyle = lastStyle;
trollFace.initialize();