JavaScript学习笔记——编写战舰游戏 Part2:JavaScript部分
编程思路
- 核心:跟随游戏的步骤逻辑,思考其中所需的功能,并尽量将各部分功能分离,可以使编程思路更清晰、代码易读性更强,也方便调试
- 先写大框架,有需要的功能直接调用(虽然未编写),交给后面的代码实现细节功能
- 初始化:随机生成战舰的位置(这一功能最后再实现,先使用硬编码指定战舰位置(即:指定固定的位置),优先编写和调试后面的核心功能)
- 获取玩家输入:用controller对象实现。将输入的"A3"转换为"03",判定是否击中交给后面的对象model处理;且通过model中击沉的战舰数量判断游戏是否结束;
- 处理击中和未击中:用model对象实现,接收到"03"这样的参数后,判断是否击中、是否重复击中同一位置、战舰是被击沉;击中/未击中的图像显示交给后面的对象view实现;
- 图形化的游戏显示:用view对象实现,传入击中/未击中的单元格坐标,在对应位置显示战舰或MISS图像。
摘要:知识要点
每个战舰为ship对象,包含两个数组
locations
和hits
,保存一艘战舰的位置和被击中部位若有多个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对象,包含两个数组
locations
和hitis
,保存一艘战舰的位置和被击中部位 - 若有多个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对象,包含两个数组
locations
和hits
,保存一艘战舰的位置和被击中部位- 若有多个ship对象,将它们保存在同一个数组ships中(数组元素为对象)
思路:
- 大循环:要生成几艘战舰,就环循几次
- 大循环内部:
每次在游戏板上随机生成一艘战舰(可能于已有战舰重叠)
生成后判断是否重叠:若重叠则重新随机生成;若不重叠将新战舰加入ships
数组
注意,这里的[生成-判断-若重叠,重新生成],此逻辑适合用
do while
循环
实现方案:
generateShips
:主方法,它创建model
对象中的ships数组且保证无重叠(战舰数由model对象的属性numShips
指定)generateAShipRandomly
:这个方法随机在游戏板上生成一个战舰对象ship
(包含两个数组locations
和hits
)[生成战舰可能与已有的重叠]
的位置可能与其他战舰重叠,也可能不重叠。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部分相关推荐
- JavaScript学习笔记:迷宫游戏
文章目录 一.游戏运行效果 二.实现步骤 1.在HBuilder里创建项目MazeGame,添加maze.html 2.在脚本里创建迷宫数组用于设置单元格顶边与右边 3.在body里绘制迷宫 4.设置 ...
- web前端学习(四)JavaScript学习笔记部分(7)-- JavaScript DOM对象控制HTML元素详解...
1.方法 getElementsByName() 获取name 可以获取一个数组类型数据(参数加引号) getElementsByTagName() 获取元素 getAttribute() 获取元 ...
- 千锋JavaScript学习笔记
千锋JavaScript学习笔记 文章目录 千锋JavaScript学习笔记 写在前面 1. JS基础 1.1 变量 1.2 数据类型 1.3 数据类型转换 1.4 运算符 1.5 条件 1.6 循环 ...
- JavaScript学习笔记(五)---cookie、Proxy、服务器、PHP语言、http协议、同步异步、事件轮循机制、ajax编写、接口
JavaScript学习笔记(五)---cookie.Proxy.服务器.PHP语言.http协议.同步异步.事件轮循机制.ajax编写.接口 1.cookie 1.1cookie概念 1.2cook ...
- JavaScript 学习笔记(1)
1. 何为 Jscript JScript 是一种解释型的.基于对象的脚本语言. 局限性: 1) 不能使用该语言来编写独立运行的应用程序 2) 没有对读写文件的内置 ...
- JavaScript学习笔记之DOM篇,带你全面了解什么是DOM
DOM在前面的JavaScript学习笔记(一)–JS基础里简单提到过,它是浏览器厂商提供的用来控制html / css 的代码的文档对象模型,是JavaScript的重要组成部分,现在带大家详细了解 ...
- JavaScript学习笔记(一)-Learning Advanced JavaScript
JavaScript学习笔记 (一)- Learning Advanced JavaScript Learning Advanced JavaScript #2: Goal: To be able t ...
- JavaScript学习笔记——函数 Part4:向函数传递函数、从函数返回函数(函数是一等公民)
要点 函数是值,这个值就是函数引用 函数是一等公民:函数引用是一等值 可将函数引用赋给变量.含在数据结构(如对象)中.传递给其他函数或从其他函数返回 函数是一等公民 不要再认为函数是特殊的,有别于Ja ...
- JavaScript学习笔记(第二部分)总共四部分
JavaScript学习笔记(第二部分)总共四部分 4 对象(Object) 字符串String.数值Number.布尔值Boolean.空值Null.未定义Undefined是基本的数据类型,这些数 ...
最新文章
- dataframe两个表合并_Part25:Pandas基础(Series,DataFrame类的创建、索引、切片、算术方法)...
- JBoss Portal CAS 的配置
- UbuntuServer16.04LTS中安装Mysql并配置远程访问
- lua IDE all
- Redis的常用命令及数据类型
- 操作表格_Excel表格基础操作-新手入门级
- 半个小时用计算机怎么算,CPA机考计算器操作指南,掌握这些快捷键,考试“延长”半小时!...
- 学习笔记——指针那些事儿
- spring-第十八篇之spring AOP基于XML配置文件的管理方式
- Remoting事件序列一:客户端触发服务器端事件
- k2运营商服务器无响应,【求救】K2提示“等待PPP客户端连接”拨号失败
- git官网下载比较慢的解决方法
- PAT(甲级)2020年春季考试 7-2 The Judger (25分)
- 用计算机算标准曲线,标准曲线计算软件
- 洛谷-P1007-魔法少女
- 2019中国国际大数据产业博览会将5月26-29日贵阳举行
- 用mac原生的日历和automator,实现定时发微信
- 批量打印--不展现直接后台打印
- 现货白银分析离不开SLV持仓分析
- 2012年度十大优秀免费云空间推荐