# 物件導向JS繼承 - 遊戲實作 Memory Blacks
### **撰寫程式:HTML | CSS(SASS) | JS(jQuery ∕ 原生JS)**
###### codepen:https://codepen.io/liu_0821/pen/yLEKObd

## 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
---
##### ヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)ノヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人( ゚∀)人
##### 還有一些部分可以再加強,等之後再回來修改~~~
##### 以上 如果註解哪裡有錯誤或有問題,歡迎提出來一起討論~~~~