Javascript 不绘制所有图块的 2d tilemap

Javascript 2d tilemap without drawing all tiles

我正在 JavaScript 中创建 2D 平台游戏。我正在尝试创建一个 tilemap,它不会在应该有空气的块中生成(在 tilemap 中标记为 0)。我没有收到任何错误。但是,canvas 中没有生成任何方块。除非我删除碰撞检测,否则游戏也会崩溃。

主要js

//drawing variables
var canvas;
var context;

//game variables
var gameLoop;
var player;
var borders = [];
var mapHeight = 12;
var mapWidth = 22;
var tilesize = 50;

//input variables
var upKey;
var rightKey;
var downKey;
var leftKey;

//runs once pace has been loaded
window.onload = function(){
    //canvas and context variable setup
    canvas = document.getElementById("gameCanvas")
    context = canvas.getContext("2d")

    //key listeners 
    setupInputs();

    //create player
    player = new Player(400, 400);

    //create border
    let tilemap = [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
    ]

    //create a box (border) based on tilemap position. Let 0's be air
    for (let row = 0; row < mapHeight; row++){
        for(let col = 0; col < mapWidth; col++){
            if(tilemap[row][col] !== 0){
                borders.push(new Border(tilemap[row]*tilesize, tilemap[col]*tilesize, tilesize, tilesize, tilemap[row][col]));
            }
        }
    }

    //initialize main game loop. Framerate = 30/sec
    gameLoop = setInterval(step, 1000/30);

    //draw canvas
    context.fillStyle = "#f4f4f4f4"
    context.fillRect(0, 0, mapWidth*tilesize, mapHeight*tilesize);
}

function step(){
    player.step();

    //draw after updates
    draw();
}

function draw(){
    //Clear previous canvas
    context.fillStyle = "#f4f4f4f4"
    context.fillRect(0, 0, mapWidth*tilesize, mapHeight*tilesize);

    //draw the player
    player.draw();

    //draw borders
    for(let i = 0; i < borders.length; i++){
        borders[i].draw();
    }
}

//keyboard inputs
function setupInputs(){
    document.addEventListener("keydown", function(event){
        if(event.key === "w"){
            upKey = true;
        } else if(event.key === "a"){
            leftKey = true;
        } else if(event.key === "s"){
            downKey = true;
        } else if(event.key === "d"){
            rightKey = true;
        }
    });
    document.addEventListener("keyup", function(event){
        if(event.key === "w"){
            upKey = false;
        } else if(event.key === "a"){
            leftKey = false;
        } else if(event.key === "s"){
            downKey = false;
        } else if(event.key === "d"){
            rightKey = false;
        }
    });
}

//Checking to see if player and border intersect
function checkIntersection(r1, r2){
    if (r1.x >= r2.x + r2.width){
        return false;
    } else if (r1.x + r1.width <= r2.x){
        return false;
    } else if (r1.y >= r2.y + r2.height){
        return false;
    } else if (r1.y + r1.height <= r2.y){
        return false;
    } else {
        return true;
    }
}

播放器 js

function Player(x, y){
    //Player variables
    this.x = x;
    this.y = y;
    this.xvel = 0;
    this.yvel = 0;
    this.friction = 0.6;
    this.maxVel = 12;
    this.width = tilesize;
    this.height = tilesize;
    this.active = true;
    this.falling = false;


    this.step = function(){
        if(this.active){
            if(!leftKey && !rightKey || leftKey && rightKey){
                this.xvel *= this.friction;
            } else if (rightKey){
                this.xvel++;
            } else if (leftKey){
                this.xvel--;
            }

            if(upKey){
                //check if standing on ground
                if (this.yvel === 0 && this.falling === false){
                    this.yvel -= 70;
                    this.falling = true;
                } else{
                    this.yvel += 0;
                }
            }
            this.yvel += 1;

            //not allowing velocity to surpass maximum velocity
            if (this.xvel > this.maxVel){
                this.xvel = this.maxVel;
            } else if(this.xvel < -this.maxVel){
                this.xvel = -this.maxVel; 
            }
            if (this.yvel > this.maxVel){
                this.yvel = this.maxVel;
            } else if(this.yvel < -this.maxVel){
                this.yvel = -this.maxVel; 
            }
            if (this.xvel > 0){
                this.xvel = Math.floor(this.xvel);
            } else {
                this.xvel = Math.ceil(this.xvel);
            }
            if (this.yvel > 0){
                this.yvel = Math.floor(this.yvel);
            } else {
                this.yvel = Math.ceil(this.yvel);
            }

            //collision rectangles
            let horizontalRect = {
                x: this.x + this.xvel,
                y: this.y,
                width: this.width,
                height: this.height
            }
            let verticalRect = {
                x: this.x,
                y: this.y + this.yvel,
                width: this.width,
                height: this.height
            }
            //Collision deteQction
            for(let i = 0; i<borders.length; i++){
                let borderRect = {
                    x: borders[i].x,
                    y: borders[i].y,
                    width: borders[i].width,
                    height: borders[i].height
                }
                if (checkIntersection(horizontalRect, borderRect)){
                    while (checkIntersection(horizontalRect, borderRect)){
                        horizontalRect.x -= Math.sign(this.xvel);
                    }
                    this.x = horizontalRect.x;
                    this.xvel = 0;
                }
                if (checkIntersection(verticalRect, borderRect)){
                    while(checkIntersection(verticalRect, borderRect)){
                        verticalRect.y -= Math.sign(this.yvel);
                    }
                    this.y = verticalRect.y;
                    this.yvel = 0;
                    this.falling = false;
                }
            }
            if (this.x + this.xvel > mapWidth*tilesize - tileisze){
                this.xvel = 0;
            }
            if (this.x + this.xvel < 0){
                this.xvel = 0;
            }
            
            this.x += this.xvel;
            this.y += this.yvel;
        }
    }

    this.draw = function(){
        context.fillStyle = "orange";
        context.fillRect(this.x, this.y, this.width, this.height);
    }
}

