---
# System prepended metadata

title: 物件導向JS繼承 - 遊戲實作 Memory Blacks
tags: [jquery, SASS, HTML & CSS, JavaScript]

---


# 物件導向JS繼承 - 遊戲實作 Memory Blacks

### **撰寫程式：HTML ｜ CSS（SASS） ｜ JS（jQuery ∕ 原生JS）**
###### codepen：https://codepen.io/liu_0821/pen/yLEKObd

![](https://i.imgur.com/hjSC5ff.jpg)

## HTML
> ###### 原生HTML撰寫
```
<div class="infos">
  <h2>Project 4.</h2>
  <h1>Memory<br>Blocks</h1>
  <h3 class="status">-</h3>
</div>
<div class="blocks">
  <div class="row">
    <div class="block block1" onclick = "newGame.userSendInput('1')"></div>
    <div class="block block2" onclick = "newGame.userSendInput('2')"></div> 
  </div>
   <div class="row">
    <div class="block block3" onclick = "newGame.userSendInput('3')"></div>
    <div class="block block4" onclick = "newGame.userSendInput('4')"></div>
  </div>
  <div class="row">
    <div class="inputStatus"></div>
  </div>
</div>
```

****
## CSS（SASS）
> ###### 與原生CSS不同之處，原生CSS使用大括弧{}套用CSS特性，SASS則利用縮排區分HTML之間的層級關係。

###### @mixin name 集合 - 放置元素（可帶參數）， 使用 +name 可直接把內裡元素帶入css中
###### $ - sass的變數

```
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap')
*
  font-family: "Roboto"
  font-weight: 700
  background-color: #222

$colorRed: #FF5353
$colorYellow: #FFC429
$colorBlue: #5980C1
$colorWhite: #FBE9B7

@mixin size($w,$h:$w)
  width: $w
  height: $h
@mixin flexCenter
  display: flex
  justify-content: center
  align-items: center
@mixin setBlackColor($color)
  box-shadow: 0px 0px 35px rgba($color,0.2)
  //背景顏色透明 transparent
  background-color: transparent
  //框線亮度提高 lighten()
  border: 1px solid lighten($color,5)
  &:hover
    background-color: rgba($color,0.3)
    box-shadow: 0px 0px 35px rgba($color,0.5)
  &:active,&.active
    background-color: $color
    box-shadow: 0px 0px 35px $color
  
html,body
  background-color: #232526
  color: white
  +size(100%)
  +flexCenter
  margin: 0
  
  .infos
    position: absolute
    left: 40px
    top: 40px
    h1
      font-size: 40px
      margin-top: 0
      margin-bottom: 0
      /*文字間距*/
      letter-spacing: 1px
      line-height: 1.2
    h3
      font-weight: 500
      color: $colorRed
  .blocks
    color: white
    .row  
      display: flex
    .block
      +size(100px)
      border: 1px solid white
      +flexCenter
      transition: 0.4s
      cursor: pointer
      margin: 12px
      &:active,&.active
        transition: 0s
        background-color: #fff
        color:  black
      
      &.block1
        +setBlackColor($colorRed)
      &.block2
        +setBlackColor($colorYellow)
      &.block3
        +setBlackColor($colorBlue)
      &.block4
        +setBlackColor($colorWhite)
  .inputStatus
    margin-top: 10px
    .circle
      +size(6px)
      display: inline-block
      background-color: white
      opacity: 0.3
      margin: 5px
      border-radius: 100%
      
      &.correct
        opacity: 1
    &.correct .circle
      background-color: $colorBlue
    &.wrong .circle
      background-color: $colorRed
    
```

****
## JS（jQuery ∕ 原生JS）
> ###### 使用原生JS與jQuery撰寫，利用JS物件導向的繼承來作為整體程式開發的主軸。
###### 專案所需用到的資料集合
```
// 方塊所有資料與遊戲音效集合
var blockData = [
  {selector:".block1", name:"1",pitch: "1"},
  {selector:".block2", name:"2",pitch: "2"},
  {selector:".block3", name:"3",pitch: "3"},
  {selector:".block4", name:"4",pitch: "4"},
];

// 正確與錯誤的音效
var soundsetdata = [
  {name:"correct",sets:[1,3,5,8]},
  {name:"wrong",sets:[2,4,5.5,7]}
];

//關卡關數
var levelDatas = [
  "1234",
  "12344",
  "123441",
  "12344121",
  "123412344",
  "1234124134",
  "12341241342213",
];
```
###### 方塊物件與繼承範圍
```
// 方塊物件
var Blocks = function(blockAssign,setAssign){
  this.allOn = false;
  this.blocks = blockAssign.map((data,i)=>({
    name: data.name,
    el: $(data.selector),
    audio: this.getAudioObject(data.pitch)
  }));
  this.soundSets = setAssign.map((data,i)=>({
    name: data.name,
    sets: data.sets.map((pitch)=>this.getAudioObject(pitch))
  }));
};

// 播放對應名稱的方塊聲音
Blocks.prototype.flash = function(note){
  let block = this.blocks.find(data=>data.name==note);
  if(block){
    //當重複撥放音樂，防止出現多次點選卻無聲音，播放時間要設0
    block.audio.currentTime = 0;
    block.audio.play();
    
    block.el.addClass("active");
    setTimeout(()=>{
      //判斷是否全亮或全暗
      if(this.allOn == false){
        block.el.removeClass("active");
      }
    },100)
  }
};

// 所有方塊點亮
Blocks.prototype.turnAllOn = function(){
  this.allOn = true;
  this.blocks.forEach((block)=>{
    block.el.addClass("active");
  });
};
// 所有方塊變暗
Blocks.prototype.turnAllOff = function(){
  this.allOn = false;
  this.blocks.forEach((block)=>{
    block.el.removeClass("active");
  });
};

// 新增聲音
Blocks.prototype.getAudioObject = function(pitch){
  return new Audio("https://awiclass.monoame.com/pianosound/set/"+ pitch+".wav")
};

// 播放全部特效音
Blocks.prototype.playSet = function(type){
  let sets = this.soundSets.find(set => set.name == type).sets;
  console.log(sets)
  sets.forEach((obj)=>{
    obj.currentTime = 0;
    obj.play();
  });
};
```
###### 遊戲所需物件與繼承範圍
```
var Game = function(){
  // 把上個物件的東西搬進來使用
  this.newBlocks = new Blocks(blockData,soundsetdata)
  this.levels = levelDatas;
  this.currentLevel = 0;
  this.playInterval = 400;
  this.mode = "waiting";
}

// 遊戲關卡
Game.prototype.startLevel = function(){
  this.showMessage("Level " + this.currentLevel)
  let leveldata = this.levels[this.currentLevel]
  this.startGame(leveldata)
};

// 顯示到第幾關卡
Game.prototype.showMessage = function(mes){
  console.log(mes)
  $(".status").text(mes)
};

// 遊戲開始
Game.prototype.startGame = function(ans){
  this.mode = "gamePlay";
  this.answer = ans;
  let notes = this.answer.split('');
  
  this.showStatus("")
  this.timer = setInterval(()=>{
    let char = notes.shift();
    this.playNote(char)
    if(!notes.length){
      console.log("使用者輸入")
      this.startUserInput()
      clearInterval(this.timer);
    }
  },this.playInterval);
}

// 判斷播放單一聲音
Game.prototype.playNote = function(note){
  this.newBlocks.flash(note)
};

// 切換模式 區分使用者輸入跟電腦撥放
Game.prototype.startUserInput = function(){
  this.userInput = "";
  this.mode = "userInput";
  
}; 

// 累計使用者輸入順序，並判斷是否正確
Game.prototype.userSendInput = function(inputChar){
  if(this.mode == "userInput"){
    let tempString = this.userInput + inputChar;
    this.playNote(inputChar)
    
    //產生小點點
    this.showStatus(tempString)
    
    // 判斷輸入單個字串是否一樣
    if(this.answer.indexOf(tempString)==0){
      console.log("正確")
      
      // 判斷輸入全部字串是否一樣
      if(this.answer == tempString){
        this.showMessage("全部答對")
        this.currentLevel += 1;//答對之後進入下一關
        this.mode = "waiting";
       
        setTimeout(()=>{
          this.startLevel()
        },1000);
      }
    }else{
      // 答錯
      this.showMessage("錯誤")
        this.currentLevel = 0;//答對之後進入下一關
        this.mode = "waiting";
      
        setTimeout(()=>{
          this.startLevel()
        },1000);
    }
    
    // 紀錄之前user輸入的內容
    this.userInput += inputChar;
    
  }
};

// 處理答題的小點點
Game.prototype.showStatus = function(tempString){
  $(".inputStatus").html("")
  this.answer.split("").forEach((data,i)=>{
    var circle = $("<div class = 'circle'></div>")
    if( i < tempString.length){
      circle.addClass("correct")
    }
    $(".inputStatus").append(circle)
  });
  
  // 判斷答題完成turnAllOn是否關閉
  if(tempString == ''){
    this.newBlocks.turnAllOff()
  }
  
  // 判斷答案正確
  if(tempString == this.answer){
    $(".inputStatus").addClass("correct")
    setTimeout(()=>{
      this.newBlocks.turnAllOn()
      
      // 播放答對特效的聲音
      this.newBlocks.playSet("correct")
    },500)
  }else{
    // 可能還沒輸入完成
    $(".inputStatus").removeClass("correct")
  }
  
  // 判斷答案如果錯誤
  if(this.answer.indexOf(tempString)!=0){
    $(".inputStatus").addClass("wrong")
    setTimeout(()=>{
      this.newBlocks.turnAllOn()
      // 播放答錯特效的聲音
      this.newBlocks.playSet("wrong")
    },500)
  }else{
    // 可能還沒輸入完成
    $(".inputStatus").removeClass("wrong")
  }
}

var newGame = new Game();

```
###### 遊戲開始
```
setTimeout(()=>{
  newGame.startLevel()
},1000)
```


---

### 待改：
1. #### 載入新增遊戲開始鈕
2. #### 遊戲關卡ajax載入
3. #### 闖關成功後重新載入OR返回開始鈕
4. #### 有想到不錯的再來改d(`･∀･)b


---

##### ヽ(∀ﾟ )人(ﾟ∀ﾟ)人( ﾟ∀)人(∀ﾟ )人(ﾟ∀ﾟ)人( ﾟ∀)ﾉヽ(∀ﾟ )人(ﾟ∀ﾟ)人( ﾟ∀)人(∀ﾟ )人(ﾟ∀ﾟ)人( ﾟ∀)人(∀ﾟ )人( ﾟ∀)人

##### 還有一些部分可以再加強，等之後再回來修改~~~
##### 以上 如果註解哪裡有錯誤或有問題，歡迎提出來一起討論~~~~

