📜  使用 JavaScript 的 Pig 游戏设计

📅  最后修改于: 2021-08-30 11:48:59             🧑  作者: Mango

在这篇文章中,我们将解释制作著名的猪游戏所需的步骤和各种逻辑,这是一个虚拟骰子游戏。

关于游戏:在这个游戏中,用户界面(UI)包含可以做三件事的用户/玩家,它们如下:

这场比赛将有两名球员。在游戏开始时,玩家 1将成为当前玩家玩家 2将成为非活动玩家

  1. 掷骰子:当前玩家必须掷骰子,然后会产生一个随机数。如果当前玩家在骰子上得到除 1 以外的任何数字,则该数字将添加到当前分数(最初当前分数将为 0 ),然后新分数将显示在当前分数部分下。注意:如果当前玩家的骰子点数为1,则玩家将被切换,即当前玩家将变为非活动状态,反之亦然。
  2. 保持:如果当前玩家点击保持,则当前得分将被添加到总得分中。当活动玩家单击保持按钮时,将评估总分。如果总分 >= 100,则当前玩家获胜,否则玩家将被切换。
  3. 重置:所有分数设置为 0,玩家 1设置为起始玩家(当前玩家)。

游戏制作:由网页浏览器渲染的游戏,它是在HTML、CSS (前端)和JavaScript (后端)的帮助下构建的。游戏的主要逻辑在于 JS 文件,而外观和用户界面则由HTML 和 CSS呈现在这个项目中,基本上有四种类型的文件:

  • HTML 文件 (index.html)
  • CSS 文件 (style.css)
  • JavaScript 文件(script.js 文件)
  • 图像(dice.png 文件)

我们将分析所有这些文件,从而让您了解他们在这个游戏中的工作/贡献。所以,首先让我们从index.html文件开始:

HTML 文件: Index.html 文件是使 Web 浏览器能够理解并解释我们正在制作的文档类型的文件。它代表超文本标记语言,我们的网络浏览器通过V8 引擎读取这个文件并理解它的组件(它以一种语言解析代码,以便浏览器可以理解它)。下面是这个游戏的 HTML 代码:

HTML


  

    
    
  
    
    
    Pig Game Design using JavaScript

  

    
        
            
                

                    Total Score
                    Player 1                 

                

43

            
               
                

Current Score

                   

0

               
        
           
            
                

                    Total Score
                    Player 2                 

                

24

            
               
                

Current Score

                   

0

               
        
                                           
       


CSS
* {
    margin: 0;
    padding: 0;
}
  
body {
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}
  
.container {
    position: relative;
    width: 80%;
    height: 90%;
    background-color: lightgreen;
    overflow: hidden;
    display: flex;
}
  
.player {
    flex: 50%;
    padding: 150px;
    display: flex;
    flex-direction: column;
    align-items: center;
    transition: all 0.75s;
}
  
.name {
    position: relative;
    font-weight: bold;
    font-size: 38px;
    text-align: center;
}
  
.pl {
    font-size: 24px;
}
  
.tscore {
    background-color: #fff;
    border-radius: 9px;
    width: 65%;
    padding: 2rem;
    text-align: center;
    transition: all 0.75s;
}
  
.score {
    font-size: 38px;
    font-weight: bold;
    margin-bottom: auto;
    padding-top: 10px;
}
  
.player--active {
    background-color: green;
}
  
.player--active .current {
    opacity: 1;
}
  
.current {
    margin-top: 10rem;
    background-color: #fff;
    border-radius: 9px;
    width: 65%;
    padding: 2rem;
    text-align: center;
    transition: all 0.75s;
}
  
.current-label {
    text-transform: uppercase;
    margin-bottom: 1rem;
    font-size: 1.7rem;
    color: #ddd;
}
  
.current-score {
    font-size: 3.5rem;
}
  
.btn {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    color: rgb(7, 124, 69);
    border: none;
    font-size: 30px;
    cursor: pointer;
    font-weight: bold;
    background-color: rgba(255, 255, 255, 0.6);
    backdrop-filter: blur(10px);
    padding: 10px 30px;
    border-radius: 10px;
}
  
.btn--new {
    top: 4rem;
}
  
.btn--roll {
    top: 39.3rem;
}
  
.btn--hold {
    top: 46.1rem;
}
  
.dice {
    position: absolute;
    left: 50%;
    top: 24rem;
    transform: translateX(-50%);
}
  
.player--winner {
    background-color: #003612;
}
  
.player--winner .name {
    font-weight: 700;
    color: #c7365f;
}
  
