前言
javascript與程序的語言比如C#或者java不一樣,他並沒有“類”的概念,雖然最新的ECMAScript提出了Class的概念,我們卻沒有怎么用
就單以C#與Java來說,要到真正理解面向對象的程度也是要花一點功夫的,特別是初學的同學往往意識不到面向對象的好處,因為我們編碼的流程是這樣的
① 面向過程
這個時候,我們要思想一個東西,往往就用一個大代碼段完成了
② 方法重用
我們有時候再也受不了重復的代碼在一個地方存在了,於是這個時候我們會將相同的邏輯抽象為一個方法
③ 當代碼達到一定量的時候,我們發現另一個模塊似乎實現了相似的功能,當前這種情況越來越多的發生時,我們會將之變成一個“類”
④ 類的出現又帶來了繼承等特性,這個就是我們所謂面向對象了
面向過程VS面向對象
面向過程的代碼效率高,代碼清晰,甚至本身不會發生耦合的現象,但這個只是適用於代碼量較小或者說復雜度低的系統
系統稍微變大,面向過程的代碼將變得難以維護並且難以擴展
面向對象的代碼自然效率要稍低,至少代碼復雜度要上升,對初學者來說不太好理解,加上模塊划分后方法東一個西一個
如果沒有好的設計,出來的代碼會互相影響,系統層次混亂,但是好的面向對象的設計會讓系統代碼變得優雅並且有所依據
比如沒有面向對象的類,什么觀察者、工廠模式是玩不轉的
所以面向過程與面向對象的設計沒有明顯的好壞之分,要看使用場景,系統比較復雜的話就不要去搞面向過程了,因為多人維護一個文件比上面所以問題都要復雜
此文以一個小型的坦克大戰游戲來介紹“面向對象”的在前端javascript中的一些使用場景,希望對各位理解面向對象編程有所幫助
由於本人水平有限,文中想法有誤,請您提出
游戲簡介
游戲源碼
https://github.com/yexiaochai/blade/tree/master(請看其中的tank文件夾)
http://yexiaochai.github.io/blade/tank/index.html(demo地址)
俗話說得好,沒圖你說個JB,我這里當然是有圖的!
游戲說明
做這個游戲的目的其實主要是驗證Blade框架ui.abstract.view的設計是否合理,因為我准備將他用到實際工作中了,於是這里便花了周末兩天做這個游戲
PS:游戲中的圖片全部是“偷”的,到現在連偷的誰的都不知道了,代碼完全自己寫的,這里沒有抄襲
這里說是坦克大戰,其實不然,因為小時候紅白機的坦克大戰實現起來還是要復雜的多,要實現那種程度的話兩天時間怕是妄想了
於是這里實現的便是簡版了,說是簡版,其實他的原型是我們小時候玩的手柄游戲機中的坦克大戰,不知各位還記不記得
功能玩法
游戲玩法便是與NPC坦克不停的廝殺,廝殺過程中英雄坦克會不停的升級,升級后整體性能會提升,但是隨着級數增加NPC坦克的數量會不停增加
所以一般20多級我就掛了,掛了后也未做什么處理,主要原因是這兩天寫得太累了!!!
游戲擴展
事實上這個游戲是可以擴展的,雖然我這里未做擴展
首先子彈可做擴展,比如英雄的子彈可以變成激光或者散彈
其次NPC是可擴展的,擴展時候NPC可以設定為跟着英雄跑
以上都未實現:),這里這樣說是因為游戲本身是以面向對象的方式實現,所以就算我要實現以上功能可以十分方便
代碼缺陷
最初的想法很好,寫面向對象的程序,但是真正代碼過程中仍然有一些不夠“面向對象”的寫法,如果后面有時間對他進行重構,這是主要要做的事情
另外,代碼寫了后只經過了簡單測試,有BUG就不要見外了,可以留言嘛
代碼實現
其實游戲的實現,首先要有一個全局的控制器,我這里全局的控制器為app

