51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 1709|回复: 1

jQuery版坦克游戏,缺陷面向对象重构版!

[复制链接]

该用户从未签到

发表于 2018-5-18 15:34:05 | 显示全部楼层 |阅读模式
此版本经过精心重构,面向接口设计,模仿类式继承和多继承(掺元类),多处使用闭包优化实现,使用单
体工厂模式降低代码间耦合,添加图片及地图的预加载等等;重构版用新算法替代了大量DOM相关的操作,
大大提高了游戏性能,即使在ie6下玩,效果也差强人意。

源码说明:
1.tank.main.js 定义了基本的接口及其实现,如图:

Javascript代码  
// 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、子弹等。
Javascript代码  
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 图片及地图预加载。
Javascript代码  
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])) {   
                            var tmp = obj[prop].split('.')[0].split('-'), suffix = patrn.exec(obj[prop])[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]);   
                        }   
                    }   
                    levelArr.splice(--level, 1);   
                } else {   
                    levelArr[level++] = prop;   
                    arguments.callee(obj[prop]);   
                }   
            }   
        })(givenObj);   
        return urllist;   
    };   
回复

使用道具 举报

该用户从未签到

 楼主| 发表于 2018-5-18 15:34:35 | 显示全部楼层
  1.    // 构造器   
  2.     return function(urlObj, callback) {   
  3.         this.callback = callback;   
  4.         if (!TankWar.mySite) { // 如果没有启动预加载,直接进入回调   
  5.             this.progressBar(100);   
  6.             return;   
  7.         }   
  8.         this.urlList = obj2array(urlObj);   
  9.         this.total = this.urlList.length;   
  10.         this.succeedcount = 0;   
  11.         this.errorcount = 0;   
  12.         this.init();   
  13.     }   
  14. })();   

  15. PreLoad.prototype = {   
  16.     loadImg: function(url) {   
  17.         var img = new Image(), that = this;   
  18.         img.onload = function() {   
  19.             that.complete(url, '图片');   
  20.         }   
  21.         img.onerror = function() {   
  22.             that.error(url);   
  23.         }   
  24.         img.src = url;   
  25.     },   
  26.     loadMap: function(url) {   
  27.         var that = this;   
  28.         $.getJSON(url, function(map) {   
  29.             TankWar.maps.push(map);   
  30.             that.complete(url, '地图');   
  31.         });   
  32.     },   
  33.     complete: function(url, type) {   
  34.         this.progressBar(Math.round(++this.succeedcount*100/this.total), url, type);   
  35.     },   
  36.     error: function(url) {   
  37.         throw new Error('load '+ url +' failed.');   
  38.     },   
  39.     progressBar: function(percent, url, type) {   
  40.         if (url && type) {   
  41.             $('#percent span').text(percent);   
  42.             $('#loading span').text(type + ': ' + url.substr(url.lastIndexOf('/') + 1, url.length));   
  43.         }   
  44.         $('#bar').stop().animate({left: 550 - 550*percent/100}, 200);   
  45.         if (percent === 100) this.over();   
  46.     },   
  47.     over: function() {   
  48.         var that = this;   
  49.         setTimeout(function() {   
  50.             that.callback();   
  51.         }, 500);   
  52.     },   
  53.     init: function() {   
  54.         $('#percent, #loading').show();   
  55.         for (var i = 0; i < this.total; i++) {   
  56.             if (/\.json$/.test(this.urlList))   
  57.                 this.loadMap(this.urlList);   
  58.             else  
  59.                 this.loadImg(this.urlList);   
  60.         }   
  61.     }   
  62. };  

  63. 5.util.js 接口、接口检查、继承等的实现。
  64. 6.tank.namespace.js & tank.config.js 命名空间及常用参数。
  65. Javascript代码  
  66. var config = {};   
  67. config.my_site = ''; // 如果将此游戏放在您网站上,请配置网址如:config.my_site = 'http://www.mysite.com/tank',将会自动启用预加载技术,以获得更好的游戏体验   

  68. config.develop_model = 'product'; // develop|test|product 如果是product,将不进行接口检查,以提高游戏速度   

  69. 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}]; // 每一关的敌方坦克数量,目前有三关   

  70. config.default_scene = 'lawn'; // 默认场景   

  71. // 游戏参数   
  72. config.player1_lives = 4;   
  73. config.player1_speed = 2;   
  74. config.player1_move_keys = { 37: 'left', 38: 'up', 39: 'right', 40: 'down'};   
  75. config.player1_fire_key = 32;   

  76. config.player2_lives = 4;   
  77. config.player2_speed = 2;   
  78. config.player2_move_keys = { 65: 'left', 87: 'up', 68: 'right', 83: 'down'};   
  79. config.player2_fire_key = 71;   

  80. config.enemy_red_speed = 1;   
  81. config.enemy_blue_speed = 1.5;   
  82. config.enemy_yellow_speed = 2;   
  83. config.enemy_green_speed = 2.5;   
  84. config.bullet_speed = 10;  

  85. 7.1-3.json 地图
  86. Javascript代码  
  87. [{   
  88.     "type": "b", // b为敌方坦克出生地,你可以添加/减少b的个数,调节敌方坦克数量   
  89.     "y": 10,   
  90.     "x": 9   
  91. }, {   
  92.     "type": "b",   
  93.     "y": 9,   
  94.     "x": 332   
  95. }, {   
  96.     "type": "b",   
  97.     "y": 9,   
  98.     "x": 653   
  99. },   

  100. {   
  101.     "type": "h", // 子弹打不破的石头   
  102.     "y": 172,   
  103.     "x": 10   
  104. },   

  105. {   
  106.     "type": "l", // 草地   
  107.     "y": 212,   
  108.     "x": 43   
  109. }, {   
  110.     "type": "e", // 子弹两次可以打破的砖头   
  111.     "y": 212,   
  112.     "x": 79   
  113. }]  
复制代码


回复 支持 反对

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-3-29 16:18 , Processed in 0.070433 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表