编程思路

  • 核心:跟随游戏的步骤逻辑,思考其中所需的功能,并尽量将各部分功能分离,可以使编程思路更清晰、代码易读性更强,也方便调试
  • 先写大框架,有需要的功能直接调用(虽然未编写),交给后面的代码实现细节功能
  1. 初始化:随机生成战舰的位置(这一功能最后再实现,先使用硬编码指定战舰位置(即:指定固定的位置),优先编写和调试后面的核心功能
  2. 获取玩家输入:用controller对象实现。将输入的"A3"转换为"03",判定是否击中交给后面的对象model处理;且通过model中击沉的战舰数量判断游戏是否结束;
  3. 处理击中和未击中:用model对象实现,接收到"03"这样的参数后,判断是否击中、是否重复击中同一位置、战舰是被击沉;击中/未击中的图像显示交给后面的对象view实现;
  4. 图形化的游戏显示:用view对象实现,传入击中/未击中的单元格坐标,在对应位置显示战舰或MISS图像。

摘要:知识要点

  • 每个战舰为ship对象,包含两个数组locationshits,保存一艘战舰的位置和被击中部位

  • 若有多个ship对象,将它们保存在同一个数组ships中(数组元素为对象

  • 玩家提交输入后,通过遍历每艘战舰的位置,判定是否击中

  • 根据是否击中,使用对应网格的<td>元素的setAttribute()将其设置为之前在CSS中规定好的类(类的background已被设置为战舰或MISS图像),网页将实时地根据其class(类)的不同改变其背景图像

  • 消息显示区为<div>元素,innerHTML对应其文本

  • 输入框为<input>元素(type"text"),value对输入框的内容,placeholder可预设其文本,将value置为""可清空文本

  • 开火按钮为<input>元素(type"button"),value对应按钮上的文本

  • "button"<input>元素的onclick事件:检测到点击事件时触发

  • [优化:回车自动提交表单]"text"<input>元素的onkeypress事件:(在输入框中)检测到按键事件时触发

  • [优化:回车自动提交表单]事件处理程序:判断当前按下的按键是否为回车,如果是,自动调用fireButton.click()实现按钮点击操作

设计方案

将这款游戏设计为几个较为独立的对象(每个对象有其主要职责,从而便于独立创建和测试游戏的各个部分),并利用DOM与用户交互。这种设计让问题理解起来容易得多。

  • 这样做的优点在于:可以相对独立的编写代码,并分别测试每个对象,确保对象履行其职责(每个对象的测试代码可能需要单独编写,仅用于测试该对象的功能)

总共有3个要设计并实现的对象:model、view和controller

  • model将存储游戏的状态,如每艘战舰的位置以及哪个部位被击中;
  • view负责在model存储的状态改变时更新界面;
  • controller将各个部分整合以实现游戏逻辑:将用户输入交给model以更新状态、判断游戏是否结束

1.view对象

  • view对象用于显示当前的游戏状态
  • 需要displayMessage方法:在左上角显示hit/miss/you sank my battleship信息
  • 需要displayHit方法:在某指定网格内显示战舰图像
  • 需要displayMiss方法:在某指定网格内显示MISS图像

具体实现方案

displayMessage方法:传入要显示的信息msg,通过getElementById获取用于显示消息的<div>元素,用innerHTML设置其文本为msg

displayHit方法:传入指定网格<td>的id,通过getElementById获取相应的<td>元素,并将其class属性设置为hit类,即可将网格背景设置为战舰图像(利用了之前定义的hit类,其background属性为战舰图像)

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对象

model对象用于存储游戏的状态、判断是否击中以完成状态转换状态改变通知view

  • 需要一些属性:保存网格大小、总战舰数量、战舰长度、被击沉的战舰数
  • 每个战舰为ship对象,包含两个数组locationshitis,保存一艘战舰的位置和被击中部位
  • 若有多个ship对象,将它们保存在同一个数组ships中(数组元素为对象
  • 需要fire方法:向战舰开火时给出开火位置,判断是否击中、击沉,并更新状态
  • 需要isSunk方法:返回战舰是否被击沉
  • 注意model对象所保存的状态改变时(如击中),要通知view。因为view模型并不会主动的检查状态的变化
  • 需要generateShip方法:随机生成战舰

核心逻辑:如何检测击中

遍历每艘船,比较船的位置和开火的位置

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);//猜测的位置上有战舰if (ship.hits[index] === "hit") {//Already hitreturn true;} else if (index >= 0) {//HITif (this.isSunk(ship)) {//SUNK}return true;}}//MISSreturn false;},//...
};

注意,查看是否命中战舰时,代码为
var ship = this.ships[i];
var locations = ship.locations;
var index = locations.indexOf(guess);
这里使用串接(Chaining)将对象引用连接,避免创建临时变量,也减少了代码量

串接后的代码为
var ship = this.ships[i];
var index = ship.locations.indexOf(guess);
此处ship没有串接,以免串接链条过长(阅读理解困难)

3.controller对象

controller对象用于整合各部分:处理用户输入,记录猜测次数、让model根据当前猜测更新自己、判断游戏是否结束

  • 需要guesses属性:记录猜测次数
  • 需要processGuess方法:传入用户猜测,对其处理(如"A3"转换为"03"),将结果交给model,检测游戏是否结束
var controller = {guesses: 0,processGuess: function(guess) {var location = parseGuess(guess);//"A3"转换为"03"的辅助函数if (location) {//有效输入this.guesses++;var hit = model.fire(location);if (hit && model.shipsSunk === model.numShips) {view.displayMessage("You sank all my battleships, in " + this.guesses + " guesses");}}}
}

怎样获取玩家输入:事件处理程序

事件处理程序能将HTML中的表单<form>元素与游戏关联

  • 点击Fire!按钮时(单击事件),调用事件处理程序
  • 事件处理程序(回调函数)事先编写好,用于获取输入框的内容并交给controller

单击事件与事件处理程序

window.onload = init;
function init() {//网页加载完毕后,再开始接收输入并开始游戏// place the ships on the game boardmodel.generateShipLocations();// Fire! button onclick handlervar fireButton = document.getElementById("fireButton");fireButton.onclick = handleFireButton;
}

事件处理程序用于获取输入框的内容并交给controller

function handleFireButton() {var guessInput = document.getElementById("guessInput");var guess = guessInput.value.toUpperCase();//获取输入框的内容controller.processGuess(guess);//输入框的内容交给controllerguessInput.value = "";//重置输入框,玩家无需手动删除上一个输入
}

优化:希望按下回车也能触发事件

  • HTML中<input>元素的onkeypress事件:(在输入框中)检测到按键事件时,就触发对应的事件处理程序,可将所需的按键处理程序赋给onkeypress

  • 事件处理程序:判断当前按下的按键是否为回车,如果是,自动调用fireButton.click()实现按钮点击操作

这里使用了guessInput元素的onkeypress事件

  • guessInput.onkeypress = handleKeyPress;
  • 浏览器向事件处理程序handleKeyPress传递一个事件对象,其中包含有关用户按下了哪个键的信息
 function init() {//网页加载完毕后,再开始接收输入并开始游戏//...var guessInput = document.getElementById("guessInput");guessInput.onkeypress = handleKeyPress;//...
}function handleKeyPress(e) {//包含有关用户按下了哪个键的信息var fireButton = document.getElementById("fireButton");// in IE9 and earlier, the event object doesn't get passed// to the event handler correctly, so we use window.event instead.e = e || window.event;if (e.keyCode === 13) {fireButton.click();return false;//返回false,让表单不做其他的提交等操作}
}

4.随机生成战舰

  • 每个战舰为ship对象,包含两个数组locationshits,保存一艘战舰的位置和被击中部位
  • 若有多个ship对象,将它们保存在同一个数组ships中(数组元素为对象

思路:

  • 大循环:要生成几艘战舰,就环循几次
  • 大循环内部:
    每次在游戏板上随机生成一艘战舰(可能于已有战舰重叠)
    生成后判断是否重叠:若重叠则重新随机生成;若不重叠将新战舰加入ships数组

注意,这里的[生成-判断-若重叠,重新生成],此逻辑适合用do while循环

实现方案:

  • generateShips:主方法,它创建model对象中的ships数组且保证无重叠(战舰数由model对象的属性numShips指定)
  • generateAShipRandomly:这个方法随机在游戏板上生成一个战舰对象ship(包含两个数组locationshits)[生成战舰可能与已有的重叠]
    的位置可能与其他战舰重叠,也可能不重叠。
  • collision:传入ship对象,并判断它是否与已有的战舰重叠

ps. 生成新战舰时无需顾及hits的生成:检测是否击中,看的是hits数组中对应元素是否为"hit"(因为被击中时才设置hits数组中相应索引的值为"hit"),一开始默认hits数组为空即可

 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;}};

JavaScript学习笔记——编写战舰游戏 Part2:JavaScript部分相关推荐

  1. JavaScript学习笔记:迷宫游戏

    文章目录 一.游戏运行效果 二.实现步骤 1.在HBuilder里创建项目MazeGame,添加maze.html 2.在脚本里创建迷宫数组用于设置单元格顶边与右边 3.在body里绘制迷宫 4.设置 ...

  2. web前端学习(四)JavaScript学习笔记部分(7)-- JavaScript DOM对象控制HTML元素详解...

    1.方法 getElementsByName() 获取name 可以获取一个数组类型数据(参数加引号) getElementsByTagName() 获取元素   getAttribute() 获取元 ...

  3. 千锋JavaScript学习笔记

    千锋JavaScript学习笔记 文章目录 千锋JavaScript学习笔记 写在前面 1. JS基础 1.1 变量 1.2 数据类型 1.3 数据类型转换 1.4 运算符 1.5 条件 1.6 循环 ...

  4. JavaScript学习笔记(五)---cookie、Proxy、服务器、PHP语言、http协议、同步异步、事件轮循机制、ajax编写、接口

    JavaScript学习笔记(五)---cookie.Proxy.服务器.PHP语言.http协议.同步异步.事件轮循机制.ajax编写.接口 1.cookie 1.1cookie概念 1.2cook ...

  5. JavaScript 学习笔记(1)

    1.     何为 Jscript JScript 是一种解释型的.基于对象的脚本语言. 局限性: 1)        不能使用该语言来编写独立运行的应用程序 2)        没有对读写文件的内置 ...

  6. JavaScript学习笔记之DOM篇,带你全面了解什么是DOM

    DOM在前面的JavaScript学习笔记(一)–JS基础里简单提到过,它是浏览器厂商提供的用来控制html / css 的代码的文档对象模型,是JavaScript的重要组成部分,现在带大家详细了解 ...

  7. JavaScript学习笔记(一)-Learning Advanced JavaScript

    JavaScript学习笔记 (一)- Learning Advanced JavaScript Learning Advanced JavaScript #2: Goal: To be able t ...

  8. JavaScript学习笔记——函数 Part4:向函数传递函数、从函数返回函数(函数是一等公民)

    要点 函数是值,这个值就是函数引用 函数是一等公民:函数引用是一等值 可将函数引用赋给变量.含在数据结构(如对象)中.传递给其他函数或从其他函数返回 函数是一等公民 不要再认为函数是特殊的,有别于Ja ...

  9. JavaScript学习笔记(第二部分)总共四部分

    JavaScript学习笔记(第二部分)总共四部分 4 对象(Object) 字符串String.数值Number.布尔值Boolean.空值Null.未定义Undefined是基本的数据类型,这些数 ...

