【问题标题】:Changing game mode in Tic Tac Toe [The Odin Project]在井字游戏中更改游戏模式 [The Odin Project]
【发布时间】:2021-11-09 03:56:44
【问题描述】:

这是我在这里的第一个问题,所以如果我做错了,请事先道歉!

我正在学习使用 The Odin Project 编写代码。到目前为止,我一直在做每个项目,没有任何大问题:这里和那里有些困难,但经过一番思考,我能够成功地解决它们。

目前,我在 井字游戏 JavaScript 项目。我已经完成了游戏,实现了两种游戏模式:VS Player 2VS CPU。两者都是通过我为避免使用提示而制作的 HTML 欢迎窗口首先选择的,并且两者都可以正常运行。但是,每当我尝试通过放置在界面中的按钮更改游戏模式时,我都会失败并且模式保持不变。

例如,如果我想从 VS Player 2 模式 更改为 VS CPU 模式,分数会重置并且玩家的名字会更改,但游戏会继续运行VS Player 2 模式。没有显示控制台错误,所以我确定这与范围或逻辑有关!

我已经尝试了很多东西,包括使用 JavaScript DOM 操作生成 HTML 欢迎窗口,但我无法让它工作。自从我遇到这个问题已经过去两周了,我仍然在碰壁!

这是管理模式选择的代码部分:

const modeSelector = {
constants: [
    /* 0 */ modalOverlay = document.getElementById("modal-overlay"),
    /* 1 */ modalWindow = document.getElementById("modal-window"),
    /* 2 */ modalContent = document.getElementById("modal-content"),
    /* 3 */ gameButtons = document.getElementById("game-buttons"),
    /* 4 */ popUpVs = document.getElementById("popUpVs"),
    /* 5 */ popUpCpu = document.getElementById("popUpCpu"),     
    /* 6 */ vsPlayerTwo = document.getElementById("vsplayer2"),
    /* 7 */ vsCPU = document.getElementById("vsCPU"),
],
gameMode(mode) {
    if (mode === "") {
    } else if (mode === "vsPlayerTwoMode") {
        gameBoard.cleanBoard();
        p1 = playerCreator.createPlayer();
        p1.name = playerCreator.names[0].innerHTML;
        playerCreator.scores[0].innerHTML = "0";
        p2 = playerCreator.createPlayer();
        p2.name = playerCreator.names[1].innerHTML;
        playerCreator.scores[1].innerHTML = "0";
        movements.movement();
        rules.turnChanger();
    } else if (mode === "vsCPUMode") {
        gameBoard.cleanBoard();
        p1 = playerCreator.createPlayer();
        p1.name = playerCreator.names[0].innerHTML;
        playerCreator.scores[0].innerHTML = "0";
        p2 = playerCreator.createPlayer();
        p2.name = "CPU"
        playerCreator.names[1].innerHTML = p2.name;
        playerCreator.scores[1].innerHTML = "0";
        movements.vsCpuMovement();
        rules.turnChanger();
    }
},
reset() {
    gameBoard.cleanBoard();
    modeSelector.gameMode("");
    playerCreator.names[0].innerHTML = "0";
    playerCreator.names[1].innerHTML = "0";
    playerCreator.scores[0].innerHTML = "0";
    playerCreator.scores[1].innerHTML = "0";
},    
popUpMode(mode) {
    if (mode === "popUpVsMode") {
        modeSelector.constants[2].innerHTML = "Insert Player 1 name";
    } else if (mode === "popUpCPUMode") {
        modeSelector.constants[2].innerHTML = "Insert Player name";
    }
    modeSelector.constants[2].style.marginLeft = "130px";
    modeSelector.constants[4].remove();
    modeSelector.constants[5].remove();
    let p1NameBar = document.createElement("input");
    p1NameBar.id = "p1NameBar";
    p1NameBar.maxLength = "6";
    modeSelector.constants[3].appendChild(p1NameBar);
    let p1NameOk = document.createElement("h2");
    p1NameOk.class = "popUpButton";
    p1NameOk.type = "button";
    p1NameOk.id = "popUpP1NameOk";
    p1NameOk.innerHTML = "OK";
    modeSelector.constants[3].appendChild(p1NameOk);
    p1NameOk.addEventListener("click", () => {
        if (mode === "popUpVsMode") {
            playerCreator.names[0].innerHTML = p1NameBar.value;
            modeSelector.constants[2].innerHTML = "Insert Player 2 name";
            modeSelector.constants[2].style.marginLeft = "130px";
            p1NameBar.remove();
            p1NameOk.remove();
            let p2NameBar = document.createElement("input");
            p2NameBar.id = "p2NameBar";
            p2NameBar.maxLength = "6";
            modeSelector.constants[3].appendChild(p2NameBar);
            let p2NameOk = document.createElement("h2");
            p2NameOk.className = "popUpButton";
            p2NameOk.type = "button";
            p2NameOk.id = "popUpP2NameOk";
            p2NameOk.innerHTML = "OK";
            modeSelector.constants[3].appendChild(p2NameOk);
            p2NameOk.addEventListener("click", () => {
                playerCreator.names[1].innerHTML= p2NameBar.value;
                document.body.removeChild(modalOverlay);
                modeSelector.gameMode("vsPlayerTwoMode");                    
            });
        } else if (mode === "popUpCPUMode") {
            playerCreator.names[0].innerHTML = p1NameBar.value;
            document.body.removeChild(modalOverlay);
            modeSelector.gameMode("vsCPUMode");
        }
    });
},
inGameMode(mode) {
    modeSelector.reset()
    let inGameOverlay = document.createElement("div");
    inGameOverlay.id = "inGameOverlay";
    inGameOverlay.className = "modal-overlay";
    document.body.appendChild(inGameOverlay);
    let inGameWindow = document.createElement("div");
    inGameWindow.className = "modal-window";
    inGameWindow.id = "inGameWindow";
    inGameOverlay.appendChild(inGameWindow);
    let inGameTitleBar = document.createElement("div");
    inGameTitleBar.className = "modal-titlebar";
    inGameTitleBar.id = "inGameTitleBar";
    inGameWindow.appendChild(inGameTitleBar);
    let inGameTitle = document.createElement("span");
    inGameTitle.className = "modal-title";
    inGameTitle.id = "inGameTitle";
    if (mode === "VSP2") {
        inGameTitle.innerHTML = "VS Player 2 Mode";
        inGameTitle.style.marginLeft = "160px";
        inGameTitleBar.appendChild(inGameTitle);
        let inGameContent = document.createElement("div");
        inGameContent.className = "modal-content";
        inGameContent.id = "inGameContent";
        inGameContent.innerHTML = "Insert Player 1 name";
        inGameContent.style.marginLeft = "120px";
        inGameWindow.appendChild(inGameContent);
        let inGameButtons = document.createElement("div");
        inGameButtons.className = "modal-buttons";
        inGameButtons.id = "inGameButtons";
        inGameWindow.appendChild(inGameButtons);
        let inGameP1NameBar = document.createElement("input");
        inGameP1NameBar.id = "p1NameBar";
        inGameP1NameBar.maxLength = "6";
        inGameButtons.appendChild(inGameP1NameBar);
        let inGameP1NameOk = document.createElement("h2");
        inGameP1NameOk.className = "popUpButton";
        inGameP1NameOk.type = "button";
        inGameP1NameOk.id = "popUpP1NameOk";
        inGameP1NameOk.innerHTML = "OK";
        inGameButtons.appendChild(inGameP1NameOk);
        inGameP1NameOk.addEventListener("click", () => {
            playerCreator.names[0].innerHTML = inGameP1NameBar.value;
            inGameContent.innerHTML = "Insert Player 2 name";
            inGameContent.style.marginLeft = "130px";
            inGameP1NameBar.remove();
            inGameP1NameOk.remove();
            let inGameP2NameBar = document.createElement("input");
            inGameP2NameBar.id = "p2NameBar";
            inGameP2NameBar.maxLength = "6";
            inGameButtons.appendChild(inGameP2NameBar);
            let inGameP2NameOk = document.createElement("h2");
            inGameP2NameOk.className = "popUpButton";
            inGameP2NameOk.type = "button";
            inGameP2NameOk.id = "popUpP2NameOk";
            inGameP2NameOk.innerHTML = "OK";
            inGameButtons.appendChild(inGameP2NameOk);
            inGameP2NameOk.addEventListener("click", () => {
                playerCreator.names[1].innerHTML= inGameP2NameBar.value;
                document.body.removeChild(inGameOverlay);
                modeSelector.gameMode("vsPlayerTwoMode");
            })
        });
    } else if (mode === "VSCPU") {
        inGameTitle.innerHTML = "VS CPU Mode";
        inGameTitle.style.marginLeft = "210px";
        inGameTitleBar.appendChild(inGameTitle);
        let inGameContent = document.createElement("div");
        inGameContent.className = "modal-content";
        inGameContent.id = "inGameContent";
        inGameContent.innerHTML = "Insert Player 1 name";
        inGameContent.style.marginLeft = "120px";
        inGameWindow.appendChild(inGameContent);
        let inGameButtons = document.createElement("div");
        inGameButtons.className = "modal-buttons";
        inGameButtons.id = "inGameButtons";
        inGameWindow.appendChild(inGameButtons);
        let inGameP1NameBar = document.createElement("input");
        inGameP1NameBar.id = "p1NameBar";
        inGameP1NameBar.maxLength = "6";
        inGameButtons.appendChild(inGameP1NameBar);
        let inGameP1NameOk = document.createElement("h2");
        inGameP1NameOk.className = "popUpButton";
        inGameP1NameOk.type = "button";
        inGameP1NameOk.id = "popUpP1NameOk";
        inGameP1NameOk.innerHTML = "OK";
        inGameButtons.appendChild(inGameP1NameOk);
        inGameP1NameOk.addEventListener("click", () => {
            playerCreator.names[0].innerHTML = inGameP1NameBar.value;
            document.body.removeChild(inGameOverlay);
            modeSelector.gameMode("vsCPUMode");
        });    
    }
    let inGameInfo = document.createElement("div");
    inGameInfo.id = "modal-info";
    inGameInfo.innerHTML = "You can reset the game mode during the match by clicking on any of the buttons to the right";
    inGameWindow.appendChild(inGameInfo);
},    

};