.hidden {
    display: none;
}


Javascript
'use strict';
  
// Selecting elements
const player0El = document.querySelector('.player--0');
const player1El = document.querySelector('.player--1');
const score0El = document.querySelector('#score--0');
const score1El = document.getElementById('score--1');
const current0El = document.getElementById('current--0');
const current1El = document.getElementById('current--1');
  
const diceEl = document.querySelector('.dice');
const btnNew = document.querySelector('.btn--new');
const btnRoll = document.querySelector('.btn--roll');
const btnHold = document.querySelector('.btn--hold');
  
let scores, currentScore, activePlayer, playing;


Javascript
// Rolling dice functionality
btnRoll.addEventListener('click', function () {
  if (playing) {
    
    // 1. Generating a random dice roll
    const dice = Math.trunc(Math.random() * 6) + 1;
  
    // 2. Display dice
    diceEl.classList.remove('hidden');
    diceEl.src = `dice-${dice}.png`;
  
    // 3. Check for rolled 1
    if (dice !== 1) {
      
      // Add dice to current score
      currentScore += dice;
      document.getElementById(
        `current--${activePlayer}`
      ).textContent = currentScore;
    } else {
      
      // Switch to next player
      switchPlayer();
    }
  }
});


Javascript
const switchPlayer = function () {
  document.getElementById(`current--${activePlayer}`).textContent = 0;
  currentScore = 0;
  activePlayer = activePlayer === 0 ? 1 : 0;
  player0El.classList.toggle('player--active');
  player1El.classList.toggle('player--active');
};


Javascript
btnHold.addEventListener('click', function () {
  if (playing) {
    
    // 1. Add current score to active player's score
    scores[activePlayer] += currentScore;
    // scores[1] = scores[1] + currentScore
  
    document.getElementById(`score--${activePlayer}`)
      .textContent = scores[activePlayer];
  
    // 2. Check if player's score is >= 100
    if (scores[activePlayer] >= 100) {
      
      // Finish the game
      playing = false;
      diceEl.classList.add('hidden');
  
      document
        .querySelector(`.player--${activePlayer}`)
        .classList.add('player--winner');
      document
        .querySelector(`.player--${activePlayer}`)
        .classList.remove('player--active');
    } else {
      
      // Switch to the next player
      switchPlayer();
    }
  }
});


Javascript
// Starting conditions
const init = function () {
  scores = [0, 0];
  currentScore = 0;
  activePlayer = 0;
  playing = true;
  
  score0El.textContent = 0;
  score1El.textContent = 0;
  current0El.textContent = 0;
  current1El.textContent = 0;
  
  diceEl.classList.add('hidden');
  player0El.classList.remove('player--winner');
  player1El.classList.remove('player--winner');
  player0El.classList.add('player--active');
  player1El.classList.remove('player--active');
};
init();


在上面的代码中,我们使用了各种类(例如:btn btn–roll、rte),这些将用于 CSS 文件中的样式目的,并将在 CSS 文件下讨论它们。

CSS 文件:为了格式化由 HTML 创建的标记和样式,我们需要级联样式表,以便标记(代码)看起来更好。下面是游戏的 CSS 代码。在深入代码之前,只需快速查看哪些类和 id 用于什么目的:

  1. 对于整个 HTML 页面和元素: * 将影响标记中的每个元素和标签。我们使用了另外 2 个标签来提供一些特定的样式,即 html 和 body 标签的样式。
  2. 布局元素:在那里定义了主要标签播放器类样式。我们为 main 标签定义了position属性并将其属性设置为relative。
  3. 自制类:使页面更具吸引力所需的通用样式。
  4. 绝对定位类:我已经设置了btn 和其他类的位置属性并将其值设置为绝对,因为我们必须确保按钮和其他元素始终位于页面中的正确位置。绝对位置将根据相对定位的元素(在这种情况下,它是主标签)来安排该特定元素。

CSS

* {
    margin: 0;
    padding: 0;
}
  
body {
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}
  
.container {
    position: relative;
    width: 80%;
    height: 90%;
    background-color: lightgreen;
    overflow: hidden;
    display: flex;
}
  
.player {
    flex: 50%;
    padding: 150px;
    display: flex;
    flex-direction: column;
    align-items: center;
    transition: all 0.75s;
}
  
.name {
    position: relative;
    font-weight: bold;
    font-size: 38px;
    text-align: center;
}
  
.pl {
    font-size: 24px;
}
  
