User prompt
erase final score
User prompt
make random goal place
User prompt
make wide and big labyrinth
User prompt
make generate labyrinth normal dificult route
User prompt
erase final score text
User prompt
fix sound asset
User prompt
add sound asset in game
User prompt
add sound asset
Code edit (1 edits merged)
Please save this source code
User prompt
Gyro Labyrinth Escape
Initial prompt
random labyrinth with gyroscope control towards the goal finish
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball: The player-controlled ball var Ball = Container.expand(function () { var self = Container.call(this); var ballSize = 0; self.setSize = function (size) { ballSize = size; if (self.ballAsset) { self.removeChild(self.ballAsset); } self.ballAsset = self.attachAsset('ball', { width: ballSize, height: ballSize, color: 0x3a8cff, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5 }); }; self.radius = function () { return ballSize / 2; }; return self; }); // Finish: The goal area var Finish = Container.expand(function () { var self = Container.call(this); var finishSize = 0; self.setSize = function (size) { finishSize = size; if (self.finishAsset) { self.removeChild(self.finishAsset); } self.finishAsset = self.attachAsset('finish', { width: finishSize, height: finishSize, color: 0x44de83, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5 }); }; return self; }); // MazeCell: Represents a single cell in the maze grid var MazeCell = Container.expand(function () { var self = Container.call(this); // Walls: {top, right, bottom, left} self.walls = [true, true, true, true]; self.visited = false; self.xIndex = 0; self.yIndex = 0; self.size = 0; self.wallGraphics = []; // Draws the cell walls self.draw = function () { // Remove old wall graphics for (var i = 0; i < self.wallGraphics.length; i++) { self.removeChild(self.wallGraphics[i]); } self.wallGraphics = []; var s = self.size; var wallColor = 0x222222; var wallThickness = Math.max(8, Math.floor(s * 0.12)); // Top wall if (self.walls[0]) { var topWall = LK.getAsset('wall', { width: s, height: wallThickness, color: wallColor, shape: 'box', anchorX: 0, anchorY: 0 }); topWall.x = 0; topWall.y = 0; self.addChild(topWall); self.wallGraphics.push(topWall); } // Right wall if (self.walls[1]) { var rightWall = LK.getAsset('wall', { width: wallThickness, height: s, color: wallColor, shape: 'box', anchorX: 0, anchorY: 0 }); rightWall.x = s - wallThickness; rightWall.y = 0; self.addChild(rightWall); self.wallGraphics.push(rightWall); } // Bottom wall if (self.walls[2]) { var bottomWall = LK.getAsset('wall', { width: s, height: wallThickness, color: wallColor, shape: 'box', anchorX: 0, anchorY: 0 }); bottomWall.x = 0; bottomWall.y = s - wallThickness; self.addChild(bottomWall); self.wallGraphics.push(bottomWall); } // Left wall if (self.walls[3]) { var leftWall = LK.getAsset('wall', { width: wallThickness, height: s, color: wallColor, shape: 'box', anchorX: 0, anchorY: 0 }); leftWall.x = 0; leftWall.y = 0; self.addChild(leftWall); self.wallGraphics.push(leftWall); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf7f7f7 }); /**** * Game Code ****/ // Maze parameters var MAZE_COLS = 18; var MAZE_ROWS = 24; var MAZE_MARGIN = 40; // px margin around maze (reduce margin to fit bigger maze) var mazeCellSize = 0; var mazeOriginX = 0; var mazeOriginY = 0; var maze = []; var mazeContainer = new Container(); game.addChild(mazeContainer); // Ball and Finish var ball = null; var finish = null; // Ball physics var ballPos = { x: 0, y: 0 }; var ballVel = { x: 0, y: 0 }; var ballMaxSpeed = 32; var ballAccel = 2.2; var ballFriction = 0.96; // Gyro state var gyro = { x: 0, y: 0 }; // Timer var startTime = 0; var elapsedTime = 0; // Final score text removed as per requirements // Helper: Clamp function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } // Helper: Shuffle array function shuffle(arr) { for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var t = arr[i]; arr[i] = arr[j]; arr[j] = t; } return arr; } // Maze generation: Recursive backtracker with bias for normal difficulty route function generateMaze(cols, rows) { var grid = []; for (var y = 0; y < rows; y++) { var row = []; for (var x = 0; x < cols; x++) { var cell = new MazeCell(); cell.xIndex = x; cell.yIndex = y; cell.size = mazeCellSize; cell.visited = false; row.push(cell); } grid.push(row); } function cellAt(x, y) { if (x < 0 || y < 0 || x >= cols || y >= rows) return null; return grid[y][x]; } var stack = []; var current = grid[0][0]; current.visited = true; var visitedCount = 1; var totalCells = cols * rows; // Bias: prefer to move generally toward the finish, but not always var finishX = cols - 1; var finishY = rows - 1; while (visitedCount < totalCells) { // Find unvisited neighbors var neighbors = []; var dirs = [[0, -1, 0], // top [1, 0, 1], // right [0, 1, 2], // bottom [-1, 0, 3] // left ]; for (var d = 0; d < 4; d++) { var nx = current.xIndex + dirs[d][0]; var ny = current.yIndex + dirs[d][1]; var neighbor = cellAt(nx, ny); if (neighbor && !neighbor.visited) { neighbors.push({ cell: neighbor, dir: d }); } } if (neighbors.length > 0) { // Bias: 60% chance to pick neighbor that is closer to finish, else random var pick; if (Math.random() < 0.6) { // Sort neighbors by distance to finish, pick the closest neighbors.sort(function (a, b) { var da = Math.abs(a.cell.xIndex - finishX) + Math.abs(a.cell.yIndex - finishY); var db = Math.abs(b.cell.xIndex - finishX) + Math.abs(b.cell.yIndex - finishY); return da - db; }); // 50% chance to pick the closest, 50% chance to pick second closest (if exists) if (neighbors.length > 1 && Math.random() < 0.5) { pick = neighbors[1]; } else { pick = neighbors[0]; } } else { pick = neighbors[Math.floor(Math.random() * neighbors.length)]; } // Remove wall between current and neighbor current.walls[pick.dir] = false; var opp = (pick.dir + 2) % 4; pick.cell.walls[opp] = false; stack.push(current); current = pick.cell; current.visited = true; visitedCount++; } else if (stack.length > 0) { current = stack.pop(); } } // Draw all cells for (var y = 0; y < rows; y++) { for (var x = 0; x < cols; x++) { grid[y][x].draw(); } } return grid; } // Place maze in center of screen, calculate cell size function layoutMaze() { var availW = 2048 - MAZE_MARGIN * 2; var availH = 2732 - MAZE_MARGIN * 2; mazeCellSize = Math.floor(Math.min(availW / MAZE_COLS, availH / MAZE_ROWS)); var mazeW = mazeCellSize * MAZE_COLS; var mazeH = mazeCellSize * MAZE_ROWS; mazeOriginX = Math.floor((2048 - mazeW) / 2); mazeOriginY = Math.floor((2732 - mazeH) / 2); mazeContainer.x = mazeOriginX; mazeContainer.y = mazeOriginY; } // Remove all children from mazeContainer function clearMaze() { while (mazeContainer.children.length > 0) { mazeContainer.removeChild(mazeContainer.children[0]); } } // Place ball at start function placeBall() { if (ball) { ball.destroy(); } ball = new Ball(); ball.setSize(Math.floor(mazeCellSize * 0.55)); ballPos.x = mazeCellSize / 2; ballPos.y = mazeCellSize / 2; ball.x = ballPos.x; ball.y = ballPos.y; ballVel.x = 0; ballVel.y = 0; mazeContainer.addChild(ball); } // Place finish at bottom-right cell function placeFinish() { if (finish) { finish.destroy(); } finish = new Finish(); finish.setSize(Math.floor(mazeCellSize * 0.55)); // Pick a random cell in the maze that is not the start cell (0,0) var finishCellX = 0; var finishCellY = 0; while (finishCellX === 0 && finishCellY === 0) { finishCellX = Math.floor(Math.random() * MAZE_COLS); finishCellY = Math.floor(Math.random() * MAZE_ROWS); } var fx = finishCellX * mazeCellSize + mazeCellSize / 2; var fy = finishCellY * mazeCellSize + mazeCellSize / 2; finish.x = fx; finish.y = fy; mazeContainer.addChild(finish); // Store finish cell for win check finish.finishCellX = finishCellX; finish.finishCellY = finishCellY; } // Check collision between ball and maze walls function resolveBallMazeCollision() { var r = ball.radius(); var cx = Math.floor(ballPos.x / mazeCellSize); var cy = Math.floor(ballPos.y / mazeCellSize); // Clamp to maze bounds cx = clamp(cx, 0, MAZE_COLS - 1); cy = clamp(cy, 0, MAZE_ROWS - 1); var cell = maze[cy][cx]; var px = ballPos.x - cx * mazeCellSize; var py = ballPos.y - cy * mazeCellSize; var s = mazeCellSize; var wallThickness = Math.max(8, Math.floor(s * 0.12)); // Top wall if (cell.walls[0] && py - r < wallThickness) { ballPos.y = cy * mazeCellSize + wallThickness + r; ballVel.y = Math.max(0, ballVel.y); } // Right wall if (cell.walls[1] && px + r > s - wallThickness) { ballPos.x = cx * mazeCellSize + s - wallThickness - r; ballVel.x = Math.min(0, ballVel.x); } // Bottom wall if (cell.walls[2] && py + r > s - wallThickness) { ballPos.y = cy * mazeCellSize + s - wallThickness - r; ballVel.y = Math.min(0, ballVel.y); } // Left wall if (cell.walls[3] && px - r < wallThickness) { ballPos.x = cx * mazeCellSize + wallThickness + r; ballVel.x = Math.max(0, ballVel.x); } // Clamp to maze area ballPos.x = clamp(ballPos.x, r, MAZE_COLS * mazeCellSize - r); ballPos.y = clamp(ballPos.y, r, MAZE_ROWS * mazeCellSize - r); } // Check if ball reached finish function checkBallFinish() { // Use the actual finish cell position var fx = finish.finishCellX * mazeCellSize + mazeCellSize / 2; var fy = finish.finishCellY * mazeCellSize + mazeCellSize / 2; var dx = ballPos.x - fx; var dy = ballPos.y - fy; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < ball.radius() + finish.finishAsset.width / 2 - 6) { return true; } return false; } // Start new game function startGame() { clearMaze(); layoutMaze(); maze = generateMaze(MAZE_COLS, MAZE_ROWS); // Add all cells to container for (var y = 0; y < MAZE_ROWS; y++) { for (var x = 0; x < MAZE_COLS; x++) { var cell = maze[y][x]; cell.x = x * mazeCellSize; cell.y = y * mazeCellSize; mazeContainer.addChild(cell); } } placeFinish(); placeBall(); startTime = Date.now(); elapsedTime = 0; } // Gyroscope/motion support function handleDeviceMotion(event) { // event.accelerationIncludingGravity.{x,y,z} // On iOS, x is left/right, y is up/down, z is toward/away // On Android, axes may be swapped, so we allow both var ax = 0, ay = 0; if (event.accelerationIncludingGravity) { ax = event.accelerationIncludingGravity.x || 0; ay = event.accelerationIncludingGravity.y || 0; } // Heuristic: On portrait, invert y for natural tilt gyro.x = clamp(ax, -6, 6); gyro.y = clamp(-ay, -6, 6); } if (typeof window !== "undefined" && window.addEventListener) { window.addEventListener('devicemotion', handleDeviceMotion, true); } // Fallback: Touch drag to control ball if no gyro var dragging = false; var lastTouch = { x: 0, y: 0 }; game.down = function (x, y, obj) { // Only start drag if inside maze area var gx = x - mazeOriginX; var gy = y - mazeOriginY; if (gx >= 0 && gx < mazeCellSize * MAZE_COLS && gy >= 0 && gy < mazeCellSize * MAZE_ROWS) { dragging = true; lastTouch.x = x; lastTouch.y = y; } }; game.up = function (x, y, obj) { dragging = false; }; game.move = function (x, y, obj) { if (dragging) { var dx = x - lastTouch.x; var dy = y - lastTouch.y; // Simulate tilt by touch drag gyro.x = clamp(dx * 0.2, -6, 6); gyro.y = clamp(dy * 0.2, -6, 6); lastTouch.x = x; lastTouch.y = y; } }; // Main update loop game.update = function () { // Ball physics // Use gyro.x/gyro.y as acceleration ballVel.x += ballAccel * gyro.x * 0.18; ballVel.y += ballAccel * gyro.y * 0.18; // Friction ballVel.x *= ballFriction; ballVel.y *= ballFriction; // Clamp speed ballVel.x = clamp(ballVel.x, -ballMaxSpeed, ballMaxSpeed); ballVel.y = clamp(ballVel.y, -ballMaxSpeed, ballMaxSpeed); // Move ball ballPos.x += ballVel.x; ballPos.y += ballVel.y; resolveBallMazeCollision(); ball.x = ballPos.x; ball.y = ballPos.y; // Timer elapsedTime = (Date.now() - startTime) / 1000; // Win condition if (checkBallFinish()) { LK.getSound('finish_reached').play(); LK.effects.flashScreen(0x44de83, 800); LK.showYouWin(); } }; // Start game on load startGame(); // Reset game on game over or win LK.on('gameover', function () { startGame(); }); LK.on('youwin', function () { startGame(); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball: The player-controlled ball
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballSize = 0;
self.setSize = function (size) {
ballSize = size;
if (self.ballAsset) {
self.removeChild(self.ballAsset);
}
self.ballAsset = self.attachAsset('ball', {
width: ballSize,
height: ballSize,
color: 0x3a8cff,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
};
self.radius = function () {
return ballSize / 2;
};
return self;
});
// Finish: The goal area
var Finish = Container.expand(function () {
var self = Container.call(this);
var finishSize = 0;
self.setSize = function (size) {
finishSize = size;
if (self.finishAsset) {
self.removeChild(self.finishAsset);
}
self.finishAsset = self.attachAsset('finish', {
width: finishSize,
height: finishSize,
color: 0x44de83,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
// MazeCell: Represents a single cell in the maze grid
var MazeCell = Container.expand(function () {
var self = Container.call(this);
// Walls: {top, right, bottom, left}
self.walls = [true, true, true, true];
self.visited = false;
self.xIndex = 0;
self.yIndex = 0;
self.size = 0;
self.wallGraphics = [];
// Draws the cell walls
self.draw = function () {
// Remove old wall graphics
for (var i = 0; i < self.wallGraphics.length; i++) {
self.removeChild(self.wallGraphics[i]);
}
self.wallGraphics = [];
var s = self.size;
var wallColor = 0x222222;
var wallThickness = Math.max(8, Math.floor(s * 0.12));
// Top wall
if (self.walls[0]) {
var topWall = LK.getAsset('wall', {
width: s,
height: wallThickness,
color: wallColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
topWall.x = 0;
topWall.y = 0;
self.addChild(topWall);
self.wallGraphics.push(topWall);
}
// Right wall
if (self.walls[1]) {
var rightWall = LK.getAsset('wall', {
width: wallThickness,
height: s,
color: wallColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
rightWall.x = s - wallThickness;
rightWall.y = 0;
self.addChild(rightWall);
self.wallGraphics.push(rightWall);
}
// Bottom wall
if (self.walls[2]) {
var bottomWall = LK.getAsset('wall', {
width: s,
height: wallThickness,
color: wallColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
bottomWall.x = 0;
bottomWall.y = s - wallThickness;
self.addChild(bottomWall);
self.wallGraphics.push(bottomWall);
}
// Left wall
if (self.walls[3]) {
var leftWall = LK.getAsset('wall', {
width: wallThickness,
height: s,
color: wallColor,
shape: 'box',
anchorX: 0,
anchorY: 0
});
leftWall.x = 0;
leftWall.y = 0;
self.addChild(leftWall);
self.wallGraphics.push(leftWall);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf7f7f7
});
/****
* Game Code
****/
// Maze parameters
var MAZE_COLS = 18;
var MAZE_ROWS = 24;
var MAZE_MARGIN = 40; // px margin around maze (reduce margin to fit bigger maze)
var mazeCellSize = 0;
var mazeOriginX = 0;
var mazeOriginY = 0;
var maze = [];
var mazeContainer = new Container();
game.addChild(mazeContainer);
// Ball and Finish
var ball = null;
var finish = null;
// Ball physics
var ballPos = {
x: 0,
y: 0
};
var ballVel = {
x: 0,
y: 0
};
var ballMaxSpeed = 32;
var ballAccel = 2.2;
var ballFriction = 0.96;
// Gyro state
var gyro = {
x: 0,
y: 0
};
// Timer
var startTime = 0;
var elapsedTime = 0;
// Final score text removed as per requirements
// Helper: Clamp
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Helper: Shuffle array
function shuffle(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
return arr;
}
// Maze generation: Recursive backtracker with bias for normal difficulty route
function generateMaze(cols, rows) {
var grid = [];
for (var y = 0; y < rows; y++) {
var row = [];
for (var x = 0; x < cols; x++) {
var cell = new MazeCell();
cell.xIndex = x;
cell.yIndex = y;
cell.size = mazeCellSize;
cell.visited = false;
row.push(cell);
}
grid.push(row);
}
function cellAt(x, y) {
if (x < 0 || y < 0 || x >= cols || y >= rows) return null;
return grid[y][x];
}
var stack = [];
var current = grid[0][0];
current.visited = true;
var visitedCount = 1;
var totalCells = cols * rows;
// Bias: prefer to move generally toward the finish, but not always
var finishX = cols - 1;
var finishY = rows - 1;
while (visitedCount < totalCells) {
// Find unvisited neighbors
var neighbors = [];
var dirs = [[0, -1, 0],
// top
[1, 0, 1],
// right
[0, 1, 2],
// bottom
[-1, 0, 3] // left
];
for (var d = 0; d < 4; d++) {
var nx = current.xIndex + dirs[d][0];
var ny = current.yIndex + dirs[d][1];
var neighbor = cellAt(nx, ny);
if (neighbor && !neighbor.visited) {
neighbors.push({
cell: neighbor,
dir: d
});
}
}
if (neighbors.length > 0) {
// Bias: 60% chance to pick neighbor that is closer to finish, else random
var pick;
if (Math.random() < 0.6) {
// Sort neighbors by distance to finish, pick the closest
neighbors.sort(function (a, b) {
var da = Math.abs(a.cell.xIndex - finishX) + Math.abs(a.cell.yIndex - finishY);
var db = Math.abs(b.cell.xIndex - finishX) + Math.abs(b.cell.yIndex - finishY);
return da - db;
});
// 50% chance to pick the closest, 50% chance to pick second closest (if exists)
if (neighbors.length > 1 && Math.random() < 0.5) {
pick = neighbors[1];
} else {
pick = neighbors[0];
}
} else {
pick = neighbors[Math.floor(Math.random() * neighbors.length)];
}
// Remove wall between current and neighbor
current.walls[pick.dir] = false;
var opp = (pick.dir + 2) % 4;
pick.cell.walls[opp] = false;
stack.push(current);
current = pick.cell;
current.visited = true;
visitedCount++;
} else if (stack.length > 0) {
current = stack.pop();
}
}
// Draw all cells
for (var y = 0; y < rows; y++) {
for (var x = 0; x < cols; x++) {
grid[y][x].draw();
}
}
return grid;
}
// Place maze in center of screen, calculate cell size
function layoutMaze() {
var availW = 2048 - MAZE_MARGIN * 2;
var availH = 2732 - MAZE_MARGIN * 2;
mazeCellSize = Math.floor(Math.min(availW / MAZE_COLS, availH / MAZE_ROWS));
var mazeW = mazeCellSize * MAZE_COLS;
var mazeH = mazeCellSize * MAZE_ROWS;
mazeOriginX = Math.floor((2048 - mazeW) / 2);
mazeOriginY = Math.floor((2732 - mazeH) / 2);
mazeContainer.x = mazeOriginX;
mazeContainer.y = mazeOriginY;
}
// Remove all children from mazeContainer
function clearMaze() {
while (mazeContainer.children.length > 0) {
mazeContainer.removeChild(mazeContainer.children[0]);
}
}
// Place ball at start
function placeBall() {
if (ball) {
ball.destroy();
}
ball = new Ball();
ball.setSize(Math.floor(mazeCellSize * 0.55));
ballPos.x = mazeCellSize / 2;
ballPos.y = mazeCellSize / 2;
ball.x = ballPos.x;
ball.y = ballPos.y;
ballVel.x = 0;
ballVel.y = 0;
mazeContainer.addChild(ball);
}
// Place finish at bottom-right cell
function placeFinish() {
if (finish) {
finish.destroy();
}
finish = new Finish();
finish.setSize(Math.floor(mazeCellSize * 0.55));
// Pick a random cell in the maze that is not the start cell (0,0)
var finishCellX = 0;
var finishCellY = 0;
while (finishCellX === 0 && finishCellY === 0) {
finishCellX = Math.floor(Math.random() * MAZE_COLS);
finishCellY = Math.floor(Math.random() * MAZE_ROWS);
}
var fx = finishCellX * mazeCellSize + mazeCellSize / 2;
var fy = finishCellY * mazeCellSize + mazeCellSize / 2;
finish.x = fx;
finish.y = fy;
mazeContainer.addChild(finish);
// Store finish cell for win check
finish.finishCellX = finishCellX;
finish.finishCellY = finishCellY;
}
// Check collision between ball and maze walls
function resolveBallMazeCollision() {
var r = ball.radius();
var cx = Math.floor(ballPos.x / mazeCellSize);
var cy = Math.floor(ballPos.y / mazeCellSize);
// Clamp to maze bounds
cx = clamp(cx, 0, MAZE_COLS - 1);
cy = clamp(cy, 0, MAZE_ROWS - 1);
var cell = maze[cy][cx];
var px = ballPos.x - cx * mazeCellSize;
var py = ballPos.y - cy * mazeCellSize;
var s = mazeCellSize;
var wallThickness = Math.max(8, Math.floor(s * 0.12));
// Top wall
if (cell.walls[0] && py - r < wallThickness) {
ballPos.y = cy * mazeCellSize + wallThickness + r;
ballVel.y = Math.max(0, ballVel.y);
}
// Right wall
if (cell.walls[1] && px + r > s - wallThickness) {
ballPos.x = cx * mazeCellSize + s - wallThickness - r;
ballVel.x = Math.min(0, ballVel.x);
}
// Bottom wall
if (cell.walls[2] && py + r > s - wallThickness) {
ballPos.y = cy * mazeCellSize + s - wallThickness - r;
ballVel.y = Math.min(0, ballVel.y);
}
// Left wall
if (cell.walls[3] && px - r < wallThickness) {
ballPos.x = cx * mazeCellSize + wallThickness + r;
ballVel.x = Math.max(0, ballVel.x);
}
// Clamp to maze area
ballPos.x = clamp(ballPos.x, r, MAZE_COLS * mazeCellSize - r);
ballPos.y = clamp(ballPos.y, r, MAZE_ROWS * mazeCellSize - r);
}
// Check if ball reached finish
function checkBallFinish() {
// Use the actual finish cell position
var fx = finish.finishCellX * mazeCellSize + mazeCellSize / 2;
var fy = finish.finishCellY * mazeCellSize + mazeCellSize / 2;
var dx = ballPos.x - fx;
var dy = ballPos.y - fy;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < ball.radius() + finish.finishAsset.width / 2 - 6) {
return true;
}
return false;
}
// Start new game
function startGame() {
clearMaze();
layoutMaze();
maze = generateMaze(MAZE_COLS, MAZE_ROWS);
// Add all cells to container
for (var y = 0; y < MAZE_ROWS; y++) {
for (var x = 0; x < MAZE_COLS; x++) {
var cell = maze[y][x];
cell.x = x * mazeCellSize;
cell.y = y * mazeCellSize;
mazeContainer.addChild(cell);
}
}
placeFinish();
placeBall();
startTime = Date.now();
elapsedTime = 0;
}
// Gyroscope/motion support
function handleDeviceMotion(event) {
// event.accelerationIncludingGravity.{x,y,z}
// On iOS, x is left/right, y is up/down, z is toward/away
// On Android, axes may be swapped, so we allow both
var ax = 0,
ay = 0;
if (event.accelerationIncludingGravity) {
ax = event.accelerationIncludingGravity.x || 0;
ay = event.accelerationIncludingGravity.y || 0;
}
// Heuristic: On portrait, invert y for natural tilt
gyro.x = clamp(ax, -6, 6);
gyro.y = clamp(-ay, -6, 6);
}
if (typeof window !== "undefined" && window.addEventListener) {
window.addEventListener('devicemotion', handleDeviceMotion, true);
}
// Fallback: Touch drag to control ball if no gyro
var dragging = false;
var lastTouch = {
x: 0,
y: 0
};
game.down = function (x, y, obj) {
// Only start drag if inside maze area
var gx = x - mazeOriginX;
var gy = y - mazeOriginY;
if (gx >= 0 && gx < mazeCellSize * MAZE_COLS && gy >= 0 && gy < mazeCellSize * MAZE_ROWS) {
dragging = true;
lastTouch.x = x;
lastTouch.y = y;
}
};
game.up = function (x, y, obj) {
dragging = false;
};
game.move = function (x, y, obj) {
if (dragging) {
var dx = x - lastTouch.x;
var dy = y - lastTouch.y;
// Simulate tilt by touch drag
gyro.x = clamp(dx * 0.2, -6, 6);
gyro.y = clamp(dy * 0.2, -6, 6);
lastTouch.x = x;
lastTouch.y = y;
}
};
// Main update loop
game.update = function () {
// Ball physics
// Use gyro.x/gyro.y as acceleration
ballVel.x += ballAccel * gyro.x * 0.18;
ballVel.y += ballAccel * gyro.y * 0.18;
// Friction
ballVel.x *= ballFriction;
ballVel.y *= ballFriction;
// Clamp speed
ballVel.x = clamp(ballVel.x, -ballMaxSpeed, ballMaxSpeed);
ballVel.y = clamp(ballVel.y, -ballMaxSpeed, ballMaxSpeed);
// Move ball
ballPos.x += ballVel.x;
ballPos.y += ballVel.y;
resolveBallMazeCollision();
ball.x = ballPos.x;
ball.y = ballPos.y;
// Timer
elapsedTime = (Date.now() - startTime) / 1000;
// Win condition
if (checkBallFinish()) {
LK.getSound('finish_reached').play();
LK.effects.flashScreen(0x44de83, 800);
LK.showYouWin();
}
};
// Start game on load
startGame();
// Reset game on game over or win
LK.on('gameover', function () {
startGame();
});
LK.on('youwin', function () {
startGame();
});