/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var DrawingPoint = Container.expand(function () {
var self = Container.call(this);
var pointGraphics = self.attachAsset('drawingLine', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var ResetButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('resetButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2('Try Again', {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
resetGame();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf0f0f0
});
/****
* Game Code
****/
// Game state variables
var isDrawing = false;
var drawingPoints = [];
var drawnPath = [];
var currentPercentage = 0;
var bestPercentage = storage.bestPercentage || 0;
var gameCompleted = false;
var centerX = 1024;
var centerY = 1366;
var targetRadius = 200;
// UI Elements
var instructionText = new Text2('Touch and drag to draw a perfect circle!', {
size: 60,
fill: 0x333333
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = centerX;
instructionText.y = 400;
game.addChild(instructionText);
var percentageText = new Text2('0%', {
size: 120,
fill: 0x333333
});
percentageText.anchor.set(0.5, 0.5);
percentageText.x = centerX;
percentageText.y = centerY;
game.addChild(percentageText);
var bestPercentageText = new Text2('Best: ' + bestPercentage + '%', {
size: 50,
fill: 0x666666
});
bestPercentageText.anchor.set(0.5, 0);
bestPercentageText.y = 50;
LK.gui.top.addChild(bestPercentageText);
// Perfect circle guide (faint)
var perfectGuide = game.addChild(LK.getAsset('perfectCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
alpha: 0.2
}));
// Reset button
var resetBtn = game.addChild(new ResetButton());
resetBtn.x = 1900;
resetBtn.y = 2600;
resetBtn.alpha = 0;
function resetGame() {
isDrawing = false;
gameCompleted = false;
currentPercentage = 0;
// Clear drawn path
for (var i = 0; i < drawingPoints.length; i++) {
drawingPoints[i].destroy();
}
drawingPoints = [];
drawnPath = [];
// Reset UI
instructionText.setText('Touch and drag to draw a perfect circle!');
percentageText.setText('0%');
resetBtn.alpha = 0;
// Reset guide
perfectGuide.alpha = 0.2;
}
function startDrawing(x, y) {
if (gameCompleted) return;
isDrawing = true;
drawnPath = [];
// Clear previous drawing
for (var i = 0; i < drawingPoints.length; i++) {
drawingPoints[i].destroy();
}
drawingPoints = [];
instructionText.setText('Drawing...');
addDrawingPoint(x, y);
}
function addDrawingPoint(x, y) {
if (!isDrawing) return;
// Check if point is over reset button area
var buttonLeft = resetBtn.x - 100;
var buttonRight = resetBtn.x + 100;
var buttonTop = resetBtn.y - 40;
var buttonBottom = resetBtn.y + 40;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
return; // Don't draw over reset button
}
var point = game.addChild(new DrawingPoint());
point.x = x;
point.y = y;
drawingPoints.push(point);
drawnPath.push({
x: x,
y: y
});
// Color coding based on distance from perfect circle
var distance = Math.sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY));
var deviation = Math.abs(distance - targetRadius);
if (deviation < 20) {
point.children[0].tint = 0x00ff00; // Green - excellent
} else if (deviation < 50) {
point.children[0].tint = 0xffff00; // Yellow - good
} else {
point.children[0].tint = 0xff0000; // Red - poor
}
}
function finishDrawing() {
if (!isDrawing || drawnPath.length < 10) return;
isDrawing = false;
gameCompleted = true;
// Calculate percentage
currentPercentage = calculateCircleScore(drawnPath);
// Update UI
percentageText.setText(currentPercentage + '%');
instructionText.setText('Perfect: ' + currentPercentage + '%');
// Update best percentage
if (currentPercentage > bestPercentage) {
bestPercentage = currentPercentage;
storage.bestPercentage = bestPercentage;
bestPercentageText.setText('Best: ' + bestPercentage + '%');
// Celebration for new best
if (currentPercentage >= 90) {
LK.effects.flashScreen(0x00ff00, 1000);
LK.getSound('perfectScore').play();
}
} else if (currentPercentage >= 80) {
LK.getSound('goodScore').play();
}
// Show reset button
tween(resetBtn, {
alpha: 1
}, {
duration: 500
});
// Celebration effects for high scores
if (currentPercentage >= 95) {
for (var i = 0; i < drawingPoints.length; i++) {
tween(drawingPoints[i], {
scaleX: 2,
scaleY: 2
}, {
duration: 300
});
tween(drawingPoints[i], {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
}
}
function calculateCircleScore(path) {
if (path.length < 1) return 0;
var score = 100;
var totalDeviation = 0;
var pointCount = path.length;
// Calculate average deviation from perfect circle
for (var i = 0; i < path.length; i++) {
var point = path[i];
var distance = Math.sqrt((point.x - centerX) * (point.x - centerX) + (point.y - centerY) * (point.y - centerY));
var deviation = Math.abs(distance - targetRadius);
totalDeviation += deviation;
}
var avgDeviation = totalDeviation / pointCount;
// More generous scoring for real-time feedback (lower deviation is better)
if (avgDeviation < 15) {
score = 100 - avgDeviation * 2; // 100% at perfect, 70% at 15px deviation
} else if (avgDeviation < 30) {
score = 70 - (avgDeviation - 15) * 2; // 70% to 40%
} else if (avgDeviation < 50) {
score = 40 - (avgDeviation - 30) * 1; // 40% to 20%
} else {
score = Math.max(5, 20 - (avgDeviation - 50) * 0.3); // Minimum 5%
}
// Check for circle completeness (bonus for closed circles) - only if we have enough points
if (path.length >= 10) {
var firstPoint = path[0];
var lastPoint = path[path.length - 1];
var closureDistance = Math.sqrt((firstPoint.x - lastPoint.x) * (firstPoint.x - lastPoint.x) + (firstPoint.y - lastPoint.y) * (firstPoint.y - lastPoint.y));
if (closureDistance < 50) {
score += 10; // Bonus for closed circle
}
}
return Math.min(100, Math.max(0, Math.round(score)));
}
// Event handlers
game.down = function (x, y, obj) {
startDrawing(x, y);
};
game.move = function (x, y, obj) {
if (isDrawing) {
addDrawingPoint(x, y);
}
};
game.up = function (x, y, obj) {
if (isDrawing) {
finishDrawing();
}
};
game.update = function () {
// Update percentage display during drawing
if (isDrawing && drawnPath.length > 0) {
currentPercentage = calculateCircleScore(drawnPath);
percentageText.setText(currentPercentage + '%');
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var DrawingPoint = Container.expand(function () {
var self = Container.call(this);
var pointGraphics = self.attachAsset('drawingLine', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var ResetButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('resetButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2('Try Again', {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
resetGame();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf0f0f0
});
/****
* Game Code
****/
// Game state variables
var isDrawing = false;
var drawingPoints = [];
var drawnPath = [];
var currentPercentage = 0;
var bestPercentage = storage.bestPercentage || 0;
var gameCompleted = false;
var centerX = 1024;
var centerY = 1366;
var targetRadius = 200;
// UI Elements
var instructionText = new Text2('Touch and drag to draw a perfect circle!', {
size: 60,
fill: 0x333333
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = centerX;
instructionText.y = 400;
game.addChild(instructionText);
var percentageText = new Text2('0%', {
size: 120,
fill: 0x333333
});
percentageText.anchor.set(0.5, 0.5);
percentageText.x = centerX;
percentageText.y = centerY;
game.addChild(percentageText);
var bestPercentageText = new Text2('Best: ' + bestPercentage + '%', {
size: 50,
fill: 0x666666
});
bestPercentageText.anchor.set(0.5, 0);
bestPercentageText.y = 50;
LK.gui.top.addChild(bestPercentageText);
// Perfect circle guide (faint)
var perfectGuide = game.addChild(LK.getAsset('perfectCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
alpha: 0.2
}));
// Reset button
var resetBtn = game.addChild(new ResetButton());
resetBtn.x = 1900;
resetBtn.y = 2600;
resetBtn.alpha = 0;
function resetGame() {
isDrawing = false;
gameCompleted = false;
currentPercentage = 0;
// Clear drawn path
for (var i = 0; i < drawingPoints.length; i++) {
drawingPoints[i].destroy();
}
drawingPoints = [];
drawnPath = [];
// Reset UI
instructionText.setText('Touch and drag to draw a perfect circle!');
percentageText.setText('0%');
resetBtn.alpha = 0;
// Reset guide
perfectGuide.alpha = 0.2;
}
function startDrawing(x, y) {
if (gameCompleted) return;
isDrawing = true;
drawnPath = [];
// Clear previous drawing
for (var i = 0; i < drawingPoints.length; i++) {
drawingPoints[i].destroy();
}
drawingPoints = [];
instructionText.setText('Drawing...');
addDrawingPoint(x, y);
}
function addDrawingPoint(x, y) {
if (!isDrawing) return;
// Check if point is over reset button area
var buttonLeft = resetBtn.x - 100;
var buttonRight = resetBtn.x + 100;
var buttonTop = resetBtn.y - 40;
var buttonBottom = resetBtn.y + 40;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
return; // Don't draw over reset button
}
var point = game.addChild(new DrawingPoint());
point.x = x;
point.y = y;
drawingPoints.push(point);
drawnPath.push({
x: x,
y: y
});
// Color coding based on distance from perfect circle
var distance = Math.sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY));
var deviation = Math.abs(distance - targetRadius);
if (deviation < 20) {
point.children[0].tint = 0x00ff00; // Green - excellent
} else if (deviation < 50) {
point.children[0].tint = 0xffff00; // Yellow - good
} else {
point.children[0].tint = 0xff0000; // Red - poor
}
}
function finishDrawing() {
if (!isDrawing || drawnPath.length < 10) return;
isDrawing = false;
gameCompleted = true;
// Calculate percentage
currentPercentage = calculateCircleScore(drawnPath);
// Update UI
percentageText.setText(currentPercentage + '%');
instructionText.setText('Perfect: ' + currentPercentage + '%');
// Update best percentage
if (currentPercentage > bestPercentage) {
bestPercentage = currentPercentage;
storage.bestPercentage = bestPercentage;
bestPercentageText.setText('Best: ' + bestPercentage + '%');
// Celebration for new best
if (currentPercentage >= 90) {
LK.effects.flashScreen(0x00ff00, 1000);
LK.getSound('perfectScore').play();
}
} else if (currentPercentage >= 80) {
LK.getSound('goodScore').play();
}
// Show reset button
tween(resetBtn, {
alpha: 1
}, {
duration: 500
});
// Celebration effects for high scores
if (currentPercentage >= 95) {
for (var i = 0; i < drawingPoints.length; i++) {
tween(drawingPoints[i], {
scaleX: 2,
scaleY: 2
}, {
duration: 300
});
tween(drawingPoints[i], {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
}
}
function calculateCircleScore(path) {
if (path.length < 1) return 0;
var score = 100;
var totalDeviation = 0;
var pointCount = path.length;
// Calculate average deviation from perfect circle
for (var i = 0; i < path.length; i++) {
var point = path[i];
var distance = Math.sqrt((point.x - centerX) * (point.x - centerX) + (point.y - centerY) * (point.y - centerY));
var deviation = Math.abs(distance - targetRadius);
totalDeviation += deviation;
}
var avgDeviation = totalDeviation / pointCount;
// More generous scoring for real-time feedback (lower deviation is better)
if (avgDeviation < 15) {
score = 100 - avgDeviation * 2; // 100% at perfect, 70% at 15px deviation
} else if (avgDeviation < 30) {
score = 70 - (avgDeviation - 15) * 2; // 70% to 40%
} else if (avgDeviation < 50) {
score = 40 - (avgDeviation - 30) * 1; // 40% to 20%
} else {
score = Math.max(5, 20 - (avgDeviation - 50) * 0.3); // Minimum 5%
}
// Check for circle completeness (bonus for closed circles) - only if we have enough points
if (path.length >= 10) {
var firstPoint = path[0];
var lastPoint = path[path.length - 1];
var closureDistance = Math.sqrt((firstPoint.x - lastPoint.x) * (firstPoint.x - lastPoint.x) + (firstPoint.y - lastPoint.y) * (firstPoint.y - lastPoint.y));
if (closureDistance < 50) {
score += 10; // Bonus for closed circle
}
}
return Math.min(100, Math.max(0, Math.round(score)));
}
// Event handlers
game.down = function (x, y, obj) {
startDrawing(x, y);
};
game.move = function (x, y, obj) {
if (isDrawing) {
addDrawingPoint(x, y);
}
};
game.up = function (x, y, obj) {
if (isDrawing) {
finishDrawing();
}
};
game.update = function () {
// Update percentage display during drawing
if (isDrawing && drawnPath.length > 0) {
currentPercentage = calculateCircleScore(drawnPath);
percentageText.setText(currentPercentage + '%');
}
};