User prompt
Intenta solucionar todos los problemas que se puedan surgir por las paredes
User prompt
Soluciónalo
User prompt
Quita el efecto de basio
User prompt
Quita la textura Sun
User prompt
Ahora vamos a usar el método Raycasting
User prompt
Cambia la línea del medio por una textura que casi no se vea
User prompt
Has la línea del medio invisible sin afectar sus funciones
User prompt
crea un apartado solo para las paredes y pon la textura Sun
User prompt
Utiliza la textura Sun para poner esos espacios negros
User prompt
Pon que las paredes sean de color amarillo
User prompt
Vamos a migrar todo al método de Raycasting 2D
User prompt
Has una versión mejorada de pseude-3D
User prompt
Ponle textura a las paredes
User prompt
Optimiza y mejora el método que utiliza pseude-3D para crear paredes y profundidad
User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'self.wallColumns[i]')' in or related to this line: 'self.wallColumns[i].visible = false;' Line Number: 257
User prompt
Has eso por favor
User prompt
Has que las texturas de la pared sean cuadradas
User prompt
Please fix the bug: 'wallRenderer.generateWalls is not a function. (In 'wallRenderer.generateWalls()', 'wallRenderer.generateWalls' is undefined)' in or related to this line: 'wallRenderer.generateWalls();' Line Number: 731
User prompt
Para generar las paredes usa muchas imágenes de la textura haz que solo 1 textura para evitar lag u problemas visuales
User prompt
No sé cómo hacer las paredes te dejo a decisión tuya para hacerlas
User prompt
De ves de líneas haz que las paredes sean cuadrados
User prompt
Haz que se pueda mirar hacia arriba y hacia abajo
User prompt
limítalo a 1
User prompt
Haz que las paredes no sigan al jugador si no que miren a un punto fijo como el punto central al donde están y disminuye la cantidad de paredes que caben en una línea limítalo a 10
User prompt
Haz una textura nueva para añadir color a la pared
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var CeilingTileRenderer = Container.expand(function () { var self = Container.call(this); self.tiles = []; // Generate ceiling tiles in safe positions (away from corners and walls) self.generateTiles = function () { for (var x = 2; x < worldGrid.width - 2; x++) { for (var y = 2; y < worldGrid.height - 2; y++) { // Only place tiles in open areas (not near walls or corners) if (!worldGrid.hasWallAt(x * worldGrid.cellSize, y * worldGrid.cellSize) && !self.isNearCorner(x, y) && self.isSafePosition(x, y)) { var tile = { worldX: x * worldGrid.cellSize + worldGrid.cellSize / 2, worldY: y * worldGrid.cellSize + worldGrid.cellSize / 2, sprite: null }; self.tiles.push(tile); } } } }; // Check if position is near a corner self.isNearCorner = function (gridX, gridY) { // Check 3x3 area around position for wall density var wallCount = 0; for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { var checkX = gridX + dx; var checkY = gridY + dy; if (checkX >= 0 && checkX < worldGrid.width && checkY >= 0 && checkY < worldGrid.height) { if (worldGrid.walls[checkX][checkY]) { wallCount++; } } } } return wallCount >= 3; // Near corner if 3+ walls nearby }; // Check if position is safe (center of open areas) self.isSafePosition = function (gridX, gridY) { // Ensure there's open space in all 4 cardinal directions var directions = [{ x: 0, y: -1 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 }]; for (var i = 0; i < directions.length; i++) { var checkX = gridX + directions[i].x; var checkY = gridY + directions[i].y; if (checkX >= 0 && checkX < worldGrid.width && checkY >= 0 && checkY < worldGrid.height) { if (worldGrid.walls[checkX][checkY]) { return false; } } } return true; }; self.render = function (player) { // Clear existing sprites for (var i = 0; i < self.tiles.length; i++) { if (self.tiles[i].sprite) { self.tiles[i].sprite.visible = false; } } var visibleTiles = []; // Calculate which tiles are visible and their screen positions for (var i = 0; i < self.tiles.length; i++) { var tile = self.tiles[i]; var dx = tile.worldX - player.x; var dy = tile.worldY - player.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only render tiles within reasonable distance if (distance < 800) { // Calculate angle relative to player's view direction var tileAngle = Math.atan2(dy, dx); var angleDiff = tileAngle - player.angle; // Normalize angle difference while (angleDiff > Math.PI) angleDiff -= 2 * Math.PI; while (angleDiff < -Math.PI) angleDiff += 2 * Math.PI; // Check if tile is within field of view var fov = Math.PI / 3; if (Math.abs(angleDiff) < fov / 2) { // Calculate screen X position var screenX = 1024 + angleDiff / (fov / 2) * 1024; if (screenX >= 0 && screenX <= 2048) { // Apply pitch offset to ceiling tiles var pitchOffset = player.pitch * 400; visibleTiles.push({ tile: tile, distance: distance, screenX: screenX, screenY: 400 - 200 * (1000 / (distance + 100)) + pitchOffset // Project to ceiling with pitch }); } } } } // Sort by distance (farthest first) visibleTiles.sort(function (a, b) { return b.distance - a.distance; }); // Render visible tiles for (var i = 0; i < visibleTiles.length; i++) { var visibleTile = visibleTiles[i]; var tile = visibleTile.tile; if (!tile.sprite) { tile.sprite = self.addChild(LK.getAsset('ceilingTile', { anchorX: 0.5, anchorY: 0.5 })); } tile.sprite.x = visibleTile.screenX; tile.sprite.y = visibleTile.screenY; tile.sprite.visible = true; // Scale based on distance var scale = Math.max(0.1, 20 / (visibleTile.distance + 20)); tile.sprite.scaleX = scale; tile.sprite.scaleY = scale; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); self.x = 1024; self.y = 1366; self.angle = 0; self.pitch = 0; // Vertical look angle (up/down) self.speed = 3; self.rotSpeed = 0.1; // Smooth interpolation properties self.targetX = 1024; self.targetY = 1366; self.targetAngle = 0; self.targetPitch = 0; self.smoothingFactor = 0.15; // Player visual for debugging (will be hidden in first person) var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); playerGraphics.visible = false; // Hide for first person view self.moveForward = function () { var newX = self.targetX + Math.cos(self.targetAngle) * self.speed; var newY = self.targetY + Math.sin(self.targetAngle) * self.speed; // Constrain Y coordinate to not go below 0 if (newY < 0) { newY = 0; } // Check collision with world grid using improved collision detection if (!worldGrid.checkCollision(newX, newY)) { self.targetX = newX; self.targetY = newY; } }; self.moveBackward = function () { var newX = self.targetX - Math.cos(self.targetAngle) * self.speed; var newY = self.targetY - Math.sin(self.targetAngle) * self.speed; // Constrain Y coordinate to not go below 0 if (newY < 0) { newY = 0; } // Check collision with world grid using improved collision detection if (!worldGrid.checkCollision(newX, newY)) { self.targetX = newX; self.targetY = newY; } }; self.turnLeft = function () { self.targetAngle -= self.rotSpeed; }; self.turnRight = function () { self.targetAngle += self.rotSpeed; }; self.lookUp = function () { self.targetPitch = Math.max(-Math.PI / 3, self.targetPitch - self.rotSpeed); // Limit to -60 degrees }; self.lookDown = function () { self.targetPitch = Math.min(Math.PI / 3, self.targetPitch + self.rotSpeed); // Limit to +60 degrees }; self.updateSmooth = function () { // Smooth interpolation for position self.x += (self.targetX - self.x) * self.smoothingFactor; self.y += (self.targetY - self.y) * self.smoothingFactor; // Smooth interpolation for rotation with angle wrapping var angleDiff = self.targetAngle - self.angle; // Handle angle wrapping (ensure shortest rotation path) if (angleDiff > Math.PI) angleDiff -= 2 * Math.PI; if (angleDiff < -Math.PI) angleDiff += 2 * Math.PI; self.angle += angleDiff * self.smoothingFactor; // Smooth interpolation for pitch var pitchDiff = self.targetPitch - self.pitch; self.pitch += pitchDiff * self.smoothingFactor; }; return self; }); var Renderer3D = Container.expand(function () { var self = Container.call(this); self.wallColumns = []; self.floorColumns = []; self.ceilingColumns = []; self.zBuffer = []; // Track depth for each column // Create pseudo-3D rendering columns (walls handled by WallRenderer) for (var i = 0; i < 512; i++) { var wallCol = self.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 1.0 })); wallCol.x = i * 4; wallCol.y = 1366; wallCol.visible = false; // Disable to prevent conflicts self.wallColumns.push(wallCol); var floorCol = self.addChild(LK.getAsset('piso', { anchorX: 0.5, anchorY: 0.5 })); floorCol.x = i * 4; floorCol.y = 1366; self.floorColumns.push(floorCol); var ceilCol = self.addChild(LK.getAsset('ceilingStrip', { anchorX: 0.5, anchorY: 0.5 })); ceilCol.x = i * 4; ceilCol.y = 700; self.ceilingColumns.push(ceilCol); } self.render = function (player) { var fov = Math.PI / 3; // 60 degrees field of view var halfFov = fov / 2; var screenHeight = 2732; var screenCenter = screenHeight / 2; // Center line at Y = 1366 // Calculate pitch offset for vertical look var pitchOffset = player.pitch * 400; // Scale pitch to screen movement // Store column data for depth sorting var columnData = []; for (var i = 0; i < 512; i++) { var rayAngle = player.angle - halfFov + i / 512 * fov; var rayData = self.castRayWithCoords(player.x, player.y, rayAngle); var distance = rayData.distance; var hitX = rayData.hitX; var hitY = rayData.hitY; // Store column data with distance for sorting columnData.push({ index: i, distance: distance, hitX: hitX, hitY: hitY, rayAngle: rayAngle }); } // Sort columns by distance (farthest first for proper depth) columnData.sort(function (a, b) { return b.distance - a.distance; }); // Initialize z-buffer for this frame for (var k = 0; k < 512; k++) { self.zBuffer[k] = 1000; // Start with maximum distance } // First pass: Update z-buffer with closest distances for (var j = 0; j < columnData.length; j++) { var data = columnData[j]; var i = data.index; var distance = data.distance; // Only update z-buffer if this is closer if (distance < self.zBuffer[i]) { self.zBuffer[i] = distance; } } // Second pass: Render only if at closest distance for (var j = 0; j < columnData.length; j++) { var data = columnData[j]; var i = data.index; var distance = data.distance; var hitX = data.hitX; var hitY = data.hitY; // Only render if this column is at the closest distance if (Math.abs(distance - self.zBuffer[i]) < 1) { // Calculate wall height to maintain square aspect ratio var baseWallSize = 400; // Base size for square walls var wallHeight = Math.max(100, baseWallSize * (1000 / (distance + 100))); var halfWallHeight = wallHeight / 2; // Calculate wall boundaries for floor/ceiling positioning var wallTop = screenCenter - halfWallHeight + pitchOffset; var wallBottom = screenCenter + halfWallHeight + pitchOffset; // Hide wall columns completely - using dedicated WallRenderer instead self.wallColumns[i].visible = false; // FLOOR: Bottom section (from wall bottom to screen bottom) with pitch offset var floorTop = wallBottom; var floorHeight = screenHeight - floorTop; self.floorColumns[i].y = floorTop + floorHeight / 2; // Center floor in bottom section self.floorColumns[i].height = Math.max(2, floorHeight); self.floorColumns[i].visible = true; // CEILING: Top section (from screen top to wall top) with pitch offset var ceilingHeight = wallTop; self.ceilingColumns[i].y = ceilingHeight / 2; // Center ceiling in top section self.ceilingColumns[i].height = Math.max(2, ceilingHeight); self.ceilingColumns[i].visible = true; } else { // Hide columns that are behind walls self.wallColumns[i].visible = false; self.floorColumns[i].visible = false; self.ceilingColumns[i].visible = false; } } }; self.castRay = function (startX, startY, angle) { var rayData = self.castRayWithCoords(startX, startY, angle); return rayData.distance; }; self.castRayWithCoords = function (startX, startY, angle) { var rayX = startX; var rayY = startY; var deltaX = Math.cos(angle) * 2; var deltaY = Math.sin(angle) * 2; var distance = 0; // Raycast using world coordinate system while (distance < 1000) { rayX += deltaX; rayY += deltaY; distance += 2; // Check for walls using world grid if (worldGrid.hasWallAt(rayX, rayY)) { break; } } // Align hit coordinates to grid boundaries for straight walls var gridX = Math.floor(rayX / worldGrid.cellSize); var gridY = Math.floor(rayY / worldGrid.cellSize); var alignedHitX = gridX * worldGrid.cellSize; var alignedHitY = gridY * worldGrid.cellSize; return { distance: distance, hitX: alignedHitX, hitY: alignedHitY }; }; return self; }); var SensitivityConfig = Container.expand(function () { var self = Container.call(this); // Load saved sensitivity or default to 50 self.sensitivity = storage.sensitivity || 50; self.isVisible = false; // Create background panel var background = self.addChild(LK.getAsset('untexturedArea', { anchorX: 0, anchorY: 0, width: 300, height: 200, alpha: 0.8 })); background.tint = 0x222222; // Create title text var titleText = new Text2('Sensitivity', { size: 40, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.x = 150; titleText.y = 20; self.addChild(titleText); // Create sensitivity value text var valueText = new Text2(self.sensitivity.toString(), { size: 35, fill: 0xFFFFFF }); valueText.anchor.set(0.5, 0); valueText.x = 150; valueText.y = 70; self.addChild(valueText); // Create decrease button (larger for mobile) var decreaseBtn = self.addChild(LK.getAsset('untexturedArea', { anchorX: 0.5, anchorY: 0.5, width: 70, height: 60 })); decreaseBtn.x = 80; decreaseBtn.y = 130; decreaseBtn.tint = 0x666666; var decreaseText = new Text2('-', { size: 40, fill: 0xFFFFFF }); decreaseText.anchor.set(0.5, 0.5); decreaseText.x = 80; decreaseText.y = 130; self.addChild(decreaseText); // Create increase button (larger for mobile) var increaseBtn = self.addChild(LK.getAsset('untexturedArea', { anchorX: 0.5, anchorY: 0.5, width: 70, height: 60 })); increaseBtn.x = 220; increaseBtn.y = 130; increaseBtn.tint = 0x666666; var increaseText = new Text2('+', { size: 40, fill: 0xFFFFFF }); increaseText.anchor.set(0.5, 0.5); increaseText.x = 220; increaseText.y = 130; self.addChild(increaseText); // Update sensitivity display self.updateDisplay = function () { valueText.setText(self.sensitivity.toString()); // Save to storage storage.sensitivity = self.sensitivity; }; // Toggle visibility self.toggle = function () { self.isVisible = !self.isVisible; self.visible = self.isVisible; }; // Handle decrease button with visual feedback decreaseBtn.down = function (x, y, obj) { decreaseBtn.tint = 0x888888; // Lighten on press if (self.sensitivity > 0) { self.sensitivity = Math.max(0, self.sensitivity - 5); self.updateDisplay(); } }; decreaseBtn.up = function (x, y, obj) { decreaseBtn.tint = 0x666666; // Reset color on release }; // Handle increase button with visual feedback increaseBtn.down = function (x, y, obj) { increaseBtn.tint = 0x888888; // Lighten on press if (self.sensitivity < 100) { self.sensitivity = Math.min(100, self.sensitivity + 5); self.updateDisplay(); } }; increaseBtn.up = function (x, y, obj) { increaseBtn.tint = 0x666666; // Reset color on release }; // Add background click handler to prevent game interactions background.down = function (x, y, obj) { // Prevent event from bubbling to game return true; }; background.up = function (x, y, obj) { // Prevent event from bubbling to game return true; }; background.move = function (x, y, obj) { // Prevent event from bubbling to game return true; }; // Initially hidden self.visible = false; return self; }); var WallRenderer = Container.expand(function () { var self = Container.call(this); // Enhanced pseudo-3D wall rendering system self.wallColumns = []; self.columnCount = 256; // Higher resolution for smoother walls self.maxRenderDistance = 1000; self.fogStart = 400; self.wallHeight = 600; // Base wall height for perspective // Initialize wall column system for pseudo-3D self.initColumns = function () { if (self.wallColumns.length === 0) { for (var i = 0; i < self.columnCount; i++) { var column = self.addChild(LK.getAsset('wall', { anchorX: 0.5, anchorY: 1.0 // Anchor at bottom for proper ground positioning })); column.x = i * (2048 / self.columnCount); // Distribute across screen width column.y = 1366; // Ground level column.visible = false; self.wallColumns.push(column); } } }; // Enhanced pseudo-3D wall rendering with column-based approach self.render = function (player) { self.initColumns(); var fov = Math.PI / 2.5; // Slightly wider FOV for better immersion var halfFov = fov / 2; var screenHeight = 2732; var screenCenter = screenHeight / 2; var pitchOffset = player.pitch * 350; // Enhanced pitch sensitivity // Calculate wall data for each column var columnData = []; for (var i = 0; i < self.columnCount; i++) { // Calculate ray angle for this column var columnRatio = i / self.columnCount; var rayAngle = player.angle - halfFov + columnRatio * fov; // Cast ray to find wall distance and hit point var rayResult = self.castEnhancedRay(player.x, player.y, rayAngle); // Store column rendering data columnData.push({ index: i, distance: rayResult.distance, hitX: rayResult.hitX, hitY: rayResult.hitY, wallSide: rayResult.wallSide, // Which side of wall was hit textureOffset: rayResult.textureOffset }); } // Render walls column by column for (var i = 0; i < columnData.length; i++) { var data = columnData[i]; var column = self.wallColumns[data.index]; if (data.distance < self.maxRenderDistance) { // Calculate perspective-corrected wall height var correctedDistance = data.distance * Math.cos(player.angle - (player.angle - halfFov + data.index / self.columnCount * fov)); var projectedHeight = self.wallHeight * (400 / (correctedDistance + 1)); projectedHeight = Math.max(20, Math.min(projectedHeight, screenHeight * 1.5)); // Calculate wall boundaries with pitch var halfHeight = projectedHeight / 2; var wallTop = screenCenter - halfHeight + pitchOffset; var wallBottom = screenCenter + halfHeight + pitchOffset; // Position and scale the wall column column.y = wallBottom; // Anchor at bottom column.height = projectedHeight; column.width = Math.max(2, 2048 / self.columnCount + 1); // Slight overlap to prevent gaps // Enhanced depth-based visual effects var distanceRatio = Math.min(1, data.distance / self.maxRenderDistance); // Progressive fog effect var fogIntensity = Math.max(0, (data.distance - self.fogStart) / (self.maxRenderDistance - self.fogStart)); var fogAlpha = Math.max(0.2, 1.0 - fogIntensity); column.alpha = fogAlpha; // Depth-based tinting for atmospheric effect var baseTint = 0xf5f5f5; var depthTint = Math.floor(255 * (1.0 - distanceRatio * 0.4)); // Darken with distance var tintValue = depthTint << 16 | depthTint << 8 | depthTint; column.tint = Math.max(0x404040, tintValue); // Wall side shading for better depth perception if (data.wallSide === 'vertical') { column.tint = Math.floor(column.tint * 0.85); // Darken vertical walls slightly } // Texture offset for wall variety (simulated) var textureShift = data.textureOffset * 20 % 40 - 20; column.x = data.index * (2048 / self.columnCount) + textureShift * 0.1; column.visible = true; } else { column.visible = false; } } }; // Enhanced raycasting with wall side detection and texture coordinates self.castEnhancedRay = function (startX, startY, angle) { var rayX = startX; var rayY = startY; var stepSize = 4; // Smaller steps for better accuracy var deltaX = Math.cos(angle) * stepSize; var deltaY = Math.sin(angle) * stepSize; var distance = 0; var lastX = rayX; var lastY = rayY; // Cast ray with enhanced collision detection while (distance < self.maxRenderDistance) { lastX = rayX; lastY = rayY; rayX += deltaX; rayY += deltaY; distance += stepSize; if (worldGrid.hasWallAt(rayX, rayY)) { // Determine which side of the wall was hit var hitGridX = Math.floor(rayX / worldGrid.cellSize); var hitGridY = Math.floor(rayY / worldGrid.cellSize); var lastGridX = Math.floor(lastX / worldGrid.cellSize); var lastGridY = Math.floor(lastY / worldGrid.cellSize); var wallSide = 'horizontal'; if (hitGridX !== lastGridX) { wallSide = 'vertical'; } // Calculate texture offset for wall variety var textureOffset = 0; if (wallSide === 'vertical') { textureOffset = rayY % worldGrid.cellSize / worldGrid.cellSize; } else { textureOffset = rayX % worldGrid.cellSize / worldGrid.cellSize; } return { distance: distance, hitX: rayX, hitY: rayY, wallSide: wallSide, textureOffset: textureOffset }; } } // No wall hit within render distance return { distance: self.maxRenderDistance, hitX: rayX, hitY: rayY, wallSide: 'horizontal', textureOffset: 0 }; }; // Generate walls method (required by game code) self.generateWalls = function () { self.initColumns(); }; return self; }); /**** * Initialize Game ****/ // Create player var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // World coordinate system - grid-based layout var worldGrid = { cellSize: 200, width: 20, // 20x20 grid height: 20, walls: [], // Will store wall positions // Initialize world grid with walls initializeGrid: function initializeGrid() { for (var x = 0; x < this.width; x++) { this.walls[x] = []; for (var y = 0; y < this.height; y++) { // Create backrooms-style layout var isWall = false; // Outer boundaries are always walls if (x === 0 || x === this.width - 1 || y === 0 || y === this.height - 1) { isWall = true; } // Create maze-like structure typical of backrooms else if (x % 3 === 0 && y % 3 === 0 || x % 4 === 0 && y % 2 === 0 || y % 4 === 0 && x % 2 === 0) { isWall = true; } this.walls[x][y] = isWall; } } }, // Check if a world position has a wall hasWallAt: function hasWallAt(worldX, worldY) { var gridX = Math.floor(worldX / this.cellSize); var gridY = Math.floor(worldY / this.cellSize); if (gridX < 0 || gridX >= this.width || gridY < 0 || gridY >= this.height) { return true; // Outside bounds = wall } return this.walls[gridX][gridY]; }, // Check collision with wall boundaries (with player radius) checkCollision: function checkCollision(worldX, worldY, radius) { radius = radius || 20; // Default player radius var gridX = Math.floor(worldX / this.cellSize); var gridY = Math.floor(worldY / this.cellSize); // Check the four corners of the player's bounding box var corners = [{ x: worldX - radius, y: worldY - radius }, { x: worldX + radius, y: worldY - radius }, { x: worldX - radius, y: worldY + radius }, { x: worldX + radius, y: worldY + radius }]; for (var i = 0; i < corners.length; i++) { var corner = corners[i]; var cornerGridX = Math.floor(corner.x / this.cellSize); var cornerGridY = Math.floor(corner.y / this.cellSize); // Check bounds if (cornerGridX < 0 || cornerGridX >= this.width || cornerGridY < 0 || cornerGridY >= this.height) { return true; } // Check wall collision if (this.walls[cornerGridX][cornerGridY]) { return true; } } return false; }, // Convert screen coordinates to world coordinates screenToWorld: function screenToWorld(screenX, screenY) { return { x: screenX, y: screenY }; }, // Convert world coordinates to screen coordinates worldToScreen: function worldToScreen(worldX, worldY) { return { x: worldX, y: worldY }; } }; // Initialize the world grid worldGrid.initializeGrid(); // Add wall line completion system worldGrid.completeWallLines = function () { // Trace horizontal lines and complete them for (var y = 0; y < this.height; y++) { var wallStart = -1; var wallEnd = -1; // Find wall segments in this row for (var x = 0; x < this.width; x++) { if (this.walls[x][y]) { if (wallStart === -1) { wallStart = x; // Start of wall segment } wallEnd = x; // Update end of wall segment } else { // If we found a wall segment, complete the line between start and end if (wallStart !== -1 && wallEnd !== -1 && wallEnd > wallStart) { for (var fillX = wallStart; fillX <= wallEnd; fillX++) { this.walls[fillX][y] = true; // Fill the gap } } wallStart = -1; // Reset for next segment wallEnd = -1; } } // Complete any remaining segment at end of row if (wallStart !== -1 && wallEnd !== -1 && wallEnd > wallStart) { for (var fillX = wallStart; fillX <= wallEnd; fillX++) { this.walls[fillX][y] = true; } } } // Trace vertical lines and complete them for (var x = 0; x < this.width; x++) { var wallStart = -1; var wallEnd = -1; // Find wall segments in this column for (var y = 0; y < this.height; y++) { if (this.walls[x][y]) { if (wallStart === -1) { wallStart = y; // Start of wall segment } wallEnd = y; // Update end of wall segment } else { // If we found a wall segment, complete the line between start and end if (wallStart !== -1 && wallEnd !== -1 && wallEnd > wallStart) { for (var fillY = wallStart; fillY <= wallEnd; fillY++) { this.walls[x][fillY] = true; // Fill the gap } } wallStart = -1; // Reset for next segment wallEnd = -1; } } // Complete any remaining segment at end of column if (wallStart !== -1 && wallEnd !== -1 && wallEnd > wallStart) { for (var fillY = wallStart; fillY <= wallEnd; fillY++) { this.walls[x][fillY] = true; } } } }; // Apply wall line completion after initial generation worldGrid.completeWallLines(); // Create wall renderer var wallRenderer = new WallRenderer(); game.addChild(wallRenderer); wallRenderer.generateWalls(); // Create ceiling tile renderer var ceilingTileRenderer = new CeilingTileRenderer(); game.addChild(ceilingTileRenderer); ceilingTileRenderer.generateTiles(); // Create player var player = new Player(); // Position player at a valid starting location in the world grid player.x = worldGrid.cellSize * 1.5; // Start in cell (1,1) player.y = worldGrid.cellSize * 1.5; game.addChild(player); // Create 3D renderer var renderer3D = new Renderer3D(); game.addChild(renderer3D); // Create coordinate display text var coordXText = new Text2('X: 0', { size: 60, fill: 0xFFFFFF }); coordXText.anchor.set(0, 0); coordXText.x = 120; // Avoid top-left 100x100 area coordXText.y = 120; LK.gui.addChild(coordXText); var coordZText = new Text2('Z: 0', { size: 60, fill: 0xFFFFFF }); coordZText.anchor.set(0, 0); coordZText.x = 120; // Avoid top-left 100x100 area coordZText.y = 200; LK.gui.addChild(coordZText); // Create settings button in top-right corner (larger for mobile) var settingsButton = LK.getAsset('untexturedArea', { anchorX: 1, anchorY: 0, width: 120, height: 120 }); settingsButton.tint = 0x444444; settingsButton.alpha = 0.7; LK.gui.topRight.addChild(settingsButton); var settingsText = new Text2('⚙', { size: 60, fill: 0xFFFFFF }); settingsText.anchor.set(0.5, 0.5); settingsText.x = -60; settingsText.y = 60; LK.gui.topRight.addChild(settingsText); // Create sensitivity configuration panel var sensitivityConfig = new SensitivityConfig(); sensitivityConfig.x = 2048 - 320; sensitivityConfig.y = 100; LK.gui.addChild(sensitivityConfig); // Movement flags var moveForward = false; var moveBackward = false; var turnLeft = false; var turnRight = false; var lookUp = false; var lookDown = false; // Touch controls for movement var touchStartX = 0; var touchStartY = 0; var touchActive = false; // Settings button click handler settingsButton.down = function (x, y, obj) { sensitivityConfig.toggle(); }; game.down = function (x, y, obj) { touchStartX = x; touchStartY = y; touchActive = true; // Forward movement on touch moveForward = true; }; game.up = function (x, y, obj) { touchActive = false; moveForward = false; moveBackward = false; turnLeft = false; turnRight = false; lookUp = false; lookDown = false; }; game.move = function (x, y, obj) { if (!touchActive) return; var deltaX = x - touchStartX; var deltaY = y - touchStartY; // Horizontal movement for turning if (Math.abs(deltaX) > 50) { if (deltaX > 0) { turnRight = true; turnLeft = false; } else { turnLeft = true; turnRight = false; } } else { turnLeft = false; turnRight = false; } // Vertical movement - split between forward/backward and look up/down if (Math.abs(deltaY) > 50) { // If touch is in upper part of screen, use for looking up/down if (y < 1366) { // Upper half of screen for vertical look if (deltaY < 0) { lookUp = true; lookDown = false; } else { lookDown = true; lookUp = false; } moveForward = false; moveBackward = false; } else { // Lower half of screen for movement if (deltaY < 0) { moveForward = true; moveBackward = false; } else { moveBackward = true; moveForward = false; } lookUp = false; lookDown = false; } } else { lookUp = false; lookDown = false; } }; game.update = function () { // Update player rotation speed based on sensitivity (0-100 maps to 0.05-0.2) var sensitivityValue = sensitivityConfig.sensitivity; player.rotSpeed = 0.05 + sensitivityValue / 100 * 0.15; // Handle movement if (moveForward) { player.moveForward(); } if (moveBackward) { player.moveBackward(); } if (turnLeft) { player.turnLeft(); } if (turnRight) { player.turnRight(); } if (lookUp) { player.lookUp(); } if (lookDown) { player.lookDown(); } // Apply smooth interpolation player.updateSmooth(); // Render in optimal order: floor/ceiling first, then walls, then ceiling details renderer3D.render(player); // Handles floor and ceiling strips wallRenderer.render(player); // Enhanced wall rendering ceilingTileRenderer.render(player); // Ceiling decorations on top // Update coordinate display var gridX = Math.floor(player.x / worldGrid.cellSize); var gridZ = Math.floor(player.y / worldGrid.cellSize); coordXText.setText('X: ' + gridX); coordZText.setText('Z: ' + gridZ); };
===================================================================
--- original.js
+++ change.js
@@ -214,16 +214,17 @@
self.wallColumns = [];
self.floorColumns = [];
self.ceilingColumns = [];
self.zBuffer = []; // Track depth for each column
- // Create pseudo-3D rendering columns
+ // Create pseudo-3D rendering columns (walls handled by WallRenderer)
for (var i = 0; i < 512; i++) {
var wallCol = self.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 1.0
}));
wallCol.x = i * 4;
wallCol.y = 1366;
+ wallCol.visible = false; // Disable to prevent conflicts
self.wallColumns.push(wallCol);
var floorCol = self.addChild(LK.getAsset('piso', {
anchorX: 0.5,
anchorY: 0.5
@@ -296,9 +297,9 @@
var halfWallHeight = wallHeight / 2;
// Calculate wall boundaries for floor/ceiling positioning
var wallTop = screenCenter - halfWallHeight + pitchOffset;
var wallBottom = screenCenter + halfWallHeight + pitchOffset;
- // Hide wall columns to avoid conflict with optimized WallRenderer
+ // Hide wall columns completely - using dedicated WallRenderer instead
self.wallColumns[i].visible = false;
// FLOOR: Bottom section (from wall bottom to screen bottom) with pitch offset
var floorTop = wallBottom;
var floorHeight = screenHeight - floorTop;
@@ -470,148 +471,152 @@
return self;
});
var WallRenderer = Container.expand(function () {
var self = Container.call(this);
- // Optimized wall rendering using multiple layers for depth
- self.wallLayers = [];
- self.maxLayers = 8; // Number of depth layers for better 3D effect
- self.layerDistance = 100; // Distance between layers
- // Initialize layered wall textures for depth effect
- self.initTextures = function () {
- if (self.wallLayers.length === 0) {
- for (var i = 0; i < self.maxLayers; i++) {
- var layer = self.addChild(LK.getAsset('wall', {
+ // Enhanced pseudo-3D wall rendering system
+ self.wallColumns = [];
+ self.columnCount = 256; // Higher resolution for smoother walls
+ self.maxRenderDistance = 1000;
+ self.fogStart = 400;
+ self.wallHeight = 600; // Base wall height for perspective
+ // Initialize wall column system for pseudo-3D
+ self.initColumns = function () {
+ if (self.wallColumns.length === 0) {
+ for (var i = 0; i < self.columnCount; i++) {
+ var column = self.addChild(LK.getAsset('wall', {
anchorX: 0.5,
- anchorY: 0.5
+ anchorY: 1.0 // Anchor at bottom for proper ground positioning
}));
- layer.visible = false;
- // Each layer gets progressively darker for depth
- layer.alpha = 1.0 - i * 0.1;
- // Apply subtle tinting for depth perception while preserving texture
- var depthTint = 0xf0f0f0 - i * 0x101010;
- layer.tint = depthTint;
- self.wallLayers.push(layer);
+ column.x = i * (2048 / self.columnCount); // Distribute across screen width
+ column.y = 1366; // Ground level
+ column.visible = false;
+ self.wallColumns.push(column);
}
}
};
- // Optimized pseudo-3D wall rendering with improved depth perception
+ // Enhanced pseudo-3D wall rendering with column-based approach
self.render = function (player) {
- self.initTextures();
- var fov = Math.PI / 3; // 60 degrees field of view
- var maxDistance = 800; // Maximum render distance
- var rayCount = 16; // Number of rays for depth sampling
- var angleStep = fov / rayCount;
- var screenCenterX = 1024;
- var screenCenterY = 1366;
- var pitchOffset = player.pitch * 300;
- // Hide all layers initially
- for (var i = 0; i < self.wallLayers.length; i++) {
- self.wallLayers[i].visible = false;
+ self.initColumns();
+ var fov = Math.PI / 2.5; // Slightly wider FOV for better immersion
+ var halfFov = fov / 2;
+ var screenHeight = 2732;
+ var screenCenter = screenHeight / 2;
+ var pitchOffset = player.pitch * 350; // Enhanced pitch sensitivity
+ // Calculate wall data for each column
+ var columnData = [];
+ for (var i = 0; i < self.columnCount; i++) {
+ // Calculate ray angle for this column
+ var columnRatio = i / self.columnCount;
+ var rayAngle = player.angle - halfFov + columnRatio * fov;
+ // Cast ray to find wall distance and hit point
+ var rayResult = self.castEnhancedRay(player.x, player.y, rayAngle);
+ // Store column rendering data
+ columnData.push({
+ index: i,
+ distance: rayResult.distance,
+ hitX: rayResult.hitX,
+ hitY: rayResult.hitY,
+ wallSide: rayResult.wallSide,
+ // Which side of wall was hit
+ textureOffset: rayResult.textureOffset
+ });
}
- // Cast multiple rays to create layered depth effect
- var layerData = [];
- for (var rayIndex = 0; rayIndex < rayCount; rayIndex++) {
- var rayAngle = player.angle - fov / 2 + rayIndex * angleStep;
- var rayResult = self.castOptimizedRay(player.x, player.y, rayAngle, maxDistance);
- if (rayResult.hit) {
- // Calculate which layer this wall should appear in
- var layerIndex = Math.floor(rayResult.distance / self.layerDistance);
- if (layerIndex < self.maxLayers) {
- if (!layerData[layerIndex]) {
- layerData[layerIndex] = {
- distance: rayResult.distance,
- size: 0,
- count: 0
- };
- }
- layerData[layerIndex].distance += rayResult.distance;
- layerData[layerIndex].count++;
+ // Render walls column by column
+ for (var i = 0; i < columnData.length; i++) {
+ var data = columnData[i];
+ var column = self.wallColumns[data.index];
+ if (data.distance < self.maxRenderDistance) {
+ // Calculate perspective-corrected wall height
+ var correctedDistance = data.distance * Math.cos(player.angle - (player.angle - halfFov + data.index / self.columnCount * fov));
+ var projectedHeight = self.wallHeight * (400 / (correctedDistance + 1));
+ projectedHeight = Math.max(20, Math.min(projectedHeight, screenHeight * 1.5));
+ // Calculate wall boundaries with pitch
+ var halfHeight = projectedHeight / 2;
+ var wallTop = screenCenter - halfHeight + pitchOffset;
+ var wallBottom = screenCenter + halfHeight + pitchOffset;
+ // Position and scale the wall column
+ column.y = wallBottom; // Anchor at bottom
+ column.height = projectedHeight;
+ column.width = Math.max(2, 2048 / self.columnCount + 1); // Slight overlap to prevent gaps
+ // Enhanced depth-based visual effects
+ var distanceRatio = Math.min(1, data.distance / self.maxRenderDistance);
+ // Progressive fog effect
+ var fogIntensity = Math.max(0, (data.distance - self.fogStart) / (self.maxRenderDistance - self.fogStart));
+ var fogAlpha = Math.max(0.2, 1.0 - fogIntensity);
+ column.alpha = fogAlpha;
+ // Depth-based tinting for atmospheric effect
+ var baseTint = 0xf5f5f5;
+ var depthTint = Math.floor(255 * (1.0 - distanceRatio * 0.4)); // Darken with distance
+ var tintValue = depthTint << 16 | depthTint << 8 | depthTint;
+ column.tint = Math.max(0x404040, tintValue);
+ // Wall side shading for better depth perception
+ if (data.wallSide === 'vertical') {
+ column.tint = Math.floor(column.tint * 0.85); // Darken vertical walls slightly
}
+ // Texture offset for wall variety (simulated)
+ var textureShift = data.textureOffset * 20 % 40 - 20;
+ column.x = data.index * (2048 / self.columnCount) + textureShift * 0.1;
+ column.visible = true;
+ } else {
+ column.visible = false;
}
}
- // Render visible layers with proper scaling and positioning
- for (var layerIndex = 0; layerIndex < self.maxLayers; layerIndex++) {
- if (layerData[layerIndex] && layerData[layerIndex].count > 0) {
- var avgDistance = layerData[layerIndex].distance / layerData[layerIndex].count;
- var layer = self.wallLayers[layerIndex];
- // Calculate size based on distance with perspective correction
- var baseSize = 1000;
- var wallSize = Math.max(50, baseSize / (avgDistance / 100 + 1));
- // Apply aspect ratio correction for square walls
- wallSize = Math.min(wallSize, 800); // Cap maximum size
- // Position at screen center with pitch offset
- layer.x = screenCenterX;
- layer.y = screenCenterY + pitchOffset;
- // Set square dimensions maintaining texture aspect ratio
- layer.width = wallSize;
- layer.height = wallSize;
- // Distance-based alpha for depth fog effect
- var fogAlpha = Math.max(0.3, 1.0 - avgDistance / maxDistance);
- layer.alpha = fogAlpha * (1.0 - layerIndex * 0.06);
- // Apply subtle depth tinting while preserving texture details
- var baseTint = 0xe0e0e0; // Slightly darker base
- var depthReduction = layerIndex * 0x0c0c0c;
- layer.tint = Math.max(0x404040, baseTint - depthReduction);
- // Add subtle texture scaling variation for depth
- var textureScale = 1.0 + layerIndex * 0.05;
- layer.scaleX = textureScale;
- layer.scaleY = textureScale;
- layer.visible = true;
- }
- }
};
- // Optimized raycasting with early termination
- self.castOptimizedRay = function (startX, startY, angle, maxDist) {
+ // Enhanced raycasting with wall side detection and texture coordinates
+ self.castEnhancedRay = function (startX, startY, angle) {
var rayX = startX;
var rayY = startY;
- var stepSize = 8; // Larger steps for better performance
+ var stepSize = 4; // Smaller steps for better accuracy
var deltaX = Math.cos(angle) * stepSize;
var deltaY = Math.sin(angle) * stepSize;
var distance = 0;
- // Fast raycast with adaptive step size
- while (distance < maxDist) {
+ var lastX = rayX;
+ var lastY = rayY;
+ // Cast ray with enhanced collision detection
+ while (distance < self.maxRenderDistance) {
+ lastX = rayX;
+ lastY = rayY;
rayX += deltaX;
rayY += deltaY;
distance += stepSize;
if (worldGrid.hasWallAt(rayX, rayY)) {
- // Fine-tune hit position with smaller steps
- var backStepX = rayX - deltaX;
- var backStepY = rayY - deltaY;
- var fineStepX = deltaX * 0.2;
- var fineStepY = deltaY * 0.2;
- var fineDistance = distance - stepSize;
- // Refine hit point for accuracy
- for (var i = 0; i < 5; i++) {
- backStepX += fineStepX;
- backStepY += fineStepY;
- fineDistance += stepSize * 0.2;
- if (worldGrid.hasWallAt(backStepX, backStepY)) {
- return {
- hit: true,
- distance: fineDistance,
- hitX: backStepX,
- hitY: backStepY
- };
- }
+ // Determine which side of the wall was hit
+ var hitGridX = Math.floor(rayX / worldGrid.cellSize);
+ var hitGridY = Math.floor(rayY / worldGrid.cellSize);
+ var lastGridX = Math.floor(lastX / worldGrid.cellSize);
+ var lastGridY = Math.floor(lastY / worldGrid.cellSize);
+ var wallSide = 'horizontal';
+ if (hitGridX !== lastGridX) {
+ wallSide = 'vertical';
}
+ // Calculate texture offset for wall variety
+ var textureOffset = 0;
+ if (wallSide === 'vertical') {
+ textureOffset = rayY % worldGrid.cellSize / worldGrid.cellSize;
+ } else {
+ textureOffset = rayX % worldGrid.cellSize / worldGrid.cellSize;
+ }
return {
- hit: true,
distance: distance,
hitX: rayX,
- hitY: rayY
+ hitY: rayY,
+ wallSide: wallSide,
+ textureOffset: textureOffset
};
}
}
+ // No wall hit within render distance
return {
- hit: false,
- distance: maxDist,
+ distance: self.maxRenderDistance,
hitX: rayX,
- hitY: rayY
+ hitY: rayY,
+ wallSide: 'horizontal',
+ textureOffset: 0
};
};
// Generate walls method (required by game code)
self.generateWalls = function () {
- self.initTextures();
+ self.initColumns();
};
return self;
});
@@ -936,14 +941,12 @@
player.lookDown();
}
// Apply smooth interpolation
player.updateSmooth();
- // Render the pseudo-3D view
- renderer3D.render(player);
- // Render walls
- wallRenderer.render(player);
- // Render ceiling tiles
- ceilingTileRenderer.render(player);
+ // Render in optimal order: floor/ceiling first, then walls, then ceiling details
+ renderer3D.render(player); // Handles floor and ceiling strips
+ wallRenderer.render(player); // Enhanced wall rendering
+ ceilingTileRenderer.render(player); // Ceiling decorations on top
// Update coordinate display
var gridX = Math.floor(player.x / worldGrid.cellSize);
var gridZ = Math.floor(player.y / worldGrid.cellSize);
coordXText.setText('X: ' + gridX);