/**** * Classes ****/ // Block class for a single voxel block var Block = Container.expand(function () { var self = Container.call(this); // Default block type is 'dirt' self.type = 'dirt'; // Attach the block asset, anchor at center self.blockAsset = self.attachAsset('block_' + self.type, { anchorX: 0.5, anchorY: 0.5 }); // Supported types: 'dirt', 'grass', 'stone' // Set block type and update asset self.setType = function (type) { self.type = type; // Remove old asset if present if (self.blockAsset) { self.removeChild(self.blockAsset); } self.blockAsset = self.attachAsset('block_' + self.type, { anchorX: 0.5, anchorY: 0.5 }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ ; // --- Block Placement/Removal Controls --- // Helper: Convert screen/game coordinates to grid coordinates function getGridCoords(x, y) { var gx = Math.floor((x - gridOffsetX) / BLOCK_SIZE); var gy = Math.floor((y - gridOffsetY) / BLOCK_SIZE); if (gx < 0 || gx >= GRID_WIDTH || gy < 0 || gy >= GRID_HEIGHT) return null; return { x: gx, y: gy }; } // Track current action: 'place' or 'remove' var currentAction = 'place'; // Default to placing blocks // Track current block type for placement var blockTypes = ['dirt', 'grass', 'stone']; var currentBlockTypeIndex = 0; var currentBlockType = blockTypes[currentBlockTypeIndex]; // Palette UI: tap top-right to toggle between 'place' and 'remove', and select block type var paletteSize = 180; var paletteX = 2048 - paletteSize - 40; var paletteY = 40; // Draw palette action button (place/remove) var paletteBtn = LK.getAsset('block_dirt', { anchorX: 0.5, anchorY: 0.5, x: paletteX + paletteSize / 2, y: paletteY + paletteSize / 2, width: paletteSize, height: paletteSize }); game.addChild(paletteBtn); // Draw block swap button (cycle block type) var swapBtnSize = 120; var swapBtnX = paletteX + paletteSize / 2; var swapBtnY = paletteY + paletteSize + 40 + swapBtnSize / 2; var swapBtn = LK.getAsset('block_' + currentBlockType, { anchorX: 0.5, anchorY: 0.5, x: swapBtnX, y: swapBtnY, width: swapBtnSize, height: swapBtnSize }); game.addChild(swapBtn); // Overlay text for mode var paletteTxt = new Text2('Place', { size: 60, fill: "#fff" }); paletteTxt.anchor.set(0.5, 0.5); paletteTxt.x = paletteBtn.x; paletteTxt.y = paletteBtn.y; game.addChild(paletteTxt); // Overlay text for block type var swapTxt = new Text2(currentBlockType.charAt(0).toUpperCase() + currentBlockType.slice(1), { size: 40, fill: "#fff" }); swapTxt.anchor.set(0.5, 0.5); swapTxt.x = swapBtn.x; swapTxt.y = swapBtn.y; game.addChild(swapTxt); // Toggle action on palette tap paletteBtn.down = function (x, y, obj) { currentAction = currentAction === 'place' ? 'remove' : 'place'; paletteTxt.setText(currentAction.charAt(0).toUpperCase() + currentAction.slice(1)); }; // Cycle block type on swapBtn tap swapBtn.down = function (x, y, obj) { currentBlockTypeIndex = (currentBlockTypeIndex + 1) % blockTypes.length; currentBlockType = blockTypes[currentBlockTypeIndex]; // Remove old asset if present if (swapBtn.blockAsset) { swapBtn.removeChild(swapBtn.blockAsset); } swapBtn.blockAsset = swapBtn.attachAsset('block_' + currentBlockType, { anchorX: 0.5, anchorY: 0.5, x: swapBtnX, y: swapBtnY, width: swapBtnSize, height: swapBtnSize }); swapTxt.setText(currentBlockType.charAt(0).toUpperCase() + currentBlockType.slice(1)); }; // Handle placing/removing blocks on grid game.down = function (x, y, obj) { // Ignore palette area if (x >= paletteX && x <= paletteX + paletteSize && y >= paletteY && y <= paletteY + paletteSize) { // Let paletteBtn handle its own .down return; } var grid = getGridCoords(x, y); if (!grid) return; var gx = grid.x, gy = grid.y; if (currentAction === 'place') { if (!worldGrid[gy][gx]) { // Place a block of the selected type worldGrid[gy][gx] = currentBlockType; var block = new Block(); block.setType(currentBlockType); block.x = gridOffsetX + gx * BLOCK_SIZE + BLOCK_SIZE / 2; block.y = gridOffsetY + gy * BLOCK_SIZE + BLOCK_SIZE / 2; game.addChild(block); blockObjects[gy][gx] = block; } } else if (currentAction === 'remove') { if (worldGrid[gy][gx]) { // Remove block worldGrid[gy][gx] = null; if (blockObjects[gy][gx]) { blockObjects[gy][gx].destroy(); blockObjects[gy][gx] = null; } } } }; // --- Voxel 2D Sandbox Setup --- // Grid size (number of blocks) var GRID_WIDTH = 20; var GRID_HEIGHT = 15; // Block size in pixels (auto from asset, but fallback to 100) var BLOCK_SIZE = 100; // 2D array to store block types, initialize with 'dirt' for a flat world var worldGrid = []; for (var y = 0; y < GRID_HEIGHT; y++) { worldGrid[y] = []; for (var x = 0; x < GRID_WIDTH; x++) { if (y === Math.floor(GRID_HEIGHT / 2)) { worldGrid[y][x] = 'grass'; } else if (y > Math.floor(GRID_HEIGHT / 2) && y < GRID_HEIGHT - 2) { worldGrid[y][x] = 'dirt'; } else if (y >= GRID_HEIGHT - 2) { worldGrid[y][x] = 'stone'; } else { worldGrid[y][x] = null; } } } // Store block objects for easy access/removal var blockObjects = []; // Center the grid in the game area var gridOffsetX = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2; var gridOffsetY = (2732 - GRID_HEIGHT * BLOCK_SIZE) / 2; // Render the initial world for (var y = 0; y < GRID_HEIGHT; y++) { blockObjects[y] = []; for (var x = 0; x < GRID_WIDTH; x++) { if (worldGrid[y][x]) { var block = new Block(); block.setType(worldGrid[y][x]); block.x = gridOffsetX + x * BLOCK_SIZE + BLOCK_SIZE / 2; block.y = gridOffsetY + y * BLOCK_SIZE + BLOCK_SIZE / 2; game.addChild(block); blockObjects[y][x] = block; } else { blockObjects[y][x] = null; } } }
/****
* Classes
****/
// Block class for a single voxel block
var Block = Container.expand(function () {
var self = Container.call(this);
// Default block type is 'dirt'
self.type = 'dirt';
// Attach the block asset, anchor at center
self.blockAsset = self.attachAsset('block_' + self.type, {
anchorX: 0.5,
anchorY: 0.5
});
// Supported types: 'dirt', 'grass', 'stone'
// Set block type and update asset
self.setType = function (type) {
self.type = type;
// Remove old asset if present
if (self.blockAsset) {
self.removeChild(self.blockAsset);
}
self.blockAsset = self.attachAsset('block_' + self.type, {
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
;
// --- Block Placement/Removal Controls ---
// Helper: Convert screen/game coordinates to grid coordinates
function getGridCoords(x, y) {
var gx = Math.floor((x - gridOffsetX) / BLOCK_SIZE);
var gy = Math.floor((y - gridOffsetY) / BLOCK_SIZE);
if (gx < 0 || gx >= GRID_WIDTH || gy < 0 || gy >= GRID_HEIGHT) return null;
return {
x: gx,
y: gy
};
}
// Track current action: 'place' or 'remove'
var currentAction = 'place'; // Default to placing blocks
// Track current block type for placement
var blockTypes = ['dirt', 'grass', 'stone'];
var currentBlockTypeIndex = 0;
var currentBlockType = blockTypes[currentBlockTypeIndex];
// Palette UI: tap top-right to toggle between 'place' and 'remove', and select block type
var paletteSize = 180;
var paletteX = 2048 - paletteSize - 40;
var paletteY = 40;
// Draw palette action button (place/remove)
var paletteBtn = LK.getAsset('block_dirt', {
anchorX: 0.5,
anchorY: 0.5,
x: paletteX + paletteSize / 2,
y: paletteY + paletteSize / 2,
width: paletteSize,
height: paletteSize
});
game.addChild(paletteBtn);
// Draw block swap button (cycle block type)
var swapBtnSize = 120;
var swapBtnX = paletteX + paletteSize / 2;
var swapBtnY = paletteY + paletteSize + 40 + swapBtnSize / 2;
var swapBtn = LK.getAsset('block_' + currentBlockType, {
anchorX: 0.5,
anchorY: 0.5,
x: swapBtnX,
y: swapBtnY,
width: swapBtnSize,
height: swapBtnSize
});
game.addChild(swapBtn);
// Overlay text for mode
var paletteTxt = new Text2('Place', {
size: 60,
fill: "#fff"
});
paletteTxt.anchor.set(0.5, 0.5);
paletteTxt.x = paletteBtn.x;
paletteTxt.y = paletteBtn.y;
game.addChild(paletteTxt);
// Overlay text for block type
var swapTxt = new Text2(currentBlockType.charAt(0).toUpperCase() + currentBlockType.slice(1), {
size: 40,
fill: "#fff"
});
swapTxt.anchor.set(0.5, 0.5);
swapTxt.x = swapBtn.x;
swapTxt.y = swapBtn.y;
game.addChild(swapTxt);
// Toggle action on palette tap
paletteBtn.down = function (x, y, obj) {
currentAction = currentAction === 'place' ? 'remove' : 'place';
paletteTxt.setText(currentAction.charAt(0).toUpperCase() + currentAction.slice(1));
};
// Cycle block type on swapBtn tap
swapBtn.down = function (x, y, obj) {
currentBlockTypeIndex = (currentBlockTypeIndex + 1) % blockTypes.length;
currentBlockType = blockTypes[currentBlockTypeIndex];
// Remove old asset if present
if (swapBtn.blockAsset) {
swapBtn.removeChild(swapBtn.blockAsset);
}
swapBtn.blockAsset = swapBtn.attachAsset('block_' + currentBlockType, {
anchorX: 0.5,
anchorY: 0.5,
x: swapBtnX,
y: swapBtnY,
width: swapBtnSize,
height: swapBtnSize
});
swapTxt.setText(currentBlockType.charAt(0).toUpperCase() + currentBlockType.slice(1));
};
// Handle placing/removing blocks on grid
game.down = function (x, y, obj) {
// Ignore palette area
if (x >= paletteX && x <= paletteX + paletteSize && y >= paletteY && y <= paletteY + paletteSize) {
// Let paletteBtn handle its own .down
return;
}
var grid = getGridCoords(x, y);
if (!grid) return;
var gx = grid.x,
gy = grid.y;
if (currentAction === 'place') {
if (!worldGrid[gy][gx]) {
// Place a block of the selected type
worldGrid[gy][gx] = currentBlockType;
var block = new Block();
block.setType(currentBlockType);
block.x = gridOffsetX + gx * BLOCK_SIZE + BLOCK_SIZE / 2;
block.y = gridOffsetY + gy * BLOCK_SIZE + BLOCK_SIZE / 2;
game.addChild(block);
blockObjects[gy][gx] = block;
}
} else if (currentAction === 'remove') {
if (worldGrid[gy][gx]) {
// Remove block
worldGrid[gy][gx] = null;
if (blockObjects[gy][gx]) {
blockObjects[gy][gx].destroy();
blockObjects[gy][gx] = null;
}
}
}
};
// --- Voxel 2D Sandbox Setup ---
// Grid size (number of blocks)
var GRID_WIDTH = 20;
var GRID_HEIGHT = 15;
// Block size in pixels (auto from asset, but fallback to 100)
var BLOCK_SIZE = 100;
// 2D array to store block types, initialize with 'dirt' for a flat world
var worldGrid = [];
for (var y = 0; y < GRID_HEIGHT; y++) {
worldGrid[y] = [];
for (var x = 0; x < GRID_WIDTH; x++) {
if (y === Math.floor(GRID_HEIGHT / 2)) {
worldGrid[y][x] = 'grass';
} else if (y > Math.floor(GRID_HEIGHT / 2) && y < GRID_HEIGHT - 2) {
worldGrid[y][x] = 'dirt';
} else if (y >= GRID_HEIGHT - 2) {
worldGrid[y][x] = 'stone';
} else {
worldGrid[y][x] = null;
}
}
}
// Store block objects for easy access/removal
var blockObjects = [];
// Center the grid in the game area
var gridOffsetX = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2;
var gridOffsetY = (2732 - GRID_HEIGHT * BLOCK_SIZE) / 2;
// Render the initial world
for (var y = 0; y < GRID_HEIGHT; y++) {
blockObjects[y] = [];
for (var x = 0; x < GRID_WIDTH; x++) {
if (worldGrid[y][x]) {
var block = new Block();
block.setType(worldGrid[y][x]);
block.x = gridOffsetX + x * BLOCK_SIZE + BLOCK_SIZE / 2;
block.y = gridOffsetY + y * BLOCK_SIZE + BLOCK_SIZE / 2;
game.addChild(block);
blockObjects[y][x] = block;
} else {
blockObjects[y][x] = null;
}
}
}