はてなハイク STAR FIGHTERS

    @@ -6,56 +6,136 @@ * @require */ + + + (function(){ - var Z_INDEX_USER = 65536; - var Z_INDEX_BULLET = 65535; - - var inherit = function(parent, initializer){ + + if(! 'forEach' in Array.prototype){ + Array.prototype.forEach = function(cb){ + for(var i=0,l=this.length;i<l;i++){ + cb(this[i],i); + } + }; + } + + if(! 'map' in Array.prototype){ + Array.prototype.map = function(cb){ + var R = []; + for(var i=0,l=this.length;i<l;i++){ + R.push(cb(this[i],i)); + } + return R; + }; + } + + + var defclass = function(spec, initializer){ + var base = spec.base; var klass = function(){}; - klass.prototype = parent.prototype; + klass.prototype = base.prototype; var proto = new klass; if(!('initialize' in proto)) proto.initialize = function(){}; var R = function(){ + this.constructor = arguments.callee; this.initialize.apply(this,arguments); }; + R.prototype = proto; + R.name = spec.name; + R.base = base; + + R.getBaseClasses = function(){ + var R = [this]; + if('getBaseClasses' in base){ + R.push.apply(R, base.getBaseClasses()); + } else { + R.push(base); + } + return R; + }; + initializer.apply( proto, [ - parent.prototype, + base.prototype, R ] ); + return R; }; var Game = new ( - inherit( - Object, function(SUPER, Class){ + defclass( + { + base: Object, + name: 'Game' + }, + function(SUPER, Class){ this.initialize = function(){ - this.elems = {}; + this.allElems = {}; + this.elemDic = {}; }; this.registerElement = function(elem){ - this.elems[elem.key] = elem; + var self = this; + var elemDic = this.elemDic; + this.allElems[elem.key] = elem; + elem.constructor.getBaseClasses().forEach( + function(cl){ + if(!cl.name) return; + (elemDic[cl.name] || (elemDic[cl.name] = {}))[elem.key] = elem; + } + ); }; this.unregisterElement = function(elem){ - delete this.elems[elem.key]; + delete this.allElems[elem.key]; + var elemDic = this.elemDic; + elem.constructor.getBaseClasses().forEach( + function(cl){ + if(!(cl.name && elemDic[cl.name])) return; + delete elemDic[cl.name][elem.key]; + } + ); }; this.iter = function(){ - var elems = this.elems; - var f; - for(f in elems){ - elems[f].step(); + var allElems = this.allElems; + var elemDic = this.elemDic; + var i,l, ff; + var elem; + for(f in allElems){ + allElems[f].step(); } - // 衝突検知と、衝突後の処理を書く - for(f in elems){ - elems[f].redraw(); + var checked = {}; + for(f in allElems){ + elem = allElems[f]; + var tclasses = elem.getTargetClasses(); + l = tclasses.length; + if(l < 1) continue; + var chslot = (checked[f] || (checked[f] = {})); + for(i=0,l=tclasses.length;i<l;i++){ + var edslot = elemDic[tclasses[i]]; + for(ff in edslot){ + if(chslot[ff]) continue; + chslot[ff] = true; + var target = edslot[f]; + if(!elem.checkHit(target)) continue; + (checked[target.key] || + (checked[target.key] = {}))[f] = true; + elem.onHit(target); + target.onHit(elem); + } + } + } + + for(f in allElems){ + allElems[f].redraw(); } }; @@ -66,12 +146,16 @@ setTimeout(arguments.callee, 10); })(); }; - } - )); + }) // end of defclass + ); // end of new - var GameElement = inherit( - Object, function(SUPER, Class){ + var GameElement = defclass( + { + base: Object, + name: 'GameElement' + }, + function(SUPER, Class){ var counter = 0; this.initialize = function(x,y){ @@ -89,7 +173,6 @@ Game.registerElement(this); }; - this.initImage = function(url){ var img = document.createElement('img'); document.body.appendChild(img); @@ -111,7 +194,9 @@ }; }; - this.getSpeed = function(){ return this._speed; }; + this.getSpeed = function(){ + return this._speed; + }; this.setDirection = function(x, y){ this._dx = x; @@ -131,6 +216,7 @@ return { x:this._pdx, y:this._pdy }; }; + // ステップごとの自己状態の変更 this.step = function(){ this._pre_x = this._x; this._pre_y = this._y; @@ -138,11 +224,18 @@ this._y = this._y + this._dy * this._speed; }; + // + this.dispose = function(){ + var img = this._img; + img.parentNode.removeChild(img); + }; + + // フレームごとの計算結果を元に this.redraw = function(){ var img = this._img; if(this._dispose){ Game.unregisterElement(this); - img.parentNode.removeChild(img); + this.dispose(); return; } var sx = this._x; @@ -151,12 +244,24 @@ img.style.left = (sx - img.clientWidth / 2) + 'px'; }; + // 当たり判定の対象となるクラス群 + this.getTargetClasses = function(){ + return []; + }; + + // 自身のフレームごとの軌跡 this.getTrack = function(){ var R = 0; return R; }; - this.onHit = function(){ + // elem との当り判定を書く + this.checkHit = function(elem){ + return false; + }; + + // elem 衝突時の振舞い + this.onHit = function(elem){ }; this.onScreen = function(){ @@ -168,8 +273,12 @@ } ); - var Bullet = inherit( - GameElement, function(SUPER, Class){ + var Bullet = defclass( + { + base: GameElement, + name: 'Bullet' + }, + function(SUPER, Class){ this.initialize = function(x,y){ SUPER.initialize.apply(this,[x, y]); this.setDirection(0,-1); @@ -184,8 +293,11 @@ } ); - var User = inherit( - GameElement, function(SUPER,Class){ + var User = defclass( + { + base: GameElement, + name: 'User' + }, function(SUPER,Class){ this.initialize = function(name){ SUPER.initialize.apply(this,[0,0]); @@ -212,12 +324,6 @@ }; }; - this.getSpeed = function(){ - var dir = this.getDirection(); - if(dir.x == 0 && dir.y == 0) return 0; - return SUPER.getSpeed.apply(this); - }; - this.shot = function(){ var pos = this.getPosition(); var bullet = new Bullet(pos.x, pos.y); @@ -226,10 +332,28 @@ bullet.setSpeed(this.getSpeed() + 2); }; - this.setDirection = function(x,y){ - SUPER.setDirection.apply(this, [x,y]); - if(x != 0 || y != 0) this.setShotDirection(x, y); - }; + + this.setDirection = ( + function(){ + var timer = null; + return function(x,y){ + var self = this; + SUPER.setDirection.apply(this, [x,y]); + if(x != 0 || y != 0){ + if(timer){ + clearTimeout(timer); + timer = null; + } else { + timer = setTimeout( + function(){ + self.setShotDirection(x, y); + timer = null; + }, + 50); + } + } + }; + })(); this.move = function(dir){ var _move_k = this._move_k; @@ -311,11 +435,10 @@ case 40: //DOWN user.move('s'); break; - case 90: // Z - user.shot(); } }, true ); + window.addEventListener( 'keyup', function(e){ @@ -324,7 +447,7 @@ switch(e.keyCode){ case 37: //LEFT user.move('W'); - break; + break; case 38: //UP user.move('N'); break; @@ -334,6 +457,8 @@ case 40: //DOWN user.move('S'); break; + case 90: // Z + user.shot(); } }, true );
  • /*
     * @title haiku-shoot
     * @description はてなハイクで星を撃つ
     * @include http://h.hatena.ne.jp/*
     * @license MIT License
     * @require 
     */
    
    
    
    
    (function(){
    
    
       if(! 'forEach' in Array.prototype){
         Array.prototype.forEach = function(cb){
           for(var i=0,l=this.length;i<l;i++){
             cb(this[i],i);
           }
         };
       }
    
       if(! 'map' in Array.prototype){
         Array.prototype.map = function(cb){
           var R = [];
           for(var i=0,l=this.length;i<l;i++){
             R.push(cb(this[i],i));
           }
           return R;
         };
       }
    
    
       var defclass = function(spec, initializer){
         var base = spec.base;
         var klass = function(){};
         klass.prototype = base.prototype;
         var proto = new klass;
         if(!('initialize' in proto)) proto.initialize = function(){};
         var R     = function(){
           this.constructor = arguments.callee;
           this.initialize.apply(this,arguments);
         };
    
         R.prototype = proto;
         R.name      = spec.name;
         R.base      = base;
    
         R.getBaseClasses = function(){
           var R = [this];
           if('getBaseClasses' in base){
             R.push.apply(R, base.getBaseClasses());
           } else {
             R.push(base);
           }
           return R;
         };
    
         initializer.apply(
           proto,
           [
             base.prototype,
             R
           ]
         );
    
         return R;
       };
    
       var Game = new (
         defclass(
           {
             base: Object,
             name: 'Game'
           },
           function(SUPER, Class){
    
             this.initialize = function(){
               this.allElems = {};
               this.elemDic  = {};
             };
    
             this.registerElement = function(elem){
               var self = this;
               var elemDic = this.elemDic;
               this.allElems[elem.key] = elem;
               elem.constructor.getBaseClasses().forEach(
                 function(cl){
                   if(!cl.name) return;
                   (elemDic[cl.name] || (elemDic[cl.name] = {}))[elem.key] = elem;
                 }
               );
             };
    
             this.unregisterElement = function(elem){
               delete this.allElems[elem.key];
               var elemDic = this.elemDic;
               elem.constructor.getBaseClasses().forEach(
                 function(cl){
                   if(!(cl.name && elemDic[cl.name])) return;
                   delete elemDic[cl.name][elem.key];
                 }
               );
             };
    
             this.iter = function(){
               var allElems = this.allElems;
               var elemDic  = this.elemDic;
               var i,l, ff;
               var elem;
               for(f in allElems){
                 allElems[f].step();
               }
    
               var checked = {};
               for(f in allElems){
                 elem = allElems[f];
                 var tclasses = elem.getTargetClasses();
                 l = tclasses.length;
                 if(l < 1) continue;
                 var chslot = (checked[f] || (checked[f] = {}));
                 for(i=0,l=tclasses.length;i<l;i++){
                   var edslot = elemDic[tclasses[i]];
                   for(ff in edslot){
                     if(chslot[ff]) continue;
                     chslot[ff] = true;
                     var target = edslot[f];
                     if(!elem.checkHit(target)) continue;
                     (checked[target.key] ||
                      (checked[target.key] = {}))[f] = true;
                     elem.onHit(target);
                     target.onHit(elem);
                   }
                 }
               }
    
               for(f in allElems){
                 allElems[f].redraw();
               }
             };
    
             this.start = function(){
               var self = this;
               (function(){
                  self.iter();
                  setTimeout(arguments.callee, 10);
                })();
             };
           }) // end of defclass
       ); // end of new
    
    
        var GameElement = defclass(
          {
            base: Object,
            name: 'GameElement'
          },
          function(SUPER, Class){
            var counter = 0;
    
            this.initialize = function(x,y){
              this.key    = 'elem_'+ counter++;
              this._x     = x;
              this._y     = y;
              this._pre_x = NaN;
              this._pre_y = NaN;
              this._spped = 1;
              this._dx    = 0;
              this._dy    = 0;
              this._pdx   = 0;
              this._pdy   = -1;
              this._img   = null;
              Game.registerElement(this);
            };
    
            this.initImage = function(url){
              var img = document.createElement('img');
              document.body.appendChild(img);
              img.style.position = 'fixed';
              img.style.zIndex   = '65536';
              img.src = url;
              this._img = img;
              return img;
            };
    
            this.setSpeed = function(s){
              this._speed = s;
            };
    
            this.getPosition  = function(){
              return {
                x: this._x ,
                y: this._y
              };
            };
    
            this.getSpeed = function(){
              return this._speed;
            };
    
            this.setDirection = function(x, y){
              this._dx = x;
              this._dy = y;
            };
    
            this.getDirection = function(){
              return { x:this._dx, y:this._dy };
            };
    
            this.setShotDirection = function(x,y){
              this._pdx = x;
              this._pdy = y;
            };
    
            this.getShotDirection = function(){
              return { x:this._pdx, y:this._pdy };
            };
    
            // ステップごとの自己状態の変更
            this.step = function(){
              this._pre_x = this._x;
              this._pre_y = this._y;
              this._x = this._x + this._dx * this._speed;
              this._y = this._y + this._dy * this._speed;
            };
    
            // 
            this.dispose = function(){
              var img = this._img;
              img.parentNode.removeChild(img);
            };
    
            // フレームごとの計算結果を元に
            this.redraw = function(){
              var img = this._img;
              if(this._dispose){
                Game.unregisterElement(this);
                this.dispose();
                return;
              }
              var sx  = this._x;
              var sy  = this._y;
              img.style.top  = (sy - img.clientHeight / 2) + 'px';
              img.style.left = (sx - img.clientWidth / 2) + 'px';
            };
    
            // 当たり判定の対象となるクラス群
            this.getTargetClasses = function(){
              return [];
            };
    
            // 自身のフレームごとの軌跡
            this.getTrack = function(){
              var R = 0;
              return R;
            };
    
            // elem との当り判定を書く
            this.checkHit = function(elem){
              return false;
            };
    
            // elem 衝突時の振舞い
            this.onHit = function(elem){
            };
    
            this.onScreen = function(){
              var sx = this._x;
              var sy = this._y;
              return (sx > 0 && sx < window.innerWidth &&
                      sy > 0 && sy < window.innerHeight);
            };
          }
        );
    
        var Bullet = defclass(
          {
            base: GameElement,
            name: 'Bullet'
          },
          function(SUPER, Class){
            this.initialize = function(x,y){
              SUPER.initialize.apply(this,[x, y]);
              this.setDirection(0,-1);
              this.setSpeed(10);
              this.initImage('http://s.hatena.ne.jp/images/star.gif');
            };
    
            this.step = function(){
              SUPER.step.apply(this, []);
              if(!this.onScreen()) this._dispose = true;
            };
          }
        );
    
        var User = defclass(
          {
            base: GameElement,
            name: 'User'
          }, function(SUPER,Class){
    
            this.initialize = function(name){
              SUPER.initialize.apply(this,[0,0]);
              this.name    = name;
              var img = this.initImage(
                'http://www.st-hatena.com/users/' + name.substring(0,2) +
                  '/' + name + '/profile.gif'
              );
              this.setSpeed(5);
              this._move_k = {n:0,s:0,w:0,e:0};
              this._x      =
                Math.floor((window.innerWidth - img.clientWidth ) / 2
                          );
              this._y      =
                (window.innerHeight - img.clientHeight - 10);
              this.setShotDirection(0,-1);
            };
    
            this.getHotSpot = function(){
              var img = this._img;
              return {
                x: Math.floor(this._x + img.clientWidth  / 2),
                y: Math.floor(this._y + img.clientHeight / 2)
              };
            };
    
            this.shot = function(){
              var pos = this.getPosition();
              var bullet = new Bullet(pos.x, pos.y);
              var dir = this.getShotDirection();
              bullet.setDirection(dir.x, dir.y);
              bullet.setSpeed(this.getSpeed() + 2);
            };
    
    
            this.setDirection = (
              function(){
                var timer = null;
                return function(x,y){
                  var self = this;
                  SUPER.setDirection.apply(this, [x,y]);
                  if(x != 0 || y != 0){
                    if(timer){
                      clearTimeout(timer);
                      timer = null;
                    } else {
                      timer = setTimeout(
                        function(){
                          self.setShotDirection(x, y);
                          timer = null;
                        },
                        50);
                    }
                  }
                };
              })();
    
            this.move = function(dir){
              var _move_k = this._move_k;
              switch(dir){
              case 'n':
                _move_k.n = 1;
                if(_move_k.s > 0) _move_k.s = 2;
                break;
              case 'N':
                _move_k.n = 0;
                if(_move_k.s > 0) _move_k.s = 1;
                break;
              case 's':
                _move_k.s = 1;
                if(_move_k.n > 0) _move_k.n = 2;
                break;
              case 'S':
                _move_k.s = 0;
                if(_move_k.n > 0) _move_k.n = 1;
                break;
              case 'w':
                _move_k.w = 1;
                if(_move_k.e > 0) _move_k.e = 2;
                break;
              case 'W':
                _move_k.w = 0;
                if(_move_k.e > 0) _move_k.e = 1;
                break;
              case 'e':
                _move_k.e = 1;
                if(_move_k.w > 0) _move_k.w = 2;
                break;
              case 'E':
                _move_k.e = 0;
                if(_move_k.w > 0) _move_k.w = 1;
              }
              var x = 0;
              var y = 0;
              if(_move_k.s != _move_k.n){
                y = _move_k.s > _move_k.n ? 1 : -1;
              }
              if(_move_k.w != _move_k.e){
                x = _move_k.e > _move_k.w ? 1 : -1;
              }
              this.setDirection(x,y);
            };
            
            
          }
        );
       
       var name = document.evaluate(
         './/p[@class="username"]/a/text()',
            document.body,
         null,
         XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
         null).snapshotItem(0).data;
       
       var user = new User(name);
       
       document.addEventListener( 'keypress',
                                  function(e){ e.preventDefault();}, false);
       
       window.addEventListener(
         'keydown', function(e){
           e.stopPropagation();
           e.preventDefault();
           //       console.log(e.keyCode);
           switch(e.keyCode){
           case 37: //LEFT
             user.move('w');
             break;
           case 38: //UP
             user.move('n');
             break;
           case 39: //RIGHT
             user.move('e');
             break;
           case 40: //DOWN
             user.move('s');
             break;
           }
         }, true
       );
    
       
       window.addEventListener(
         'keyup', function(e){
           e.stopPropagation();
           e.preventDefault();
           switch(e.keyCode){
           case 37: //LEFT
             user.move('W');
             break;
           case 38: //UP
             user.move('N');
             break;
           case 39: //RIGHT
             user.move('E');
             break;
           case 40: //DOWN
             user.move('S');
             break;
           case 90: // Z
             user.shot();
           }
         }, true
       );
       
       Game.start();
       
     })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2010/12/30 15:12:55 - 2010-12-30
  2. 2010/12/18 15:13:34 - 2010-12-18
  3. 2010/12/18 15:11:50 - 2010-12-18
  4. 2010/12/18 05:40:03 - 2010-12-18
  5. 2010/12/18 03:58:19 - 2010-12-18
  6. 2010/12/18 03:47:07 - 2010-12-18
  7. 2010/12/18 02:09:19 - 2010-12-18
  8. 2010/12/18 00:16:28 - 2010-12-18
  9. 2010/12/16 23:55:19 - 2010-12-16
  10. 2010/12/16 22:56:54 - 2010-12-16
  11. 2010/12/15 21:59:33 - 2010-12-15
  12. 2010/12/15 04:50:59 - 2010-12-15
  13. 2010/12/14 23:45:13 - 2010-12-14
  14. 2010/12/14 23:27:59 - 2010-12-14
  15. 2010/12/14 23:18:54 - 2010-12-14