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
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;
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 wallWidth = wallHeight; // Make it square
var halfWallHeight = wallHeight / 2;
// WALLS: Position in middle section (centered on screen) with pitch offset
var wallTop = screenCenter - halfWallHeight + pitchOffset;
var wallBottom = screenCenter + halfWallHeight + pitchOffset;
self.wallColumns[i].height = wallHeight;
self.wallColumns[i].width = wallWidth;
self.wallColumns[i].y = wallBottom; // Anchor at bottom of wall
self.wallColumns[i].visible = true;
// Align wall columns to exact grid lines for straight wall appearance
var gridX = Math.floor(hitX / worldGrid.cellSize);
var gridY = Math.floor(hitY / worldGrid.cellSize);
// Snap to exact grid boundaries to create straight walls
var alignedX = gridX * worldGrid.cellSize;
var alignedY = gridY * worldGrid.cellSize;
// Position wall column at aligned grid position
self.wallColumns[i].x = i * 4; // Keep screen column position
// 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);
self.wallSegments = [];
// Generate wall segments based on world grid with limited quantity
self.generateWalls = function () {
// Clear existing walls
for (var i = 0; i < self.wallSegments.length; i++) {
if (self.wallSegments[i].sprite) {
self.wallSegments[i].sprite.destroy();
}
}
self.wallSegments = [];
var processed = [];
// Initialize processed grid
for (var x = 0; x < worldGrid.width; x++) {
processed[x] = [];
for (var y = 0; y < worldGrid.height; y++) {
processed[x][y] = false;
}
}
// Find continuous wall segments with limit of 1 per line
for (var x = 0; x < worldGrid.width; x++) {
var horizontalWallsInRow = 0; // Limit horizontal walls per row
for (var y = 0; y < worldGrid.height; y++) {
if (worldGrid.walls[x][y] && !processed[x][y] && horizontalWallsInRow < 1) {
// Try to create horizontal segment first
var segmentEndX = x;
while (segmentEndX < worldGrid.width && worldGrid.walls[segmentEndX][y] && !processed[segmentEndX][y]) {
segmentEndX++;
}
// Create horizontal segment if it spans more than one cell
if (segmentEndX - x > 1 && horizontalWallsInRow < 1) {
var segmentCenterX = (x + segmentEndX - 1) / 2;
var wallSegment = {
startX: x,
endX: segmentEndX - 1,
startY: y,
endY: y,
worldX: segmentCenterX * worldGrid.cellSize + worldGrid.cellSize / 2,
worldY: y * worldGrid.cellSize + worldGrid.cellSize / 2,
centerX: segmentCenterX * worldGrid.cellSize + worldGrid.cellSize / 2,
centerY: y * worldGrid.cellSize + worldGrid.cellSize / 2,
width: (segmentEndX - x) * worldGrid.cellSize,
height: worldGrid.cellSize,
sprite: null,
lastVisible: false
};
self.wallSegments.push(wallSegment);
horizontalWallsInRow++;
// Mark cells as processed
for (var markX = x; markX < segmentEndX; markX++) {
processed[markX][y] = true;
}
} else {
// Try to create vertical segment
var segmentEndY = y;
while (segmentEndY < worldGrid.height && worldGrid.walls[x][segmentEndY] && !processed[x][segmentEndY]) {
segmentEndY++;
}
// Create vertical segment if it spans more than one cell
if (segmentEndY - y > 1 && horizontalWallsInRow < 1) {
var segmentCenterY = (y + segmentEndY - 1) / 2;
var wallSegment = {
startX: x,
endX: x,
startY: y,
endY: segmentEndY - 1,
worldX: x * worldGrid.cellSize + worldGrid.cellSize / 2,
worldY: segmentCenterY * worldGrid.cellSize + worldGrid.cellSize / 2,
centerX: x * worldGrid.cellSize + worldGrid.cellSize / 2,
centerY: segmentCenterY * worldGrid.cellSize + worldGrid.cellSize / 2,
width: worldGrid.cellSize,
height: (segmentEndY - y) * worldGrid.cellSize,
sprite: null,
lastVisible: false
};
self.wallSegments.push(wallSegment);
horizontalWallsInRow++;
// Mark cells as processed
for (var markY = y; markY < segmentEndY; markY++) {
processed[x][markY] = true;
}
} else if (horizontalWallsInRow < 1) {
// Single cell wall
var wallSegment = {
startX: x,
endX: x,
startY: y,
endY: y,
worldX: x * worldGrid.cellSize + worldGrid.cellSize / 2,
worldY: y * worldGrid.cellSize + worldGrid.cellSize / 2,
centerX: x * worldGrid.cellSize + worldGrid.cellSize / 2,
centerY: y * worldGrid.cellSize + worldGrid.cellSize / 2,
width: worldGrid.cellSize,
height: worldGrid.cellSize,
sprite: null,
lastVisible: false
};
self.wallSegments.push(wallSegment);
horizontalWallsInRow++;
processed[x][y] = true;
}
}
}
}
}
};
// Render walls in 3D perspective with fixed center points
self.render = function (player) {
var fov = Math.PI / 3; // 60 degrees field of view
var maxDistance = 1000;
// Sort walls by distance for proper depth rendering
var visibleWalls = [];
for (var i = 0; i < self.wallSegments.length; i++) {
var wall = self.wallSegments[i];
var dx = wall.centerX - player.x;
var dy = wall.centerY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only process walls within reasonable distance
if (distance < maxDistance) {
// Calculate angle relative to player's view direction using fixed center point
var wallAngle = Math.atan2(dy, dx);
var angleDiff = wallAngle - player.angle;
// Normalize angle difference
while (angleDiff > Math.PI) angleDiff -= 2 * Math.PI;
while (angleDiff < -Math.PI) angleDiff += 2 * Math.PI;
// Check if wall is within field of view (with some margin)
if (Math.abs(angleDiff) < fov / 2 + 0.5) {
visibleWalls.push({
wall: wall,
distance: distance,
angleDiff: angleDiff
});
}
}
// Hide walls that are too far or out of view
if (wall.sprite && (distance >= maxDistance || Math.abs(angleDiff) >= fov / 2 + 0.5)) {
wall.sprite.visible = false;
wall.lastVisible = false;
}
}
// Sort visible walls by distance (farthest first)
visibleWalls.sort(function (a, b) {
return b.distance - a.distance;
});
// Limit to maximum 1 visible wall at once
var maxVisibleWalls = Math.min(1, visibleWalls.length);
// Render visible walls
for (var i = 0; i < maxVisibleWalls; i++) {
var visibleWall = visibleWalls[i];
var wall = visibleWall.wall;
var distance = visibleWall.distance;
var angleDiff = visibleWall.angleDiff;
// Create sprite if it doesn't exist
if (!wall.sprite) {
wall.sprite = self.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
}
// Calculate screen position
var screenX = 1024 + angleDiff / (fov / 2) * 1024;
var screenY = 1366; // Center height
// Calculate size based on distance
var baseSize = 200;
var sizeScale = Math.max(0.1, 400 / (distance + 100));
var wallWidth = Math.max(20, wall.width * sizeScale);
var wallHeight = Math.max(20, wall.height * sizeScale);
// Position and scale the wall sprite
wall.sprite.x = screenX;
wall.sprite.y = screenY;
wall.sprite.width = wallWidth;
wall.sprite.height = wallHeight;
wall.sprite.visible = true;
wall.lastVisible = true;
// Add depth-based transparency
var alpha = Math.max(0.3, 1.0 - distance / maxDistance);
wall.sprite.alpha = alpha;
}
// Hide walls beyond the limit
for (var i = maxVisibleWalls; i < visibleWalls.length; i++) {
var hiddenWall = visibleWalls[i].wall;
if (hiddenWall.sprite) {
hiddenWall.sprite.visible = false;
hiddenWall.lastVisible = false;
}
}
};
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 the pseudo-3D view
renderer3D.render(player);
// Render walls
wallRenderer.render(player);
// Render ceiling tiles
ceilingTileRenderer.render(player);
// 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
@@ -97,13 +97,15 @@
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)) // Project to ceiling
+ screenY: 400 - 200 * (1000 / (distance + 100)) + pitchOffset // Project to ceiling with pitch
});
}
}
}
@@ -137,14 +139,16 @@
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,
@@ -182,8 +186,14 @@
};
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;
@@ -192,8 +202,11 @@
// 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 () {
@@ -230,8 +243,10 @@
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;
@@ -279,11 +294,11 @@
var baseWallSize = 400; // Base size for square walls
var wallHeight = Math.max(100, baseWallSize * (1000 / (distance + 100)));
var wallWidth = wallHeight; // Make it square
var halfWallHeight = wallHeight / 2;
- // WALLS: Position in middle section (centered on screen)
- var wallTop = screenCenter - halfWallHeight;
- var wallBottom = screenCenter + halfWallHeight;
+ // WALLS: Position in middle section (centered on screen) with pitch offset
+ var wallTop = screenCenter - halfWallHeight + pitchOffset;
+ var wallBottom = screenCenter + halfWallHeight + pitchOffset;
self.wallColumns[i].height = wallHeight;
self.wallColumns[i].width = wallWidth;
self.wallColumns[i].y = wallBottom; // Anchor at bottom of wall
self.wallColumns[i].visible = true;
@@ -294,15 +309,15 @@
var alignedX = gridX * worldGrid.cellSize;
var alignedY = gridY * worldGrid.cellSize;
// Position wall column at aligned grid position
self.wallColumns[i].x = i * 4; // Keep screen column position
- // FLOOR: Bottom section (from wall bottom to screen bottom)
+ // 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)
+ // 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;
@@ -878,8 +893,10 @@
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;
@@ -899,8 +916,10 @@
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;
@@ -917,17 +936,37 @@
} else {
turnLeft = false;
turnRight = false;
}
- // Vertical movement for forward/backward
+ // Vertical movement - split between forward/backward and look up/down
if (Math.abs(deltaY) > 50) {
- if (deltaY < 0) {
- moveForward = true;
+ // 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 {
- moveBackward = true;
- moveForward = false;
+ // 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)
@@ -945,8 +984,14 @@
}
if (turnRight) {
player.turnRight();
}
+ if (lookUp) {
+ player.lookUp();
+ }
+ if (lookDown) {
+ player.lookDown();
+ }
// Apply smooth interpolation
player.updateSmooth();
// Render the pseudo-3D view
renderer3D.render(player);