任务目的
- 练习JavaScript面向对象设计
- 实践一些基础的设计模式
任务描述
- 创建一个虚拟宇宙,包括一个行星和飞船
- 每个飞船由以下部分组成
- 动力系统,可以完成飞行和停止飞行两个行为,暂定所有飞船的动力系统飞行速度是一致的,比如每秒20px,飞行过程中会按照一定速率消耗能源(比如每秒减5%)
- 能源系统,提供能源,并且在宇宙中通过太阳能充电(比如每秒增加2%,具体速率自定)
信号接收处理系统,用于接收行星上的信号 - 自爆系统,用于自我销毁
- 每个飞船的能源是有限的,用一个属性来表示能源剩余量,这是一个百分比,表示还剩余多少能源。
- 能源耗尽时,飞船会自动停止飞行
- 飞船有两个状态:飞行中和停止,飞船的行为会改变这个属性状态
- 飞船的自我销毁方法会立即销毁飞船自身
- 行星上有一个指挥官(不需要在页面上表现出其形象),指挥官可以通过行星上的信号发射器发布如下命令
- 创建一个新的飞船进入轨道,最多可以创建4个飞船,刚被创建的飞船会停留在某一个轨道上静止不动
- 命令某个飞船开始飞行,飞行后飞船会围绕行星做环绕运动,需要模拟出这个动画效果
- 命令某个飞船停止飞行
- 命令某个飞船销毁,销毁后飞船消失、飞船标示可以用于下次新创建的飞船
- 你需要设计类似如下指令格式的数据格式
{id: 1,commond: 'stop'} - 指挥官通过信号发射器发出的命令是通过一种叫做Mediator的介质进行广播
- Mediator是单向传播的,只能从行星发射到宇宙中,在发射过程中,有30%的信息传送失败(丢包)概率,你需要模拟这个丢包率,另外每次信息正常传送的时间需要1秒
- 指挥官并不知道自己的指令是不是真的传给了飞船,飞船的状态他是不知道的,他只能通过自己之前的操作来假设飞船当前的状态
- 每个飞船通过信号接收器,接受到通过Mediator传达过来的指挥官的广播信号,但因为是广播信号,所以每个飞船能接受到指挥官发出给所有飞船的所有指令,因此需要通过读取信息判断这个指令是不是发给自己的
整体思路
首先是HTML框架,需要一个虚拟宇宙,一个指挥操控板,一个消息框。
<div id="main" class="clear_fix">
<div id="universe">
<div class="path path_four">
<div class="craft craft_four"></div>
<div class="path path_three">
<div class="craft craft_three"></div>
<div class="path path_two">
<div class="craft craft_two"></div>
<div class="path path_one">
<div class="craft craft_one"></div>
<div class="planet">用爱发电</div>
</div>
</div>
</div>
</div>
</div>
<div id="messager">
<h1>消息窗</h1>
</div>
</div>
<div id="control_panel">
<ul>
<li class="li_hidden" id="0">
<label>轨道一: </label>
<button type="button" value="start" class="btn_start">开始</button>
<button type="button" value="stop" class="btn_stop">暂停</button>
<button type="button" value="destroy" class="btn_destroy">销毁</button>
</li>
<li class="li_hidden" id="1">
<label>轨道二: </label>
<button type="button" value="start" class="btn_start">开始</button>
<button type="button" value="stop" class="btn_stop">暂停</button>
<button type="button" value="destroy" class="btn_destroy">销毁</button>
</li>
<li class="li_hidden" id="2">
<label>轨道三: </label>
<button type="button" value="start" class="btn_start">开始</button>
<button type="button" value="stop" class="btn_stop">暂停</button>
<button type="button" value="destroy" class="btn_destroy">销毁</button>
</li>
<li class="li_hidden" id="3">
<label>轨道四: </label>
<button type="button" value="start" class="btn_start">开始</button>
<button type="button" value="stop" class="btn_stop">暂停</button>
<button type="button" value="destroy" class="btn_destroy">销毁</button>
</li>
</ul>
<label id="panel_title">飞船操控板</label>
<button id="craft_creat">增加新飞船</button>
</div>
然后需要创建一个飞船类,有飞船id,飞船能量,飞船状态,飞船能量控制四个属性、还有飞船启动,停止,销毁三个方法和飞船能量消耗,能量增加两个方法。
//飞船类
function Craft(craftId,onPath){
//飞船id
this.id = craftId;
//飞船是否部署到预定轨道
this.onPath = onPath;
//飞船能量
this.power = 100;
//飞船是否正在飞行
this.state = "stop";
//控制飞船动力系统
this.timer = null;
}
Craft.prototype = {
constructor: Craft,
receiveCommond : function(commond){
if(commond.id == this.id){
order = commond.commond;
switch(order){
case 'start':
msgControl("有嘎达~ 信息传递成功!");
this.powerConsume();
this.state = "start";
this.start();
break;
case 'stop':
msgControl("有嘎达~ 信息传递成功!");
this.powerAdd();
this.state = "stop";
this.stop();
break;
case 'destroy':
msgControl("有嘎达~ 信息传递成功!");
this.destroy();
break;
}
}
},
start : function(){
msgControl("Nozomi power注入! 嗨~ 噗咻!" + (this.id+1) + "号飞船已经起飞");
var craftImg = document.querySelector("." + craftsMap[this.id]);
craftImg.classList.add("craft_start");
},
stop : function(){
msgControl("sa si su se so!" + (this.id+1) + "号飞船停止飞行");
var craftImg = document.querySelector("." + craftsMap[this.id]);
craftImg.classList.remove("craft_start");
},
destroy : function(){
msgControl("dame! dame!" + (this.id+1) + "号飞船被销毁了");
//先停止再销毁
// this.stop();
var craftImg = document.querySelector("." + craftsMap[this.id]);
craftImg.classList.remove("craft_start");
clearInterval(this.timer);
var id = this.id;
//销毁飞船
var craftImg = document.querySelector("." + craftsMap[id]);
craftImg.classList.remove("craft_create");
//销毁控制板
document.getElementById(id).classList.add("li_hidden");
//将飞船从轨道撤下
for(var i=0,len=crafts.length;i<len;i++){
if(crafts[i].id == id){
crafts[i].onPath = "off";
crafts[i].power = 100;
crafts[i].state = "stop";
}
}
},
powerConsume : function(){
var _this = this;
if(this.timer){
clearInterval(this.timer);
}
this.timer = setInterval(run,100);
function run(){
if(_this.power>0){
_this.power = _this.power - 1;
var craftImg = document.querySelector("." + craftsMap[_this.id]);
craftImg.innerHTML = _this.power + "%";
}else{
_this.stop();
_this.state = "charge";
clearInterval(_this.timer);
_this.powerAdd();
}
}
},
powerAdd : function(){
var _this = this;
if(this.timer){
clearInterval(this.timer);
}
this.timer = setInterval(run,300);
function run(){
if(_this.power<100){
_this.power = _this.power + 1;
var craftImg = document.querySelector("." + craftsMap[_this.id]);
craftImg.innerHTML = _this.power + "%";
}else{
if(_this.state == "charge"){
_this.start();
_this.state = "start";
clearInterval(_this.timer);
_this.powerConsume();
}else{
clearInterval(_this.timer);
}
}
}
}
}
然后需要一个指挥官类,可以创建飞船和指挥飞船
//指挥官类
function Commonder(){
this.craftId = 0;
}
Commonder.prototype = {
createCommond : function(id,order){
return {
id: id,
commond: order
};
},
createCraft : function(){
for(var i=0,len=crafts.length;i<len;i++){
if(crafts[i].onPath == "off"){
var id = crafts[i].id;
crafts[i].onPath = "on";
//使飞船送达预定轨道
var craftImg = document.querySelector("." + craftsMap[id]);
craftImg.classList.add("craft_create");
craftImg.innerHTML = crafts[i].power + "%";
//创建panel
var panel = document.getElementById(id);
panel.classList.remove("li_hidden");
var btns = panel.getElementsByTagName('button');
for(var i=0,len=btns.length;i<len;i++){
addHandler(btns[i],"click",function(e){
btnHandler(e);
});
}
msgControl("飞船" + (id+1) + "号已到达预定轨道,请发送下个命令撒~");
break;
}
}
}
}
最后是一个消息发射器,负责信号的发送。这里每次会给所有飞船发送信号
//信号发射器类
function Mediator(){
}
Mediator.prototype = {
sendCommond : function(commond){
var num = Math.floor(Math.random()*10+1);
if(num > 3){
for(var i=0,len=crafts.length;i<len;i++){
if(crafts[i].onPath == "on"){
crafts[i].receiveCommond(commond);
}
}
}else{
msgControl("信息传递失败!再试一次,fight だよ!");
}
},
}
对了,还有一个信号显示器,负责消息的显示
//信号显示器
function msgControl(str){
var msg = $("messager");
msg.innerHTML += "<p>" + str + "</p>";
msg.scrollTop = msg.scrollHeight;
console.log(str);
}
飞船的飞行效果是利用animation实现的,当飞船能量消耗完毕后,会自动暂停恢复能量。如果期间收到起飞命令,会起飞,否则会在能量到达100%时自动启动。