【问题标题】:Why is my dungeon generation algorithm crashing? (SOLVED)为什么我的地牢生成算法崩溃了? (解决了)
【发布时间】:2022-06-11 03:08:49
【问题描述】:

看似随意,我的代码在尝试将地板添加到新房间时会崩溃。该选项卡将冻结,并且此错误似乎发生在我尝试生成房间的任何地方。这发生在 Edge、Firefox 和 Chrome 上。下面是我的 index JS 文件,也是调用添加房间地板的函数的地方。

import {Character} from "./Character.js";
import {Player} from "./Player.js"
import {Room} from "./Room.js"

const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");


canvas.width = 1280;
canvas.height = 768;
ctx.fillRect(0, 0, 1280, 768);

let enemies = [];
let rooms = [];
let activeHitboxes = [];
let isMouseClicked = false;
let mousePosX = 640;
let mousePosY = 360;
let currentRoom = -1;
let player;
let intervalCode = 0;
let score;

document.body.onkeydown = function (e) {
    if (e.key === " " ||
        e.code === "Space"
    ) {
        if(intervalCode !== 0){
        clearInterval(intervalCode);}
        startGame();
    }
}

canvas.onmousedown = (event) => { //update mouseclick status
    const {
        mousePress = true
    } = event;
    isMouseClicked = mousePress;
}

canvas.onmouseup = (event) => {
    const {
        mousePress = false
    } = event;
    isMouseClicked = mousePress;
}

canvas.onmousemove = (event) => { //update mouse position
    const {
        clientX, clientY
    } = event
    mousePosX = clientX - event.currentTarget.getBoundingClientRect().left
    mousePosY = clientY - event.currentTarget.getBoundingClientRect().top
}


function animate() { //gameplay loop
    ctx.fillStyle = '#000'
    ctx.fillRect(0, 0, 1280, 768);

    rooms[currentRoom].draw();

    for (let i = 0; i < rooms[currentRoom].hCells; i++) {
        for (let j = 0; j < rooms[currentRoom].vCells; j++) {

            if (rooms[currentRoom].tiles[i][j].type === 2) {
                activeHitboxes.push({
                    width: 64,
                    height: 64,
                    x: rooms[currentRoom].tiles[i][j].location.x,
                    y: rooms[currentRoom].tiles[i][j].location.y,
                    isSourcePlayer: false,
                    isDoor: false
                })
                activeHitboxes.push({
                    width: 64,
                    height: 64,
                    x: rooms[currentRoom].tiles[i][j].location.x,
                    y: rooms[currentRoom].tiles[i][j].location.y,
                    isSourcePlayer: true,
                    isDoor: false
                })
            } else if (rooms[currentRoom].tiles[i][j].type === 4) {


                if (i === 0) {
                    activeHitboxes.push({
                        width: 64,
                        height: 64,
                        x: rooms[currentRoom].tiles[i][j].location.x,
                        y: rooms[currentRoom].tiles[i][j].location.y,
                        isSourcePlayer: true,
                        isDoor: true,
                        isEntry: true
                    })
                    activeHitboxes.push({
                        width: 64,
                        height: 64,
                        x: rooms[currentRoom].tiles[i][j].location.x,
                        y: rooms[currentRoom].tiles[i][j].location.y,
                        isSourcePlayer: false,
                        isDoor: true,
                        isEntry: true

                    })
                }


                activeHitboxes.push({
                    width: 64,
                    height: 64,
                    x: rooms[currentRoom].tiles[i][j].location.x,
                    y: rooms[currentRoom].tiles[i][j].location.y,
                    isSourcePlayer: true,
                    isDoor: true,
                    isEntry: false
                })

                activeHitboxes.push({
                    width: 64,
                    height: 64,
                    x: rooms[currentRoom].tiles[i][j].location.x,
                    y: rooms[currentRoom].tiles[i][j].location.y,
                    isSourcePlayer: false,
                    isDoor: true,
                    isEntry: false

                })
            }
        }
    }


    player.updateMouse(mousePosX, mousePosY, isMouseClicked);
    checkHits(player);
    if (player.getHealth() > 0) {
        player.update(activeHitboxes);
    } else {
        player.shrink(1);
        player.draw();
        ctx.font = "48px inter";
        ctx.fillStyle = "#fff"
        ctx.strokeText("PRESS SPACE TO RETRY", canvas.width / 2 - 281, canvas.height / 2)
        ctx.fillText("PRESS SPACE TO RETRY", canvas.width / 2 - 281, canvas.height / 2)
    }
    for (let i = 0; i < enemies.length; i++) {
        enemies[i].update();
        checkHits(enemies[i]);
        checkHits(player);
    }
    activeHitboxes = [];
}

