User prompt
add a button that opens the inventory and add a button that opens a new map
User prompt
fix the bug where if the player touches the tree he would go inside it
Code edit (1 edits merged)
Please save this source code
User prompt
Forest Explorer
Initial prompt
make me a 2D explorer game that takes place in the forest
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Berry = Container.expand(function () { var self = Container.call(this); var berryGraphics = self.attachAsset('berry', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; return self; }); var Creature = Container.expand(function () { var self = Container.call(this); var creatureGraphics = self.attachAsset('creature', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1; self.direction = Math.random() * Math.PI * 2; self.changeDirectionTimer = 0; self.update = function () { self.changeDirectionTimer++; if (self.changeDirectionTimer > 120) { self.direction = Math.random() * Math.PI * 2; self.changeDirectionTimer = 0; } self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; // Keep creatures in bounds if (self.x < 50 || self.x > 1998) { self.direction = Math.PI - self.direction; } if (self.y < 50 || self.y > 2682) { self.direction = -self.direction; } }; return self; }); var Crystal = Container.expand(function () { var self = Container.call(this); var crystalGraphics = self.attachAsset('crystal', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.glowDirection = 1; self.update = function () { if (!self.collected) { crystalGraphics.alpha += 0.02 * self.glowDirection; if (crystalGraphics.alpha >= 1) { self.glowDirection = -1; } else if (crystalGraphics.alpha <= 0.3) { self.glowDirection = 1; } } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.targetX = null; self.targetY = null; self.isMoving = false; self.moveTo = function (x, y) { self.targetX = x; self.targetY = y; self.isMoving = true; }; self.update = function () { if (self.isMoving && self.targetX !== null && self.targetY !== null) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { self.x = self.targetX; self.y = self.targetY; self.isMoving = false; } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } } }; return self; }); var Thorn = Container.expand(function () { var self = Container.call(this); var thornGraphics = self.attachAsset('thorn', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var Tree = Container.expand(function () { var self = Container.call(this); var treeGraphics = self.attachAsset('tree', { anchorX: 0.5, anchorY: 1.0 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d5016 }); /**** * Game Code ****/ // Game variables var player; var crystals = []; var berries = []; var trees = []; var thorns = []; var creatures = []; var areasDiscovered = 0; var itemsCollected = 0; // UI Elements var scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(scoreTxt); var itemsTxt = new Text2('Items: 0', { size: 60, fill: 0xFFFFFF }); itemsTxt.anchor.set(0, 0); itemsTxt.y = 100; LK.gui.topRight.addChild(itemsTxt); // Inventory button var inventoryBtn = new Text2('BAG', { size: 60, fill: 0xFFFFFF }); inventoryBtn.anchor.set(0, 0); inventoryBtn.x = 120; inventoryBtn.y = 20; LK.gui.topLeft.addChild(inventoryBtn); // Map button var mapBtn = new Text2('MAP', { size: 60, fill: 0xFFFFFF }); mapBtn.anchor.set(0, 0); mapBtn.x = 120; mapBtn.y = 90; LK.gui.topLeft.addChild(mapBtn); // Inventory state var inventoryOpen = false; var inventoryPanel = null; // Map state var mapOpen = false; var mapPanel = null; // Create ground var ground = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); // Create player player = game.addChild(new Player()); player.x = 1024; player.y = 1366; // Generate forest elements function generateForestElements() { // Create trees for (var i = 0; i < 15; i++) { var tree = new Tree(); tree.x = Math.random() * 1900 + 100; tree.y = Math.random() * 2500 + 200; trees.push(tree); game.addChild(tree); } // Create crystals for (var i = 0; i < 8; i++) { var crystal = new Crystal(); crystal.x = Math.random() * 1800 + 200; crystal.y = Math.random() * 2400 + 200; crystals.push(crystal); game.addChild(crystal); } // Create berries for (var i = 0; i < 12; i++) { var berry = new Berry(); berry.x = Math.random() * 1800 + 200; berry.y = Math.random() * 2400 + 200; berries.push(berry); game.addChild(berry); } // Create thorns for (var i = 0; i < 6; i++) { var thorn = new Thorn(); thorn.x = Math.random() * 1800 + 200; thorn.y = Math.random() * 2400 + 200; thorns.push(thorn); game.addChild(thorn); } // Create creatures for (var i = 0; i < 4; i++) { var creature = new Creature(); creature.x = Math.random() * 1600 + 300; creature.y = Math.random() * 2200 + 300; creatures.push(creature); game.addChild(creature); } } generateForestElements(); // Touch controls game.down = function (x, y, obj) { player.moveTo(x, y); }; // Inventory button click handler inventoryBtn.down = function (x, y, obj) { if (!inventoryOpen) { // Create inventory panel inventoryPanel = game.addChild(LK.getAsset('ground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.8, scaleY: 0.6 })); inventoryPanel.tint = 0x654321; inventoryPanel.alpha = 0.9; // Add inventory title var invTitle = new Text2('INVENTORY', { size: 80, fill: 0xFFFFFF }); invTitle.anchor.set(0.5, 0); invTitle.x = 1024; invTitle.y = 900; game.addChild(invTitle); inventoryPanel.titleText = invTitle; // Add close button var closeBtn = new Text2('CLOSE', { size: 60, fill: 0xFF4444 }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = 1024; closeBtn.y = 1800; closeBtn.down = function () { inventoryPanel.destroy(); inventoryPanel.titleText.destroy(); this.destroy(); inventoryOpen = false; inventoryPanel = null; }; game.addChild(closeBtn); inventoryPanel.closeBtn = closeBtn; inventoryOpen = true; } else { // Close inventory if (inventoryPanel) { inventoryPanel.destroy(); inventoryPanel.titleText.destroy(); inventoryPanel.closeBtn.destroy(); inventoryOpen = false; inventoryPanel = null; } } }; // Map button click handler mapBtn.down = function (x, y, obj) { if (!mapOpen) { // Create map panel mapPanel = game.addChild(LK.getAsset('ground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.9, scaleY: 0.8 })); mapPanel.tint = 0x8B4513; mapPanel.alpha = 0.95; // Add map title var mapTitle = new Text2('FOREST MAP', { size: 80, fill: 0xFFFFFF }); mapTitle.anchor.set(0.5, 0); mapTitle.x = 1024; mapTitle.y = 800; game.addChild(mapTitle); mapPanel.titleText = mapTitle; // Add player position indicator var playerDot = game.addChild(LK.getAsset('crystal', { anchorX: 0.5, anchorY: 0.5, x: 1024 + (player.x - 1024) * 0.4, y: 1366 + (player.y - 1366) * 0.3, scaleX: 0.8, scaleY: 0.8 })); playerDot.tint = 0x00FF00; mapPanel.playerDot = playerDot; // Add close button var closeMapBtn = new Text2('CLOSE', { size: 60, fill: 0xFF4444 }); closeMapBtn.anchor.set(0.5, 0.5); closeMapBtn.x = 1024; closeMapBtn.y = 1900; closeMapBtn.down = function () { mapPanel.destroy(); mapPanel.titleText.destroy(); mapPanel.playerDot.destroy(); this.destroy(); mapOpen = false; mapPanel = null; }; game.addChild(closeMapBtn); mapPanel.closeBtn = closeMapBtn; mapOpen = true; } else { // Close map if (mapPanel) { mapPanel.destroy(); mapPanel.titleText.destroy(); mapPanel.playerDot.destroy(); mapPanel.closeBtn.destroy(); mapOpen = false; mapPanel = null; } } }; // Update score display function updateScore() { scoreTxt.setText('Score: ' + LK.getScore()); itemsTxt.setText('Items: ' + itemsCollected); } // Check collisions and interactions game.update = function () { // Check crystal collection for (var i = crystals.length - 1; i >= 0; i--) { var crystal = crystals[i]; if (!crystal.collected && player.intersects(crystal)) { crystal.collected = true; crystal.alpha = 0; itemsCollected++; LK.setScore(LK.getScore() + 10); updateScore(); LK.getSound('collect').play(); crystals.splice(i, 1); crystal.destroy(); } } // Check berry collection for (var i = berries.length - 1; i >= 0; i--) { var berry = berries[i]; if (!berry.collected && player.intersects(berry)) { berry.collected = true; berry.alpha = 0; itemsCollected++; LK.setScore(LK.getScore() + 5); updateScore(); LK.getSound('collect').play(); berries.splice(i, 1); berry.destroy(); } } // Check thorn collisions for (var i = 0; i < thorns.length; i++) { var thorn = thorns[i]; if (player.intersects(thorn)) { // Push player away from thorn var dx = player.x - thorn.x; var dy = player.y - thorn.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { player.x = thorn.x + dx / distance * 60; player.y = thorn.y + dy / distance * 60; } player.isMoving = false; LK.effects.flashObject(player, 0xff0000, 500); LK.getSound('danger').play(); } } // Check creature encounters for (var i = 0; i < creatures.length; i++) { var creature = creatures[i]; if (player.intersects(creature)) { // Push player away from creature var dx = player.x - creature.x; var dy = player.y - creature.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { player.x = creature.x + dx / distance * 70; player.y = creature.y + dy / distance * 70; } player.isMoving = false; LK.effects.flashObject(player, 0x8b4513, 500); LK.setScore(Math.max(0, LK.getScore() - 2)); updateScore(); LK.getSound('danger').play(); } } // Check tree collisions (blocking) for (var i = 0; i < trees.length; i++) { var tree = trees[i]; if (player.intersects(tree)) { // Push player away from tree (account for tree anchor being at bottom center) var dx = player.x - tree.x; var dy = player.y - (tree.y - tree.height / 2); // Adjust for tree anchor point var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { // Increase push distance to ensure player stays outside tree bounds player.x = tree.x + dx / distance * (tree.width / 2 + 35); player.y = tree.y - tree.height / 2 + dy / distance * (tree.height / 2 + 35); } player.isMoving = false; } } // Keep player in bounds if (player.x < 30) player.x = 30; if (player.x > 2018) player.x = 2018; if (player.y < 30) player.y = 30; if (player.y > 2702) player.y = 2702; // Check win condition if (itemsCollected >= 15) { LK.showYouWin(); } }; // Play ambient forest music LK.playMusic('forestAmbient'); // Initialize score display updateScore();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Berry = Container.expand(function () {
var self = Container.call(this);
var berryGraphics = self.attachAsset('berry', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
return self;
});
var Creature = Container.expand(function () {
var self = Container.call(this);
var creatureGraphics = self.attachAsset('creature', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1;
self.direction = Math.random() * Math.PI * 2;
self.changeDirectionTimer = 0;
self.update = function () {
self.changeDirectionTimer++;
if (self.changeDirectionTimer > 120) {
self.direction = Math.random() * Math.PI * 2;
self.changeDirectionTimer = 0;
}
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
// Keep creatures in bounds
if (self.x < 50 || self.x > 1998) {
self.direction = Math.PI - self.direction;
}
if (self.y < 50 || self.y > 2682) {
self.direction = -self.direction;
}
};
return self;
});
var Crystal = Container.expand(function () {
var self = Container.call(this);
var crystalGraphics = self.attachAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.glowDirection = 1;
self.update = function () {
if (!self.collected) {
crystalGraphics.alpha += 0.02 * self.glowDirection;
if (crystalGraphics.alpha >= 1) {
self.glowDirection = -1;
} else if (crystalGraphics.alpha <= 0.3) {
self.glowDirection = 1;
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.targetX = null;
self.targetY = null;
self.isMoving = false;
self.moveTo = function (x, y) {
self.targetX = x;
self.targetY = y;
self.isMoving = true;
};
self.update = function () {
if (self.isMoving && self.targetX !== null && self.targetY !== null) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.isMoving = false;
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
};
return self;
});
var Thorn = Container.expand(function () {
var self = Container.call(this);
var thornGraphics = self.attachAsset('thorn', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Tree = Container.expand(function () {
var self = Container.call(this);
var treeGraphics = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 1.0
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d5016
});
/****
* Game Code
****/
// Game variables
var player;
var crystals = [];
var berries = [];
var trees = [];
var thorns = [];
var creatures = [];
var areasDiscovered = 0;
var itemsCollected = 0;
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
var itemsTxt = new Text2('Items: 0', {
size: 60,
fill: 0xFFFFFF
});
itemsTxt.anchor.set(0, 0);
itemsTxt.y = 100;
LK.gui.topRight.addChild(itemsTxt);
// Inventory button
var inventoryBtn = new Text2('BAG', {
size: 60,
fill: 0xFFFFFF
});
inventoryBtn.anchor.set(0, 0);
inventoryBtn.x = 120;
inventoryBtn.y = 20;
LK.gui.topLeft.addChild(inventoryBtn);
// Map button
var mapBtn = new Text2('MAP', {
size: 60,
fill: 0xFFFFFF
});
mapBtn.anchor.set(0, 0);
mapBtn.x = 120;
mapBtn.y = 90;
LK.gui.topLeft.addChild(mapBtn);
// Inventory state
var inventoryOpen = false;
var inventoryPanel = null;
// Map state
var mapOpen = false;
var mapPanel = null;
// Create ground
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Generate forest elements
function generateForestElements() {
// Create trees
for (var i = 0; i < 15; i++) {
var tree = new Tree();
tree.x = Math.random() * 1900 + 100;
tree.y = Math.random() * 2500 + 200;
trees.push(tree);
game.addChild(tree);
}
// Create crystals
for (var i = 0; i < 8; i++) {
var crystal = new Crystal();
crystal.x = Math.random() * 1800 + 200;
crystal.y = Math.random() * 2400 + 200;
crystals.push(crystal);
game.addChild(crystal);
}
// Create berries
for (var i = 0; i < 12; i++) {
var berry = new Berry();
berry.x = Math.random() * 1800 + 200;
berry.y = Math.random() * 2400 + 200;
berries.push(berry);
game.addChild(berry);
}
// Create thorns
for (var i = 0; i < 6; i++) {
var thorn = new Thorn();
thorn.x = Math.random() * 1800 + 200;
thorn.y = Math.random() * 2400 + 200;
thorns.push(thorn);
game.addChild(thorn);
}
// Create creatures
for (var i = 0; i < 4; i++) {
var creature = new Creature();
creature.x = Math.random() * 1600 + 300;
creature.y = Math.random() * 2200 + 300;
creatures.push(creature);
game.addChild(creature);
}
}
generateForestElements();
// Touch controls
game.down = function (x, y, obj) {
player.moveTo(x, y);
};
// Inventory button click handler
inventoryBtn.down = function (x, y, obj) {
if (!inventoryOpen) {
// Create inventory panel
inventoryPanel = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.8,
scaleY: 0.6
}));
inventoryPanel.tint = 0x654321;
inventoryPanel.alpha = 0.9;
// Add inventory title
var invTitle = new Text2('INVENTORY', {
size: 80,
fill: 0xFFFFFF
});
invTitle.anchor.set(0.5, 0);
invTitle.x = 1024;
invTitle.y = 900;
game.addChild(invTitle);
inventoryPanel.titleText = invTitle;
// Add close button
var closeBtn = new Text2('CLOSE', {
size: 60,
fill: 0xFF4444
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = 1024;
closeBtn.y = 1800;
closeBtn.down = function () {
inventoryPanel.destroy();
inventoryPanel.titleText.destroy();
this.destroy();
inventoryOpen = false;
inventoryPanel = null;
};
game.addChild(closeBtn);
inventoryPanel.closeBtn = closeBtn;
inventoryOpen = true;
} else {
// Close inventory
if (inventoryPanel) {
inventoryPanel.destroy();
inventoryPanel.titleText.destroy();
inventoryPanel.closeBtn.destroy();
inventoryOpen = false;
inventoryPanel = null;
}
}
};
// Map button click handler
mapBtn.down = function (x, y, obj) {
if (!mapOpen) {
// Create map panel
mapPanel = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.9,
scaleY: 0.8
}));
mapPanel.tint = 0x8B4513;
mapPanel.alpha = 0.95;
// Add map title
var mapTitle = new Text2('FOREST MAP', {
size: 80,
fill: 0xFFFFFF
});
mapTitle.anchor.set(0.5, 0);
mapTitle.x = 1024;
mapTitle.y = 800;
game.addChild(mapTitle);
mapPanel.titleText = mapTitle;
// Add player position indicator
var playerDot = game.addChild(LK.getAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024 + (player.x - 1024) * 0.4,
y: 1366 + (player.y - 1366) * 0.3,
scaleX: 0.8,
scaleY: 0.8
}));
playerDot.tint = 0x00FF00;
mapPanel.playerDot = playerDot;
// Add close button
var closeMapBtn = new Text2('CLOSE', {
size: 60,
fill: 0xFF4444
});
closeMapBtn.anchor.set(0.5, 0.5);
closeMapBtn.x = 1024;
closeMapBtn.y = 1900;
closeMapBtn.down = function () {
mapPanel.destroy();
mapPanel.titleText.destroy();
mapPanel.playerDot.destroy();
this.destroy();
mapOpen = false;
mapPanel = null;
};
game.addChild(closeMapBtn);
mapPanel.closeBtn = closeMapBtn;
mapOpen = true;
} else {
// Close map
if (mapPanel) {
mapPanel.destroy();
mapPanel.titleText.destroy();
mapPanel.playerDot.destroy();
mapPanel.closeBtn.destroy();
mapOpen = false;
mapPanel = null;
}
}
};
// Update score display
function updateScore() {
scoreTxt.setText('Score: ' + LK.getScore());
itemsTxt.setText('Items: ' + itemsCollected);
}
// Check collisions and interactions
game.update = function () {
// Check crystal collection
for (var i = crystals.length - 1; i >= 0; i--) {
var crystal = crystals[i];
if (!crystal.collected && player.intersects(crystal)) {
crystal.collected = true;
crystal.alpha = 0;
itemsCollected++;
LK.setScore(LK.getScore() + 10);
updateScore();
LK.getSound('collect').play();
crystals.splice(i, 1);
crystal.destroy();
}
}
// Check berry collection
for (var i = berries.length - 1; i >= 0; i--) {
var berry = berries[i];
if (!berry.collected && player.intersects(berry)) {
berry.collected = true;
berry.alpha = 0;
itemsCollected++;
LK.setScore(LK.getScore() + 5);
updateScore();
LK.getSound('collect').play();
berries.splice(i, 1);
berry.destroy();
}
}
// Check thorn collisions
for (var i = 0; i < thorns.length; i++) {
var thorn = thorns[i];
if (player.intersects(thorn)) {
// Push player away from thorn
var dx = player.x - thorn.x;
var dy = player.y - thorn.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
player.x = thorn.x + dx / distance * 60;
player.y = thorn.y + dy / distance * 60;
}
player.isMoving = false;
LK.effects.flashObject(player, 0xff0000, 500);
LK.getSound('danger').play();
}
}
// Check creature encounters
for (var i = 0; i < creatures.length; i++) {
var creature = creatures[i];
if (player.intersects(creature)) {
// Push player away from creature
var dx = player.x - creature.x;
var dy = player.y - creature.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
player.x = creature.x + dx / distance * 70;
player.y = creature.y + dy / distance * 70;
}
player.isMoving = false;
LK.effects.flashObject(player, 0x8b4513, 500);
LK.setScore(Math.max(0, LK.getScore() - 2));
updateScore();
LK.getSound('danger').play();
}
}
// Check tree collisions (blocking)
for (var i = 0; i < trees.length; i++) {
var tree = trees[i];
if (player.intersects(tree)) {
// Push player away from tree (account for tree anchor being at bottom center)
var dx = player.x - tree.x;
var dy = player.y - (tree.y - tree.height / 2); // Adjust for tree anchor point
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Increase push distance to ensure player stays outside tree bounds
player.x = tree.x + dx / distance * (tree.width / 2 + 35);
player.y = tree.y - tree.height / 2 + dy / distance * (tree.height / 2 + 35);
}
player.isMoving = false;
}
}
// Keep player in bounds
if (player.x < 30) player.x = 30;
if (player.x > 2018) player.x = 2018;
if (player.y < 30) player.y = 30;
if (player.y > 2702) player.y = 2702;
// Check win condition
if (itemsCollected >= 15) {
LK.showYouWin();
}
};
// Play ambient forest music
LK.playMusic('forestAmbient');
// Initialize score display
updateScore();