I am trying to implement a small tetris base game i found on github into my webserver but the images for the tiles are not loading into the game and in the console it says failed to load resouce 404 not found and it describes the file it is looking for as the colors i listed in the array in the following code but missing the .png extensions even though the array explicitly should request the image source to be from within a resources folder with the .png extensions
Furthermore it is loading one color, blue but when i refresh the page to try get another color it says for example failed to find file "orange"
The file structure is withi the htdocs of the apache, whose service is running on localhost, and is like the following,
- Htdocs
- index.html (with link to tetris.html)
- tetris.html
- tetris.js
- tetris.css
- resources folder (this includes all colors in .png form for example blue.png)
window.onload = () => {
const
background = document.getElementById("background"),
scoreLbl = document.getElementById("score"),
linesLbl = document.getElementById("lines"),
canvas = document.getElementById("game-canvas"),
ctx = canvas.getContext("2d");
let audio = new Audio("resources/music.mp3");
class Tetromino {
static COLORS = [".resources/blue.png", ".resources/green.png", ".resources/yellow.png", ".resources/red.png", ".resources/orange.png", ".resources/light-blue.png", ".resources/purple.png"];
static BLOCK_SIZE = 28;
static DELAY = 400;
static DELAY_INCREASED = 5;
constructor(xs, ys, color = null) {
this.x = xs;
this.y = ys;
this.length = xs.length;
this.color = color;
this.img = new Image();
// Set up a promise to track image loading
this.imgLoaded = new Promise((resolve, reject) => {
this.img.onload = resolve;
this.img.onerror = reject;
});
if (color !== null) {
this.img.src = Tetromino.COLORS[color];
console.log(this.img.src);
console.log((TETROMINOES.COLORS[color]));
}
}
update(updFunc) {
for (let i = 0; i < this.length; ++i) {
ctx.clearRect(
this.x[i] * Tetromino.BLOCK_SIZE,
this.y[i] * Tetromino.BLOCK_SIZE,
Tetromino.BLOCK_SIZE,
Tetromino.BLOCK_SIZE
);
updFunc(i);
}
this.draw();
}
draw() {
if (!this.img.complete) {
this.img.onload = () => this.draw();
return;
}
// Print the current tetromine
for (let i = 0; i < this.length; ++i) {
ctx.drawImage(
this.img,
this.x[i] * Tetromino.BLOCK_SIZE,
this.y[i] * Tetromino.BLOCK_SIZE,
Tetromino.BLOCK_SIZE,
Tetromino.BLOCK_SIZE
);
}
}
collides(checkFunc) {
for (let i = 0; i < this.length; ++i) {
const { x, y } = checkFunc(i);
if (x < 0 || x >= FIELD_WIDTH || y < 0 || y >= FIELD_HEIGHT || FIELD[y][x] !== false)
return true;
}
return false;
}
merge() {
for (let i = 0; i < this.length; ++i) {
FIELD[this.y[i]][this.x[i]] = this.color;
}
}
rotate() {
const
maxX = Math.max(...this.x),
minX = Math.min(...this.x),
minY = Math.min(...this.y),
nx = [],
ny = [];
if (!this.collides(i => {
nx.push(maxX + minY - tetromino.y[i]);
ny.push(tetromino.x[i] - minX + minY);
return { x: nx[i], y: ny[i] };
})) {
this.update(i => {
this.x[i] = nx[i];
this.y[i] = ny[i];
});
}
}
}
const
FIELD_WIDTH = 10,
FIELD_HEIGHT = 20,
FIELD = Array.from({ length: FIELD_HEIGHT }),
MIN_VALID_ROW = 4,
TETROMINOES = [
new Tetromino([0, 0, 0, 0], [0, 1, 2, 3]),
new Tetromino([0, 0, 1, 1], [0, 1, 0, 1]),
new Tetromino([0, 1, 1, 1], [0, 0, 1, 2]),
new Tetromino([0, 0, 0, 1], [0, 1, 2, 0]),
new Tetromino([0, 1, 1, 2], [0, 0, 1, 1]),
new Tetromino([0, 1, 1, 2], [1, 1, 0, 1]),
new Tetromino([0, 1, 1, 2], [1, 1, 0, 0])
];
let tetromino = null,
delay,
score,
lines;
(function setup() {
canvas.style.top = Tetromino.BLOCK_SIZE;
canvas.style.left = Tetromino.BLOCK_SIZE;
ctx.canvas.width = FIELD_WIDTH * Tetromino.BLOCK_SIZE;
ctx.canvas.height = FIELD_HEIGHT * Tetromino.BLOCK_SIZE;
// Scale background
const scale = Tetromino.BLOCK_SIZE / 13.83333333333;
background.style.width = scale * 166;
background.style.height = scale * 304;
// Offset each block to the middle of the table width
const middle = Math.floor(FIELD_WIDTH / 2);
for (const t of TETROMINOES) t.x = t.x.map(x => x + middle);
reset();
draw();
})();
function reset() {
// Make false all blocks
FIELD.forEach((_, y) => FIELD[y] = Array.from({ length: FIELD_WIDTH }).map(_ => false));
ctx.clearRect(0, 0, canvas.width, canvas.height);
delay = Tetromino.DELAY;
score = 0;
lines = 0;
}
function playMusic() {
audio.play();
}
function draw() {
if (tetromino) {
// Collision?
if (tetromino.collides(i => ({ x: tetromino.x[i], y: tetromino.y[i] + 1 }))) {
tetromino.merge();
// Prepare for new tetromino
tetromino = null;
// Check for completed rows
let completedRows = 0;
for (let y = FIELD_HEIGHT - 1; y >= MIN_VALID_ROW; --y)
if (FIELD[y].every(e => e !== false)) {
for (let ay = y; ay >= MIN_VALID_ROW; --ay)
FIELD[ay] = [...FIELD[ay - 1]];
++completedRows;
// Keep the same row
++y;
}
if (completedRows) {
// Print againt the table
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let y = MIN_VALID_ROW; y < FIELD_HEIGHT; ++y) {
for (let x = 0; x < FIELD_WIDTH; ++x) {
if (FIELD[y][x] !== false) new Tetromino([x], [y], FIELD[y][x]).draw();
}
}
score += [40, 100, 300, 1200][completedRows - 1];
lines += completedRows;
} else {
// Check if player has lost
if (FIELD[MIN_VALID_ROW - 1].some(block => block !== false)) {
alert("Game Over! n nScore: "+ score + "nLines Cleared: " + lines);
reset();
}
}
} else
tetromino.update(i => ++tetromino.y[i]);
}
// No tetromino failing
else {
scoreLbl.innerText = score;
linesLbl.innerText = lines;
// Create random tetromino
tetromino = (({ x, y }, color) =>
new Tetromino([...x], [...y], color)
)(
TETROMINOES[Math.floor(Math.random() * (TETROMINOES.length - 1))],
Math.floor(Math.random() * (Tetromino.COLORS.length - 1))
);
tetromino.draw();
}
setTimeout(draw, delay);
}
// Move
window.onkeydown = event => {
playMusic();
switch (event.key) {
case "ArrowLeft":
if (!tetromino.collides(i => ({ x: tetromino.x[i] - 1, y: tetromino.y[i] })))
tetromino.update(i => --tetromino.x[i]);
break;
case "ArrowRight":
if (!tetromino.collides(i => ({ x: tetromino.x[i] + 1, y: tetromino.y[i] })))
tetromino.update(i => ++tetromino.x[i]);
break;
case "ArrowDown":
delay = Tetromino.DELAY / Tetromino.DELAY_INCREASED;
break;
case " ":
tetromino.rotate();
break;
case "ArrowUp":
tetromino.rotate();
break;
}
}
window.onkeyup = event => {
if (event.key === "ArrowDown")
delay = Tetromino.DELAY;
}
}
I tried to load the tetris file and expect the game to be playable (it has worked before but it wasnt on apache it was on an esp32 webserver were i posted the file ) but after a lot of troubleshooting the only color that was loading was blue
The files are being served as text/html and this also removes the file extension, so it cannot find the file blue.png, for example, because the file it is trying to find is actually called blue.
2
Answers
It was a cache issue, i opened it on a different laptop.
It looks to me like you’re only calling two of the arguments and You’re not specifying a color when you create the instance of Tetromino.
This snippet is calling the XS and YS arguments but color is null.
If you look at the constructor
Color is the third argument and you’re only providing two if you look here:
color defaults to null so The block in which you’re setting the image source isn’t being called.