function generateRoom(x, y, ctx) {
    let room = new Room(768, 1280, x, y, ctx);
    room.fillRoom();
    room.addFloor(x, y);
    if (rooms.length <= 1) {
        room.addDoors();
    } else (
        room.addDoors(rooms[currentRoom - 1].getDoors())
    )
    rooms.push(room);
    currentRoom += 1;
}

function checkHits(target) { //check active hitboxes against a target
    for (let i = 0; i < activeHitboxes.length; i++) {
        checkHit(target, activeHitboxes[i].x, activeHitboxes[i].y, activeHitboxes[i].width, activeHitboxes[i].height, activeHitboxes[i].isSourcePlayer, activeHitboxes[i].isDoor, activeHitboxes[i].isEntry)
    }
}

function checkHit(target, hitboxX, hitboxY, hitboxWidth, hitboxHeight, isSourcePlayer, isDoor, isEntry) { //judge if a hitbox connected with a target
    if (hitboxX < target.getPosition().x + target.getWidth() && //if hit connects, and they're on opposite teams, remove 1 health from target;
        hitboxX + hitboxWidth > target.getPosition().x &&
        hitboxY < target.getPosition().y + target.getHeight() &&
        hitboxY + hitboxHeight > target.getPosition().y &&
        (target.isPlayer && !isSourcePlayer || !target.isPlayer && isSourcePlayer)) {
        if (isDoor && !isEntry) {
            console.log("Isnt entry")
            console.log(currentRoom);
            generateRoom(0, rooms[currentRoom].getDoors(), ctx);
            player.setPosition(96, rooms[currentRoom - 1].getDoors() * 64)
        } else if (isEntry && currentRoom >= 1) {
            console.log("Is entry")
            console.log(currentRoom);
            currentRoom--;
        } else {
            target.removeHealth();
            if (target.getHealth() === 0 && enemies.includes(target)) {
                enemies.splice(enemies.indexOf(target), 1);
            }
        }
    }
}

function startGame() {
    enemies = [];
    rooms = [];
    activeHitboxes = [];
    isMouseClicked = false;
    mousePosX = 640;
    mousePosY = 360;
    currentRoom = -1;

    player = new Player({
        position: {x: 32, y: 32},
        height: 32,
        width: 16,
        health: 1,
        ctx: ctx,
        isPlayer: true,
    });


    const enemy = new Character({
        position: {x: 400, y: 100},
        height: 32,
        width: 32,
        health: 1,
        ctx: ctx,
        isPlayer: false,

    });

    generateRoom(0, 0, ctx);
    enemies.push(enemy);
    player.draw();
    for (let i = 0; i < enemies.length; i++) {
        enemies[i].draw();
    }
    intervalCode = setInterval(animate, 1000 / 60);
}

ctx.font = "48px inter";
ctx.fillStyle = "#fff"
ctx.strokeText("PRESS SPACE TO START", canvas.width / 2 - 281, canvas.height / 2)
ctx.fillText("PRESS SPACE TO START", canvas.width / 2 - 281, canvas.height / 2)

房间等级来了

import {Tiles} from "./Tiles.js";

