User prompt
arkaplanı düzenle her bir box çizgisi belli olsun
User prompt
oyunu biraz daha büyüt
User prompt
hayır yılan kafasını dik bir şekilde yüklüyorum. o yüzden farklı açıyla yapmalısın
User prompt
baktığımız yöne doğru yılan kafası otomatik dönsün
User prompt
her şeyi biraz büyüt.
User prompt
add The snake head asset should be a separate asset at the very front, and the snake segment should appear immediately behind it
User prompt
The snake head should be a separate asset at the very front, and the snake segment should appear immediately behind it
User prompt
yılanın kafası ayrı bir texture olsun
Code edit (1 edits merged)
Please save this source code
User prompt
Kırmızı Duvar Yılanı
Initial prompt
2d yılan oyunu yap. yılan, kendi kuyruğuna çarptığında ve ekranı saran kırmızı duvarlara çarptığında game over ekranı gelsin ve kullanıcı yansın. her yem yediğinde kuyruğu uzasın ve max uzunluğa ulaşırsa oyun bitsin
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Food Class var Food = Container.expand(function () { var self = Container.call(this); // Food is a yellow ellipse var foodAsset = self.attachAsset('food', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Food2 Class (rare food) var Food2 = Container.expand(function () { var self = Container.call(this); // Use food2 asset var foodAsset = self.attachAsset('food2', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Food3 Class (health food) var Food3 = Container.expand(function () { var self = Container.call(this); // Use food3 asset var foodAsset = self.attachAsset('food3', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Food4 Class (special food every 4th) var Food4 = Container.expand(function () { var self = Container.call(this); // Use food4 asset var foodAsset = self.attachAsset('food4', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Snake Head Class var SnakeHead = Container.expand(function () { var self = Container.call(this); // Head is a unique asset var headAsset = self.attachAsset('snakeHead', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Snake Segment Class var SnakeSegment = Container.expand(function () { var self = Container.call(this); // Each segment is a green box var segmentAsset = self.attachAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Wall Class var Wall = Container.expand(function () { var self = Container.call(this); // Wall is a red box var wallAsset = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // --- Game Constants --- // --- Asset Initialization --- // Snake Head Class var GRID_SIZE = 180; // Each cell is 180x180 px (increased) var GRID_COLS = Math.floor(2048 / GRID_SIZE); // 11 var GRID_ROWS = Math.floor(2732 / GRID_SIZE); // 15 var BOARD_WIDTH = GRID_COLS * GRID_SIZE; var BOARD_HEIGHT = GRID_ROWS * GRID_SIZE; var BOARD_OFFSET_X = Math.floor((2048 - BOARD_WIDTH) / 2); var BOARD_OFFSET_Y = Math.floor((2732 - BOARD_HEIGHT) / 2); var SNAKE_START_LENGTH = 2; var SNAKE_MAX_LENGTH = (GRID_COLS - 2) * (GRID_ROWS - 2); // Win when snake fills entire playable area // --- Game State --- var snake = []; var snakeDir = { x: 1, y: 0 }; // Start moving right var nextDir = { x: 1, y: 0 }; var moveTimer = 0; var MOVE_INTERVAL = 210; // ms (biraz yavaşlatıldı) var foods = []; var walls = []; var isDead = false; var isWin = false; var pendingGrowth = 0; var lastTouch = null; var darknessOverlay = null; var isDarkened = false; var isSlowed = false; var originalMoveInterval = MOVE_INTERVAL; var darknessTimer = null; var speedTimer = null; var isMultiplierActive = false; var multiplierTimer = null; var scoreMultiplier = 1; var isYellowBackgroundActive = false; var yellowBackgroundTimer = null; var originalBackgroundColor = 0x181818; var playerLives = 1; var lastTapTime = 0; var isDoubleTouch = false; var speedMultiplier = 1; var touchStartTime = 0; var DOUBLE_TAP_THRESHOLD = 300; // ms // --- Score UI --- var scoreTxt = new Text2('1', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Health UI --- var healthTxt = new Text2('♥ 1', { size: 80, fill: 0xFF0000 }); healthTxt.anchor.set(1, 0); LK.gui.topRight.addChild(healthTxt); // --- Helper Functions --- function gridToPos(col, row) { return { x: BOARD_OFFSET_X + col * GRID_SIZE + GRID_SIZE / 2, y: BOARD_OFFSET_Y + row * GRID_SIZE + GRID_SIZE / 2 }; } function posToGrid(x, y) { var col = Math.floor((x - BOARD_OFFSET_X) / GRID_SIZE); var row = Math.floor((y - BOARD_OFFSET_Y) / GRID_SIZE); return { col: col, row: row }; } function randomFreeCell() { // Returns a random cell not occupied by snake or wall var free = []; for (var r = 1; r < GRID_ROWS - 1; r++) { for (var c = 1; c < GRID_COLS - 1; c++) { var occupied = false; for (var i = 0; i < snake.length; i++) { if (snake[i].col === c && snake[i].row === r) { occupied = true; break; } } for (var f = 0; f < foods.length; f++) { if (foods[f].col === c && foods[f].row === r) { occupied = true; break; } } if (!occupied) free.push({ col: c, row: r }); } } if (free.length === 0) return null; return free[Math.floor(Math.random() * free.length)]; } function setScore(val) { LK.setScore(val); scoreTxt.setText(val); } // --- Wall Setup --- function createWalls() { // Top and bottom for (var c = 0; c < GRID_COLS; c++) { var wallTop = new Wall(); var wallBottom = new Wall(); var posTop = gridToPos(c, 0); var posBottom = gridToPos(c, GRID_ROWS - 1); wallTop.x = posTop.x; wallTop.y = posTop.y; wallTop.col = c; wallTop.row = 0; wallBottom.x = posBottom.x; wallBottom.y = posBottom.y; wallBottom.col = c; wallBottom.row = GRID_ROWS - 1; game.addChild(wallTop); game.addChild(wallBottom); walls.push(wallTop, wallBottom); } // Left and right for (var r = 1; r < GRID_ROWS - 1; r++) { var wallLeft = new Wall(); var wallRight = new Wall(); var posLeft = gridToPos(0, r); var posRight = gridToPos(GRID_COLS - 1, r); wallLeft.x = posLeft.x; wallLeft.y = posLeft.y; wallLeft.col = 0; wallLeft.row = r; wallRight.x = posRight.x; wallRight.y = posRight.y; wallRight.col = GRID_COLS - 1; wallRight.row = r; game.addChild(wallLeft); game.addChild(wallRight); walls.push(wallLeft, wallRight); } } // --- Snake Setup --- function createSnake() { var startCol = Math.floor(GRID_COLS / 2); var startRow = Math.floor(GRID_ROWS / 2); // First segment is the head var head = new SnakeHead(); head.col = startCol; head.row = startRow; var pos = gridToPos(head.col, head.row); head.x = pos.x; head.y = pos.y; game.addChild(head); snake.push(head); // The rest are segments for (var i = 1; i < SNAKE_START_LENGTH; i++) { var seg = new SnakeSegment(); seg.col = startCol - i; seg.row = startRow; var pos2 = gridToPos(seg.col, seg.row); seg.x = pos2.x; seg.y = pos2.y; game.addChild(seg); snake.push(seg); } } // --- Food Setup --- // Track how many foods have been spawned var foodSpawnCount = 0; var food4Timers = []; // Track timers for Food4 items function spawnFood() { // Determine target food count based on score var targetFoodCount = LK.getScore() > 10 ? 2 : 1; // If we already have the target number of foods or more, don't spawn any new ones if (foods.length >= targetFoodCount) { return; } // Calculate how many foods we need to spawn to reach target var foodsToSpawn = targetFoodCount - foods.length; // Spawn the required number of foods for (var f = 0; f < foodsToSpawn; f++) { var cell = randomFreeCell(); if (!cell) continue; foodSpawnCount = typeof foodSpawnCount === "number" ? foodSpawnCount + 1 : 1; var newFood; var randomChance = Math.random(); if (foodSpawnCount % 4 === 0) { newFood = new Food4(); // Start timer to convert Food4 to Food after 10 seconds var food4Timer = LK.setTimeout(function () { // Check if this Food4 still exists and hasn't been eaten var foodIndex = foods.indexOf(newFood); if (foodIndex !== -1 && newFood instanceof Food4) { // Convert Food4 to regular Food var oldCol = newFood.col; var oldRow = newFood.row; var oldPos = gridToPos(oldCol, oldRow); // Remove old Food4 newFood.destroy(); foods.splice(foodIndex, 1); // Create new Food in same position var regularFood = new Food(); regularFood.col = oldCol; regularFood.row = oldRow; regularFood.x = oldPos.x; regularFood.y = oldPos.y; game.addChild(regularFood); foods.push(regularFood); } // Remove timer from tracking array var timerIndex = food4Timers.indexOf(food4Timer); if (timerIndex !== -1) { food4Timers.splice(timerIndex, 1); } }, 10000); // Track the timer food4Timers.push(food4Timer); } else if (randomChance < 0.10) { // 10% chance for Food3 newFood = new Food3(); } else if (randomChance < 0.20) { // 20% chance for Food2 (but only if not Food3) newFood = new Food2(); } else { newFood = new Food(); } newFood.col = cell.col; newFood.row = cell.row; var pos = gridToPos(cell.col, cell.row); newFood.x = pos.x; newFood.y = pos.y; game.addChild(newFood); foods.push(newFood); } } // --- Collision Checks --- function isWall(col, row) { return col === 0 || row === 0 || col === GRID_COLS - 1 || row === GRID_ROWS - 1; } function isSnake(col, row, skipHead) { for (var i = skipHead ? 1 : 0; i < snake.length; i++) { if (snake[i].col === col && snake[i].row === row) return true; } return false; } // --- Game Over / Win --- function triggerGameOver() { if (isDead) return; playerLives--; healthTxt.setText('♥ ' + playerLives); if (playerLives <= 0) { isDead = true; LK.effects.flashScreen(0xff0000, 1000); // Create a container for the game over display var goCont = new Container(); // Create large snake head asset (350x350) var snakeHeadAsset = LK.getAsset('snakeHead', { anchorX: 0.5, anchorY: 0.5, width: 350, height: 350 }); snakeHeadAsset.x = 350 / 2; snakeHeadAsset.y = 350 / 2; // Create large food asset (350x350) var foodAsset = LK.getAsset('food', { anchorX: 0.5, anchorY: 0.5, width: 350, height: 350 }); foodAsset.x = 350 + 350 / 2 + 40; // 40px gap between foodAsset.y = 350 / 2; // Set container size and center on screen var totalWidth = 350 * 2 + 40; var totalHeight = 350; goCont.x = 0; goCont.y = 0; goCont.width = totalWidth; goCont.height = totalHeight; goCont.addChild(snakeHeadAsset); goCont.addChild(foodAsset); // Show the container as a popup overlay (LK will remove it on reset) LK.showGameOver({ customContent: goCont, customContentWidth: totalWidth, customContentHeight: totalHeight, customContentY: 120 // Place below the menu (menu is top 100px, so 120px for margin) }); } else { // Player still has lives, flash red and continue LK.effects.flashScreen(0xff0000, 500); // Reset snake tail length to 1 (only keep the head) for (var i = snake.length - 1; i > 0; i--) { snake[i].destroy(); snake.splice(i, 1); } // Move snake head to a safe position automatically var head = snake[0]; var safeCell = randomFreeCell(); if (safeCell) { // Update head position data head.col = safeCell.col; head.row = safeCell.row; var safePos = gridToPos(safeCell.col, safeCell.row); // Smoothly animate to safe position tween(head, { x: safePos.x, y: safePos.y }, { duration: 300, easing: tween.easeOut }); } } } function triggerWin() { if (isWin) return; isWin = true; LK.effects.flashScreen(0x00ff00, 1000); LK.showYouWin(); } // --- Input Handling (Touch/Drag) --- var dragStart = null; var dragDir = null; function getDirectionFromDrag(dx, dy) { if (Math.abs(dx) > Math.abs(dy)) { return dx > 0 ? { x: 1, y: 0 } : { x: -1, y: 0 }; } else { return dy > 0 ? { x: 0, y: 1 } : { x: 0, y: -1 }; } } game.down = function (x, y, obj) { dragStart = { x: x, y: y }; dragDir = null; // Double-tap detection var currentTime = Date.now(); if (currentTime - lastTapTime < DOUBLE_TAP_THRESHOLD) { // Double tap detected isDoubleTouch = true; speedMultiplier = 2; touchStartTime = currentTime; } else { // Single tap isDoubleTouch = false; speedMultiplier = 1; } lastTapTime = currentTime; }; game.move = function (x, y, obj) { if (!dragStart) return; var dx = x - dragStart.x; var dy = y - dragStart.y; if (Math.abs(dx) > 40 || Math.abs(dy) > 40) { var dir = getDirectionFromDrag(dx, dy); // Prevent reversing if ((dir.x !== -snakeDir.x || dir.y !== -snakeDir.y) && (dir.x !== snakeDir.x || dir.y !== snakeDir.y)) { nextDir = dir; dragDir = dir; dragStart = null; } } }; game.up = function (x, y, obj) { dragStart = null; dragDir = null; // Reset speed when touch is released isDoubleTouch = false; speedMultiplier = 1; }; // --- Main Game Loop --- game.update = function () { if (isDead || isWin) return; moveTimer += 1000 / 60; var currentMoveInterval = MOVE_INTERVAL / speedMultiplier; if (moveTimer < currentMoveInterval) return; moveTimer = 0; // Update direction if (nextDir.x !== -snakeDir.x || nextDir.y !== -snakeDir.y) { snakeDir = { x: nextDir.x, y: nextDir.y }; } // Calculate new head position var head = snake[0]; var newCol = head.col + snakeDir.x; var newRow = head.row + snakeDir.y; // Check collisions if (isWall(newCol, newRow) || isSnake(newCol, newRow, false)) { triggerGameOver(); return; } // Move snake // Remove old head and replace with segment var oldHead = snake[0]; var newSegment = new SnakeSegment(); newSegment.col = oldHead.col; newSegment.row = oldHead.row; newSegment.x = oldHead.x; newSegment.y = oldHead.y; game.addChild(newSegment); snake[0].destroy(); snake[0] = newSegment; // Add new head at the front var newHead = new SnakeHead(); newHead.col = newCol; newHead.row = newRow; var pos = gridToPos(newCol, newRow); newHead.x = pos.x; newHead.y = pos.y; // Set rotation based on direction (asset is upright, so rotate by +90deg for right) if (snakeDir.x === 1 && snakeDir.y === 0) { newHead.rotation = Math.PI / 2; // Right } else if (snakeDir.x === -1 && snakeDir.y === 0) { newHead.rotation = -Math.PI / 2; // Left } else if (snakeDir.x === 0 && snakeDir.y === -1) { newHead.rotation = 0; // Up } else if (snakeDir.x === 0 && snakeDir.y === 1) { newHead.rotation = Math.PI; // Down } game.addChild(newHead); snake.unshift(newHead); // Check food collision var ateFood = false; var eatenFood = null; for (var f = 0; f < foods.length; f++) { if (foods[f].col === newCol && foods[f].row === newRow) { ateFood = true; eatenFood = foods[f]; break; } } if (ateFood) { // Remove the eaten food from the foods array and destroy it for (var i = 0; i < foods.length; i++) { if (foods[i] === eatenFood) { foods[i].destroy(); foods.splice(i, 1); break; } } // Calculate score based on food type and multiplier var pointsToAdd; if (eatenFood instanceof Food4) { pointsToAdd = 1 * scoreMultiplier; // Food4 gives 1 point } else { pointsToAdd = 2 * scoreMultiplier; // Food and Food2 give 2 points } setScore(LK.getScore() + pointsToAdd); pendingGrowth += 1; // Play feeding sound LK.getSound('feeding').play(); // Handle Food3 special effect - give health (max 3 lives) or bonus points if at max health if (eatenFood instanceof Food3) { if (playerLives < 3) { playerLives++; healthTxt.setText('♥ ' + playerLives); } else { // Player is at max health (3 lives), give 50 bonus points setScore(LK.getScore() + 50); } } // Handle Food2 special effect - 10x multiplier for 5 seconds if (eatenFood instanceof Food2) { if (!isMultiplierActive) { isMultiplierActive = true; scoreMultiplier = 10; // Clear any existing multiplier timer if (multiplierTimer) { LK.clearTimeout(multiplierTimer); } // Remove multiplier after exactly 5 seconds multiplierTimer = LK.setTimeout(function () { scoreMultiplier = 1; isMultiplierActive = false; multiplierTimer = null; }, 5000); } // Yellow background effect for 5 seconds if (!isYellowBackgroundActive) { isYellowBackgroundActive = true; game.setBackgroundColor(0xFFFF00); // Yellow color // Clear any existing yellow background timer if (yellowBackgroundTimer) { LK.clearTimeout(yellowBackgroundTimer); } // Restore original background after exactly 5 seconds yellowBackgroundTimer = LK.setTimeout(function () { game.setBackgroundColor(originalBackgroundColor); isYellowBackgroundActive = false; yellowBackgroundTimer = null; }, 5000); } } // Play feeding2 sound only when Food4 is eaten if (eatenFood instanceof Food4) { // Clear any associated timer since Food4 was eaten for (var t = food4Timers.length - 1; t >= 0; t--) { LK.clearTimeout(food4Timers[t]); food4Timers.splice(t, 1); } LK.getSound('feeding2').play(); // Create darkness effect for 10 seconds if (!isDarkened) { isDarkened = true; darknessOverlay = LK.getAsset('snakeSegment', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732, tint: 0x000000 }); darknessOverlay.alpha = 0; game.addChild(darknessOverlay); // Fade in darkness tween(darknessOverlay, { alpha: 0.7 }, { duration: 500 }); // Clear any existing darkness timer if (darknessTimer) { LK.clearTimeout(darknessTimer); } // Remove darkness after exactly 5 seconds darknessTimer = LK.setTimeout(function () { if (darknessOverlay) { tween(darknessOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { if (darknessOverlay) { darknessOverlay.destroy(); darknessOverlay = null; } isDarkened = false; darknessTimer = null; } }); } }, 5000); } // Reduce snake speed by 50% for 10 seconds if (!isSlowed) { isSlowed = true; MOVE_INTERVAL = originalMoveInterval * 1.5; // 50% slower // Clear any existing speed timer if (speedTimer) { LK.clearTimeout(speedTimer); } // Restore original speed after exactly 5 seconds speedTimer = LK.setTimeout(function () { MOVE_INTERVAL = originalMoveInterval; isSlowed = false; speedTimer = null; }, 5000); } } spawnFood(); // Win condition if (snake.length >= SNAKE_MAX_LENGTH) { triggerWin(); return; } } // Remove tail if not growing if (pendingGrowth > 0) { pendingGrowth--; } else { var tail = snake.pop(); tail.destroy(); } }; // --- Draw Board Grid Background --- var gridBg = new Container(); for (var r = 0; r < GRID_ROWS; r++) { for (var c = 0; c < GRID_COLS; c++) { // Use a light color for grid lines, and a slightly darker fill for the cell var cell = LK.getAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5, x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2, y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2, width: GRID_SIZE, height: GRID_SIZE, tint: (r + c) % 2 === 0 ? 0x232323 : 0x1a1a1a // subtle checkerboard for clarity }); // Add a border by overlaying a slightly smaller cell of the same color as background var border = LK.getAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5, x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2, y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2, width: GRID_SIZE - 8, height: GRID_SIZE - 8, tint: (r + c) % 2 === 0 ? 0x181818 : 0x181818 // match background }); gridBg.addChild(cell); gridBg.addChild(border); } } game.addChild(gridBg); // --- Game Initialization --- createWalls(); createSnake(); spawnFood(); setScore(1); ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Food Class
var Food = Container.expand(function () {
var self = Container.call(this);
// Food is a yellow ellipse
var foodAsset = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Food2 Class (rare food)
var Food2 = Container.expand(function () {
var self = Container.call(this);
// Use food2 asset
var foodAsset = self.attachAsset('food2', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Food3 Class (health food)
var Food3 = Container.expand(function () {
var self = Container.call(this);
// Use food3 asset
var foodAsset = self.attachAsset('food3', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Food4 Class (special food every 4th)
var Food4 = Container.expand(function () {
var self = Container.call(this);
// Use food4 asset
var foodAsset = self.attachAsset('food4', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Snake Head Class
var SnakeHead = Container.expand(function () {
var self = Container.call(this);
// Head is a unique asset
var headAsset = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Snake Segment Class
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
// Each segment is a green box
var segmentAsset = self.attachAsset('snakeSegment', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Wall Class
var Wall = Container.expand(function () {
var self = Container.call(this);
// Wall is a red box
var wallAsset = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// --- Game Constants ---
// --- Asset Initialization ---
// Snake Head Class
var GRID_SIZE = 180; // Each cell is 180x180 px (increased)
var GRID_COLS = Math.floor(2048 / GRID_SIZE); // 11
var GRID_ROWS = Math.floor(2732 / GRID_SIZE); // 15
var BOARD_WIDTH = GRID_COLS * GRID_SIZE;
var BOARD_HEIGHT = GRID_ROWS * GRID_SIZE;
var BOARD_OFFSET_X = Math.floor((2048 - BOARD_WIDTH) / 2);
var BOARD_OFFSET_Y = Math.floor((2732 - BOARD_HEIGHT) / 2);
var SNAKE_START_LENGTH = 2;
var SNAKE_MAX_LENGTH = (GRID_COLS - 2) * (GRID_ROWS - 2); // Win when snake fills entire playable area
// --- Game State ---
var snake = [];
var snakeDir = {
x: 1,
y: 0
}; // Start moving right
var nextDir = {
x: 1,
y: 0
};
var moveTimer = 0;
var MOVE_INTERVAL = 210; // ms (biraz yavaşlatıldı)
var foods = [];
var walls = [];
var isDead = false;
var isWin = false;
var pendingGrowth = 0;
var lastTouch = null;
var darknessOverlay = null;
var isDarkened = false;
var isSlowed = false;
var originalMoveInterval = MOVE_INTERVAL;
var darknessTimer = null;
var speedTimer = null;
var isMultiplierActive = false;
var multiplierTimer = null;
var scoreMultiplier = 1;
var isYellowBackgroundActive = false;
var yellowBackgroundTimer = null;
var originalBackgroundColor = 0x181818;
var playerLives = 1;
var lastTapTime = 0;
var isDoubleTouch = false;
var speedMultiplier = 1;
var touchStartTime = 0;
var DOUBLE_TAP_THRESHOLD = 300; // ms
// --- Score UI ---
var scoreTxt = new Text2('1', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Health UI ---
var healthTxt = new Text2('♥ 1', {
size: 80,
fill: 0xFF0000
});
healthTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(healthTxt);
// --- Helper Functions ---
function gridToPos(col, row) {
return {
x: BOARD_OFFSET_X + col * GRID_SIZE + GRID_SIZE / 2,
y: BOARD_OFFSET_Y + row * GRID_SIZE + GRID_SIZE / 2
};
}
function posToGrid(x, y) {
var col = Math.floor((x - BOARD_OFFSET_X) / GRID_SIZE);
var row = Math.floor((y - BOARD_OFFSET_Y) / GRID_SIZE);
return {
col: col,
row: row
};
}
function randomFreeCell() {
// Returns a random cell not occupied by snake or wall
var free = [];
for (var r = 1; r < GRID_ROWS - 1; r++) {
for (var c = 1; c < GRID_COLS - 1; c++) {
var occupied = false;
for (var i = 0; i < snake.length; i++) {
if (snake[i].col === c && snake[i].row === r) {
occupied = true;
break;
}
}
for (var f = 0; f < foods.length; f++) {
if (foods[f].col === c && foods[f].row === r) {
occupied = true;
break;
}
}
if (!occupied) free.push({
col: c,
row: r
});
}
}
if (free.length === 0) return null;
return free[Math.floor(Math.random() * free.length)];
}
function setScore(val) {
LK.setScore(val);
scoreTxt.setText(val);
}
// --- Wall Setup ---
function createWalls() {
// Top and bottom
for (var c = 0; c < GRID_COLS; c++) {
var wallTop = new Wall();
var wallBottom = new Wall();
var posTop = gridToPos(c, 0);
var posBottom = gridToPos(c, GRID_ROWS - 1);
wallTop.x = posTop.x;
wallTop.y = posTop.y;
wallTop.col = c;
wallTop.row = 0;
wallBottom.x = posBottom.x;
wallBottom.y = posBottom.y;
wallBottom.col = c;
wallBottom.row = GRID_ROWS - 1;
game.addChild(wallTop);
game.addChild(wallBottom);
walls.push(wallTop, wallBottom);
}
// Left and right
for (var r = 1; r < GRID_ROWS - 1; r++) {
var wallLeft = new Wall();
var wallRight = new Wall();
var posLeft = gridToPos(0, r);
var posRight = gridToPos(GRID_COLS - 1, r);
wallLeft.x = posLeft.x;
wallLeft.y = posLeft.y;
wallLeft.col = 0;
wallLeft.row = r;
wallRight.x = posRight.x;
wallRight.y = posRight.y;
wallRight.col = GRID_COLS - 1;
wallRight.row = r;
game.addChild(wallLeft);
game.addChild(wallRight);
walls.push(wallLeft, wallRight);
}
}
// --- Snake Setup ---
function createSnake() {
var startCol = Math.floor(GRID_COLS / 2);
var startRow = Math.floor(GRID_ROWS / 2);
// First segment is the head
var head = new SnakeHead();
head.col = startCol;
head.row = startRow;
var pos = gridToPos(head.col, head.row);
head.x = pos.x;
head.y = pos.y;
game.addChild(head);
snake.push(head);
// The rest are segments
for (var i = 1; i < SNAKE_START_LENGTH; i++) {
var seg = new SnakeSegment();
seg.col = startCol - i;
seg.row = startRow;
var pos2 = gridToPos(seg.col, seg.row);
seg.x = pos2.x;
seg.y = pos2.y;
game.addChild(seg);
snake.push(seg);
}
}
// --- Food Setup ---
// Track how many foods have been spawned
var foodSpawnCount = 0;
var food4Timers = []; // Track timers for Food4 items
function spawnFood() {
// Determine target food count based on score
var targetFoodCount = LK.getScore() > 10 ? 2 : 1;
// If we already have the target number of foods or more, don't spawn any new ones
if (foods.length >= targetFoodCount) {
return;
}
// Calculate how many foods we need to spawn to reach target
var foodsToSpawn = targetFoodCount - foods.length;
// Spawn the required number of foods
for (var f = 0; f < foodsToSpawn; f++) {
var cell = randomFreeCell();
if (!cell) continue;
foodSpawnCount = typeof foodSpawnCount === "number" ? foodSpawnCount + 1 : 1;
var newFood;
var randomChance = Math.random();
if (foodSpawnCount % 4 === 0) {
newFood = new Food4();
// Start timer to convert Food4 to Food after 10 seconds
var food4Timer = LK.setTimeout(function () {
// Check if this Food4 still exists and hasn't been eaten
var foodIndex = foods.indexOf(newFood);
if (foodIndex !== -1 && newFood instanceof Food4) {
// Convert Food4 to regular Food
var oldCol = newFood.col;
var oldRow = newFood.row;
var oldPos = gridToPos(oldCol, oldRow);
// Remove old Food4
newFood.destroy();
foods.splice(foodIndex, 1);
// Create new Food in same position
var regularFood = new Food();
regularFood.col = oldCol;
regularFood.row = oldRow;
regularFood.x = oldPos.x;
regularFood.y = oldPos.y;
game.addChild(regularFood);
foods.push(regularFood);
}
// Remove timer from tracking array
var timerIndex = food4Timers.indexOf(food4Timer);
if (timerIndex !== -1) {
food4Timers.splice(timerIndex, 1);
}
}, 10000);
// Track the timer
food4Timers.push(food4Timer);
} else if (randomChance < 0.10) {
// 10% chance for Food3
newFood = new Food3();
} else if (randomChance < 0.20) {
// 20% chance for Food2 (but only if not Food3)
newFood = new Food2();
} else {
newFood = new Food();
}
newFood.col = cell.col;
newFood.row = cell.row;
var pos = gridToPos(cell.col, cell.row);
newFood.x = pos.x;
newFood.y = pos.y;
game.addChild(newFood);
foods.push(newFood);
}
}
// --- Collision Checks ---
function isWall(col, row) {
return col === 0 || row === 0 || col === GRID_COLS - 1 || row === GRID_ROWS - 1;
}
function isSnake(col, row, skipHead) {
for (var i = skipHead ? 1 : 0; i < snake.length; i++) {
if (snake[i].col === col && snake[i].row === row) return true;
}
return false;
}
// --- Game Over / Win ---
function triggerGameOver() {
if (isDead) return;
playerLives--;
healthTxt.setText('♥ ' + playerLives);
if (playerLives <= 0) {
isDead = true;
LK.effects.flashScreen(0xff0000, 1000);
// Create a container for the game over display
var goCont = new Container();
// Create large snake head asset (350x350)
var snakeHeadAsset = LK.getAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5,
width: 350,
height: 350
});
snakeHeadAsset.x = 350 / 2;
snakeHeadAsset.y = 350 / 2;
// Create large food asset (350x350)
var foodAsset = LK.getAsset('food', {
anchorX: 0.5,
anchorY: 0.5,
width: 350,
height: 350
});
foodAsset.x = 350 + 350 / 2 + 40; // 40px gap between
foodAsset.y = 350 / 2;
// Set container size and center on screen
var totalWidth = 350 * 2 + 40;
var totalHeight = 350;
goCont.x = 0;
goCont.y = 0;
goCont.width = totalWidth;
goCont.height = totalHeight;
goCont.addChild(snakeHeadAsset);
goCont.addChild(foodAsset);
// Show the container as a popup overlay (LK will remove it on reset)
LK.showGameOver({
customContent: goCont,
customContentWidth: totalWidth,
customContentHeight: totalHeight,
customContentY: 120 // Place below the menu (menu is top 100px, so 120px for margin)
});
} else {
// Player still has lives, flash red and continue
LK.effects.flashScreen(0xff0000, 500);
// Reset snake tail length to 1 (only keep the head)
for (var i = snake.length - 1; i > 0; i--) {
snake[i].destroy();
snake.splice(i, 1);
}
// Move snake head to a safe position automatically
var head = snake[0];
var safeCell = randomFreeCell();
if (safeCell) {
// Update head position data
head.col = safeCell.col;
head.row = safeCell.row;
var safePos = gridToPos(safeCell.col, safeCell.row);
// Smoothly animate to safe position
tween(head, {
x: safePos.x,
y: safePos.y
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
function triggerWin() {
if (isWin) return;
isWin = true;
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
}
// --- Input Handling (Touch/Drag) ---
var dragStart = null;
var dragDir = null;
function getDirectionFromDrag(dx, dy) {
if (Math.abs(dx) > Math.abs(dy)) {
return dx > 0 ? {
x: 1,
y: 0
} : {
x: -1,
y: 0
};
} else {
return dy > 0 ? {
x: 0,
y: 1
} : {
x: 0,
y: -1
};
}
}
game.down = function (x, y, obj) {
dragStart = {
x: x,
y: y
};
dragDir = null;
// Double-tap detection
var currentTime = Date.now();
if (currentTime - lastTapTime < DOUBLE_TAP_THRESHOLD) {
// Double tap detected
isDoubleTouch = true;
speedMultiplier = 2;
touchStartTime = currentTime;
} else {
// Single tap
isDoubleTouch = false;
speedMultiplier = 1;
}
lastTapTime = currentTime;
};
game.move = function (x, y, obj) {
if (!dragStart) return;
var dx = x - dragStart.x;
var dy = y - dragStart.y;
if (Math.abs(dx) > 40 || Math.abs(dy) > 40) {
var dir = getDirectionFromDrag(dx, dy);
// Prevent reversing
if ((dir.x !== -snakeDir.x || dir.y !== -snakeDir.y) && (dir.x !== snakeDir.x || dir.y !== snakeDir.y)) {
nextDir = dir;
dragDir = dir;
dragStart = null;
}
}
};
game.up = function (x, y, obj) {
dragStart = null;
dragDir = null;
// Reset speed when touch is released
isDoubleTouch = false;
speedMultiplier = 1;
};
// --- Main Game Loop ---
game.update = function () {
if (isDead || isWin) return;
moveTimer += 1000 / 60;
var currentMoveInterval = MOVE_INTERVAL / speedMultiplier;
if (moveTimer < currentMoveInterval) return;
moveTimer = 0;
// Update direction
if (nextDir.x !== -snakeDir.x || nextDir.y !== -snakeDir.y) {
snakeDir = {
x: nextDir.x,
y: nextDir.y
};
}
// Calculate new head position
var head = snake[0];
var newCol = head.col + snakeDir.x;
var newRow = head.row + snakeDir.y;
// Check collisions
if (isWall(newCol, newRow) || isSnake(newCol, newRow, false)) {
triggerGameOver();
return;
}
// Move snake
// Remove old head and replace with segment
var oldHead = snake[0];
var newSegment = new SnakeSegment();
newSegment.col = oldHead.col;
newSegment.row = oldHead.row;
newSegment.x = oldHead.x;
newSegment.y = oldHead.y;
game.addChild(newSegment);
snake[0].destroy();
snake[0] = newSegment;
// Add new head at the front
var newHead = new SnakeHead();
newHead.col = newCol;
newHead.row = newRow;
var pos = gridToPos(newCol, newRow);
newHead.x = pos.x;
newHead.y = pos.y;
// Set rotation based on direction (asset is upright, so rotate by +90deg for right)
if (snakeDir.x === 1 && snakeDir.y === 0) {
newHead.rotation = Math.PI / 2; // Right
} else if (snakeDir.x === -1 && snakeDir.y === 0) {
newHead.rotation = -Math.PI / 2; // Left
} else if (snakeDir.x === 0 && snakeDir.y === -1) {
newHead.rotation = 0; // Up
} else if (snakeDir.x === 0 && snakeDir.y === 1) {
newHead.rotation = Math.PI; // Down
}
game.addChild(newHead);
snake.unshift(newHead);
// Check food collision
var ateFood = false;
var eatenFood = null;
for (var f = 0; f < foods.length; f++) {
if (foods[f].col === newCol && foods[f].row === newRow) {
ateFood = true;
eatenFood = foods[f];
break;
}
}
if (ateFood) {
// Remove the eaten food from the foods array and destroy it
for (var i = 0; i < foods.length; i++) {
if (foods[i] === eatenFood) {
foods[i].destroy();
foods.splice(i, 1);
break;
}
}
// Calculate score based on food type and multiplier
var pointsToAdd;
if (eatenFood instanceof Food4) {
pointsToAdd = 1 * scoreMultiplier; // Food4 gives 1 point
} else {
pointsToAdd = 2 * scoreMultiplier; // Food and Food2 give 2 points
}
setScore(LK.getScore() + pointsToAdd);
pendingGrowth += 1;
// Play feeding sound
LK.getSound('feeding').play();
// Handle Food3 special effect - give health (max 3 lives) or bonus points if at max health
if (eatenFood instanceof Food3) {
if (playerLives < 3) {
playerLives++;
healthTxt.setText('♥ ' + playerLives);
} else {
// Player is at max health (3 lives), give 50 bonus points
setScore(LK.getScore() + 50);
}
}
// Handle Food2 special effect - 10x multiplier for 5 seconds
if (eatenFood instanceof Food2) {
if (!isMultiplierActive) {
isMultiplierActive = true;
scoreMultiplier = 10;
// Clear any existing multiplier timer
if (multiplierTimer) {
LK.clearTimeout(multiplierTimer);
}
// Remove multiplier after exactly 5 seconds
multiplierTimer = LK.setTimeout(function () {
scoreMultiplier = 1;
isMultiplierActive = false;
multiplierTimer = null;
}, 5000);
}
// Yellow background effect for 5 seconds
if (!isYellowBackgroundActive) {
isYellowBackgroundActive = true;
game.setBackgroundColor(0xFFFF00); // Yellow color
// Clear any existing yellow background timer
if (yellowBackgroundTimer) {
LK.clearTimeout(yellowBackgroundTimer);
}
// Restore original background after exactly 5 seconds
yellowBackgroundTimer = LK.setTimeout(function () {
game.setBackgroundColor(originalBackgroundColor);
isYellowBackgroundActive = false;
yellowBackgroundTimer = null;
}, 5000);
}
}
// Play feeding2 sound only when Food4 is eaten
if (eatenFood instanceof Food4) {
// Clear any associated timer since Food4 was eaten
for (var t = food4Timers.length - 1; t >= 0; t--) {
LK.clearTimeout(food4Timers[t]);
food4Timers.splice(t, 1);
}
LK.getSound('feeding2').play();
// Create darkness effect for 10 seconds
if (!isDarkened) {
isDarkened = true;
darknessOverlay = LK.getAsset('snakeSegment', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
tint: 0x000000
});
darknessOverlay.alpha = 0;
game.addChild(darknessOverlay);
// Fade in darkness
tween(darknessOverlay, {
alpha: 0.7
}, {
duration: 500
});
// Clear any existing darkness timer
if (darknessTimer) {
LK.clearTimeout(darknessTimer);
}
// Remove darkness after exactly 5 seconds
darknessTimer = LK.setTimeout(function () {
if (darknessOverlay) {
tween(darknessOverlay, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
if (darknessOverlay) {
darknessOverlay.destroy();
darknessOverlay = null;
}
isDarkened = false;
darknessTimer = null;
}
});
}
}, 5000);
}
// Reduce snake speed by 50% for 10 seconds
if (!isSlowed) {
isSlowed = true;
MOVE_INTERVAL = originalMoveInterval * 1.5; // 50% slower
// Clear any existing speed timer
if (speedTimer) {
LK.clearTimeout(speedTimer);
}
// Restore original speed after exactly 5 seconds
speedTimer = LK.setTimeout(function () {
MOVE_INTERVAL = originalMoveInterval;
isSlowed = false;
speedTimer = null;
}, 5000);
}
}
spawnFood();
// Win condition
if (snake.length >= SNAKE_MAX_LENGTH) {
triggerWin();
return;
}
}
// Remove tail if not growing
if (pendingGrowth > 0) {
pendingGrowth--;
} else {
var tail = snake.pop();
tail.destroy();
}
};
// --- Draw Board Grid Background ---
var gridBg = new Container();
for (var r = 0; r < GRID_ROWS; r++) {
for (var c = 0; c < GRID_COLS; c++) {
// Use a light color for grid lines, and a slightly darker fill for the cell
var cell = LK.getAsset('snakeSegment', {
anchorX: 0.5,
anchorY: 0.5,
x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2,
y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2,
width: GRID_SIZE,
height: GRID_SIZE,
tint: (r + c) % 2 === 0 ? 0x232323 : 0x1a1a1a // subtle checkerboard for clarity
});
// Add a border by overlaying a slightly smaller cell of the same color as background
var border = LK.getAsset('snakeSegment', {
anchorX: 0.5,
anchorY: 0.5,
x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2,
y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2,
width: GRID_SIZE - 8,
height: GRID_SIZE - 8,
tint: (r + c) % 2 === 0 ? 0x181818 : 0x181818 // match background
});
gridBg.addChild(cell);
gridBg.addChild(border);
}
}
game.addChild(gridBg);
// --- Game Initialization ---
createWalls();
createSnake();
spawnFood();
setScore(1);
;
a woman head. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d pixel art red brick wall. In-Game asset. 2d. High contrast. No shadows
2d pixel art snake skin. High contrast. No shadows
add 10x text to this pic
kalp şekli pixel art 2d. In-Game asset. High contrast. No shadows