最新文章

  1. dataframe两个表合并_Part25:Pandas基础(Series,DataFrame类的创建、索引、切片、算术方法)...
  2. JBoss Portal CAS 的配置
  3. UbuntuServer16.04LTS中安装Mysql并配置远程访问
  4. lua IDE all
  5. Redis的常用命令及数据类型
  6. 操作表格_Excel表格基础操作-新手入门级
  7. 半个小时用计算机怎么算,CPA机考计算器操作指南,掌握这些快捷键,考试“延长”半小时!...
  8. 学习笔记——指针那些事儿
  9. spring-第十八篇之spring AOP基于XML配置文件的管理方式
  10. Remoting事件序列一:客户端触发服务器端事件
  11. k2运营商服务器无响应,【求救】K2提示“等待PPP客户端连接”拨号失败
  12. git官网下载比较慢的解决方法
  13. PAT(甲级)2020年春季考试 7-2 The Judger (25分)
  14. 用计算机算标准曲线,标准曲线计算软件
  15. 洛谷-P1007-魔法少女
  16. 2019中国国际大数据产业博览会将5月26-29日贵阳举行
  17. 用mac原生的日历和automator,实现定时发微信
  18. 批量打印--不展现直接后台打印
  19. 现货白银分析离不开SLV持仓分析
  20. 2012年度十大优秀免费云空间推荐

热门文章

  1. Bootstrap使用前必须在head 标签内导入bootstrap的链接,否则bootstrap无效
  2. 无人自动驾驶汽车的信息安全问题与汽车以太网安全防护
  3. 矿山计算机管理,矿山现代化管理
  4. 阿里云总裁胡晓明:保护客户数据隐私是第一原则
  5. 2022年深圳市疫情补贴政策及怎么申请,补贴5000万
  6. iPhone手机连接电脑无反应,是什么原因?
  7. java中的土味情话,java中的土味情话
  8. 如何高效的学习 Nginx 源码,汲取养分?
  9. OpenStack Octavia(Rocky)的实现与分析
  10. win10能上qq但是打不开网页?