.tscore {
    background-color: #fff;
    border-radius: 9px;
    width: 65%;
    padding: 2rem;
    text-align: center;
    transition: all 0.75s;
}
  
.score {
    font-size: 38px;
    font-weight: bold;
    margin-bottom: auto;
    padding-top: 10px;
}
  
.player--active {
    background-color: green;
}
  
.player--active .current {
    opacity: 1;
}
  
.current {
    margin-top: 10rem;
    background-color: #fff;
    border-radius: 9px;
    width: 65%;
    padding: 2rem;
    text-align: center;
    transition: all 0.75s;
}
  
.current-label {
    text-transform: uppercase;
    margin-bottom: 1rem;
    font-size: 1.7rem;
    color: #ddd;
}
  
.current-score {
    font-size: 3.5rem;
}
  
.btn {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    color: rgb(7, 124, 69);
    border: none;
    font-size: 30px;
    cursor: pointer;
    font-weight: bold;
    background-color: rgba(255, 255, 255, 0.6);
    backdrop-filter: blur(10px);
    padding: 10px 30px;
    border-radius: 10px;
}
  
.btn--new {
    top: 4rem;
}
  
.btn--roll {
    top: 39.3rem;
}
  
.btn--hold {
    top: 46.1rem;
}
  
.dice {
    position: absolute;
    left: 50%;
    top: 24rem;
    transform: translateX(-50%);
}
  
.player--winner {
    background-color: #003612;
}
  
.player--winner .name {
    font-weight: 700;
    color: #c7365f;
}
  
.hidden {
    display: none;
}

在 HTML 代码中,我们提供了各种类的名称。在这个文件中,我们提供了它们不同的功能。 CSS 文件在使网页或游戏看起来不错(仅在 Web 浏览器的情况下)方面起着重要作用。

到目前为止,我们已经完美地制作了游戏的用户界面,现在是更棘手的部分。所以让我们深入研究游戏逻辑……

JavaScript 文件:有一些JavaScript 变量,我们可以使用两种类型的变量,即let 和 constant。用let声明的变量可以修改,用constant声明的变量不能修改。在 JavaScript 文件中,我们基本上是在做DOM 操作(JavaScript 中的一切都是一种对象,所以我们将 UI 称为文档对象模型)。所以 document.querySelector() 是一种从 DOM 中选择元素的方法。

为了理解逻辑的工作流程,我们必须首先理解事件监听器概念。事件侦听器是根据特定事件执行操作的函数。他们等待特定事件发生。这个游戏共有 03 个事件监听器: btnRoll、btnHold、btnNew。我们将了解所有这些事件侦听器的功能:

注意:在进入事件处理程序部分之前,我们必须在文件中声明一些变量,以便稍后在我们的游戏逻辑中使用它们。

Javascript

'use strict';
  
// Selecting elements
const player0El = document.querySelector('.player--0');
const player1El = document.querySelector('.player--1');
const score0El = document.querySelector('#score--0');
const score1El = document.getElementById('score--1');
const current0El = document.getElementById('current--0');
const current1El = document.getElementById('current--1');
  
const diceEl = document.querySelector('.dice');
const btnNew = document.querySelector('.btn--new');
const btnRoll = document.querySelector('.btn--roll');
const btnHold = document.querySelector('.btn--hold');
  
let scores, currentScore, activePlayer, playing;

在 JavaScript 文件的最开始,有一行“use strict”。 “use strict”的目的是表示代码应该在“严格模式”下执行。除 Internet Explorer 9 及更低版本外,所有现代浏览器都支持“严格使用”。

现在让我们开始查看 3 个事件处理程序中每一个的代码。

1. btnRoll 事件处理程序:

Javascript

// Rolling dice functionality
btnRoll.addEventListener('click', function () {
  if (playing) {
    
    // 1. Generating a random dice roll
    const dice = Math.trunc(Math.random() * 6) + 1;
  
    // 2. Display dice
    diceEl.classList.remove('hidden');
    diceEl.src = `dice-${dice}.png`;
  
    // 3. Check for rolled 1
    if (dice !== 1) {
      
      // Add dice to current score
      currentScore += dice;
      document.getElementById(
        `current--${activePlayer}`
      ).textContent = currentScore;
    } else {
      
      // Switch to next player
      switchPlayer();
    }
  }
});

