Почему "пиксели" на моем масштабированном холсте HTML5 странным образом выровнены?
В настоящее время я пишу небольшой клон змеи на основе HTML5 canvas/JS, похожий на классический Nokia Snake. (Это моя первая игра на HTML5 canvas/JS, а также моя первая игра, я все еще начинающий программист;))
Для простоты я решил сделать каждый сегмент змеи и вишни всего 1 пикселем, однако это обычно приводит к очень маленькой змее и вишне!;) Поэтому я решил масштабировать холст с помощью.scale(8,8), чтобы каждый пиксель был 8*8.
var snake = {
length: 4,
direction: "right",
color: "#9A9A92",
location: [{x: 32, y: 32}, {x: 31, y: 32}, {x: 30, y: 32}, {x: 29, y: 32}]
}
var cherry = {
color: "#B1001D",
x: Math.random()*65,
y: Math.random()*65
}
var ToDrawOrNotToDraw; //limits the speed of the snake
function moveSnake(eventinfo){
if(eventinfo.keyCode === 38){
if(snake.direction === "left" || snake.direction === "right"){
snake.direction = "up";
}
}
else if(eventinfo.keyCode === 40){
if(snake.direction === "left" || snake.direction === "right"){
snake.direction = "down";
}
}
else if(eventinfo.keyCode === 37){
if(snake.direction === "up" || snake.direction === "down"){
snake.direction = "left";
}
}
else if(eventinfo.keyCode === 39){
if(snake.direction === "up" || snake.direction === "down"){
snake.direction = "right";
}
}
}
function draw(){
requestAnimationFrame(draw);
if(ToDrawOrNotToDraw === true){
var snakeDrawerCount = 0;
ctx.fillStyle = "#006C00";
ctx.fillRect(0, 0, 64, 64);
ctx.fillStyle = cherry.color;
ctx.fillRect(cherry.x, cherry.y, 1, 1);
ctx.fillStyle = snake.color;
if(snake.direction === "up"){
if(snake.location[0].y > 0){
snake.location.unshift({x: snake.location[0].x, y: snake.location[0].y-1});
snake.location.pop();
}
else {
snake.location.unshift({x: snake.location[0].x, y: 64});
snake.location.pop();
}
}
else if(snake.direction === "down"){
if(snake.location[0].y < 64){
snake.location.unshift({x: snake.location[0].x, y: snake.location[0].y+1});
snake.location.pop();
}
else {
snake.location.unshift({x: snake.location[0].x, y: 0});
snake.location.pop();
}
}
else if(snake.direction === "left"){
if(snake.location[0].x > 0){
snake.location.unshift({x: snake.location[0].x-1, y: snake.location[0].y});
snake.location.pop();
}
else {
snake.location.unshift({x: 64, y: snake.location[0].y});
snake.location.pop();
}
}
else {
if(snake.location[0].x < 64){
snake.location.unshift({x: snake.location[0].x+1, y: snake.location[0].y});
snake.location.pop();
}
else {
snake.location.unshift({x: 0, y: snake.location[0].y});
snake.location.pop();
}
}
var snakeDrawerCount = 0;
while(snakeDrawerCount < snake.location.length){
ctx.fillRect(snake.location[snakeDrawerCount].x, snake.location[snakeDrawerCount].y, 1, 1);
snakeDrawerCount++;
}
ToDrawOrNotToDraw = false;
}
else {
ToDrawOrNotToDraw = true;
}
}
var cnvs = document.getElementById("cnvs");
window.addEventListener("keydown", moveSnake, false);
var ctx = cnvs.getContext("2d");
ctx.scale(8,8); //actual size of the canvas is 512 * 512, this gives the 'pixels'/blocks on the canvas a size of 8 * 8, so the canvas will get a size of 64 * 64 'pixels'/blocks
draw();
Но по какой-то странной причине эти "большие пиксели" (8 * 8 масштабированных пикселей), кажется, перекрывают друг друга. Как можно увидеть здесь:
К сожалению, я абсолютно не знаю, почему это происходит, потому что, как я упоминал ранее, я все еще очень плохо знаком с HTML5 canvas/JS, раньше я программировал только на Qt/QML/C++ и Python. Я попробовал свою игру как в Firefox 26 (в Linux), так и в Konqueror (в KDE 4.11)(WebKit), похоже, проблема в обоих браузерах, поэтому я не думаю, что это связано с Gecko.
1 ответ
Проблема возникает из-за сглаживания / субпикселов.
Ваша вишня помещается в доску по случайному значению. Проблема со случайным значением состоит в том, что это значение с плавающей запятой, поэтому вишня будет смещена на несколько десятичных знаков и, следовательно, подпиксирована браузером.
При использовании масштабирования результат этого субпикселя также масштабируется (что более очевидно, когда вы меняете его цвет, например, на белый), и в результате он не соответствует сетке.
Чтобы решить эту проблему, просто принудительно задайте целочисленное положение x и y, например, так:
var cherry = {
color: "#B1001D",
x: Math.random()*65|0,
y: Math.random()*65|0
}
|
является побитовым оператором ИЛИ, который сначала переводит число в целое число, чтобы иметь возможность использовать побитовое ИЛИ. Так как мы ИЛИ с 0, результат остается тем же самым (кроме сокращения десятичного числа с плавающей точкой).