javascript
JavaScript学习笔记——编写战舰游戏 Part2:JavaScript部分
編程思路
- 核心:跟隨游戲的步驟邏輯,思考其中所需的功能,并盡量將各部分功能分離,可以使編程思路更清晰、代碼易讀性更強(qiáng),也方便調(diào)試
- 先寫大框架,有需要的功能直接調(diào)用(雖然未編寫),交給后面的代碼實(shí)現(xiàn)細(xì)節(jié)功能
摘要:知識(shí)要點(diǎn)
-
每個(gè)戰(zhàn)艦為ship對(duì)象,包含兩個(gè)數(shù)組locations和hits,保存一艘戰(zhàn)艦的位置和被擊中部位
-
若有多個(gè)ship對(duì)象,將它們保存在同一個(gè)數(shù)組ships中(數(shù)組元素為對(duì)象)
-
玩家提交輸入后,通過(guò)遍歷每艘戰(zhàn)艦的位置,判定是否擊中
-
根據(jù)是否擊中,使用對(duì)應(yīng)網(wǎng)格的<td>元素的setAttribute()將其設(shè)置為之前在CSS中規(guī)定好的類(類的background已被設(shè)置為戰(zhàn)艦或MISS圖像),網(wǎng)頁(yè)將實(shí)時(shí)地根據(jù)其class(類)的不同改變其背景圖像
-
消息顯示區(qū)為<div>元素,innerHTML對(duì)應(yīng)其文本
-
輸入框?yàn)?lt;input>元素(type為"text"),value對(duì)輸入框的內(nèi)容,placeholder可預(yù)設(shè)其文本,將value置為""可清空文本
-
開(kāi)火按鈕為<input>元素(type為"button"),value對(duì)應(yīng)按鈕上的文本
-
"button"型<input>元素的onclick事件:檢測(cè)到點(diǎn)擊事件時(shí)觸發(fā)
-
[優(yōu)化:回車自動(dòng)提交表單]"text"型<input>元素的onkeypress事件:(在輸入框中)檢測(cè)到按鍵事件時(shí)觸發(fā)
-
[優(yōu)化:回車自動(dòng)提交表單]事件處理程序:判斷當(dāng)前按下的按鍵是否為回車,如果是,自動(dòng)調(diào)用fireButton.click()實(shí)現(xiàn)按鈕點(diǎn)擊操作
設(shè)計(jì)方案
將這款游戲設(shè)計(jì)為幾個(gè)較為獨(dú)立的對(duì)象(每個(gè)對(duì)象有其主要職責(zé),從而便于獨(dú)立創(chuàng)建和測(cè)試游戲的各個(gè)部分),并利用DOM與用戶交互。這種設(shè)計(jì)讓問(wèn)題理解起來(lái)容易得多。
- 這樣做的優(yōu)點(diǎn)在于:可以相對(duì)獨(dú)立的編寫代碼,并分別測(cè)試每個(gè)對(duì)象,確保對(duì)象履行其職責(zé)(每個(gè)對(duì)象的測(cè)試代碼可能需要單獨(dú)編寫,僅用于測(cè)試該對(duì)象的功能)
總共有3個(gè)要設(shè)計(jì)并實(shí)現(xiàn)的對(duì)象:model、view和controller
- model將存儲(chǔ)游戲的狀態(tài),如每艘戰(zhàn)艦的位置以及哪個(gè)部位被擊中;
- view負(fù)責(zé)在model存儲(chǔ)的狀態(tài)改變時(shí)更新界面;
- controller將各個(gè)部分整合以實(shí)現(xiàn)游戲邏輯:將用戶輸入交給model以更新?tīng)顟B(tài)、判斷游戲是否結(jié)束
1.view對(duì)象
- view對(duì)象用于顯示當(dāng)前的游戲狀態(tài)
- 需要displayMessage方法:在左上角顯示hit/miss/you sank my battleship信息
- 需要displayHit方法:在某指定網(wǎng)格內(nèi)顯示戰(zhàn)艦圖像
- 需要displayMiss方法:在某指定網(wǎng)格內(nèi)顯示MISS圖像
具體實(shí)現(xiàn)方案
displayMessage方法:傳入要顯示的信息msg,通過(guò)getElementById獲取用于顯示消息的<div>元素,用innerHTML設(shè)置其文本為msg
displayHit方法:傳入指定網(wǎng)格<td>的id,通過(guò)getElementById獲取相應(yīng)的<td>元素,并將其class屬性設(shè)置為hit類,即可將網(wǎng)格背景設(shè)置為戰(zhàn)艦圖像(利用了之前定義的hit類,其background屬性為戰(zhàn)艦圖像)
displayMiss方法:同理
var view = {displayMessage: function(msg) {var messageArea = document.getElementById("messageArea");messageArea.innerHTML = msg;},displayHit: function(location) {var cell = document.getElementById(location);cell.setAttribute("class", "hit");},displayMiss: function(location) {var cell = document.getElementById(location);cell.setAttribute("class", "miss");} };2.model對(duì)象
model對(duì)象用于存儲(chǔ)游戲的狀態(tài)、判斷是否擊中以完成狀態(tài)轉(zhuǎn)換、狀態(tài)改變時(shí)通知view
- 需要一些屬性:保存網(wǎng)格大小、總戰(zhàn)艦數(shù)量、戰(zhàn)艦長(zhǎng)度、被擊沉的戰(zhàn)艦數(shù)
- 每個(gè)戰(zhàn)艦為ship對(duì)象,包含兩個(gè)數(shù)組locations和hitis,保存一艘戰(zhàn)艦的位置和被擊中部位
- 若有多個(gè)ship對(duì)象,將它們保存在同一個(gè)數(shù)組ships中(數(shù)組元素為對(duì)象)
- 需要fire方法:向戰(zhàn)艦開(kāi)火時(shí)給出開(kāi)火位置,判斷是否擊中、擊沉,并更新?tīng)顟B(tài)
- 需要isSunk方法:返回戰(zhàn)艦是否被擊沉
- 注意model對(duì)象所保存的狀態(tài)改變時(shí)(如擊中),要通知view。因?yàn)関iew模型并不會(huì)主動(dòng)的檢查狀態(tài)的變化
- 需要generateShip方法:隨機(jī)生成戰(zhàn)艦
核心邏輯:如何檢測(cè)擊中
遍歷每艘船,比較船的位置和開(kāi)火的位置
var model = {boardSize: 7,numShips: 3,shipLength: 3,shipsSunk: 0,// hard-coded values for ship locations(used for test) /*ships: [{ locations: ["06", "16", "26"], hits: ["", "", ""] },{ locations: ["24", "34", "44"], hits: ["", "", ""] },{ locations: ["10", "11", "12"], hits: ["", "", ""] }], */fire: function(guess) {for (var i = 0; i < this.numShips; i++) {var ship = this.ships[i];var index = ship.locations.indexOf(guess);//猜測(cè)的位置上有戰(zhàn)艦if (ship.hits[index] === "hit") {//Already hitreturn true;} else if (index >= 0) {//HITif (this.isSunk(ship)) {//SUNK}return true;}}//MISSreturn false;},//... };注意,查看是否命中戰(zhàn)艦時(shí),代碼為
var ship = this.ships[i];
var locations = ship.locations;
var index = locations.indexOf(guess);
這里使用串接(Chaining)將對(duì)象引用連接,避免創(chuàng)建臨時(shí)變量,也減少了代碼量
串接后的代碼為
var ship = this.ships[i];
var index = ship.locations.indexOf(guess);
此處ship沒(méi)有串接,以免串接鏈條過(guò)長(zhǎng)(閱讀理解困難)
3.controller對(duì)象
controller對(duì)象用于整合各部分:處理用戶輸入,記錄猜測(cè)次數(shù)、讓model根據(jù)當(dāng)前猜測(cè)更新自己、判斷游戲是否結(jié)束
- 需要guesses屬性:記錄猜測(cè)次數(shù)
- 需要processGuess方法:傳入用戶猜測(cè),對(duì)其處理(如"A3"轉(zhuǎn)換為"03"),將結(jié)果交給model,檢測(cè)游戲是否結(jié)束
怎樣獲取玩家輸入:事件處理程序
事件處理程序能將HTML中的表單<form>元素與游戲關(guān)聯(lián)
- 點(diǎn)擊Fire!按鈕時(shí)(單擊事件),調(diào)用事件處理程序
- 事件處理程序(回調(diào)函數(shù))事先編寫好,用于獲取輸入框的內(nèi)容并交給controller
單擊事件與事件處理程序
window.onload = init; function init() {//網(wǎng)頁(yè)加載完畢后,再開(kāi)始接收輸入并開(kāi)始游戲// place the ships on the game boardmodel.generateShipLocations();// Fire! button onclick handlervar fireButton = document.getElementById("fireButton");fireButton.onclick = handleFireButton; }事件處理程序用于獲取輸入框的內(nèi)容并交給controller
function handleFireButton() {var guessInput = document.getElementById("guessInput");var guess = guessInput.value.toUpperCase();//獲取輸入框的內(nèi)容controller.processGuess(guess);//輸入框的內(nèi)容交給controllerguessInput.value = "";//重置輸入框,玩家無(wú)需手動(dòng)刪除上一個(gè)輸入 }優(yōu)化:希望按下回車也能觸發(fā)事件
-
HTML中<input>元素的onkeypress事件:(在輸入框中)檢測(cè)到按鍵事件時(shí),就觸發(fā)對(duì)應(yīng)的事件處理程序,可將所需的按鍵處理程序賦給onkeypress
-
事件處理程序:判斷當(dāng)前按下的按鍵是否為回車,如果是,自動(dòng)調(diào)用fireButton.click()實(shí)現(xiàn)按鈕點(diǎn)擊操作
這里使用了guessInput元素的onkeypress事件
- guessInput.onkeypress = handleKeyPress;
- 瀏覽器向事件處理程序handleKeyPress傳遞一個(gè)事件對(duì)象,其中包含有關(guān)用戶按下了哪個(gè)鍵的信息
4.隨機(jī)生成戰(zhàn)艦
- 每個(gè)戰(zhàn)艦為ship對(duì)象,包含兩個(gè)數(shù)組locations和hits,保存一艘戰(zhàn)艦的位置和被擊中部位
- 若有多個(gè)ship對(duì)象,將它們保存在同一個(gè)數(shù)組ships中(數(shù)組元素為對(duì)象)
思路:
- 大循環(huán):要生成幾艘戰(zhàn)艦,就環(huán)循幾次
- 大循環(huán)內(nèi)部:
每次在游戲板上隨機(jī)生成一艘戰(zhàn)艦(可能于已有戰(zhàn)艦重疊)
生成后判斷是否重疊:若重疊則重新隨機(jī)生成;若不重疊將新戰(zhàn)艦加入ships數(shù)組
注意,這里的[生成-判斷-若重疊,重新生成],此邏輯適合用do while循環(huán)
實(shí)現(xiàn)方案:
- generateShips:主方法,它創(chuàng)建model對(duì)象中的ships數(shù)組且保證無(wú)重疊(戰(zhàn)艦數(shù)由model對(duì)象的屬性numShips指定)
- generateAShipRandomly:這個(gè)方法隨機(jī)在游戲板上生成一個(gè)戰(zhàn)艦對(duì)象ship(包含兩個(gè)數(shù)組locations和hits)[生成戰(zhàn)艦可能與已有的重疊]
的位置可能與其他戰(zhàn)艦重疊,也可能不重疊。 - collision:傳入ship對(duì)象,并判斷它是否與已有的戰(zhàn)艦重疊
ps. 生成新戰(zhàn)艦時(shí)無(wú)需顧及hits的生成:檢測(cè)是否擊中,看的是hits數(shù)組中對(duì)應(yīng)元素是否為"hit"(因?yàn)楸粨糁袝r(shí)才設(shè)置hits數(shù)組中相應(yīng)索引的值為"hit"),一開(kāi)始默認(rèn)hits數(shù)組為空即可
generateShips: function() {var locations;for (var i = 0; i < this.numShips; i++) {do {locations = this.generateAShipRandomly();} while (this.collision(locations));this.ships[i].locations = locations;}console.log("Ships array: ");console.log(this.ships);},generateAShipRandomly: function() {var direction = Math.floor(Math.random() * 2);var row, col;if (direction === 1) { // horizontalrow = Math.floor(Math.random() * this.boardSize);col = Math.floor(Math.random() * (this.boardSize - this.shipLength + 1));} else { // verticalrow = Math.floor(Math.random() * (this.boardSize - this.shipLength + 1));col = Math.floor(Math.random() * this.boardSize);}var newShipLocations = [];for (var i = 0; i < this.shipLength; i++) {if (direction === 1) {newShipLocations.push(row + "" + (col + i));} else {newShipLocations.push((row + i) + "" + col);}}return newShipLocations;},collision: function(locations) {for (var i = 0; i < this.numShips; i++) {var ship = this.ships[i];for (var j = 0; j < locations.length; j++) {if (ship.locations.indexOf(locations[j]) >= 0) {return true;}}}return false;}};總結(jié)
以上是生活随笔為你收集整理的JavaScript学习笔记——编写战舰游戏 Part2:JavaScript部分的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: matlab 直流-直流变换器毕业论文,
- 下一篇: 刀魔王带你了解雕刻刀模的生产流程!