export class Room {
    vCells;//Number of vertical tiles, 11
    hCells;//number of horizontal tiles, 19
    height;
    width;
    location;
    tiles = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []];
    walkable = [[]];

    constructor(height, width, x, y, ctx) {
        this.height = height;
        this.width = width;
        this.location = {x: x, y: y};
        this.ctx = ctx;
        this.vCells = height / 64;
        this.hCells = width / 64;
    }

    fillRoom() {
        for (let i = 0; i < this.width / 64; i++) {
            for (let j = 0; j < this.height / 64; j++) {
                this.tiles[i][j] = (new Tiles(i * 64, j * 64, 2));
            }
        }

    }

    addFloor(x, y) {//randomized prim's algorithm to place walkable floor

        let startTile = this.tiles[x][y]; //get tile
        startTile.setType(3);//set start tile to floor


        //compute a frontier list, all wall tiles that are 2 "steps" away from our starting tile are stored in this list if these tiles are valid
        let frontier = []
        this.getNeighbours(x, y, frontier, 2);
        //while this frontier list is not empty, pick an arbitrary tile from this list
        while (frontier.length > 0) {
            let idx = this.getRandomInt(0, frontier.length);
            let frontierNeighbours = [];
            let pickedFrontier = frontier[idx];
            if (this.getNeighbours(pickedFrontier.location.x / 64, pickedFrontier.location.y / 64, frontierNeighbours, 3)) {//make a list of all neighbours to that frontier tile (tiles 2 distance away that are floors)

                let neighbourIDX = this.getRandomInt(0, frontierNeighbours.length);
                let neighbourX = frontierNeighbours[neighbourIDX].location.x / 64;
                let neighbourY = frontierNeighbours[neighbourIDX].location.y / 64;

                let x = pickedFrontier.location.x / 64;
                let y = pickedFrontier.location.y / 64;
                let xDiff = x - neighbourX;
                let yDiff = y - neighbourY;
                let connectorX = 0;
                let connectorY = 0;

                if (xDiff > 0) {
                    connectorX = neighbourX + 1;
                } else if (xDiff < 0) {
                    connectorX = neighbourX - 1;
                } else {
                    connectorX = neighbourX;
                }
                if (yDiff > 0) {
                    connectorY = neighbourY + 1;
                } else if (connectorY < 0) {
                    connectorY = neighbourY - 1;
                } else {
                    connectorY = neighbourY
                }
                this.getNeighbours(connectorX, connectorY, frontier, 2);
                // this.getNeighbours(x, y, frontier, 2);

                if (this.isValid(connectorX, connectorY)) {
                    this.tiles[connectorX][connectorY].setType(3);
                    this.walkable.push(this.tiles[connectorX][connectorY]);
                }
                frontier.splice(idx, 1);
            }
        }
    }


    getNeighbours(x, y, arr, type) { //Get tiles 2 blocks away

        let hasNeighbour = false;

        for (let i = -2; i <= 2; i += 2) {
            for (let j = -2; j <= 2; j += 2) {
                if (this.isValid(x + i, y + j) && this.tiles[x + i][y + j].type === type && ((Math.abs(x - x + i) >= 2 || Math.abs(y - y + j) >= 2) && ((Math.abs(x - x + i) + Math.abs(y - y + j)) !== 4))) {
                    if (!arr.includes(this.tiles[x + i][y + j])) {
                        hasNeighbour = true;
                        arr.push(this.tiles[x + i][y + j]);
                        // this.tiles[x + i][y + j].setType(0)
                    }
                }
            }
        }
        return hasNeighbour;
    }

    canAccess(x, y) {
        for (let i = -1; i <= 0; i++) {
            for (let j = -1; j <= 0; j++) {
                if (this.isValid(x + i, y + j) && Math.abs(j + i) !== 2) {
                    if (this.tiles[x + i][y + j].type === 3) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    isValid(x, y) {
        if (x >= this.hCells) {
            return false;
        }
        if (y >= this.vCells) {
            return false;
        }
        if (x < 0) {
            return false;
        }
        if (y < 0) {
            return false;
        }
        return true;
    }

    

    getDoors() {
        for (let j = 0; j < this.vCells; j++) {
            if (this.tiles[this.hCells - 1][j].type === 4) {
                return j;
            }
        }
        return -1;
    }

    getRandomInt(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min) + min); //maximum is exclusive and minimum is inclusive
    }

    draw() {
        for (let i = 0; i < this.tiles.length; i++) {
            for (let j = 0; j < this.tiles[i].length; j++) {

                if (this.tiles[i][j].type === 0) {
                    this.ctx.fillStyle = '#000'
                }
                if (this.tiles[i][j].type === 1) {
                    this.ctx.fillStyle = '#f00'
                }
                if (this.tiles[i][j].type === 2) {
                    this.ctx.fillStyle = '#000'
                }
                if (this.tiles[i][j].type === 3) {
                    this.ctx.fillStyle = '#555'
                }
                if (this.tiles[i][j].type === 4) {
                    this.ctx.fillStyle = '#00F'
                }
                this.ctx.fillRect(this.tiles[i][j].location.x, this.tiles[i][j].location.y, this.tiles[i][j].size, this.tiles[i][j].size);

            }
        }
    }
}

performance recording here

顺便说一句,由于这是我的第一个 Javascript 程序,因此也欢迎任何特定于 JavaScript 的建议,谢谢。

编辑:我发现了我的问题,对于addDoor函数,我没有更新无效的随机数,导致死循环

【问题讨论】:

  • 你有工作演示吗?
  • 一旦你得到它的工作,你可以在Code Review寻求设计建议
  • 我必须警告你,这对于我们大多数人来说是太多的代码,只是通过查看来试图弄清楚,所以不要抱太大希望。你应该使用调试器来单步调试它,看看它卡在哪里了。
  • 这发生在 ... Chrome 上。 => I particularly like this JS debugging tutorial。它已经过时了,但不要一概而论;挺好的。
  • 注释掉代码块,直到它不再崩溃,然后你就知道去哪里找了。

标签: javascript algorithm


猜你喜欢
  • 2015-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2013-01-17
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多