边框 js

function Border(x, y, width, height, type){
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.type = type;

    this.draw = function(){
        if (this.type === 1){
            context.fillStyle = "#2C2C34";
        } else if (this.type === 2){
            context.fillStyle = "#39393A";
        }
        context.fillRect(this.x, this.y, this.width, this.height);
    }
}

您的代码中似乎有几个错误,可能需要一段时间才能全部找到。你的十六进制颜色有 8 个字符 "#f4f4f4f4"。您的地图宽度为 22,但二维数组中只有 21 列。在创建边界数组时,x 点中有 rowsy 点中有 col,这似乎也不对。我觉得应该反过来。无论如何,如果您不反对使用一维数组,这里是您代码的简化版本,没有播放器内容。

边框

function Border(type, x, y){
    this.x = x;
    this.y = y;
    this.width = TILE_SIZE;
    this.height = TILE_SIZE;
    this.type = type;

    this.draw = function(){
        if (this.type === 1){
            context.fillStyle = "purple";
            context.fillRect(this.x, this.y, this.width, this.height);
        } else if (this.type === 2){
            context.fillStyle = "orange";
            context.fillRect(this.x, this.y, this.width, this.height);
        }
    }
}

和主文件

const canvas = document.getElementById("canvas")
const context = canvas.getContext("2d")
const TILE_SIZE = 25; //50 was too big for my screen based on how many rows/columns you wanted
canvas.width = 525; //TILE_SIZE * columns
canvas.height = 300; //TILE_SIZE * rows

var borders = [];

let tileMap = {
    mapHeight: 12,
    mapWidth: 21,
    size: TILE_SIZE,
    grid: [
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 2, 0, 0, 2, 0, 2, 2, 2, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
        0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
        0, 0, 2, 2, 2, 2, 0, 0, 2, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
        0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 2, 0, 0, 2, 0, 2, 2, 2, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    ]
}
//Uses 1 loop to create the grid
function createGrid() {
    for (let i = 0; i < tileMap.grid.length; i++) {
        let x = (i % tileMap.mapWidth) * tileMap.size;
        let y = Math.floor((i / tileMap.mapWidth)) * tileMap.size;
        let type = tileMap.grid[i];
        borders.push(new Border(type, x, y));
    }
};
createGrid(); //creates the grid when file is loaded

function animate() {
    context.clearRect(0, 0, canvas.width, canvas.height);
    for(let i = 0; i < borders.length; i++){
        borders[i].draw();
    }
    requestAnimationFrame(animate);
}
animate()

我只是为了这个例子在 animate 函数中插入了 for 循环。通常它会是它自己的功能,您可以在其中处理碰撞检测或绘制图块或您想用它们做的任何其他事情。

如果您必须使用二维数组,这里有一个函数可以创建它

function createGrid() {
   for (let row = 0; row < tileMap.mapHeight; row++) {
    for (let col = 0; col < tileMap.mapWidth; col++) {
       let type = tileMap.grid[row][col];
       let x = col * TILE_SIZE;
       let y = row * TILE_SIZE;
       borders.push(new Border(type, x, y))
    }
   }
}
createGrid()

如果您使用我上面发布的地图,请务必将地图改回 2d。我还注意到在您的播放器文件中您有一处 tileisze 而不是 tilesize。我会花一些时间仔细清理您的文件中的错误,因为这些可能是您出现某些问题的原因。