捕魚達人的遊戲你們都很熟悉吧,接下來的兩三天,我會將整個遊戲的原生js寫法詳細的寫出來,整個遊戲應用了面向對象的寫法:建立構造函數,在構造函數上面添加對象的屬性,而後在構造函數的原型上添加方法,固然這個程序使用了canvas來繪製,每一步的我都已經分別寫出來,詳細的步驟我在寫代碼的過程當中都已經標註了出來。html
下面是捕魚達人的素材庫:canvas
1》加載資源數組
<style> *{ padding: 0; margin: 0; } body{ background:#000; text-align:center; overflow:hidden; } #c1{ background:url(img/game_bg_2_hd.jpg); margin:40px auto; } </style> <script> var JSON={}; function loadImage(arr,fnSucc){ //爲了測試是否加載完成,設置count,在加載的時候使count++,判斷count的值來判斷是否加載完成 var count=0; for(var i=0;i<arr.length;i++){ var oImg=new Image(); //加載全部魚的資源 (function(index){ oImg.onload=function(){ //把全部資源加載,而且存到JSON中,注意,循環中有事件,事件裏的i值不對,解決用封閉空間 JSON[arr[index]]=this; count++; //當count的值和須要加載的資源的數組相同的時候,資源加載完畢 if(count==arr.length){ fnSucc && fnSucc(); } }; })(i); oImg.src='img/'+arr[i]+'.png'; } } document.addEventListener('DOMContentLoaded',function(){ var oC=document.getElementById('c1'); var gd=oC.getContext('2d'); var oImg=new Image(); loadImage(['fish1','fish2','fish3','fish4','fish5'],function(){ gd.drawImage(JSON['fish3'],0,0); }) },false); </script> </head> <body> <canvas id="c1" width="800" height="600"></canvas> </body>
/** * Created by Jason on 2016/11/2. */ var JSON={}; function loadImage(arr,fnSucc){ //爲了測試是否加載完成,設置count,在加載的時候使count++,判斷count的值來判斷是否加載完成 var count=0; for(var i=0;i<arr.length;i++){ var oImg=new Image(); //加載全部魚的資源 (function(index){ oImg.onload=function(){ //把全部資源加載,而且存到JSON中,注意,循環中有事件,事件裏的i值不對,解決用封閉空間 JSON[arr[index]]=this; count++; //當count的值和須要加載的資源的數組相同的時候,資源加載完畢 if(count==arr.length){ fnSucc && fnSucc(); } }; })(i); oImg.src='img/'+arr[i]+'.png'; } }
resource.js文件函數
/** * Created by Jason on 2016/11/2. */ //把全部資源放到一個resource的數組中,方便加載資源的函數loadImage調用 var resource=['fish1','fish2','fish3','fish4','fish5'];
index.html工具
<style> *{ padding: 0; margin: 0; } body{ background:#000; text-align:center; overflow:hidden; } #c1{ background:url(img/game_bg_2_hd.jpg); margin:40px auto; } </style> <script src="js/resource.js"></script> <script src="js/com.js"></script> <script> document.addEventListener('DOMContentLoaded',function(){ var oC=document.getElementById('c1'); var gd=oC.getContext('2d'); var oImg=new Image(); loadImage(resource,function(){ gd.drawImage(JSON['fish3'],0,0); }) },false); </script> </head> <body> <canvas id="c1" width="800" height="600"></canvas> </body>
3》先畫一條魚(如下步驟js文件中未改變的都沒有提到,你們本身把代碼歸類存放,最後我會給出全部最終版的多有文件的所有代碼)測試
關於魚遊動的角度的計算我也畫了圖this
如下尺寸通過ps測量:
//魚
var FISH_SIZE=[
null,
{w: 55, h: 37, collR: 17},
{w: 78, h: 64, collR: 24},
{w: 72, h: 56, collR: 20},
{w: 77, h: 59, collR: 22},
{w: 107, h: 122, collR: 29}
];
--------------------------
炮臺尺寸:
寬度:765, 高度:70
距離畫布上方的距離是:532
--------------------------
炮的位置:
x=431;
y=570;
--------------------------
炮的尺寸:
//炮
var CANNON_SIZE=[
null,
{w: 74, h: 74},
{w: 74, h: 76},
{w: 74, h: 76},
{w: 74, h: 83},
{w: 74, h: 85},
{w: 74, h: 90},
{w: 74, h: 94}
];
-----------------------------
//炮彈具體尺寸
var BULLET_SIZE=[
null,
{x: 86, y: 0, w: 24, h: 26},
{x: 62, y: 0, w: 25, h: 29},
{x: 30, y: 0, w: 31, h: 35},
{x: 32, y: 35, w: 27, h: 31},
{x: 30, y: 82, w: 29, h: 33},
{x: 0, y: 82, w: 30, h: 34},
{x: 0, y: 0, w: 30, h: 44}
];url
<script src="js/resource.js"></script> <script src="js/com.js"></script> <script> function d2a(n){ return n*Math.PI/180; } //各類不一樣的魚的圖片的數據 var FISH_SIZE=[ null, {w: 55, h: 37, collR: 17}, {w: 78, h: 64, collR: 24}, {w: 72, h: 56, collR: 20}, {w: 77, h: 59, collR: 22}, {w: 107, h: 122, collR: 29} ]; //使用面向對象的思想建立對象的屬性和方法,屬性寫在構造函數中,方法放在原型上 //先畫一條魚,魚的屬性有魚的種類type 位置x,y 遊動時候尾巴運動cur 遊動的方向rotate 遊動的速度iSpeed 向前運動move 先建立魚的構造函數 function Fish(type){ this.type=type; this.x=0; this.y=0; this.cur=0; this.rotate=220; this.iSpeed=1; this.move(); } //在添加魚的方法 畫魚draw 魚的運動move Fish.prototype.draw=function(gd){ //魚的圖片的寬高,不一樣魚的不一樣寬高不相同,先把魚的寬高存入一個提早量好的數組FISH_SIZE中,再根據不一樣種類的魚來獲取不一樣的寬高 var w=FISH_SIZE[this.type].w; var h=FISH_SIZE[this.type].h; //開始畫魚 注意畫魚先要save,結束之後restore 魚的出場時能夠改變方向的,因此畫魚的時候注意提早準備好角度 gd.save(); gd.translate(this.x,this.y); gd.rotate(d2a(this.rotate)); //魚是有陰影的,當魚從左面出來的時候,陰影是在魚的右下面,當魚從左面出來的時候,陰影是在魚的左下面,在旋轉完角度後魚可能從左面出來也可能從右面出來,當魚從右面出來的時候,須要scale圖片,使圖片翻轉來修正陰影的位置 if(this.rotate>45 && this.rotate<270){ gd.scale(1,-1); } //利用drawImage這個函數來畫魚,JSON['fish'+this.type]能夠選擇不一樣種類的魚,注意魚的rotate是根據頭部的位置來改變的 gd.drawImage(JSON['fish'+this.type], //使用this.cur 來變換魚的圖片的位置 0,h*this.cur,w,h, -w/2,-h/2,w,h ); gd.restore(); }; //魚的move的方法 Fish.prototype.move=function(){ //魚的運動分爲向前遊動和尾巴的擺動兩個運動 //向前遊動 向前遊動是改變的魚的this.x 和 this.y 的值 不停的往前遊動須要配合定時器的實現 //注意事件中套定時器,定時器的this的指向不許確,解決辦法是 提早存儲一個正確的this _this=this; setInterval(function(){ //將this.x this.y分解到斜線座標上 _this.x+=Math.cos(d2a(_this.rotate))*_this.iSpeed; _this.y+=Math.sin(d2a(_this.rotate))*_this.iSpeed; },30); //魚尾巴的擺動 魚尾巴的擺動是在不停的更換魚的圖片來實現的 相同的是定時器配合this.cur的加減 setInterval(function(){ //圖片的位置是改變魚的精靈圖上的魚的起始位置x y來實現 這裏用這個cur的值來關聯這組座標 _this.cur++; //循環這組數字 if(_this.cur==4){ _this.cur=0; } },200); }; document.addEventListener('DOMContentLoaded',function(){ var oC=document.getElementById('c1'); var gd=oC.getContext('2d'); //調用面向對象方法中創造的魚,並在畫布上畫出魚 loadImage(resource,function(){ var f1=new Fish(5); //給出新建立出魚的出事位置 f1.x=300; f1.y=300; //在畫魚的時候須要先清除一次畫布 一樣畫以前須要先保存,結束之後再存儲 //使魚動起來須要不停的在畫布上擦除上衣畫的魚而且不停的建立新的魚,須要配合定時器來實現 setInterval(function(){ gd.clearRect(0,0,oC.width,oC.height); gd.save(); //畫魚的方法在面向對象中都已經建立,在這直接使用就能夠 f1.draw(gd); gd.restore(); },30); }) },false); </script> </head> <body> <canvas id="c1" width="800" height="600"></canvas> </body>
4》畫炮臺 尺寸見3中的位置prototype
document.addEventListener('DOMContentLoaded',function(){ var oC=document.getElementById('c1'); var gd=oC.getContext('2d'); //開始畫炮臺 畫炮臺須要先加載資源,而後再畫,這裏沒有使用面向對象的概念 loadImage(resource,function(){ gd.drawImage(JSON['bottom'], 0,0,765,70, 0,532,765,70 ) });
5》畫炮,尺寸詳見3中的位置,代碼思路同畫魚rest
//炮 var CANNON_SIZE=[ null, {w: 74, h: 74}, {w: 74, h: 76}, {w: 74, h: 76}, {w: 74, h: 83}, {w: 74, h: 85}, {w: 74, h: 90}, {w: 74, h: 94} ]; //構建炮的構造函數 炮彈的屬性有位置x y type rotate 添加的同時注意在resource.js文件中添加須要加載的資源 function Cannon(type){ this.type=type; this.x=0; this.y=0; this.rotate=0; } //構建炮的原型,炮的原型上面有draw的方法 Cannon.prototype.draw=function(gd){ //和魚的原型相同,先要設置炮臺的尺寸w h,一樣的再2中的尺寸表中 var w=CANNON_SIZE[this.type].w; var h=CANNON_SIZE[this.type].h; //開始畫炮臺,注意先save最後再restore,炮臺是能夠旋轉的 gd.save(); gd.translate(this.x,this.y); gd.rotate(d2a(this.rotate)); gd.drawImage(JSON['cannon'+this.type], 0,0,w,h, -w/2,-h/2,w,h ); gd.restore(); }; document.addEventListener('DOMContentLoaded',function(){ var oC=document.getElementById('c1'); var gd=oC.getContext('2d'); //開始畫炮臺 畫炮臺須要先加載資源,而後再畫,這裏沒有使用面向對象的概念 loadImage(resource,function(){ gd.drawImage(JSON['bottom'], 0,0,765,70, 0,532,765,70 ) //炮是在炮臺上的,能夠在畫炮臺的時候一塊兒畫出來 var c=new Cannon(4); //設置炮的初始位置,初始位置在資源文件中已經寫明 c.x=431; c.y=570; //調用炮的方法draw來畫炮 gd.save(); c.draw(gd); c.restore(); }); /* //調用面向對象方法中創造的魚,並在畫布上畫出魚 loadImage(resource,function(){ var f1=new Fish(1); //給出新建立出魚的出事位置 f1.x=300; f1.y=300; //在畫魚的時候須要先清除一次畫布 一樣畫以前須要先保存,結束之後再存儲 //使魚動起來須要不停的在畫布上擦除上衣畫的魚而且不停的建立新的魚,須要配合定時器來實現 setInterval(function(){ gd.clearRect(0,0,oC.width,oC.height); gd.save(); //畫魚的方法在面向對象中都已經建立,在這直接使用就能夠 f1.draw(gd); gd.restore(); },30); });*/ },false); </script> </head> <body> <canvas id="c1" width="800" height="600"></canvas>
6》建立點擊畫布炮轉向的功能 相同的再這一步完成以後,給炮單獨創建一個js文件存放到js文件夾下(cannon.js文件);並把a2d的函數放到com.js文件中
關於炮的旋轉角度的計算,請看個人圖,其中寫錯了一個,懶得改了你們應該能看懂
<script src="js/resource.js"></script> <script src="js/com.js"></script> <script src="js/fish.js"></script> <script> function a2d(n){ return n*180/Math.PI; } //炮 var CANNON_SIZE=[ null, {w: 74, h: 74}, {w: 74, h: 76}, {w: 74, h: 76}, {w: 74, h: 83}, {w: 74, h: 85}, {w: 74, h: 90}, {w: 74, h: 94} ]; //構建炮的構造函數 炮彈的屬性有位置x y type rotate 添加的同時注意在resource.js文件中添加須要加載的資源 function Cannon(type){ this.type=type; this.x=0; this.y=0; this.rotate=0; } //構建炮的原型,炮的原型上面有draw的方法 Cannon.prototype.draw=function(gd){ //和魚的原型相同,先要設置炮臺的尺寸w h,一樣的再2中的尺寸表中 var w=CANNON_SIZE[this.type].w; var h=CANNON_SIZE[this.type].h; //開始畫炮臺,注意先save最後再restore,炮臺是能夠旋轉的 gd.save(); gd.translate(this.x,this.y); gd.rotate(d2a(this.rotate)); gd.drawImage(JSON['cannon'+this.type], 0,0,w,h, -w/2,-h/2,w,h ); gd.restore(); }; document.addEventListener('DOMContentLoaded',function(){ var oC=document.getElementById('c1'); var gd=oC.getContext('2d'); //開始畫炮臺 畫炮臺須要先加載資源,而後再畫,這裏沒有使用面向對象的概念 loadImage(resource,function(){ var c=new Cannon(4); //設置炮的初始位置,初始位置在資源文件中已經寫明 c.x=431; c.y=570; setInterval(function(){ //炮是在炮臺上的,能夠在畫炮臺的時候一塊兒畫出來,畫以前爲了不重繪,須要先清除畫布 gd.save(); gd.clearRect(0,0,oC.width,oC.height); gd.drawImage(JSON['bottom'], 0,0,765,70, 0,532,765,70 ); //調用炮的方法draw來畫炮 和魚的轉動相同,當點擊畫布的時候,炮須要跟隨鼠標的指向來轉動,這裏在轉動的時候咱們改改變炮的轉動角度,而後從新不停的刪除,再畫炮 這個效果思路和畫魚相同,須要配合定時器來實現 c.draw(gd); gd.restore(); },30); //當點擊畫布的時候炮的角度對着鼠標點擊的位置,並進行重繪 oC.onclick=function(ev){ //這裏須要梳理鼠標點擊的位置和炮旋轉角度之間的關係(附圖說明--炮的旋轉角度.png) var x=ev.clientX-oC.offsetLeft- c.x; var y= c.y-(ev.clientY+oC.offsetTop); //計算角度,注意角度的公式tan是臨邊比對邊,和數學公式的有所不一樣 Math.atan2(y,x);而且這裏是弧度轉角度,須要在com.js中添加a2d的函數 var d=90-a2d(Math.atan2(y,x)); c.rotate=d; }; }); /* //調用面向對象方法中創造的魚,並在畫布上畫出魚 loadImage(resource,function(){ var f1=new Fish(1); //給出新建立出魚的出事位置 f1.x=300; f1.y=300; //在畫魚的時候須要先清除一次畫布 一樣畫以前須要先保存,結束之後再存儲 //使魚動起來須要不停的在畫布上擦除上衣畫的魚而且不停的建立新的魚,須要配合定時器來實現 setInterval(function(){ gd.clearRect(0,0,oC.width,oC.height); gd.save(); //畫魚的方法在面向對象中都已經建立,在這直接使用就能夠 f1.draw(gd); gd.restore(); },30); });*/ },false); </script> </head> <body> <canvas id="c1" width="800" height="600"></canvas> </body>
7》構造炮彈 思路和前面都是同樣同樣的 注意構造函數中須要考慮的屬性 和 構造函數的原型上面的方法
<script src="js/resource.js"></script> <script src="js/com.js"></script> <script src="js/fish.js"></script> <script src="js/cannon.js"></script> <script> //開始構造炮彈 //炮彈具體尺寸 var BULLET_SIZE=[ null, {x: 86, y: 0, w: 24, h: 26}, {x: 62, y: 0, w: 25, h: 29}, {x: 30, y: 0, w: 31, h: 35}, {x: 32, y: 35, w: 27, h: 31}, {x: 30, y: 82, w: 29, h: 33}, {x: 0, y: 82, w: 30, h: 34}, {x: 0, y: 0, w: 30, h: 44} ]; //炮彈的構造函數,一樣先在resource.js中加載炮彈的資源, 炮彈的屬性有 type 位置x y rotate iSpeed move function Bullet(type){ this.type=type; this.x=0; this.y=0; this.rotate=0; this.iSpeed=1; this.move(); } //暫時想到的炮彈原型上的方法有draw move ,先寫,後面出現其餘的再補充 Bullet.prototype.draw=function(gd){ //一樣的炮彈的尺寸數據表中已經量好而且給出 var w=BULLET_SIZE[this.type].w; var h=BULLET_SIZE[this.type].h; //這裏與前面不一樣的是須要定義不一樣尺寸炮彈的起始位置,數據表中已經給出,直接獲取 var x=BULLET_SIZE[this.type].x; var y=BULLET_SIZE[this.type].y; //開始畫炮彈, gd.save(); gd.translate(this.x,this.y); gd.rotate(this.rotate); gd.drawImage(JSON['bullet'], x,y,w,h, -w/2,-h/2,w,h ); gd.restore(); }; //添加炮彈move的方法,和fish運動的思路相同 Bullet.prototype.move=function(){ //開啓定時器不停的改變炮彈的位置而且重繪,一樣,注意事件中的定時器裏的this有問題,須要提早存正確的this的指向 var _this=this; setInterval(function(){ //和魚的move有些不一樣的是炮彈的y軸的方向不一樣炮彈都是是向上射出的 _this.x+=Math.sin(d2a(_this.rotate))*_this.iSpeed; _this.y-=Math.cos(d2a(_this.rotate))*_this.iSpeed; },30); }; document.addEventListener('DOMContentLoaded',function(){ var oC=document.getElementById('c1'); var gd=oC.getContext('2d'); //開始畫炮臺 畫炮臺須要先加載資源,而後再畫,這裏沒有使用面向對象的概念 loadImage(resource,function(){ //設置炮的初始位置,初始位置在資源文件中已經寫明 var c=new Cannon(4); c.x=431; c.y=570; //存放炮彈的數組 var arrBullet=[]; setInterval(function(){ //炮是在炮臺上的,能夠在畫炮臺的時候一塊兒畫出來,畫以前爲了不重繪,須要先清除畫布 /* gd.save();*/ gd.clearRect(0,0,oC.width,oC.height); gd.drawImage(JSON['bottom'], 0,0,765,70, 0,532,765,70 ); //調用炮的方法draw來畫炮 和魚的轉動相同,當點擊畫布的時候,炮須要跟隨鼠標的指向來轉動,這裏在轉動的時候咱們改改變炮的轉動角度,而後從新不停的刪除,再畫炮 這個效果思路和畫魚相同,須要配合定時器來實現 c.draw(gd); //將當次點擊所產生的炮彈畫出來 for(var i=0;i<arrBullet.length;i++){ arrBullet[i].draw(gd); } /*gd.restore();*/ },30); //當點擊畫布的時候炮的角度對着鼠標點擊的位置,並進行重繪 oC.onclick=function(ev){ //這裏須要梳理鼠標點擊的位置和炮旋轉角度之間的關係(附圖說明--炮的旋轉角度.png) var x=ev.clientX-oC.offsetLeft- c.x; var y= c.y-(ev.clientY-oC.offsetTop); //計算角度,注意角度的公式tan是臨邊比對邊,和數學公式的有所不一樣 Math.atan2(y,x);而且這裏是弧度轉角度,須要在com.js中添加a2d的函數 var d=90-a2d(Math.atan2(y,x)); c.rotate=d; //當點擊的時候生成炮彈,因此在點擊事件中添加炮彈 var bullet=new Bullet(c.type); //炮彈的位置和旋轉角度和炮的位置和旋轉角度相同, bullet.x= c.x; bullet.y= c.y; bullet.rotate = c.rotate; //注意炮彈不能畫在這裏,若是畫在這裏會被畫炮和炮臺時所清空,固然潘丹並非只畫一個,能夠用一個數組來存儲所畫出來的炮彈,而後在炮旋轉重繪的時候同時添加炮彈,爲了讓點擊事件和定時器都能用到這個數組,這個數組應該寫到事件和定時器的父級的變量空間中 /*bullet.draw(gd);*/ //講當次點擊畫布所建立的炮彈存入arrBullet中 arrBullet.push(bullet); }; }); /* //調用面向對象方法中創造的魚,並在畫布上畫出魚 loadImage(resource,function(){ var f1=new Fish(1); //給出新建立出魚的出事位置 f1.x=300; f1.y=300; //在畫魚的時候須要先清除一次畫布 一樣畫以前須要先保存,結束之後再存儲 //使魚動起來須要不停的在畫布上擦除上衣畫的魚而且不停的建立新的魚,須要配合定時器來實現 setInterval(function(){ gd.clearRect(0,0,oC.width,oC.height); gd.save(); //畫魚的方法在面向對象中都已經建立,在這直接使用就能夠 f1.draw(gd); gd.restore(); },30); });*/ },false); </script> </head> <body> <canvas id="c1" width="800" height="600"></canvas> </body>
其中炮彈中有個小bug,明天繼續寫
轉載請註明‘轉載於Jason齊齊的博客http://www.cnblogs.com/jasonwang2y60/’