User prompt
Make the shoot button bigger
User prompt
Make it so the projectile is moving forwards on the 2d plane and projecting into the ray cast, like the monsters, not just flashing on screen.
User prompt
The projectiles don’t really work. Can you fix them?
User prompt
Make the controls slower
User prompt
Move the controls closer to the center of the screen, bigger and more spread apart
User prompt
Make the buttons bigger
User prompt
Make the movement even slower
User prompt
Make all of the movement and sensitivity of the controls way slower
User prompt
Make the movement slower
User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'map[cellY][cellX]')' in or related to this line: 'if (cellX < 0 || cellX >= MAP_SIZE || cellY < 0 || cellY >= MAP_SIZE) {' Line Number: 763
Code edit (1 edits merged)
Please save this source code
User prompt
RayCaster Dungeon Crawler
Initial prompt
3d ray casting
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var ControlButton = Container.expand(function (direction) { var self = Container.call(this); var buttonSprite = self.attachAsset('controlButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5 }); var arrowSprite = self.attachAsset('buttonArrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 }); // Set arrow direction based on button type if (direction === 'up') { arrowSprite.rotation = 0; } else if (direction === 'right') { arrowSprite.rotation = Math.PI / 2; } else if (direction === 'down') { arrowSprite.rotation = Math.PI; } else if (direction === 'left') { arrowSprite.rotation = Math.PI * 1.5; } else if (direction === 'attack') { arrowSprite.visible = false; buttonSprite = self.attachAsset('attackButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.5, scaleY: 3.5 }); } self.direction = direction; self.pressed = false; self.down = function (x, y, obj) { self.pressed = true; buttonSprite.alpha = 0.7; }; self.up = function (x, y, obj) { self.pressed = false; buttonSprite.alpha = 1; }; return self; }); var MapCell = Container.expand(function () { var self = Container.call(this); self.type = 0; // 0 = floor, 1 = wall self.monster = null; self.treasure = null; self.setType = function (type) { self.type = type; self.updateVisual(); }; self.updateVisual = function () { self.removeChildren(); if (self.type === 1) { self.attachAsset('mapWall', { anchorX: 0, anchorY: 0 }); } else { self.attachAsset('mapFloor', { anchorX: 0, anchorY: 0 }); } }; self.addMonster = function () { if (self.type === 0 && !self.monster && !self.treasure) { self.monster = true; return true; } return false; }; self.addTreasure = function () { if (self.type === 0 && !self.monster && !self.treasure) { self.treasure = true; return true; } return false; }; self.removeMonster = function () { self.monster = null; }; self.removeTreasure = function () { self.treasure = null; }; return self; }); var Monster = Container.expand(function () { var self = Container.call(this); var monsterSprite = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5 }); self.mapX = 0; self.mapY = 0; self.health = 3; self.takeDamage = function () { self.health -= 1; LK.getSound('hit').play(); // Visual feedback for hit tween(monsterSprite, { alpha: 0.2 }, { duration: 100, onFinish: function onFinish() { tween(monsterSprite, { alpha: 1 }, { duration: 100 }); } }); return self.health <= 0; }; return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); var projectileSprite = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); self.dirX = 0; self.dirY = 0; self.speed = 0.2; self.range = 5; self.distanceTraveled = 0; self.lastDistanceTraveled = 0; self.visible = true; self.update = function (deltaTime) { // Store last distance for collision detection self.lastDistanceTraveled = self.distanceTraveled; // Update distance traveled based on speed var moveDist = self.speed * deltaTime; self.distanceTraveled += moveDist; // Rotate projectile for visual effect projectileSprite.rotation += 0.1; // Return true if projectile has reached its maximum range return self.distanceTraveled >= self.range; }; return self; }); var RaycastStrip = Container.expand(function () { var self = Container.call(this); var wall = self.attachAsset('wall', { anchorX: 0, anchorY: 0 }); var ceiling = self.attachAsset('ceiling', { anchorX: 0, anchorY: 0 }); var floor = self.attachAsset('floor', { anchorX: 0, anchorY: 0 }); self.updateStrip = function (stripWidth, wallHeight, stripIdx, wallType, distance) { // Wall setup wall.width = stripWidth; wall.height = wallHeight; wall.y = (2732 - wallHeight) / 2; // Ceiling setup ceiling.width = stripWidth; ceiling.height = wall.y; ceiling.y = 0; // Floor setup floor.width = stripWidth; floor.height = ceiling.height; floor.y = wall.y + wallHeight; // Adjust positions self.x = stripIdx * stripWidth; // Add distance shading effect var shade = Math.max(0.3, 1 - distance / 10); wall.alpha = shade; ceiling.alpha = shade * 0.7; floor.alpha = shade * 0.8; }; return self; }); var Treasure = Container.expand(function () { var self = Container.call(this); var treasureSprite = self.attachAsset('treasure', { anchorX: 0.5, anchorY: 0.5 }); self.mapX = 0; self.mapY = 0; self.value = 1; // Animate the treasure to make it more appealing var _animateTreasure = function animateTreasure() { tween(treasureSprite, { rotation: Math.PI * 2 }, { duration: 2000, onFinish: function onFinish() { treasureSprite.rotation = 0; _animateTreasure(); } }); }; _animateTreasure(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 }); /**** * Game Code ****/ // Game constants var MAP_SIZE = 16; var CELL_SIZE = 20; var MINI_MAP_SCALE = 1; var STRIP_WIDTH = 8; var NUM_RAYS = Math.ceil(2048 / STRIP_WIDTH); var FOV = Math.PI / 3; // 60 degrees field of view var HALF_FOV = FOV / 2; var PLAYER_MOVE_SPEED = 0.002; // Reduced from 0.005 to make movement slower var PLAYER_TURN_SPEED = 0.002; // Reduced from 0.005 to make turning slower var WALL_HEIGHT_FACTOR = 600; var MAX_RENDER_DISTANCE = 16; var MONSTER_COUNT = 5; var TREASURE_COUNT = 10; // Game state var map = []; var player = { x: 1.5, y: 1.5, dir: 0, health: 5, score: 0, level: storage.level || 1 }; var controls = { forward: false, backward: false, left: false, right: false, attack: false }; var monsters = []; var treasures = []; var projectiles = []; var lastTime = Date.now(); // UI elements var miniMap; var rayCastView; var healthText; var scoreText; var levelText; var controlButtons = {}; var playerMarker; // Setup game function setupGame() { // Create the rayCast view container rayCastView = new Container(); game.addChild(rayCastView); // Create raycast strips for (var i = 0; i < NUM_RAYS; i++) { var strip = new RaycastStrip(); rayCastView.addChild(strip); } // Create minimap container miniMap = new Container(); miniMap.x = 20; miniMap.y = 20; game.addChild(miniMap); // Generate map generateMap(); // Create player marker playerMarker = game.addChild(LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5 })); // Create UI elements createUI(); // Create control buttons createControlButtons(); // Start background music LK.playMusic('dungeon'); } function generateMap() { // Clear existing map miniMap.removeChildren(); map = []; // Remove existing monsters and treasures for (var i = 0; i < monsters.length; i++) { monsters[i].destroy(); } monsters = []; for (var i = 0; i < treasures.length; i++) { treasures[i].destroy(); } treasures = []; // Clear projectiles for (var i = 0; i < projectiles.length; i++) { projectiles[i].destroy(); } projectiles = []; // Generate base map with borders for (var y = 0; y < MAP_SIZE; y++) { map[y] = []; for (var x = 0; x < MAP_SIZE; x++) { var cell = new MapCell(); cell.x = x * CELL_SIZE * MINI_MAP_SCALE; cell.y = y * CELL_SIZE * MINI_MAP_SCALE; // Create outer walls if (x === 0 || y === 0 || x === MAP_SIZE - 1 || y === MAP_SIZE - 1) { cell.setType(1); // Wall } else { // Random interior walls based on level difficulty var wallChance = 0.2 + player.level * 0.03; if (Math.random() < wallChance && !(x === 1 && y === 1)) { // Ensure starting position is clear cell.setType(1); // Wall } else { cell.setType(0); // Floor } } map[y][x] = cell; miniMap.addChild(cell); } } // Create monsters var monstersToPlace = MONSTER_COUNT + Math.floor(player.level * 0.5); for (var i = 0; i < monstersToPlace; i++) { placeMonster(); } // Create treasures var treasuresToPlace = TREASURE_COUNT; for (var i = 0; i < treasuresToPlace; i++) { placeTreasure(); } // Reset player position player.x = 1.5; player.y = 1.5; player.dir = 0; } function placeMonster() { // Find a random empty cell var x, y; var attempts = 0; do { x = Math.floor(Math.random() * (MAP_SIZE - 2)) + 1; y = Math.floor(Math.random() * (MAP_SIZE - 2)) + 1; attempts++; // Make sure it's not too close to the player var distToPlayer = Math.sqrt(Math.pow(x - player.x, 2) + Math.pow(y - player.y, 2)); if (attempts > 100) break; // Prevent infinite loop } while (map[y][x].type !== 0 || map[y][x].monster || map[y][x].treasure || distToPlayer < 3); if (attempts <= 100) { map[y][x].addMonster(); var monster = new Monster(); monster.mapX = x; monster.mapY = y; monster.health = 2 + Math.floor(player.level / 3); // Monsters get tougher with level monsters.push(monster); game.addChild(monster); } } function placeTreasure() { // Find a random empty cell var x, y; var attempts = 0; do { x = Math.floor(Math.random() * (MAP_SIZE - 2)) + 1; y = Math.floor(Math.random() * (MAP_SIZE - 2)) + 1; attempts++; if (attempts > 100) break; // Prevent infinite loop } while (map[y][x].type !== 0 || map[y][x].monster || map[y][x].treasure); if (attempts <= 100) { map[y][x].addTreasure(); var treasure = new Treasure(); treasure.mapX = x; treasure.mapY = y; treasure.value = 1 + Math.floor(Math.random() * player.level); treasures.push(treasure); game.addChild(treasure); } } function createUI() { // Health display healthText = new Text2('Health: ' + player.health, { size: 40, fill: 0xFF5555 }); healthText.anchor.set(0, 0); LK.gui.topRight.addChild(healthText); healthText.x = -200; healthText.y = 20; // Score display scoreText = new Text2('Score: ' + player.score, { size: 40, fill: 0xFFFF55 }); scoreText.anchor.set(0, 0); LK.gui.topRight.addChild(scoreText); scoreText.x = -200; scoreText.y = 80; // Level display levelText = new Text2('Level: ' + player.level, { size: 40, fill: 0x55FF55 }); levelText.anchor.set(0, 0); LK.gui.topRight.addChild(levelText); levelText.x = -200; levelText.y = 140; // Update UI displays updateUI(); } function updateUI() { healthText.setText('Health: ' + player.health); scoreText.setText('Score: ' + player.score); levelText.setText('Level: ' + player.level); // Update score in LK system LK.setScore(player.score); } function createControlButtons() { // Create directional buttons with increased size and more centered position controlButtons.up = new ControlButton('up'); controlButtons.up.x = 400; controlButtons.up.y = 2732 - 700; game.addChild(controlButtons.up); controlButtons.right = new ControlButton('right'); controlButtons.right.x = 650; controlButtons.right.y = 2732 - 500; game.addChild(controlButtons.right); controlButtons.down = new ControlButton('down'); controlButtons.down.x = 400; controlButtons.down.y = 2732 - 300; game.addChild(controlButtons.down); controlButtons.left = new ControlButton('left'); controlButtons.left.x = 150; controlButtons.left.y = 2732 - 500; game.addChild(controlButtons.left); // Create attack button more centered on the right side controlButtons.attack = new ControlButton('attack'); controlButtons.attack.x = 2048 - 400; controlButtons.attack.y = 2732 - 500; game.addChild(controlButtons.attack); } function rayCasting() { var rayAngle, distToWall, rayDirX, rayDirY, mapCheckX, mapCheckY; var distX, distY; var rayStartX = player.x; var rayStartY = player.y; for (var rayIdx = 0; rayIdx < NUM_RAYS; rayIdx++) { // Calculate ray angle (center ray + offset based on ray index) rayAngle = player.dir - HALF_FOV + rayIdx / NUM_RAYS * FOV; // Get direction vector rayDirX = Math.cos(rayAngle); rayDirY = Math.sin(rayAngle); // Distance to wall distToWall = 0; hitWall = false; // Step size for ray casting var stepSizeX = Math.abs(1 / rayDirX); var stepSizeY = Math.abs(1 / rayDirY); // Which block we're checking mapCheckX = Math.floor(rayStartX); mapCheckY = Math.floor(rayStartY); // Length of ray from current position to next x or y-side var sideDistX, sideDistY; // Direction to step in x or y direction (either +1 or -1) var stepX = rayDirX >= 0 ? 1 : -1; var stepY = rayDirY >= 0 ? 1 : -1; // Calculate distance to first x and y side if (rayDirX < 0) { sideDistX = (rayStartX - mapCheckX) * stepSizeX; } else { sideDistX = (mapCheckX + 1.0 - rayStartX) * stepSizeX; } if (rayDirY < 0) { sideDistY = (rayStartY - mapCheckY) * stepSizeY; } else { sideDistY = (mapCheckY + 1.0 - rayStartY) * stepSizeY; } // Perform DDA (Digital Differential Analysis) var hit = false; var side = 0; // 0 for x-side, 1 for y-side var maxDistance = MAX_RENDER_DISTANCE; while (!hit && distToWall < maxDistance) { // Jump to next map square if (sideDistX < sideDistY) { sideDistX += stepSizeX; mapCheckX += stepX; side = 0; } else { sideDistY += stepSizeY; mapCheckY += stepY; side = 1; } // Check if ray has hit a wall if (mapCheckX < 0 || mapCheckX >= MAP_SIZE || mapCheckY < 0 || mapCheckY >= MAP_SIZE || !map[mapCheckY] || !map[mapCheckY][mapCheckX]) { hit = true; distToWall = maxDistance; } else if (map[mapCheckY][mapCheckX].type === 1) { hit = true; // Calculate exact distance to avoid fisheye effect if (side === 0) { distToWall = (mapCheckX - rayStartX + (1 - stepX) / 2) / rayDirX; } else { distToWall = (mapCheckY - rayStartY + (1 - stepY) / 2) / rayDirY; } } } // Calculate height of wall based on distance var wallHeight = Math.min(2732, WALL_HEIGHT_FACTOR / distToWall); // Update the strip var strip = rayCastView.children[rayIdx]; strip.updateStrip(STRIP_WIDTH, wallHeight, rayIdx, 1, distToWall); } // Render monsters and treasures renderEntities(); } function renderEntities() { // First, hide all entities for (var i = 0; i < monsters.length; i++) { monsters[i].visible = false; } for (var i = 0; i < treasures.length; i++) { treasures[i].visible = false; } // Calculate entity positions relative to player for (var i = 0; i < monsters.length; i++) { var monster = monsters[i]; // Vector from player to monster var dx = monster.mapX - player.x; var dy = monster.mapY - player.y; // Distance to monster var dist = Math.sqrt(dx * dx + dy * dy); // Angle between player's direction and monster var angle = Math.atan2(dy, dx) - player.dir; // Normalize angle while (angle < -Math.PI) angle += Math.PI * 2; while (angle > Math.PI) angle -= Math.PI * 2; // Check if monster is in field of view if (Math.abs(angle) < HALF_FOV && dist < MAX_RENDER_DISTANCE) { // Check if there's a wall between player and monster var rayDirX = Math.cos(player.dir + angle); var rayDirY = Math.sin(player.dir + angle); var rayHit = castRayToPoint(player.x, player.y, monster.mapX, monster.mapY); if (!rayHit.hit || rayHit.dist > dist - 0.5) { // Monster is visible monster.visible = true; // Calculate screen position var screenX = (0.5 + angle / FOV) * 2048; // Calculate height based on distance var height = WALL_HEIGHT_FACTOR / dist; // Position monster monster.x = screenX; monster.y = 2732 / 2; // Scale monster based on distance var scale = height / 100; monster.scale.set(scale, scale); } } } // Render treasures with the same logic for (var i = 0; i < treasures.length; i++) { var treasure = treasures[i]; var dx = treasure.mapX - player.x; var dy = treasure.mapY - player.y; var dist = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx) - player.dir; while (angle < -Math.PI) angle += Math.PI * 2; while (angle > Math.PI) angle -= Math.PI * 2; if (Math.abs(angle) < HALF_FOV && dist < MAX_RENDER_DISTANCE) { var rayHit = castRayToPoint(player.x, player.y, treasure.mapX, treasure.mapY); if (!rayHit.hit || rayHit.dist > dist - 0.5) { treasure.visible = true; var screenX = (0.5 + angle / FOV) * 2048; var height = WALL_HEIGHT_FACTOR / dist; treasure.x = screenX; treasure.y = 2732 / 2; var scale = height / 100; treasure.scale.set(scale, scale); } } } } function castRayToPoint(startX, startY, targetX, targetY) { var rayDirX = targetX - startX; var rayDirY = targetY - startY; var distance = Math.sqrt(rayDirX * rayDirX + rayDirY * rayDirY); rayDirX /= distance; rayDirY /= distance; var mapCheckX = Math.floor(startX); var mapCheckY = Math.floor(startY); var stepSizeX = Math.abs(1 / rayDirX); var stepSizeY = Math.abs(1 / rayDirY); var stepX = rayDirX >= 0 ? 1 : -1; var stepY = rayDirY >= 0 ? 1 : -1; var sideDistX, sideDistY; if (rayDirX < 0) { sideDistX = (startX - mapCheckX) * stepSizeX; } else { sideDistX = (mapCheckX + 1.0 - startX) * stepSizeX; } if (rayDirY < 0) { sideDistY = (startY - mapCheckY) * stepSizeY; } else { sideDistY = (mapCheckY + 1.0 - startY) * stepSizeY; } var hit = false; var side = 0; var distToWall = 0; while (!hit && distToWall < distance) { if (sideDistX < sideDistY) { sideDistX += stepSizeX; mapCheckX += stepX; side = 0; distToWall = sideDistX - stepSizeX; } else { sideDistY += stepSizeY; mapCheckY += stepY; side = 1; distToWall = sideDistY - stepSizeY; } if (mapCheckX < 0 || mapCheckX >= MAP_SIZE || mapCheckY < 0 || mapCheckY >= MAP_SIZE || !map[mapCheckY] || !map[mapCheckY][mapCheckX]) { break; } else if (map[mapCheckY][mapCheckX].type === 1) { hit = true; } } return { hit: hit, dist: distToWall }; } function updateControls() { // Read from control buttons controls.forward = controlButtons.up.pressed; controls.backward = controlButtons.down.pressed; controls.left = controlButtons.left.pressed; controls.right = controlButtons.right.pressed; controls.attack = controlButtons.attack.pressed; } function updatePlayerMovement(deltaTime) { var moveSpeed = PLAYER_MOVE_SPEED * deltaTime; var turnSpeed = PLAYER_TURN_SPEED * deltaTime; var dx = 0, dy = 0; var didMove = false; // Handle rotation if (controls.left) { player.dir -= turnSpeed; while (player.dir < 0) player.dir += Math.PI * 2; } if (controls.right) { player.dir += turnSpeed; while (player.dir >= Math.PI * 2) player.dir -= Math.PI * 2; } // Handle movement if (controls.forward) { dx += Math.cos(player.dir) * moveSpeed; dy += Math.sin(player.dir) * moveSpeed; didMove = true; } if (controls.backward) { dx -= Math.cos(player.dir) * moveSpeed; dy -= Math.sin(player.dir) * moveSpeed; didMove = true; } // Collision detection var newX = player.x + dx; var newY = player.y + dy; var cellX = Math.floor(newX); var cellY = Math.floor(newY); // Check if we can move to the new position if (cellX >= 0 && cellX < MAP_SIZE && cellY >= 0 && cellY < MAP_SIZE && map[cellY] && map[cellY][cellX]) { if (map[cellY][cellX].type === 0) { player.x = newX; player.y = newY; if (didMove && LK.ticks % 20 === 0) { LK.getSound('walk').play(); } } } // Attack handling if (controls.attack && LK.ticks % 15 === 0) { attackAction(); } // Check for collisions with monsters checkMonsterCollisions(); // Check for collisions with treasures checkTreasureCollisions(); // Update player marker on minimap updateMiniMap(); } function attackAction() { LK.getSound('attack').play(); // Create a projectile var projectile = new Projectile(); // Start the projectile at a position slightly in front of the player projectile.dirX = Math.cos(player.dir); projectile.dirY = Math.sin(player.dir); // Initialize distanceTraveled to a small value so it starts in front of the player projectile.distanceTraveled = 0.5; // Calculate initial world position var mapX = player.x + projectile.distanceTraveled * projectile.dirX; var mapY = player.y + projectile.distanceTraveled * projectile.dirY; // Calculate initial screen position var dirToProjectile = Math.atan2(mapY - player.y, mapX - player.x); var angleToPlayer = dirToProjectile - player.dir; // Normalize angle while (angleToPlayer < -Math.PI) angleToPlayer += Math.PI * 2; while (angleToPlayer > Math.PI) angleToPlayer -= Math.PI * 2; // Calculate screen position var screenX = (0.5 + angleToPlayer / FOV) * 2048; var distToProjectile = projectile.distanceTraveled; var height = WALL_HEIGHT_FACTOR / distToProjectile; // Set initial position on screen projectile.x = screenX; projectile.y = 2732 / 2; var scale = height / 200; projectile.scale.set(scale, scale); // Adjust speed and range settings projectile.speed = 0.05; projectile.range = 10; // Add to the game projectiles.push(projectile); game.addChild(projectile); } function updateProjectiles(deltaTime) { for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; // Store last position for collision detection var lastMapX = player.x + projectile.lastDistanceTraveled * projectile.dirX; var lastMapY = player.y + projectile.lastDistanceTraveled * projectile.dirY; // Update projectile position var remove = projectile.update(deltaTime); // Calculate the projectile's current world coordinates var mapX = player.x + projectile.distanceTraveled * projectile.dirX; var mapY = player.y + projectile.distanceTraveled * projectile.dirY; // Check if projectile hits a wall var cellX = Math.floor(mapX); var cellY = Math.floor(mapY); var hitWall = false; if (cellX < 0 || cellX >= MAP_SIZE || cellY < 0 || cellY >= MAP_SIZE || !map[cellY] || !map[cellY][cellX]) { hitWall = true; } else if (map[cellY][cellX].type === 1) { hitWall = true; } // Check if projectile hits a monster var hitMonster = false; var hitMonsterIndex = -1; for (var j = 0; j < monsters.length; j++) { var monster = monsters[j]; var dx = monster.mapX - mapX; var dy = monster.mapY - mapY; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 0.5) { hitMonster = true; hitMonsterIndex = j; break; } } // Ray casting check for the projectile var rayHit = castRayToPoint(player.x, player.y, mapX, mapY); // Update projectile visual position in the 3D view based on its position in the map var dirToProjectile = Math.atan2(mapY - player.y, mapX - player.x); var angleToPlayer = dirToProjectile - player.dir; // Normalize angle while (angleToPlayer < -Math.PI) angleToPlayer += Math.PI * 2; while (angleToPlayer > Math.PI) angleToPlayer -= Math.PI * 2; // Calculate screen position if (Math.abs(angleToPlayer) < HALF_FOV && (!rayHit.hit || rayHit.dist > projectile.distanceTraveled - 0.1)) { var screenX = (0.5 + angleToPlayer / FOV) * 2048; var distToProjectile = projectile.distanceTraveled; var height = WALL_HEIGHT_FACTOR / distToProjectile; projectile.x = screenX; projectile.y = 2732 / 2; var scale = height / 200; projectile.scale.set(scale, scale); projectile.visible = true; } else { projectile.visible = false; } if (remove || hitWall || hitMonster) { // Handle monster hit if (hitMonster && hitMonsterIndex !== -1) { var monster = monsters[hitMonsterIndex]; var killed = monster.takeDamage(); if (killed) { // Remove monster map[Math.floor(monster.mapY)][Math.floor(monster.mapX)].removeMonster(); monster.destroy(); monsters.splice(hitMonsterIndex, 1); // Increase score player.score += 10; updateUI(); // Check for level completion checkLevelCompletion(); } } // Remove projectile projectile.destroy(); projectiles.splice(i, 1); } } } function checkMonsterCollisions() { for (var i = 0; i < monsters.length; i++) { var monster = monsters[i]; var dx = monster.mapX - player.x; var dy = monster.mapY - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 0.5) { // Player hit by monster player.health--; updateUI(); // Visual feedback LK.effects.flashScreen(0xff0000, 300); // Play sound LK.getSound('hit').play(); // Push player back slightly player.x -= dx * 0.3; player.y -= dy * 0.3; // Check game over if (player.health <= 0) { LK.showGameOver(); } break; } } } function checkTreasureCollisions() { for (var i = treasures.length - 1; i >= 0; i--) { var treasure = treasures[i]; var dx = treasure.mapX - player.x; var dy = treasure.mapY - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 0.5) { // Collect treasure player.score += treasure.value * 5; updateUI(); // Play sound LK.getSound('collect').play(); // Remove treasure map[Math.floor(treasure.mapY)][Math.floor(treasure.mapX)].removeTreasure(); treasure.destroy(); treasures.splice(i, 1); // Check level completion checkLevelCompletion(); } } } function checkLevelCompletion() { // Level is complete if all monsters are defeated and all treasures collected if (monsters.length === 0 && treasures.length === 0) { // Level up player.level++; storage.level = player.level; // Restore some health player.health = Math.min(player.health + 2, 5); // Update UI updateUI(); // Show level complete var levelCompleteText = new Text2('Level ' + (player.level - 1) + ' Complete!', { size: 80, fill: 0x55FF55 }); levelCompleteText.anchor.set(0.5, 0.5); levelCompleteText.x = 2048 / 2; levelCompleteText.y = 2732 / 2; game.addChild(levelCompleteText); // Generate new level after a delay LK.setTimeout(function () { levelCompleteText.destroy(); generateMap(); }, 2000); } } function updateMiniMap() { // Update player marker position on minimap playerMarker.x = miniMap.x + player.x * CELL_SIZE * MINI_MAP_SCALE; playerMarker.y = miniMap.y + player.y * CELL_SIZE * MINI_MAP_SCALE; // Draw player direction indicator var dirX = Math.cos(player.dir) * 15; var dirY = Math.sin(player.dir) * 15; } // Game update method game.update = function () { var currentTime = Date.now(); var deltaTime = currentTime - lastTime; lastTime = currentTime; // Update controls updateControls(); // Update player updatePlayerMovement(deltaTime); // Update projectiles updateProjectiles(deltaTime); // Update raycast view rayCasting(); }; // Game initialization setupGame(); // Event handlers game.down = function (x, y, obj) { // Handle general screen press }; game.up = function (x, y, obj) { // Handle general screen release }; game.move = function (x, y, obj) { // Handle general screen move };
===================================================================
--- original.js
+++ change.js
@@ -34,10 +34,10 @@
arrowSprite.visible = false;
buttonSprite = self.attachAsset('attackButton', {
anchorX: 0.5,
anchorY: 0.5,
- scaleX: 2.5,
- scaleY: 2.5
+ scaleX: 3.5,
+ scaleY: 3.5
});
}
self.direction = direction;
self.pressed = false;
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "RayCaster Dungeon Crawler" and with the description "A first-person dungeon crawler using ray casting technology to create a pseudo-3D experience. Navigate maze-like dungeons, defeat monsters, and collect treasures as you explore increasingly challenging levels with authentic retro visuals.". No text on banner!
Sword. In-Game asset. 2d. High contrast. No shadows
Tan wall. In-Game asset. 2d. High contrast. No shadows
Make a bunch of empty space above it.