JS精彩實戰之<智能迷宮> css
———寶貴編程經驗分享會———html
hello你們好,這裏是Web雲課堂,以前的一年裏咱們經歷了Html和CSS的系統攻城,此時的大家已是作靜態(動靜結合)網頁的高手了,本堂課的主講師JimJin將帶領你們進入成爲Web安全專家的第4階段第三小節:JavaScript的實戰實驗。本節課我將演示如何運用Web編程語言JS來製做一個完整的系統的網頁應用程序,相比以前的理論和小實驗,本次的任務將是史無前例的巨大,由於咱們要作的是一個系統,一個軟件,一個面向客戶的Application,因此這也是對以前JS基礎學習的一個總結。本次實驗完成後JavaScript也就告一段落了,以後咱們將進入服務器腳本語言的學習。程序員
OK,以上都是廢話:)若是說html和css語言都不必學(由於有圖形軟件),但不一樣於H5的標記語言和CSS的渲染語言,程序語言(涉及到大量的數值邏輯計算)JavaScript是非學不可,筆記JS是全部程序語言中最方便同時最易學的語言(有之一),因爲它凌駕於browser之上的特性,它是最方便的小軟件製做器。OK,let’s進入主題。算法
誒,實在抱歉又說了一堆廢話。。但有緣由的由於今天。。咳咳,不說了,我此次的作的軟件是一款遊戲——迷宮。有所不一樣的是,這個迷宮是一個智能迷宮,何謂智能?在個人字典裏,智能迷宮是一個自由探索自由調整,功能齊全的系統迷宮,其中包括以下功能:編程
1.可以更改全部元素的顏色。canvas
2.可以變換任意形狀的尺寸。數組
3.可以自由調整地圖的大小。瀏覽器
4.可以隨機生成迷宮的地圖。安全
5.可以自由繪製路線的位置。服務器
6.可以從起點或者從終點走。
7.可以沿途記錄走過的路徑。
8.可以瞬間移動到以前的點。
9.可以有多種難度迷宮算法。
10.可以自定義背景音樂圖片。
11.可以支持計時和分數系統。
12.可以擁有快速衝刺的技能。
13.可以提供完善的幫助提示。
很好,先貼出成品H5文件,網盤地址爲:
https://pan.baidu.com/s/1hrAVM3U
同窗們先下載這個安全文件,用自家的瀏覽器打開,但由於本人只Safari和Firefox上測試過,並不能保證全部瀏覽器兼容,因此建議大家使用Firefox、Chrome、Safari、Opera之一的瀏覽器打開,最好不要IE。。下面進入遊戲的全流程講解。
如下是做者我花了兩週時間(擠壓下來)中的邏輯步驟,固然這是整合版的,由於這其中我經歷了無數的困難,專業程序員debug的步驟都是:發現問題、解決問題;而我:感受不對勁、發現問題、尋找問題、研究問題、就決問題失敗、換條思路、發現新問題、喝口茶、重思考問題。。。。好了,因此我總結出了一我的編寫軟件的核心十個步驟,不只僅是這個遊戲:
第一步:肯定底層原理
1)畫布canvas
做爲一個惟物主義共青團員,我首先須要肯定智能迷宮及其周邊環境的物質組成原理。咱們須要肯定迷宮的物理結構,換句話說,它是用什麼作的。這裏我一開始也思考了許多方案:好比能夠採用HTML5提供的畫布元素來繪製迷宮,經過畫布上的重複性的擦寫操做來完成遊戲;固然咱們也可不用畫布而是採用表格元素,在晶格陣中每格都是可走的位置;咱們還能夠引入大量的元素並用CSS進行表格佈局;最後咱們還能夠經過一張張的地圖圖片img來表現迷宮,不過這種辦法真是,即便最壞的狀況也不會用它。。
2)絕對定位
由於方便最終我選擇了迷宮部分使用畫布canvas,而行走對象借用一個上層元素絕對定位來完成行走。絕對定位是從html流中剔除出而凌駕於畫布之上的元素,於是二者間相互獨立,絕對定位也很方便,屬於「動態元素」。
3)浮動元素
智能迷宮還須要一個控制檯,這個我決定使用一個div,其中包括一些表單控件。下面是html控制檯部分的代碼:
鍵盤控制:
起點
終點
筆尖
設置顏色:
背景:
起點:
終點:
牆體:
路徑:
是否記錄路徑:
開始製圖
座標(x,y):
起點:
終點:
筆尖:
地圖設置:
牆體厚度:
方塊邊長:
水平數量:
垂直數量:
迷宮生成算法:
簡單模式
高級模式
其餘:
背景音樂
得分系統
第二步:選擇合適的框架
1)內核很重要
迷宮的框架!!至少這是個2d的遊戲(3d本人正在研究),又至少排除了曲線圖,所有采用「橫平豎直」的矩陣結構。而後我考慮了目標對象該如何移動:是漸變仍是突變?牆該怎麼畫:虛擬仍是實體?還有——對象目標是一個色塊仍是一個圖像?最後我決定採用實體牆和正方格,移動對象也是正方形,用背景色標註。考慮到不少瀏覽器不支持顏色選擇器,顏色採用input text,支持CSS的三種顏色書寫格式:經常使用名稱、16進制和RGB格式。其餘參數使用input number,剩下的控件都是button,哦對了,還有一個input radio,待會有用。
2)分層分類
正如《星港》中上帝(就是平行宇宙外的那個五維生物)所說,「大家人類永遠只知道分工而不會合做」。固然這不是反人類,而是揭露了咱們三維宇宙的一些不變定理:系統都是由不一樣部門分層分類地分工完成的任務。
站在最高層,總共4個H5元素,分別是canvas,div控制檯(float:right),起點和終點div(position:absolute)。canvas的最底層是背景色,上層由方塊square和牆wall組成:
起點div的z_index大於終點div的z-index以便到達終點時能夠覆蓋,但他們都是直接覆蓋在canvas之上的。
控制檯div很靈活,由於是浮動定位,能夠隨瀏覽器窗口隨意漂浮。控制檯主要由一些表單元素和說明文字組成:
如右上邊所示-->-->-->
第三步:創建虛擬媒介
1)虛擬座標
爲何呢?畫布的原始座標是以像素爲單位,可是爲了知足需求,能夠自由變換尺寸和大小,須要一個內外模式之間的中介,那就是虛擬座標。由於語言上說明有點難度,因此直接上圖:
第四步:定義一些關鍵的變量
1)數值字段
這裏小編總結了一句philosophy:算法工程師靠經驗來CODING,碼農靠知識來DEBUGING!!fuck重複的變量:小編在這上面也是吃了不少虧。。並非每一個可變的量都給他分配一段變量!!尤爲咱們這裏有一個input number元素須要實時顯示value值,咱們就能夠直接利用這個value字段來存儲惟一的內存空間。
var wall_size=3;//牆的寬度
var square_size=12;//方塊的邊長
var amount_x=50;//x的最大座標
var amount_y=50;//y的最大座標
2)存儲型變量
用來存放大量數據,比較考驗數據結構的理論。
var wall=new Array();//用來存放全部的牆
var shortest_path=new Array();//用來存放隨機路徑
var direction=new Array();//用來存放可走的方向
var already_path=new Array();//用來存放沿途走過的路徑
3)狀態變量
該字段也很重要,總共設置了兩個:一個state變量用來顯示此時迷宮是在play狀態仍是draw狀態;還有一個get變量,後面會介紹用途。
4)標籤對象
Very Important!索性把全部標籤元素全給document.getElementByIdx_x_x了,否則後面引用的時候累死寶寶。雖然我並無全設:
var wall_color=document.getElementByIdx_x_x("wall_color");
var path_color=document.getElementByIdx_x_x("path_color");
var origin=document.getElementByIdx_x_x("origin");
var destination=document.getElementByIdx_x_x("destination");
var pen_x=document.getElementByIdx_x_x("pen_x");
var pen_y=document.getElementByIdx_x_x("pen_y");
var ori_x=document.getElementByIdx_x_x("ori_x");
var ori_y=document.getElementByIdx_x_x("ori_y");
var des_x=document.getElementByIdx_x_x("des_x");
var des_y=document.getElementByIdx_x_x("des_y");
var canvas=document.getElementByIdx_x_x("canvas");
5)參數變量
這個指的是那些輔助變量,好比用於循環語句的i和j,以及用於JS方法的形式參數event和key等等。
第五步:初始化
1)獨立區域
這裏強烈建議window.onload中分配一塊區域專門用來作初始化,而不要像核顯同樣集成在Html中,相信我,這樣會便於後期管理!!這裏我主要初始化的內容有:數值字段的缺省值、存儲變量的內存空間、全部標籤元素相關的屬性。因本遊戲不是大型軟件,在獨立區域只安排了部分初始化,其他都直接在html元素屬性中完成:
for(var i=0;i<=100;i++){
wall[i]=new Array();
for(var j=0;j<=100;j++)
wall[i][j]=new Object();
}
for(var i=1;i<=100;i++){
already_path[i]=new Array();
}
clear_already();
draw_border_wall();
origin.style.top=canvas.offsetTop-(-wall_size)+"px";
origin.style.left=canvas.offsetLeft-(-wall_size)+"px";
第六步:思考數據結構
1)矩陣
這一步耗費了我好多精力,咱們的矩陣框架想要合理的存儲還不是那麼容易。思來想去,最終決定用二維數組來存放,由於虛擬座標的幫助,「方塊」的座標直接寫成數組的下標,數組元素值暫定,好比能夠0無1有。
2)2.5維數組
這個是用來存放牆的。由於牆的特殊性:橫縱座標中有且只有一個小數。若是將座標值乘以2存入二維數組中會浪費內存空間,若是隻在x方向上乘以2又難以管理整個數組。因此應該採用2.5維數組,這是我起的名字,其實就是個二維數組,只不過每一個元素都有子對象,造成了「第三維」,但這「第三維」只有2層,遂我美其名曰「2.5維」。具體實現方式就是:存放座標爲方塊,子對象是right和down,值是0或1 ,如圖所示:
3)順序表
之因此沒有采用樹形存儲結構的緣由是——忘了怎麼定義樹。。不過不要緊,順序表一樣能知足需求:核心算法中將講到他。順序表是一個一維數組,最難得的是他提供pop()和push()函數,這樣就能夠把它當作一個堆棧來使用。
第七步:編寫經常使用的函數
1)優選API
瀏覽器提供的系統函數很是有用,現成的拿來直接用。好比Math對象的庫函數、Array對象的庫函數等等。這裏就不列舉了。
2)輸入輸出型
輸入輸出型函數就是一個數學函數,在SmartMaze中是一些輔助道具。這裏面的direction數組用來存放製圖時哪些方向可走,但我找了半天卻沒找到刪除數組制定元素值的函數,無奈之下只能自定義:function del_array(array,data){}也許不久的未來,或許是你讀到這篇文章的時候,JS已經支持這些新方法了,期待吧!
3)無參函數
行爲函數(沒有參數的函數)纔是咱們用到的主流函數,它的任務就是完成一系列動做,對系統作出一些改變。定義這種函數須要注意,必定要考慮周全,好比狀態變量、存儲表是否須要作出相應的更改。這裏定義了好多好多:
function set_canvas_size();//重置畫布尺寸
function set_origin_position();//重置起點位置
function set_destination_position();//重置終點位置
function clear_canvas();//清空畫布
function draw_border_wall();//畫邊界牆
function clear_already();//清空already_path數組
function look_around();//環顧可走的方向
function random_draw_wall();//隨機畫牆
function forbid_wall();//設置不可隨機畫的牆
function run();//「弱智圖」算法
function advanced_run();//「專業圖」算法
4)事件監聽
事件觸發函數是自動執行的函數,系統會一直監聽環境,當觸發條件被打破,函數當即被執行。本遊戲的三種事件:
document.getElementByIdx_x_x(「canvas_color").onchange=function();
document.getElementByIdx_x_x(「origin_color").onchange=function();
document.getElementByIdx_x_x(「destination_color」).onchange=function();
document.getElementByIdx_x_x(「wall_size").onchange=function();
document.getElementByIdx_x_x(「square_size").onchange=function();
document.getElementByIdx_x_x("amount_x").onchange=function amo_x_cha(){
document.getElementByIdx_x_x("amount_y").onchange=function amo_y_cha(){
wall_color.onchange=function(){
path_color.onchange=function(){
ori_x.onchange=「ori_x_cha()」;
ori_y.onchange=「ori_y_cha()」;
des_x.onchange=「des_x_cha()」;
des_y.onchange=「des_y_cha()」;
canvas.onclick=function(event){}
document.body.onkeydown=function(key){}
第八步:肯定核心算法
1)強大的遞歸
玩過的同窗都看到,智能迷宮(SmartMaze)5.0版本以上都提供了兩種迷宮生成算法:「弱智模式」和「專業競速」(後來改爲了簡單模式和高級模式)。其實這兩種模式的核心算法是相似的,確切說,後者是在前者的基礎上改進而來的。既然要徹底隨機,那麼給定一個起點和終點,之間惟一的路線的隨機性必然也要囊括全部的可能性。具體思路以下:從起點或終點出發,每走一格的方向都是四個方向中隨機的但同時既不能撞到牆也不能走回頭路,就像這個樣子:
這裏要擊破三個困難點:第一個是牆壁碰撞檢測;第二個則是與「已走路」之間的碰撞檢測。第三個困難點是:檢測到碰撞後該朝哪走?要知道,碰撞拐彎以後極可能走入一個死衚衕,這該怎麼辦,如今核心問題就是:如何判斷環路?哈哈,方法就多了,可是我選擇的辦法是:不判斷!咱們用遞歸!發句感慨,遞歸真是算法中最寶貴的財富:當你想不出算法的時候就想它吧:)具體實現看下一段。可是記住,遞歸函數利於人類思考,代價是大量的機器計算。
2)隨機之美
如圖所示,每走一步以後,將這個新位置寫入最短路徑(shortest_path[])的最末端,而後環顧四周(確切是三週)有無牆(wall[]),臨走前再次留戀四周看看有無已走過的路(already_path[]),以後在能走的方向上隨機選一條,走以前順便把目前的位置存入already_path。若是無路可走,嘿嘿,將剛纔最末端的那個元素給剔除掉,而後返回到上一個位置。以上兩條劇情線結束以後便重複全部的步驟,以此循環,哦對了還要一直監聽是否到達終點。因此整個求shortest_path[]算法的邏輯邏輯能夠寫成這樣:
fucntion 算法(){
if(shortest_path數組空了){
起點終點被牆分隔();
return null;
}
if(到達終點)return null;
環顧四周();
if(無路可走){
shortest_path彈掉末尾元素();
算法();
}
不然{
隨機走一方向();
更新shortest_path();
更新already_path();
算法();
}
}
3)神奇的樹
如此一來咱們就獲得了起點和終點之間的一條徹底隨機的路線:shortest_path,而後只要在不阻擋最短路徑的基礎上隨機畫入其他的牆壁,一個「弱智版」迷宮就大功告成了!:)不過說真,弱智圖真的很傻逼,並且很不美觀。因此我靈光一閃,高級算法誕生!
高級模式下整個迷宮地圖實際上是一棵樹!!你們都知道,樹是沒有環路的,全部的葉子均可以做爲根。SmartMaze7.0開始,高級算法改變成以終點爲根發散,由於雖然樹根都是對稱的,可是若是從某一點生長出來的迷宮樹,會造成一個很明顯的「樹幹」,也就是主幹路。若是從起點出發,玩家會發現只有「一條路」可走,減小了遊戲的樂趣;相反若是從終點開始製圖,迷宮的挑戰性會大大加強。!!!
爲何是一棵樹呢?其實很簡單,「弱智」迷宮是先尋路,再畫牆,而「專業」算法是一邊尋路一邊畫牆。個人靈光這樣來的:既然「弱智」迷宮的already_path[]確定覆蓋了畫布上絕大多數面積,爲什麼不把它利用起來呢?由於already_path除去shortest_path的部分,剩下的都是死路,不就能夠畫出這些死路來增長迷宮的難度嗎?此外尋路時到達終點時不中止而選擇繼續尋路,直到already_path覆蓋整個地圖後shortest_path被彈空掉了才中止,這樣就畫滿了整個限定區域,順便設計一個get狀態變量來記錄整個過程當中是否遇到了終點。搞定!而後邏輯算法這樣的:
fucntion 算法(){
if(shortest_path數組空了){
return null;
}
環顧四周();
if(無路可走){
shortest_path彈掉末尾元素();
算法();
}
不然{
隨機走一方向();
更新兩邊牆壁();
更新shortest_path();
更新already_path();
if(到達終點)get=「遇到」;
算法();
}
}
第九步:反向考慮兼容性問題
1)Get新技能!
隨着遊戲的更新換代,須要不斷向其中融入新元素,新功能。目前玩家能夠體驗到的技能如瞬間移動的融入就要和alread_path[]有機結合才能正常運行。此外我還打算寫入一個新技能叫「衝刺」,顧名思義,就是能夠快速移動以節約時間。這裏咱們儘管大開腦洞吧,又好比「穿牆術」「漂移走位」「漸變殘影」等華麗的新技能供咱們開發。
2)分數系統
這又能夠是一個龐大的成就係統,須要考慮種種兼容性問題。同上,雖然我還沒寫,但不妨幻想一下,好比:完成的地圖數量、消耗時間、走過的步數、和最短路徑的誤差量。甚至能夠在地圖上設置一些「分數球」,沿途吃到可加分等等。
3)跨平臺
雖然Web獨立於底層操做系統,當面對缺乏合適的輸入設備的手機和pad時候,一個虛擬鍵盤仍是須要的,這將是一個新的固定元素(fixed),由於虛擬鍵盤須要依附於窗口。
第十步:優化
1)拒絕臃腫
程序最怕臃腫,不只會嚴重影響軟件性能,還給debugger(調試師)帶來無盡的煩惱。要麼按照這十個邏輯步驟一五一十地書寫程序,將bug虐殺於幼蟲之時,要麼作完程序必定得複查一遍:取消重複的語句,終止多餘的計算,刪改無用的對象。只有這樣才能讓你的程序閃閃發光,獲得用戶的欣賞。
2)一塊兒來化妝
大膽的化妝吧,這裏不用考慮兼容性問題,由於只要經過CSS來美化周邊,是徹底不影響H5和JS的。背景、邊框、內外邊距都在你的自由掌控之下。除此以外,你也能夠添加新的元素來裝飾你的做品,好比過場動畫,背景音樂,特效等等,再次爲遊戲錦上添花。
3)更多的幫助
編程是程序員和上帝之間的一場競賽,程序員努力寫出更簡單易懂,連傻子都會用的軟件,而上帝則努力創造出更多更傻的傻子:就目前爲止,上帝是贏的。———一個過客。。
固然了我確定不是說玩家們是傻子,可是爲了面向不一樣的羣體,讓其市場化而不得不提供此服務。
因而我在控制檯中不一樣位置加入了許多?,其中onclick=「」。爲了追求完美,我甚至準備加入中英文語言的選擇功能,盡請期待SmartMaze12.0+。
最後一步:展望
結束了,everything is done,開源免費自由完美的SmartMaze10.0終於告一段落了。可是咱們的學習遠沒結束,至少這個智能迷宮還有許多可提高的方面。好比:
1)與服務器的交互
畢竟這是個Web應用,來電服務器相應和用戶的回饋會大大加強遊戲的可玩性,想象就刺激:多人競賽,即時挑戰,自制地圖分享,限時追逐賽……
2)轉型成3D畫面
用JavaScript作3d並不難,好吧我不應發表評論的由於本身目前還不會作。不過SmartMaze3D版本早晚會出現,也許是12.0也許是20.0,或許你讀到這段文字的時候個人智能3d版迷宮已經面世了:)
3)曲線迷宮
相比3d迷宮,我的認爲這個最有挑戰度,由於3d迷宮也能夠作成90°,但這個曲線迷宮圖還須要新的算法,至少目前沒有頭緒。Who Care?目前的做品已經夠我裝一陣子逼了。
OK,全部內容到此結束,若有疑問請留言新浪微博@IT讓生活更美好
2017.3.25 19:09