还有一些 addEventListeners 用于“点击”,它们在不同的按钮中实现上述代码,但我不想添加更多代码,所以我也在 GitHub:

https://github.com/roznerx/tic_tac_toe

提前致谢,如果我问错了,再次抱歉!

【问题讨论】:

    标签: javascript html dom-events addeventlistener tic-tac-toe


    【解决方案1】:

    如果我们首先以vsPlayerTwoMode 模式开始,那么movements.movement() 会为板上的每个框添加一个事件侦听器。当单击一个框时,它会检查它是否为空,并根据轮到它的人(基于playerCreator.turns[0]playerCreator.turns[1] 的值)将gameBoard.board[i].innerHTML 设置为XO

    现在让我们切换到vsCPUMode 模式。现在板上的每个框都附加了原始事件侦听器(来自movements.movement()),以及来自movements.vsCpuMovement() 的新事件侦听器。当一个框被点击时,它首先会评估上一段中的逻辑(在movements.movement()事件监听器中定义),然后是movements.vsCpuMovement()函数中的逻辑(特别是检查gameBoard.board[i].innerHTML == "")。

    但是,由于movements.movement() 事件在movements.vsCpuMovement() 事件之前被调用,那么gameBoard.board[i].innerHTML 将被设置为XO,具体取决于playerCreator.turns[0] 和@987654343 的先前值@。然后,movements.vsCpuMovement() 检查gameBoard.board[i].innerHTML 是否为空字符串,如果不是,则函数返回。

    重要,当模式为vsCPUMode启动时,movements.movement()事件监听器永远不会注册,所以playerCreator.turns[0]playerCreator.turns[1]的值是什么无关紧要.

    我们如何解决它?一种选择是重置playerCreator.turns[...] 的值,以便在调用movements.movement() 事件时,它会简单地忽略依赖于这些值的逻辑(这有点相当于开始游戏模式vsCPUMode)。也就是说,在line 215后面加上这两行:

    playerCreator.turns[0] = ""
    playerCreator.turns[1] = ""
    

    有没有更好的方法来解决这个问题?你打赌!我建议您在Code Review 上发布您的代码并征求人们的建议。如果您只对解决这个紧迫的问题感兴趣,我会跟踪您添加到游戏板框的所有事件侦听器,如果您更改模式,remove 任何现有的侦听器,然后再添加新的侦听器。

    【讨论】:

    • 我非常感谢您提供如此详细而亲切的回答!!!我不敢相信它与轮换系统有关!我猜我对 modeSelector 感到非常困惑和沮丧,以至于我放弃了那部分代码。老实说,我想快速解决这个问题,以便不断进步和学习新事物,但我知道我应该放慢脚步,尽可能地清理和完善代码。我并不完全赞同将每个方法都存储在对象中的想法,但这就是 The Odin Project 所建议的!我会尽快在 Code Review 上发布。再次,谢谢你!!!
    猜你喜欢
    • 2015-06-12
    • 1970-01-01
    • 2010-12-13
    • 1970-01-01
    • 2015-01-08
    • 1970-01-01
    相关资源
    最近更新 更多