每当玩家点击Roll 按钮时,这个事件处理程序就会起作用(这就是我们在那里使用 click 事件的原因)。然后是一个以if-else 块开头的回调函数。因为我们已经声明了变量play = true ,所以这个事件处理程序的 if 块将为 true ,因此 if 块的代码将被执行。以下是前面的步骤:

  1. 步骤 1:在玩家点击掷骰子按钮后,此事件处理程序使用Math.trunc()函数生成一个随机数我们使用了 Math.trunc()函数,因为该函数返回随机生成函数的整数部分,并为其添加了 1,因为random()函数可以生成从 0 到 1 的任何数字,但在我们的例子中,我们只需要从 1 到 6 的数字。
    了解骰子变量:骰子变量将存储随机生成的数字。假设 Math.random()函数生成数字 0.02。根据代码,第一个 0.02 将乘以 6。因此可变骰子现在的值为 0.12。然后 Math.trunc()函数将起作用并使骰子变量为 0 。现在 1 将被添加到变量中,这将使骰子 = 1 (这就是我们需要的骰子数字)
  2. Step2:现在我们已经得到了骰子的分数,我们要显示与骰子数量相对应的骰子。 (在 CSS 文件中,我们创建了一个名为 hidden 的类,它将在游戏开始时最初隐藏骰子)但是现在我们有一个骰子编号以骰子图像的形式显示,我们必须删除隐藏类。这是通过行diceE1.classList.remove(‘hidden’) 实现的,然后将正确的骰子图像呈现到 UI。
  3. 第三步。现在根据游戏规则,我们必须检查骰子上的数字。因此,如果骰子数不是 1,则更新当前分数。如果骰子数为 1,则调用switchPlayer()

Javascript

const switchPlayer = function () {
  document.getElementById(`current--${activePlayer}`).textContent = 0;
  currentScore = 0;
  activePlayer = activePlayer === 0 ? 1 : 0;
  player0El.classList.toggle('player--active');
  player1El.classList.toggle('player--active');
};

根据规则: “如果玩家掷出 1,那么他将失去所有当前得分” 。此功能正在实现相同的函数。

activePlayer = activePlayer === 0 ? 1 : 0 {这是一个三元运算符,其中我们说的是,如果 activeplayer 为 0,则将其设为 1,如果是,则将其设为 0。

2. btnHold 事件处理程序

Javascript

btnHold.addEventListener('click', function () {
  if (playing) {
    
    // 1. Add current score to active player's score
    scores[activePlayer] += currentScore;
    // scores[1] = scores[1] + currentScore
  
    document.getElementById(`score--${activePlayer}`)
      .textContent = scores[activePlayer];
  
    // 2. Check if player's score is >= 100
    if (scores[activePlayer] >= 100) {
      
      // Finish the game
      playing = false;
      diceEl.classList.add('hidden');
  
      document
        .querySelector(`.player--${activePlayer}`)
        .classList.add('player--winner');
      document
        .querySelector(`.player--${activePlayer}`)
        .classList.remove('player--active');
    } else {
      
      // Switch to the next player
      switchPlayer();
    }
  }
});

每当玩家点击 HOLD 按钮时,此事件处理程序就会触发。以下是此处理程序涉及的步骤:

  1. 第 1 步:一旦玩家点击保持,当前分数就会被添加到该玩家的总分中。
  2. Step2:在该步骤之后完成对总分的评估。如果发现总分大于 100,则游戏结束,然后玩家获胜者类别(使背景颜色为黑色并更改颜色和字体粗细)并删除活跃玩家类别。

3. btnNew 事件处理程序:

btnNew.addEventListener(‘click’,init):每当一个新游戏被初始化时,这个事件处理程序就会触发。它只初始化Init()函数。 Init() 类将游戏重置为开始,即它执行以下操作:

  • 使双方球员的得分为 0。
  • 使播放器 1 成为活动/当前播放器。
  • 隐藏类隐藏骰子。
  • 从两个玩家中删除玩家-获胜者类别
  • 将玩家活动类添加到玩家 1

Javascript

// Starting conditions
const init = function () {
  scores = [0, 0];
  currentScore = 0;
  activePlayer = 0;
  playing = true;
  
  score0El.textContent = 0;
  score1El.textContent = 0;
  current0El.textContent = 0;
  current1El.textContent = 0;
  
  diceEl.classList.add('hidden');
  player0El.classList.remove('player--winner');
  player1El.classList.remove('player--winner');
  player0El.classList.add('player--active');
  player1El.classList.remove('player--active');
};
init();

注意: init()函数也在加载游戏时初始化。

输出:

切块图像参考:

  • 骰子-1.png
  • 骰子-2.png
  • 骰子-3.png
  • 骰子-4.png
  • 骰子-5.png
  • 骰子-6.png