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