该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-10-13
最后修改:2011-04-19
下载地址:http://jtankwar.googlecode.com/files/tank%20war%203.0.zip 诚邀各位志士组团(HTML5+ANDROID):38155026(欢迎入群) 此文精辟点在第七页“fch415”大哥的回复里面! 此版本经过精心重构,面向接口设计,模仿类式继承和多继承(掺元类),多处使用闭包优化实现,使用单体工厂模式降低代码间耦合,添加图片及地图的预加载等等;重构版用新算法替代了大量DOM相关的操作,大大提高了游戏性能,即使在ie6下玩,效果也差强人意。 源码说明: 1.tank.main.js 定义了基本的接口及其实现,如图: // Interfaces. var Tank = new Interface('Tank', ['explode','clear','die','fire','isShot','move','stopMoving','isBlocked','attachEvents','init']); var Bullet = new Interface('Bullet',['explode','clear','die','fire','init']); var Block = new Interface('Block', ['explode','clear','die','isShot','display']); // Abstract tank, impl base methods. var AbstractTank = function(opt) { // Constructor this.id = opt.id; this.isAlive = true; this.speed = opt.speed; this.top = opt.pos.y; this.left = opt.pos.x; this.movingToward = 'up'; this.init(); this.attachEvents(); }; AbstractTank.prototype = { // Public methods move: function(_d) { ... }, stopMoving: function() { var _d = this.movingToward, thisTank = $('div#' + this.id); clearInterval(this.moveTimr); thisTank.removeClass('moving' + _d); }, isBlocked: function() { ... }, die: function() { this.isAlive = false; this.explode('mapbomb', 11); }, fire: function() { TankWarFactory.createBullet(this); return this; }, isShot: function() { throw new Error('isShot function is undefined.'); }, clear: function() { throw new Error('clear function is undefined.'); }, attachEvents: function() { throw new Error('attachEvents function is undefined.'); }, init: function() { throw new Error('init function is undefined.'); } }; 2.tank.factory.js 实例化坦克、block、子弹等。 var TankWarFactory = { createPlayerTank: function() { var tank = new PlayerTank(); Interface.ensureImplements(tank, Tank); TankWar.barrier.players.push(tank); TankWarMng.setTankCount(tank.id, --tank.lives); TankWarMng.setScore(tank, 0); }, createEnemyTank: (function() { // Private static check type of enemies. function checkType(type) { var types = TankWar.enemies.types.clone(); if (!type) type = 'r'; if (TankWar.enemies[type].leftNum > 0) return type; types.remove(type); for (var i = 0, len = types.length; i < len; i++) { if (TankWar.enemies[types[0]].leftNum === 0) { types.remove(types[0]); } else { return types[0]; } } return false; } return function(type) { // return constructor var tank; type = checkType(type); if (!type) throw new Error('No enemies alive.'); switch(type) { case 'r': tank = new EnemyRTank(); break; case 'b': tank = new EnemyBTank(); break; case 'y': tank = new EnemyYTank(); break; case 'g': tank = new EnemyGTank(); break; } Interface.ensureImplements(tank, Tank); TankWar.barrier.enemies.push(tank); TankWarMng.setTankCount(tank.id, --TankWar.enemies[type].leftNum); } })(), createBullet: function(tank) { var bullet; if (tank instanceof PlayerTank) { bullet = new PlayerBullet(tank); } else { bullet = new EnemyBullet(tank); } Interface.ensureImplements(bullet, Bullet); }, createBlock: function(param) { var block; switch(param.type) { case 'e': block = new BrickBlock(param); TankWar.barrier.normalBlocks.push(block);break; case 'h': block = new StoneBlock(param); TankWar.barrier.normalBlocks.push(block);break; case 'k': block = new KingBlock(param); TankWar.barrier.normalBlocks.push(block);break; case 'w': block = new WaterBlock(param); TankWar.barrier.waterBlocks.push(block);break; case 'b': block = new BornBlock(param); TankWar.enemies.posBorn.push({x:block.left,y:block.top,avaliable:true});break; case 'l': block = new LawnBlock(param);break; } Interface.ensureImplements(block, Block); } }; 3.tankwar.js 将页面切换、游戏初始化、事件绑定等封装为TankWarMng对象的方法。 4.tank.progress.js 图片及地图预加载。 var PreLoad = (function() { // 私有静态方法 function obj2array(givenObj) { var urllist = [], patrn = /1-\d{1,2}\.(png|json)$/, level = 0, levelArr = []; if (TankWar.mySite) levelArr[level++] = TankWar.mySite; (function(obj) { // 解析对象,将结果填进urllist数组 for (var prop in obj) { if (prop === 'urls') { for (var i = 0, n = obj[prop].length; i < n; i++) { if (patrn.test(obj[prop][i])) { var tmp = obj[prop][i].split('.')[0].split('-'), suffix = patrn.exec(obj[prop][i])[1]; for (var j = tmp[0], m = tmp[1]; j <= m; j++) { urllist.push(levelArr.join('/') + '/' + j + '.' + suffix); } } else { urllist.push(levelArr.join('/') + '/' + obj[prop][i]); } } levelArr.splice(--level, 1); } else { levelArr[level++] = prop; arguments.callee(obj[prop]); } } })(givenObj); return urllist; }; // 构造器 return function(urlObj, callback) { this.callback = callback; if (!TankWar.mySite) { // 如果没有启动预加载,直接进入回调 this.progressBar(100); return; } this.urlList = obj2array(urlObj); this.total = this.urlList.length; this.succeedcount = 0; this.errorcount = 0; this.init(); } })(); PreLoad.prototype = { loadImg: function(url) { var img = new Image(), that = this; img.onload = function() { that.complete(url, '图片'); } img.onerror = function() { that.error(url); } img.src = url; }, loadMap: function(url) { var that = this; $.getJSON(url, function(map) { TankWar.maps.push(map); that.complete(url, '地图'); }); }, complete: function(url, type) { this.progressBar(Math.round(++this.succeedcount*100/this.total), url, type); }, error: function(url) { throw new Error('load '+ url +' failed.'); }, progressBar: function(percent, url, type) { if (url && type) { $('#percent span').text(percent); $('#loading span').text(type + ': ' + url.substr(url.lastIndexOf('/') + 1, url.length)); } $('#bar').stop().animate({left: 550 - 550*percent/100}, 200); if (percent === 100) this.over(); }, over: function() { var that = this; setTimeout(function() { that.callback(); }, 500); }, init: function() { $('#percent, #loading').show(); for (var i = 0; i < this.total; i++) { if (/\.json$/.test(this.urlList[i])) this.loadMap(this.urlList[i]); else this.loadImg(this.urlList[i]); } } }; 5.util.js 接口、接口检查、继承等的实现。 6.tank.namespace.js & tank.config.js 命名空间及常用参数。 var config = {}; config.my_site = ''; // 如果将此游戏放在您网站上,请配置网址如:config.my_site = 'http://www.mysite.com/tank',将会自动启用预加载技术,以获得更好的游戏体验 config.develop_model = 'product'; // develop|test|product 如果是product,将不进行接口检查,以提高游戏速度 config.enemy_number_of_level = [{r:5,b:3,y:2,g:1},{r:10,b:5,y:3,g:2},{r:15,b:5,y:5,g:5}]; // 每一关的敌方坦克数量,目前有三关 config.default_scene = 'lawn'; // 默认场景 // 游戏参数 config.player1_lives = 4; config.player1_speed = 2; config.player1_move_keys = { 37: 'left', 38: 'up', 39: 'right', 40: 'down'}; config.player1_fire_key = 32; config.player2_lives = 4; config.player2_speed = 2; config.player2_move_keys = { 65: 'left', 87: 'up', 68: 'right', 83: 'down'}; config.player2_fire_key = 71; config.enemy_red_speed = 1; config.enemy_blue_speed = 1.5; config.enemy_yellow_speed = 2; config.enemy_green_speed = 2.5; config.bullet_speed = 10; 7.1-3.json 地图 [{ "type": "b", // b为敌方坦克出生地,你可以添加/减少b的个数,调节敌方坦克数量 "y": 10, "x": 9 }, { "type": "b", "y": 9, "x": 332 }, { "type": "b", "y": 9, "x": 653 }, { "type": "h", // 子弹打不破的石头 "y": 172, "x": 10 }, { "type": "l", // 草地 "y": 212, "x": 43 }, { "type": "e", // 子弹两次可以打破的砖头 "y": 212, "x": 79 }] 更多精彩,请下载源码:http://jtankwar.googlecode.com/files/tank%20war%203.0.zip 游戏效果截图: 主页--游戏设置 游戏中.. 暂停/退出 任务结束 此文供大家交流,共同学习,可以随便转载! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-10-13
界面挺华丽,就是杀敌要用空格键 敲得蹦蹦响 不合适上班时候玩……
|
|
返回顶楼 | |
发表时间:2010-10-14
libmw 写道 界面挺华丽,就是杀敌要用空格键 敲得蹦蹦响 不合适上班时候玩……
你自己改下,或者用2p嘛。 改键的话,在tank.start.js的pKeyEvent方法,284行 |
|
返回顶楼 | |
发表时间:2010-10-14
我想问一下,像js游戏里面的这种移动类的效果,都是通过擦除、重画来完成的么
|
|
返回顶楼 | |
发表时间:2010-10-14
嘿嘿~! 可以用用户设置按键嘛~! 挺不错的~!支持
|
|
返回顶楼 | |
发表时间:2010-10-14
越来越喜欢前端技术了,顶下
|
|
返回顶楼 | |
发表时间:2010-10-14
最后修改:2010-10-14
哈哈,有意思。
很难啊,好像子弹不能够抵消。 |
|
返回顶楼 | |
发表时间:2010-10-14
love_ai87 写道 我想问一下,像js游戏里面的这种移动类的效果,都是通过擦除、重画来完成的么
改变left和top都可以了 |
|
返回顶楼 | |
发表时间:2010-10-14
__游乐场 写道 love_ai87 写道 我想问一下,像js游戏里面的这种移动类的效果,都是通过擦除、重画来完成的么
改变left和top都可以了 嗯,楼上正解,有些效果可以用jQuery自定义动画来实现。 tank war地图是json文件,和代码分离的,大家可以自己扩展此游戏。 |
|
返回顶楼 | |
发表时间:2010-10-14
佩服,佩服啊。
|
|
返回顶楼 | |