this.app = { //英雄升級參數 levelParameter: [ { speed: 1, bulletSpeed: 4, maxBulletSize: 1, init: 0 }, { speed: 1, bulletSpeed: 4, maxBulletSize: 1, init: 0 }, { speed: 2, bulletSpeed: 6, maxBulletSize: 1, init: 0 }, { speed: 2, bulletSpeed: 6, maxBulletSize: 2, init: 32 }, { speed: 2, bulletSpeed: 6, maxBulletSize: 2, init: 32 }, { speed: 2, bulletSpeed: 6, maxBulletSize: 3, init: 64 }, { speed: 2, bulletSpeed: 6, maxBulletSize: 4, init: 64 }, { speed: 2, bulletSpeed: 6, maxBulletSize: 4, init: 96 }, { speed: 2, bulletSpeed: 7, maxBulletSize: 5, init: 96 } ], npcObj: { NO1: { dirObj: { init: 32 * 4 } }, NO2: { dirObj: { init: 32 * (4 + 2) }, datamodel: { speed: 2 } }, NO3: { dirObj: { init: 32 * (4 + 4) }, datamodel: { bulletSpeed: 6 } }, NO4: { dirObj: { init: 32 * (4 + 6) }, HP: 4 } }, maxNpcSize: 2, npcSize: 0, gameStatus: false, GAMEOBJ: { hero: [], //暫時只有一個hero,這里先不予關注 npc: [], heroBullet: [], npcBullet: [], boom: [] }, tick: $.proxy(function () { if (this.me.status == 'destroy') { this.app.gameStatus = false; } $.each(this.app.GAMEOBJ, $.proxy(function (k, v) { //首先做篩選 this.app.GAMEOBJ[k] = _.filter(this.app.GAMEOBJ[k], function (item) { return item.status !== 'destroy'; }); for (var i = 0, len = this.app.GAMEOBJ[k].length; i < len; i++) { this.app.GAMEOBJ[k][i].move(); } }, this)); this.createNPC(); this.dataChange(); this.levelUp(); if (this.app.gameStatus) { rAF($.proxy(this.app.tick, this)); } }, this), //創建NPC坦克 createNPC: $.proxy(function (opts) { opts = $.extend({ gameRule: 'npc', wrapper: this.$el.find('#map'), app: this.app }, opts, true); var flag = parseInt(Math.random() * 4); if (parseInt(Math.random() * 5) == 4) { opts.speciality = 'hp'; } opts = $.extend(opts, this.app.npcObj['NO' + (flag + 1)], true); console.log(opts) var npc = new NPCTank(opts); var i, len, bullet; npc.show(); /*這里英雄每一步移動會對NPC產生影響,同樣NPC會對影響造成影響 npc只需要關注英雄和英雄發出的子彈即可,英雄處理要復雜的對多 */ this.me.registerObserver(npc); npc.registerObserver(this.me); //缺陷,npc暫時不關注npc,可互相穿透 // $.each(this.app.GAMEOBJ.npc, function (i, item) { // npc.registerObserver(item); // }); //記錄最后一個npc以便測試 this.npc = npc; this.app.GAMEOBJ.npc.push(npc); }, this), //創建我方英雄坦克 createHero: $.proxy(function () { this.me = new Tank({ datamodel: { x: 192, y: 192 }, gameRule: 'hero', wrapper: this.$el.find('#map'), app: this.app }); this.me.show(); window.me = this.me; this.app.GAMEOBJ.hero.push(this.me); }, this), createBullet: $.proxy(function (opts) { //子彈的創建要區分是hero還是npc opts = $.extend({ wrapper: this.$el.find('#map'), app: this.app }, opts, true); var gameRule = opts.gameRule; var bullet = new Bullet(opts); bullet.show(); //這里根據子彈角色不同,會有不同的觀察對象,npc子彈對應英雄,英雄子彈對象npc! //英雄子彈需要被npc坦克以及子彈觀察 if (gameRule == 'heroBullet') { $.each(this.app.GAMEOBJ.npc, function (i, item) { bullet.registerObserver(item); }); $.each(this.app.GAMEOBJ.npcBullet, function (i, item) { bullet.registerObserver(item); }); } else if (gameRule == 'npcBullet') { //npc子彈來了,英雄就要小心了 $.each(this.app.GAMEOBJ.hero, function (i, item) { bullet.registerObserver(item); }); $.each(this.app.GAMEOBJ.heroBullet, function (i, item) { bullet.registerObserver(item); }); } this.app.GAMEOBJ[gameRule].push(bullet); return bullet; }, this), createBoom: $.proxy(function (opts) { opts = $.extend({ wrapper: this.$el.find('#map'), app: this.app }, opts, true); var boom = new Boom(opts); boom.show(); this.app.GAMEOBJ.boom.push(boom); return boom; }, this) };
這個全局控制器扮演着“游戲時鍾”的角色
1 tick: $.proxy(function () { 2 if (this.me.status == 'destroy') { 3 this.app.gameStatus = false; 4 } 5 6 $.each(this.app.GAMEOBJ, $.proxy(function (k, v) { 7 //首先做篩選 8 this.app.GAMEOBJ[k] = _.filter(this.app.GAMEOBJ[k], function (item) { 9 return item.status !== 'destroy'; 10 }); 11 for (var i = 0, len = this.app.GAMEOBJ[k].length; i < len; i++) { 12 this.app.GAMEOBJ[k][i].move(); 13 } 14 }, this)); 15 16 this.createNPC(); 17 this.dataChange(); 18 this.levelUp(); 19 20 if (this.app.gameStatus) { 21 rAF($.proxy(this.app.tick, this)); 22 } 23 24 }, this),
他每過一段時間便會通知游戲對象運動一下,再根據彼此的運動引發游戲事件,這個是一個典型的發布訂閱模型
游戲時鍾變化,然后通知到其它所有對象運動,並且需要做對象銷毀工作
而游戲對象完全繼承自一個對象“moveOBJ”運動對象,繼承關系為:
結語
本來是想多說幾句的,但是最近兩天編碼有點累,各位自己去看源碼吧,我這里扛不住了。。。。。。