Hatena::ブログ(Diary)

ぽかぽかウンティの学習帳 このページをアンテナに追加 RSSフィード



2012-03-08 prototype.js の table sort ライブラリをリファクタリング

javascript で簡単にテーブルを並べ替えできる prototype.js ライブラリで、

公式サイトが死んでいる order_by_column.js を生意気にもリファクタリングしてみた。


prototype.jsライブラリだし、便利だから使っていたが、

セル内のinnerHTMLにスペースがあった場合に綺麗にソートできていなかったので、strip() を入れた。


また、auto pagerize とかを考慮して、tr の再読み込みができるように変更した。

/**
 *
 * @class OrderByColumn
 */
var OrderByColumn = Class.create({

    /**
     *
     * @param {string} table
     * @param {Array.<string>} types
     * @constructor
     * @protected
     */
    initialize: function(table, types) {
        this.types = types;
        this.table = $(table);
        this.ths = $$('#' + this.table.id + ' thead th');
        this.loadBody();
        this.ths.each(function(x) { x.setStyle({cursor: 'pointer'}).insert(new Element('span', {className: 'OBRarrow'})).observe('click', function(e) { this.sort(x); }.bind(this)); }.bind(this));
    },

    /**
     *
     * @public
     */
    loadBody: function() {
        this.trs = $$('#' + this.table.id + ' tbody tr');
    },

    /**
     *
     * @param {Element} th
     * @private
     */
    sort: function(th) {
        var orderby = this.marking(th);
        var num = this.ths.indexOf(th);
        this.trs.sort(function(a, b) {
            var m = a.down('td', num).cleanWhitespace().innerHTML.strip();
            var n = b.down('td', num).cleanWhitespace().innerHTML.strip();
            if (this.types[num] == 'number') {
                if (!parseFloat(m)) {
                    return 1;
                }
                if (!parseFloat(n)) {
                    return -1;
                }
                return orderby == 'asc' ? (parseFloat(m) - parseFloat(n)) : (parseFloat(n) - parseFloat(m));
            }
            return orderby == 'asc'  ? ((m.toLowerCase() > n.toLowerCase()) ? 1 : -1) : ((n.toLowerCase() > m.toLowerCase()) ? 1 : -1);
        }.bind(this));
        this.refresh();
    },

    /**
     *
     * @param {Element} th
     * @returns {String}
     * @private
     */
    marking: function(th) {
        var current = th.down('span').cleanWhitespace().innerHTML.strip();
        this.ths.each(function(x) { x.down('span').update(''); });
        switch (current) {
            case '':
            case '▼':
                th.down('span').update('▲');
                return 'asc';
            case '▲':
                th.down('span').update('▼');
                return 'desc';
        }
    },

    /**
     *
     * @private
     */
    refresh: function() {
        var tbody = $$('#' + this.table.id + ' tbody').first();
        tbody.update('');
        this.trs.each(function(x) { tbody.insert(x); });
    }
});

2012-02-15 Y Combinator を Common Lisp で

id:amachang さんの Y コンビネータって何? - IT戦記 を見て Common Lisp で書いてみたが似たような感じになってしまった。

せっかくの Lisp だからもっとエレガントに書けないのかなぁ。

; 10の階乗を返す
(funcall ((lambda (f)
  ((lambda (g)
    (lambda (m)
      (funcall (funcall f (funcall g g)) m)))
   (lambda (g)
     (lambda (m)
       (funcall (funcall f (funcall g g)) m)))))
 (lambda (fact)
   (lambda (n)
     (if (= n 1)
       1
       (* n (funcall fact (- n 1)))))))
 10)

Y Combinator の Y って何なのかな。Z Combinator があるくらいだから X もあるのかな。

2012-01-21 どこでも Do a barrel roll

Google で前に話題になった「Do a barrel roll」で検索するとページが一回転するネタをどこでもやろうと試みる Javascript です。


Google みたいに加速度はつきませんが、モダンブラウザであれば一応一回転します。


デバッグツールのコンソールから実行するか、アドレスバー

javascript: を入力して、後ろに貼り付けて実行します。

(function(){var deg=0;var set=function(val){document.body.style.webkitTransform=val;document.body.style.MozTransform=val;document.body.style.msTransform=val;document.body.style.OTransform=val;document.body.style.transform=val;};var pid=window.setInterval(function(){deg+=6;if(deg>359){window.clearInterval(pid);set(null);}set('rotate('+deg+'deg)');},16);}());void 0;

2012-01-14 Windows マシンから github を使いたい

Windows マシンから github を使いたい


前職では cvs 現職では svn を使っているが、せっかくなので git も使ってみたくなった。


ググれば説明サイトがたくさん出てくるので困らなかったが、SSH接続をPUTTYごった煮でやろうとしてハマった。


作業手順は以下。gitbash を使用。 GitHub Help


  1. The world's leading software development platform ? GitHub に登録してリポジトリを開設する
  2. Google Code Archive - Long-term storage for Google Code Project Hosting.インストールする
  3. sshwindows.sf.net: OpenSSH for Windowsインストールする
  4. SSH 回りの作業は A-Liaison BLOG: githubに自分のリポジトリを作ってコミットしてみる がわかりやすくまとまっているから参考に進める
  5. マニュアル通りに進めていれば WindowsXP での各種保存先ディレクトリは以下になる
folderpath
Git プログラムC:\Program Files\Git
作業フォルダC:\Documents and Settings\{USER}
SSH関連フォルダC:\Documents and Settings\{USER}\.ssh

6. SSH config 設定は以下になる、ってか config ファイルは不要

Host github.com
    User git
    Hostname github.com
    PreferredAuthentications publickey

上記で問題なく push できた。


なお、README ファイルは作った方がよいとのこと。

clmemo@aka: Github の README ファイルを wiki 書式で書く


PUTTY だと SSHPUTTY フォルダ内にある plink.exe を使うことになるが、

push しようとするとダメで、Redirecting... 通りに対処しても無理だった。


GIT_SSH 環境変数は迷わず C:\Program Files\OpenSSH\bin\ssh.exe を指定すべきだった。

作成したリポジトリGitHub - supercaracal/duel-shooting: This is a shooting game.

2012-01-07 javascript でゲームをつくってみたいその3

引き続き、決闘型シューティングゲームDuel Shooting」を prototype.js で作っています。

前回に加えて以下の実装を行いました。

・オープニングアニメーションの追加

・経過秒数カウントの追加

・敵機の攻撃アクションの追加

もうメンテがしづらく、ソースを追うのもつらくなってきたので、そろそろクラス分けを考えています。

まだまだ framework としての prototype.js を使いこなせていません。

以下で動作確認ができます。

9leap : デュエルシューティング by bar_row - どこでも遊べる、投稿型ゲームサイト

次は SmartPhone 対応をしたいと思います。

その次は enchant.js へ移行したいと思います。

/**
 *
 * @class DuelShooting
 */
var DuelShooting = Class.create({

    /**
     *
     * @type {string}
     */
    TITLE_TEXT: 'Duel Shooting',

    /**
     *
     * @type {number}
     */
    Z_INDEX_BASE: 3000,

    /**
     *
     * @type {number}
     */
    INTERVAL_WAIT_MSEC: 16, 

    /**
     *
     * @type {number}
     */
    FUNNEL_MAX: 5,

    /**
     *
     * @type {number}
     */
    MEGA_CANNON_WAIT: 250,

    /**
     *
     * @type {number}
     */
    MEGA_CANNON_HEIGHT: 29,

    /**
     *
     * @type {number}
     */
    timerId: null,

    /**
     *
     * @type {number}
     */
    timeCountTimerId: null,

    /**
     *
     * @type {number}
     */
    timeCount: null,

    /**
     *
     * @type {Element}
     */
    modal: null,

    /**
     *
     * @type {Element}
     */
    enemy: null,

    /**
     *
     * @type {Element}
     */
    ship: null,

    /**
     *
     * @type {Element}
     */
    timeCounter: null,

    /**
     *
     * @type {number}
     */
    clientHeight: null,

    /**
     *
     * @type {number}
     */
    clientWidth: null,

    /**
     *
     * @type {string}
     */
    nextCommand: null,

    /**
     *
     * @type {Array.<Element>}
     */
    shipBullets: null,

    /**
     *
     * @type {Array.<Element>}
     */
    enemyBullets: null,

    /**
     *
     * @type {Array.<Element>}
     */
    shipFunnels: null,

    /**
     *
     * @type {Array.<Element>}
     */
    comeBackShipFunnels: null,

    /**
     *
     * @type {number}
     */
    enemyHP: null,

    /**
     *
     * @type {number}
     */
    shipHP: null,

    /**
     *
     * @typr {number}
     */
    funnelCount: null,

    /**
     *
     * @type {number}
     */
    megaCannonWaitCount: null,

    /**
     *
     * @type {number}
     */
    megaCannonHeight: null,

    /**
     *
     * @type {number}
     */
    enemyBulletCount: null,

    /**
     *
     * @protected
     * @constructor
     */
    initialize: function () {
        this.setClientHeight();
        this.setClientWidth();
        this.enemyHP = 100;
        this.shipHP = 100;
        this.createModal();
        this.createEnemy();
        this.createTimeCounter();
        this.createShip();
        this.shipBullets = [];
        this.enemyBullets = [];
        this.shipFunnels = [];
        this.funnelCount = this.FUNNEL_MAX;
        this.megaCannonWaitCount = 0;
        this.megaCannonHeight = 0;
        this.enemyBulletCount = 0;
        this.comeBackShipFunnels = [];
        this.animateOpening(
             this.TITLE_TEXT,
             (function () {
                  this.setEventListener();
                  this.addElems();
                  this.start();
             }).bind(this)
        );
    },

    /**
     *
     * @param {string} text
     * @param {Function} callback
     * @private
     */
    animateOpening: function (text, callback) {
        var modal  = new Element('div').setStyle({
            display: 'block',
            position: 'fixed',
            zIndex: this.Z_INDEX_BASE + 100,
            top: '0px',
            left: '0px',
            backgroundColor: '#111111',
            height: this.clientHeight + 'px',
            width: this.clientWidth + 'px',
            opacity: '1.0',
            filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=100)'
        });
        var title = new Element('div').setStyle({
            display: 'none',
            opacity: '0.0',
            position: 'fixed',
            zIndex: this.Z_INDEX_BASE + 101,
            fontSize: '36px',
            color: '#FFFFFF',
            top: '0px',
            left: '0px'
        }).update(text);
        modal.insert(title);
        document.body.insert(modal);
        var dim = title.getDimensions();
        title.setStyle({
            display: 'block',
            top: this.clientHeight / 2 - (dim.height / 2 - 0) + 'px',
            left: this.clientWidth / 2 - (dim.width / 2 - 0) + 'px'
        });
        var timerId = setInterval(function () {
            var opacity = title.getOpacity();
            if (opacity >= 1.0) {
                clearInterval(timerId);
                setTimeout(function () {
                    callback();
                    title.remove();
                    var timerId = setInterval(function () {
                        var opacity = modal.getOpacity();
                        if (opacity <= 0.0) {
                            clearInterval(timerId);
                            modal.remove();
                        }
                        modal.setOpacity(opacity - 0.1);
                    }, 32);
                }, 2000);
            }
            title.setOpacity(opacity + 0.1);
        }, 128);
    },

    /**
     *
     * @private
     */
    setClientHeight: function () {
        var height = (document.documentElement.clientHeight || document.body.clientHeight);
        this.clientHeight = height - (height % 10);
    },

    /**
     *
     * @private
     */
    setClientWidth: function () {
        var width = (document.documentElement.clientWidth || document.body.clientWidth);
        this.clientWidth = width - (width % 10);
    },

    /**
     *
     * @private
     */
    createModal: function () {
        this.modal = new Element('div').setStyle({
            display: 'block',
            position: 'fixed',
            zIndex: this.Z_INDEX_BASE,
            top: '0px',
            left: '0px',
            backgroundColor: '#333333',
            height: this.clientHeight + 'px',
            width: this.clientWidth + 'px',
            opacity: '0.8',
            filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=80)'
        });
    },

    /**
     *
     * @private
     */
    createEnemy: function () {
        var color = '#FF5555';
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 10, position: 'fixed', top: '0px', left: '0px' });
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 30px ' + color, textAlign: 'center', fontWeight: 800, fontSize: '20px' }).update(this.enemyHP));
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 30px ' + color, marginLeft: '30px' }));
        this.enemy = obj;
    },

    /**
     *
     * @private
     */
    createShip: function () {
        var color = '#FFFFFF';
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 10, position: 'fixed', top: this.clientHeight - 60 + 'px', left: this.clientWidth - 90 + 'px' });
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 10px ' + color, marginLeft: '30px' }));
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 10px ' + color, textAlign: 'center', fontWeight: 800, fontSize: '20px' }).update(this.shipHP));
        this.ship = obj;
    },

    /**
     *
     * @private
     */
    createTimeCounter: function () {
        this.timeCounter = new Element('div').setStyle({
             top: '2px',
             right: '10px',
             zIndex: this.Z_INDEX_BASE + 20,
             position: 'fixed',
             height: '30px',
             width: '60px',
             fontSize: '20px',
             fontWeight: 800,
             color: '#FFFFFF',
             textAlign: 'right'
         }).update('0');
    },

    /**
     *
     * @private
     */
    getEnemyAfterimage: function () {
        var color = '#FF5555';
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 1, position: 'fixed', top: '0px', left: '0px', opacity: '0.2' });
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: color, borderRadius: '6px', textAlign: 'center' }).update(this.enemyHP));
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: color, borderRadius: '6px', marginLeft: '30px' }));
        return obj;
    },

    /**
     *
     * @private
     * @return Element
     */
    getBullet: function () {
        var color = '#55FF55';
        var obj = new Element('div').setStyle({ width: '30px', height: '30px', zIndex: this.Z_INDEX_BASE + 5, position: 'fixed' });
        obj.insert(new Element('div').setStyle({ width: '20px', height: '20px', margin: '5px', backgroundColor: color, borderRadius: '20px', boxShadow: '0px 0px 10px ' + color }));
        return obj;
    },

    /**
     *
     * @private
     * @return Element
     */
    getHomingBullet: function () {
        var color = '#FF55FF';
        var outer = new Element('div').setStyle({ width: '30px', height: '30px', zIndex: this.Z_INDEX_BASE + 6, position: 'fixed' });
        var inner = new Element('div').setStyle({ width: '26px', height: '26px', margin: '2px', backgroundColor: color, borderRadius: '26px', boxShadow: '0px 0px 10px ' + color });
        return inner.wrap(outer);
    },

    /**
     *
     * @private
     */
    getFunnel: function () {
        var color = '#9999FF';
        var obj = new Element('div').setStyle({ width: '30px', height: '30px', zIndex: this.Z_INDEX_BASE + 4, position: 'fixed' });
        obj.insert(new Element('div').setStyle({ width: '6px', height: '20px', marginLeft: '12px', backgroundColor: color, borderRadius: '2px', boxShadow: '0px 0px 10px ' + color }));
        obj.insert(new Element('div').setStyle({ width: '20px', height: '10px', margin: '0px 5px 0px 5px', backgroundColor: color, borderRadius: '20px', boxShadow: '0px 0px 10px ' + color }));
        return obj;
    },

    /**
     *
     * @private
     */
    addShipBullet: function () {
        var obj = this.getBullet();
        obj.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.shipBullets.push(obj);
    },

    /**
     *
     * @private
     */
    addEnemyBullet: function () {
        var obj = this.getHomingBullet();
        obj.setStyle({ top: '60px', left: this.enemy.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.enemyBullets.push(obj);
    },

    /**
     *
     * @param {Element} elm
     * @private
     */
    addShipFunnelBullet: function (elm) {
        var obj = this.getBullet();
        obj.setStyle({ top: this.clientHeight - 120 + 'px', left: elm.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.shipBullets.push(obj);
    },

    /**
     *
     * @private
     */
    addShipMegaCannonBullet: function () {
        var objL = this.getBullet();
        var objM = this.getBullet();
        var objR = this.getBullet();
        objL.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 'px' });
        objM.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        objR.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 60 + 'px' });
        document.body.insert(objL);
        document.body.insert(objM);
        document.body.insert(objR);
        this.shipBullets.push(objL);
        this.shipBullets.push(objM);
        this.shipBullets.push(objR);
    },

    /**
     *
     * @private
     */
    addShipFunnel: function () {
        var obj = this.getFunnel();
        obj.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.shipFunnels.push(obj);
    },

    /**
     *
     * @param {Array} lefts
     * @private
     */
    addEnemyAfterimage: function (lefts) {
        if (!lefts) {
            return;
        }
        lefts.each((function (left) {
            var afterimage = this.getEnemyAfterimage();
            afterimage.setStyle({ top: this.enemy.getStyle('top'), left: left + 'px' });
            document.body.insert(afterimage);
            (function () { afterimage.remove(); }).delay(0.3);
        }).bind(this));
    },

    /**
     *
     * @private
     */
    moveShipBullets: function () {
        var enemyLeft = this.enemy.getStyle('left').replace('px', '') - 0;
        for (var i = 0, len = this.shipBullets.length; i < len; i++) {
            var elm = this.shipBullets[i];
            if (!elm) {
                continue;
            }
            var top = elm.getStyle('top').replace('px', '') - 0;
            var left = elm.getStyle('left').replace('px', '') - 0;
            if ((enemyLeft - 25 < left) && (left <= enemyLeft + 5) && (top - 10 < 30)) {
                this.shipBullets[i] = null;
                elm.remove();
                this.hitEnemy();
                continue;
            }
            if ((enemyLeft + 5 < left) && (left < enemyLeft + 60) && (top - 10 < 60)) {
                this.shipBullets[i] = null;
                elm.remove();
                this.hitEnemy();
                continue;
            }
            if ((enemyLeft + 60 <= left) && (left < enemyLeft + 90) && (top - 10 < 30)) {
                this.shipBullets[i] = null;
                elm.remove();
                this.hitEnemy();
                continue;
            }
            if (top - 10 < 0) {
                this.shipBullets[i] = null;
                elm.remove();
                continue;

            }
            elm.setStyle({ top: top - 10 + 'px' });
        }
        this.shipBullets = this.shipBullets.compact();
    },

    /**
     *
     * @private
     */
    moveEnemyBullets: function () {
        var shipLeft = this.ship.getStyle('left').replace('px', '') - 0;
        for (var i = 0, len = this.enemyBullets.length; i < len; i++) {
            var elm = this.enemyBullets[i];
            if (!elm) {
                continue;
            }
            var top = elm.getStyle('top').replace('px', '') - 0;
            var left = elm.getStyle('left').replace('px', '') - 0;
            if ((shipLeft - 25 < left) && (left <= shipLeft + 5) && ((top + 10) > (this.clientHeight - 30))) {
                this.enemyBullets[i] = null;
                elm.remove();
                this.hitShip();
                continue;
            }
            if ((shipLeft + 5 < left) && (left < shipLeft + 60) && ((top + 10) > (this.clientHeight - 60))) {
                this.enemyBullets[i] = null;
                elm.remove();
                this.hitShip();
                continue;
            }
            if ((shipLeft + 60 <= left) && (left < shipLeft + 90) && ((top + 10) > (this.clientHeight - 30))) {
                this.enemyBullets[i] = null;
                elm.remove();
                this.hitShip();
                continue;
            }
            if (this.clientHeight < (top + 5)) {
                this.enemyBullets[i] = null;
                elm.remove();
                continue;
            }
            if ((this.clientHeight / 2) < top) {
                distance = 0;
            } else if (left < shipLeft) {
                distance = 10;
            } else if ((shipLeft + 60) < left) {
                distance = -10;
            } else {
                distance = 0;
            }
            elm.setStyle({ top: top + 5 + 'px', left: left + distance + 'px' });
        }
        this.enemyBullets = this.enemyBullets.compact();
    },

    /**
     *
     * @private
     */
    moveShipFunnels: function () {
        var enemyLeft = this.enemy.getStyle('left').replace('px', '') - 0;
        for (var i = 0, len = this.shipFunnels.length; i < len; i++) {
            var elm = this.shipFunnels[i];
            if (!elm) {
                continue;
            }
            var left = elm.getStyle('left').replace('px', '') - 0;
            var move = (enemyLeft - left) > 0 ? 10 : -10;
            if (Math.abs(enemyLeft - left + move) < 30) {
                this.addShipFunnelBullet(elm);
                this.shipFunnels[i] = null;
                this.comeBackShipFunnels.push(elm);
                continue;
            }
            elm.setStyle({ left: left + move + 'px' });
        }
        this.shipFunnels = this.shipFunnels.compact();
    },

    /**
     *
     * @private
     */
    moveComeBackShipFunnels: function () {
        var shipCenterLeft = this.ship.getStyle('left').replace('px', '') - 0 + 30;
        for (var i = 0, len = this.comeBackShipFunnels.length; i < len; i++) {
            var elm = this.comeBackShipFunnels[i];
            if (!elm) {
                continue;
            }
            var left = elm.getStyle('left').replace('px', '') - 0;
            var move = (shipCenterLeft - left) > 0 ? 10 : -10;
            if (Math.abs(shipCenterLeft - left + move) < 30) {
                this.comeBackShipFunnels[i] = null;
                elm.remove();
                ++this.funnelCount;
                continue;
            }
            elm.setStyle({ left: left + move + 'px' });
        }
        this.comeBackShipFunnels = this.comeBackShipFunnels.compact();
    },

    /**
     *
     * @private
     */
    moveEnemy: function () {
        var enemyLeft = this.enemy.getStyle('left').replace('px', '') - 0;
        var move = 0;
        var moveMax = 90;
        var searchSectorX = 90;
        var searchSectorY = 30;
        var isLeft = false;
        for (var i = 0, len = this.shipBullets.length; i < len; i++) {
            var elm = this.shipBullets[i];
            if (!elm) {
                continue;
            }
            var top = elm.getStyle('top').replace('px', '') - 0;
            var left = elm.getStyle('left').replace('px', '') - 0;
            if (enemyLeft - searchSectorY <= left && (left <= enemyLeft + moveMax) && top < searchSectorX) {
                if (enemyLeft - moveMax < 0) {
                    move = moveMax;
                } else if (enemyLeft + moveMax > this.clientWidth - 90) {
                    move = -moveMax;
                } else {
                    var seed = Math.floor(Math.random() * 100);
                    move = (seed % 2) ? moveMax : -moveMax;
                }
            }
        }
        this.enemy.setStyle({ left: enemyLeft + move + 'px' });
        if (move === 0) {
            return;
        }
        var sign = (move < 0) ? -1 : 1;
        var inc = 10;
        this.addEnemyAfterimage([enemyLeft, enemyLeft + (inc * 3 * sign), enemyLeft + (inc * 6 * sign)]);
    },

    /**
     *
     * @private
     */
    hitEnemy: function () {
        this.enemy.down(0).update(--this.enemyHP);
        if (this.enemyHP < 1) {
            this.stop();
            this.enemy.down(0).update(0);
            this.enemyHP = 0;
            return;
        }
        ++this.enemyBulletCount;

    },

    /**
     *
     * @private
     */
    hitShip: function () {
        this.ship.down(1).update(--this.shipHP);
        if (this.shipHP < 1) {
            this.stop();
            this.ship.down(1).update(0);
            this.shipHP = 0;
            return;
        }
    },

    /**
     *
     * @private
     */
    setEventListener: function () {
        Event.observe(document, 'keydown', this.handler.bindAsEventListener(this));
    },

    /**
     *
     * @private
     * @param {event} e
     */
    handler: function (e) {
        var KEY_F = 70;
        var KEY_M = 77;
        switch (e.keyCode) {
            case Event.KEY_RIGHT:
               this.nextCommand = 'stepRight';
               break;
            case Event.KEY_LEFT:
               this.nextCommand = 'stepLeft';
               break;
            case Event.KEY_UP:
               this.nextCommand = 'attack';
               break;
            case KEY_F:
               this.nextCommand = 'funnel';
               break;
            case KEY_M:
               this.nextCommand = 'megaCannon';
               break;

        }
    },

    /**
     *
     * @private
     */
    stepRight: function () {
        var left = this.ship.getStyle('left').replace('px', '') - 0;
        var max = this.clientWidth - 90;
        if (left + 10 <= max) {
            this.ship.setStyle({ left: left + 10 + 'px' });
        }
    },

    /**
     *
     * @private
     */
    stepLeft: function () {
        var left = this.ship.getStyle('left').replace('px', '') - 0;
        var min = 0;
        if (min <= left - 10) {
            this.ship.setStyle({ left: left - 10 + 'px' });
        }
    },

    /**
     *
     * @private
     */
    attack: function () {
        this.addShipBullet();
    },

    /**
     *
     * @private
     */
    funnel: function () {
        if (this.funnelCount < 1) {
            this.funnelCount = 0;
            return;
        }
        --this.funnelCount;
        this.addShipFunnel();
        this.enemyBulletCount += 1;
    },

    /**
     *
     * @private
     */
    megaCannon: function () {
        if (this.megaCannonWaitCount > 0) {
            return;
        }
        this.addShipMegaCannonBullet();
        this.megaCannonWaitCount = this.MEGA_CANNON_WAIT;
        this.megaCannonHeight = this.MEGA_CANNON_HEIGHT;
        this.enemyBulletCount += 10;
    },

    /**
     *
     * @private
     */
    addElems: function () {
        document.body.insert(this.modal);
        document.body.insert(this.enemy);
        document.body.insert(this.ship);
        document.body.insert(this.timeCounter);
    },

    /**
     *
     * @private
     */
    moveElem: function () {
        this.moveShipBullets();
        this.moveShipFunnels();
        this.moveComeBackShipFunnels();
        if (this.nextCommand && (this.nextCommand in this)) {
            (this[this.nextCommand].bind(this))();
            this.nextCommand = null;
        }
        if (this.megaCannonWaitCount > 0) {
            --this.megaCannonWaitCount;
        }
        if (this.megaCannonHeight > 0) {
            this.addShipMegaCannonBullet();
            --this.megaCannonHeight;
        }
        if (this.enemyBulletCount > 0) {
            this.addEnemyBullet();
            --this.enemyBulletCount;
        }
        this.moveEnemy();
        this.moveEnemyBullets();
    },

    /**
     *
     * @private
     */
    start: function () {
        this.timerId = setInterval(this.moveElem.bind(this), this.INTERVAL_WAIT_MSEC);
        this.timeCountTimerId = setInterval((function () {
             ++this.timeCount;
             this.timeCounter.update(this.timeCount);
        }).bind(this), 1000);
    },

    /**
     *
     * @private
     */
    stop: function () {
        clearInterval(this.timerId);
        clearInterval(this.timeCountTimerId);
    }
});

2011-12-14 javascript でゲームをつくってみたいその2

前回に引き続き、prototype.js を使ってゲームをつくっています。

新たに以下の機能を実装しました。

・自機と敵機のHP(100)

・敵機の回避行動

・敵機の残像

・自機のファンネル攻撃(キーF)

・自機のメガ粒子砲攻撃(キーM)

次回は敵機の攻撃を実装します。

以下でプレイ可能です。

9leap : デュエルシューティング by bar_row - どこでも遊べる、投稿型ゲームサイト

/**
 *
 * @class Duel
 */
var Duel = Class.create({

    /**
     *
     * @type {number}
     */
    Z_INDEX_BASE: 3000,

    /**
     *
     * @type {number}
     */
    FUNNEL_MAX: 5,

    /**
     *
     * @type {number}
     */
    MEGA_CANNON_WAIT: 250,

    /**
     *
     * @type {number}
     */
    MEGA_CANNON_HEIGHT: 30,

    /**
     *
     * @type {number}
     */
    timerId: null,

    /**
     *
     * @type {Element}
     */
    modal: null,

    /**
     *
     * @type {Element}
     */
    enemy: null,

    /**
     *
     * @type {Element}
     */
    ship: null,

    /**
     *
     * @type {number}
     */
    clientHeight: null,

    /**
     *
     * @type {number}
     */
    clientWidth: null,

    /**
     *
     * @type {string}
     */
    nextCommand: null,

    /**
     *
     * @type {Array}
     */
    shipBullets: null,

    /**
     *
     * @type {Array}
     */
    shipFunnels: null,

    /**
     *
     * @type {Array}
     */
    comeBackShipFunnels: null,

    /**
     *
     * @type {number}
     */
    enemyHP: null,

    /**
     *
     * @type {number}
     */
    shipHP: null,

    /**
     *
     * @typr {number}
     */
    funnelCount: null,

    /**
     *
     * @type {number}
     */
    megaCannonWaitCount: null,

    /**
     *
     * @type {number}
     */
    megaCannonHeight: null,

    /**
     *
     * @protected
     * @constructor
     */
    initialize: function () {
        this.setClientHeight();
        this.setClientWidth();
        this.enemyHP = 100;
        this.shipHP = 100;
        this.createModal();
        this.createEnemy();
        this.createShip();
        this.shipBullets = [];
        this.shipFunnels = [];
        this.funnelCount = this.FUNNEL_MAX;
        this.megaCannonWaitCount = 0;
        this.megaCannonHeight = 0;
        this.comeBackShipFunnels = [];
        this.setEventListener();
        this.addElems();
        this.start();
    },

    /**
     *
     * @private
     */
    setClientHeight: function () {
        var height = (document.documentElement.clientHeight || document.body.clientHeight);
        this.clientHeight = height - (height % 10);
    },

    /**
     *
     * @private
     */
    setClientWidth: function () {
        var width = (document.documentElement.clientWidth || document.body.clientWidth);
        this.clientWidth = width - (width % 10);
    },

    /**
     *
     * @private
     */
    createModal: function () {
        this.modal = new Element('div').setStyle({
            display: 'block',
            position: 'fixed',
            zIndex: this.Z_INDEX_BASE,
            top: 0,
            left: 0,
            backgroundColor: '#333333',
            height: this.clientHeight + 'px',
            width: this.clientWidth + 'px',
            opacity: '0.8',
            filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=80)'
        });
    },

    /**
     *
     * @private
     */
    createEnemy: function () {
        var color = '#FF5555';
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 10, position: 'fixed', top: '0px', left: '0px' });
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 30px ' + color, textAlign: 'center', fontWeight: 800, fontSize: '20px' }).update(this.enemyHP));
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 30px ' + color, marginLeft: '30px' }));
        this.enemy = obj;
    },

    /**
     *
     * @private
     */
    createShip: function () {
        var color = '#FFFFFF';
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 10, position: 'fixed', top: this.clientHeight - 60 + 'px', left: this.clientWidth - 90 + 'px' });
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 10px ' + color, marginLeft: '30px' }));
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: color, borderRadius: '6px', boxShadow: '0px 0px 10px ' + color, textAlign: 'center', fontWeight: 800, fontSize: '20px' }).update(this.shipHP));
        this.ship = obj;
    },

    /**
     *
     * @private
     */
    getEnemyAfterimage: function () {
        var color = '#FF5555';
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 1, position: 'fixed', top: '0px', left: '0px', opacity: '0.2' });
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: color, borderRadius: '6px', textAlign: 'center' }).update(this.enemyHP));
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: color, borderRadius: '6px', marginLeft: '30px' }));
        return obj;
    },

    /**
     *
     * @private
     * @return Element
     */
    getBullet: function () {
        var color = '#55FF55';
        var obj = new Element('div').setStyle({ width: '30px', height: '30px', zIndex: this.Z_INDEX_BASE + 5, position: 'fixed' });
        obj.insert(new Element('div').setStyle({ width: '20px', height: '20px', margin: '5px', backgroundColor: color, borderRadius: '20px', boxShadow: '0px 0px 10px ' + color }));
        return obj;
    },

    /**
     *
     * @private
     */
    getFunnel: function () {
        var color = '#9999FF';
        var obj = new Element('div').setStyle({ width: '30px', height: '30px', zIndex: this.Z_INDEX_BASE + 4, position: 'fixed' });
        obj.insert(new Element('div').setStyle({ width: '6px', height: '20px', marginLeft: '12px', backgroundColor: color, borderRadius: '2px', boxShadow: '0px 0px 10px ' + color }));
        obj.insert(new Element('div').setStyle({ width: '20px', height: '10px', margin: '0px 5px 0px 5px', backgroundColor: color, borderRadius: '20px', boxShadow: '0px 0px 10px ' + color }));
        return obj;
    },

    /**
     *
     * @private
     */
    addShipBullet: function () {
        var obj = this.getBullet();
        obj.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.shipBullets.push(obj);
    },

    /**
     *
     * @param {Element} elm
     * @private
     */
    addFunnelBullet: function (elm) {
        var obj = this.getBullet();
        obj.setStyle({ top: this.clientHeight - 120 + 'px', left: elm.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.shipBullets.push(obj);
    },

    /**
     *
     * @private
     */
    addShipMegaCannonBullet: function () {
        var objL = this.getBullet();
        var objM = this.getBullet();
        var objR = this.getBullet();
        objL.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 'px' });
        objM.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        objR.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 60 + 'px' });
        document.body.insert(objL);
        document.body.insert(objM);
        document.body.insert(objR);
        this.shipBullets.push(objL);
        this.shipBullets.push(objM);
        this.shipBullets.push(objR);
    },

    /**
     *
     * @private
     */
    addShipFunnel: function () {
        var obj = this.getFunnel();
        obj.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.shipFunnels.push(obj);
    },

    /**
     *
     * @param {Array} lefts
     * @private
     */
    addEnemyAfterimage: function (lefts) {
        if (!lefts) {
            return;
        }
        lefts.each((function (left) {
            var afterimage = this.getEnemyAfterimage();
            afterimage.setStyle({ top: this.enemy.getStyle('top'), left: left + 'px' });
            document.body.insert(afterimage);
            (function () { afterimage.remove(); }).delay(0.3);
        }).bind(this));
    },

    /**
     *
     * @private
     */
    moveShipBullets: function () {
        var enemyLeft = this.enemy.getStyle('left').replace('px', '') - 0;
        for (var i = 0, len = this.shipBullets.length; i < len; i++) {
            var elm = this.shipBullets[i];
            if (!elm) {
                continue;
            }
            var top = elm.getStyle('top').replace('px', '') - 0;
            var left = elm.getStyle('left').replace('px', '') - 0;
            if ((enemyLeft - 25 < left) && (left <= enemyLeft + 5) && (top - 10 < 30)) {
                this.shipBullets[i] = null;
                elm.remove();
                this.hitEnemy();
                continue;
            }
            if ((enemyLeft + 5 < left) && (left < enemyLeft + 60) && (top - 10 < 60)) {
                this.shipBullets[i] = null;
                elm.remove();
                this.hitEnemy();
                continue;
            }
            if ((enemyLeft + 60 <= left) && (left < enemyLeft + 90) && (top - 10 < 30)) {
                this.shipBullets[i] = null;
                elm.remove();
                this.hitEnemy();
                continue;
            }
            if (top - 10 < 0) {
                this.shipBullets[i] = null;
                elm.remove();
                continue;

            }
            elm.setStyle({ top: top - 10 + 'px' });
        }
        this.shipBullets.compact();
    },

    /**
     *
     * @private
     */
    moveShipFunnels: function () {
        var enemyLeft = this.enemy.getStyle('left').replace('px', '') - 0;
        for (var i = 0, len = this.shipFunnels.length; i < len; i++) {
            var elm = this.shipFunnels[i];
            if (!elm) {
                continue;
            }
            var left = elm.getStyle('left').replace('px', '') - 0;
            var move = (enemyLeft - left) > 0 ? 10 : -10;
            if (Math.abs(enemyLeft - left + move) < 30) {
                this.addFunnelBullet(elm);
                this.shipFunnels[i] = null;
                this.comeBackShipFunnels.push(elm);
                continue;
            }
            elm.setStyle({ left: left + move + 'px' });
        }
        this.shipFunnels.compact();
    },

    /**
     *
     * @private
     */
    moveComeBackShipFunnels: function () {
        var shipCenterLeft = this.ship.getStyle('left').replace('px', '') - 0 + 30;
        for (var i = 0, len = this.comeBackShipFunnels.length; i < len; i++) {
            var elm = this.comeBackShipFunnels[i];
            if (!elm) {
                continue;
            }
            var left = elm.getStyle('left').replace('px', '') - 0;
            var move = (shipCenterLeft - left) > 0 ? 10 : -10;
            if (Math.abs(shipCenterLeft - left + move) < 30) {
                this.comeBackShipFunnels[i] = null;
                elm.remove();
                ++this.funnelCount;
                continue;
            }
            elm.setStyle({ left: left + move + 'px' });
        }
        this.comeBackShipFunnels.compact();
    },

    /**
     *
     * @private
     */
    moveEnemy: function () {
        var enemyLeft = this.enemy.getStyle('left').replace('px', '') - 0;
        var move = 0;
        var moveMax = 90;
        var searchSectorX = 90;
        var searchSectorY = 30;
        var isLeft = false;
        for (var i = 0, len = this.shipBullets.length; i < len; i++) {
            var elm = this.shipBullets[i];
            if (!elm) {
                continue;
            }
            var top = elm.getStyle('top').replace('px', '') - 0;
            var left = elm.getStyle('left').replace('px', '') - 0;
            if (enemyLeft - searchSectorY <= left && (left <= enemyLeft + moveMax) && top < searchSectorX) {
                if (enemyLeft - moveMax < 0) {
                    move = moveMax;
                } else if (enemyLeft + moveMax > this.clientWidth - 90) {
                    move = -moveMax;
                } else {
                    var seed = Math.floor(Math.random() * 100);
                    move = (seed % 2) ? moveMax : -moveMax;
                }
            }
        }
        this.enemy.setStyle({ left: enemyLeft + move + 'px' });
        if (move === 0) {
            return;
        }
        var sign = (move < 0) ? -1 : 1;
        var inc = 10;
        this.addEnemyAfterimage([enemyLeft, enemyLeft + (inc * 3 * sign), enemyLeft + (inc * 6 * sign)]);
    },

    /**
     *
     * @private
     */
    hitEnemy: function () {
        if ((this.enemyHP) < 1) {
            this.stop();
            return;
        }
        this.enemy.down(0).update(--this.enemyHP);
    },

    /**
     *
     * @private
     */
    setEventListener: function () {
        Event.observe(document, 'keydown', this.handler.bindAsEventListener(this));
    },

    /**
     *
     * @private
     * @param {event} e
     */
    handler: function (e) {
        var KEY_F = 70;
        var KEY_M = 77;
        switch (e.keyCode) {
            case Event.KEY_RIGHT:
               this.nextCommand = 'stepRight';
               break;
            case Event.KEY_LEFT:
               this.nextCommand = 'stepLeft';
               break;
            case Event.KEY_UP:
               this.nextCommand = 'attack';
               break;
            case KEY_F:
               this.nextCommand = 'funnel';
               break;
            case KEY_M:
               this.nextCommand = 'megaCannon';
               break;

        }
    },

    /**
     *
     * @private
     */
    stepRight: function () {
        var left = this.ship.getStyle('left').replace('px', '') - 0;
        var max = this.clientWidth - 90;
        if (left + 10 <= max) {
            this.ship.setStyle({ left: left + 10 + 'px' });
        }
    },

    /**
     *
     * @private
     */
    stepLeft: function () {
        var left = this.ship.getStyle('left').replace('px', '') - 0;
        var min = 0;
        if (min <= left - 10) {
            this.ship.setStyle({ left: left - 10 + 'px' });
        }
    },

    /**
     *
     * @private
     */
    attack: function () {
        this.addShipBullet();
    },

    /**
     *
     * @private
     */
    funnel: function () {
        if (this.funnelCount < 1) {
            this.funnelCount = 0;
            return;
        }
        --this.funnelCount;
        this.addShipFunnel();
    },

    /**
     *
     * @private
     */
    megaCannon: function () {
        if (this.megaCannonWaitCount > 0) {
            return;
        }
        this.addShipMegaCannonBullet();
        this.megaCannonWaitCount = this.MEGA_CANNON_WAIT;
        this.megaCannonHeight = this.MEGA_CANNON_HEIGHT;
    },

    /**
     *
     * @private
     */
    addElems: function () {
        document.body.insert(this.modal);
        document.body.insert(this.enemy);
        document.body.insert(this.ship);
    },

    /**
     *
     * @private
     */
    moveElem: function () {
        this.moveShipBullets();
        this.moveShipFunnels();
        this.moveComeBackShipFunnels();
        if (this.nextCommand && (this.nextCommand in this)) {
            (this[this.nextCommand].bind(this))();
            this.nextCommand = null;
        }
        if (this.megaCannonWaitCount > 0) {
            --this.megaCannonWaitCount;
        }
        if (this.megaCannonHeight > 0) {
            this.addShipMegaCannonBullet();
            --this.megaCannonHeight;
        }
        this.moveEnemy();
    },

    /**
     *
     * @private
     */
    start: function () {
        this.timerId = setInterval(this.moveElem.bind(this), 16);
    },

    /**
     *
     * @private
     */
    stop: function () {
        clearInterval(this.timerId);
    }
});

2011-12-12 javascript でゲームをつくってみたいその1

もちろん prototype.js の力は借りますが。


Web プログラミングはテキスト操作が主な処理ですが、画面でユーザーフレンドリーな UI を実現するには、Javascript を頑張るしかないと思います。


そこでいろいろな Effect を使う訳ですが、Effect なんてゲーム業界では普通に使われていることだと思います。


物体を「動かす」「アニメーションさせる」処理というのは、テキスト操作と違って結果がわかりやすく、プログラミングしているという実感がわき、楽しくなると思います。


enchant.js とかも用意されているので、まずは Javascript で簡単なゲームを作り、ゲームプログラミングのさわりを体感してみたいと思います。


とりあえず敵機と自機を適当に配置して自機で攻撃してみるところまで実装しました。

矢印キー上で攻撃、矢印キー左右で移動です。

var Duel = Class.create({
    Z_INDEX_BASE: 1000,
    timerId: null,
    modal: null,
    enemy: null,
    ship: null,
    clientHeight: null,
    clientWidth: null,
    nextCommand: null,
    shipBullets: null,
    initialize: function () {
        this.setClientHeight();
        this.setClientWidth();
        this.createModal();
        this.createEnemy();
        this.createShip();
        this.shipBullets = [];
        this.setEventListener();
        this.addElems();
        this.start();
    },
    setClientHeight: function () {
        var height = (document.documentElement.clientHeight || document.body.clientHeight);
        this.clientHeight = height - (height % 10);
    },
    setClientWidth: function () {
        var width = (document.documentElement.clientWidth || document.body.clientWidth);
        this.clientWidth = width - (width % 10);
    },
    createModal: function () {
        this.modal = new Element('div').setStyle({
            display: 'block',
            position: 'fixed',
            zIndex: this.Z_INDEX_BASE,
            top: 0,
            left: 0,
            backgroundColor: '#333333',
            height: this.clientHeight + 'px',
            width: this.clientWidth + 'px',
            opacity: '0.8',
            filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=80)'
        });
    },
    createEnemy: function () {
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 1, position: 'fixed', top: '0px', left: '120px' });
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: '#FF5555', borderRadius: '6px', boxShadow: '0px 0px 30px #FF5555' }));
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: '#FF5555', borderRadius: '6px', boxShadow: '0px 0px 30px #FF5555', marginLeft: '30px' }));
        this.enemy = obj;
    },
    createShip: function () {
        var obj = new Element('div').setStyle({ width: '90px', height: '60px', zIndex: this.Z_INDEX_BASE + 1, position: 'fixed', top: this.clientHeight - 60 + 'px', left: this.clientWidth - 90 + 'px' });
        obj.insert(new Element('div').setStyle({ width: '30px', height: '30px', backgroundColor: '#FFFFFF', borderRadius: '6px', boxShadow: '0px 0px 10px #FFFFFF', marginLeft: '30px' }));
        obj.insert(new Element('div').setStyle({ width: '90px', height: '30px', backgroundColor: '#FFFFFF', borderRadius: '6px', boxShadow: '0px 0px 10px #FFFFFF' }));
        this.ship = obj;
    },
    getBullet: function () {
        var obj = new Element('div').setStyle({ width: '30px', height: '30px', zIndex: this.Z_INDEX_BASE + 2, position: 'fixed' });
        obj.insert(new Element('div').setStyle({ width: '20px', height: '20px', margin: '5px', backgroundColor: '#55FF55', borderRadius: '20px', boxShadow: '0px 0px 10px #55FF55' }));
        return obj;
    },
    addShipBullet: function () {
        var obj = this.getBullet();
        obj.setStyle({ top: this.clientHeight - 90 + 'px', left: this.ship.getStyle('left').replace('px', '') - 0 + 30 + 'px' });
        document.body.insert(obj);
        this.shipBullets.push(obj);
    },
    moveShipBullets: function () {
        var enemyLeft = this.enemy.getStyle('left').replace('px', '') - 0;
        for (var i = 0, len = this.shipBullets.length; i < len; i++) {
            var elm = this.shipBullets[i];
            if (!elm) {
                continue;
            }
            var top = elm.getStyle('top').replace('px', '') - 0;
            var left = elm.getStyle('left').replace('px', '') - 0;
            if ((enemyLeft - 25 < left) && (left <= enemyLeft + 5) && (top - 10 < 30)) {
                this.shipBullets[i] = null;
                elm.remove();
                // TODO collision detection
                continue;
            }
            if ((enemyLeft + 5 < left) && (left < enemyLeft + 60) && (top - 10 < 60)) {
                this.shipBullets[i] = null;
                elm.remove();
                // TODO collision detection
                continue;
            }
            if ((enemyLeft + 60 <= left) && (left < enemyLeft + 90) && (top - 10 < 30)) {
                this.shipBullets[i] = null;
                elm.remove();
                // TODO collision detection
                continue;
            }
            if (top - 10 < 0) {
                this.shipBullets[i] = null;
                elm.remove();
                continue;

            }
            elm.setStyle({ top: top - 10 + 'px' });
        }
        this.shipBullets.compact();
    },
    setEventListener: function () {
        Event.observe(document, 'keydown', this.handler.bindAsEventListener(this));
    },
    handler: function (e) {
        var KEY_F = 70;
        var KEY_M = 77;
        switch (e.keyCode) {
            case Event.KEY_RIGHT:
               this.nextCommand = 'stepRight';
               break;
            case Event.KEY_LEFT:
               this.nextCommand = 'stepLeft';
               break;
            case Event.KEY_UP:
               this.nextCommand = 'attack';
               break;
            case KEY_F:
               this.nextCommand = 'funnel';
               break;
            case KEY_M:
               this.nextCommand = 'megaCannon';
               break;

        }
    },
    stepRight: function () {
        var left = this.ship.getStyle('left').replace('px', '') - 0;
        var max = this.clientWidth - 90;
        if (left + 10 <= max) {
            this.ship.setStyle({ left: left + 10 + 'px' });
        }
    },
    stepLeft: function () {
        var left = this.ship.getStyle('left').replace('px', '') - 0;
        var min = 0;
        if (min <= left - 10) {
            this.ship.setStyle({ left: left - 10 + 'px' });
        }
    },
    attack: function () {
        this.addShipBullet();
    },
    addElems: function () {
        document.body.insert(this.modal);
        document.body.insert(this.enemy);
        document.body.insert(this.ship);
    },
    moveElem: function () {
        this.moveShipBullets();
        if (this.nextCommand && (this.nextCommand in this)) {
            (this[this.nextCommand].bind(this))();
            this.nextCommand = null;
        }
    },
    start: function () {
        this.timerId = setInterval(this.moveElem.bind(this), 16);
    }
});

2011-12-08 特定の要素を画面から消えないようにするjavascript

prototype.js を使います。

縦に長い表をスクロールしてもヘッダー部が常に画面上部に表示されるようにしたいときなどに使用します。

ただし、セルの横幅を指定しないと思いっきりズレます。また、position: fixed を使用します。

// new StickyElement('thead_id');

/**
 *
 * @class StickyElement
 */
var StickyElement = Class.create({

    /**
     *
     * @type {number}
     */
    Z_INDEX_BASE: null,

    /**
     *
     * @type {Element}
     */
    elm: null,

    /**
     *
     * @type {number}
     */
    defaultY: null,

    /**
     *
     * @param {Element} elm
     * @param {number} idx
     * @protected
     * @constructor
     */
    initialize: function(elm, idx) {
        this.elm = $(elm);
        this.Z_INDEX_BASE = idx || 3000;
        this.defaultY = this.elm.positionedOffset()[1];
        this.setEventListener();
    },

    /**
     *
     * @private
     */
    setEventListener: function() {
        Event.observe(document, 'scroll', this.handler.bindAsEventListener(this));
    },

    /**
     *
     * @private
     */
    handler: function() {
        var xs = document.viewport.getScrollOffsets().first();
        this.elm.setStyle(this.defaultY < this.elm.cumulativeScrollOffset()[1] ? {
            position: 'fixed',
            zIndex: this.Z_INDEX_BASE,
            top: '0px',
            marginLeft: (0 < xs) ? -xs + 'px' : ''
        } : {
            position: '',
            zIndex: '',
            top: '',
            marginLeft: ''
        });
    }
});

2011-12-07 画面上部へアニメーションスクロールするボタンを配置するjavascript

prototype.js の Effect.ScrollTo を呼び出すパッドを配置します。

document.body.insert や css3、 position: fixed を使用しているため、モダンブラウザでないと動作は保証できません。

// new ScrollPadToTop();

/**
 *
 * @class ScrollPadToTop
 */
var ScrollPadToTop = Class.create({

    /**
     *
     * @protected
     * @constructor
     */
    initialize: function () {
        var elm = this.getPadElm();
        this.setEventListener(elm);
        document.body.insert(elm);
    },

    /**
     *
     * @private
     * @returns {Element}
     */
    getPadElm: function () {
        return new Element('div').setStyle({
            right: '1px',
            bottom: '50px',
            position: 'fixed',
            zIndex: 1000,
            height: '90px',
            width: '75px',
            backgroundColor: '#000000',
            opacity: 0.2,
            cursor: 'pointer',
            borderRadius: '10px'
        }).update(new Element('div').update('上').setStyle({
            margin: '22px',
            color: '#FFFFFF',
            fontWeight: 'bolder',
            fontSize: '30px'
        }));
    },

    /**
     *
     * @private
     */
    setEventListener: function (elm) {
        elm.observe('click', function () { Effect.ScrollTo(document.body, { duration: 0.7 }); });
        elm.observe('mouseover', function () { this.setStyle({ opacity: 0.8 }); });
        elm.observe('mouseout', function () { this.setStyle({ opacity: 0.2 }); });
    }
});

2011-12-06 notify 系の通知ボックスを表示するJavascript

上から出てきて上に引っ込む通知系のボックス Effect です。

prototype.js を使います。

css の position fixed が効かないブラウザでは見えないと思います。

// new HumbleAlert('ぽこたんインしたお');

/**
 *
 * @class HumbleAlert
 */
var HumbleAlert = Class.create({

    /**
     *
     * @type {number}
     */
    LENGTH_OF_MOVE: 7,

    /**
     *
     * @type {number}
     */
    INTERVAL_MSEC: 32,

    /**
     *
     * @type {number}
     */
    SWITCHING_DELAY_SEC: 2,

    /**
     *
     * @type {string}
     */
    text: null,

    /**
     *
     * @type {Element}
     */
    dialogue: null,

    /**
     *
     * @type {number}
     */
    topIni: null,

    /**
     *
     * @param {string} text
     * @constructor
     * @protected
     */
    initialize: function (text) {
        this.text = text;
        this.popup();
    },

    /**
     *
     * @private
     */
    popup: function () {
        this.createDialogue();
        this.positioning();
        this.advent();
    },

    /**
     *
     * @private
     */
    createDialogue: function () {
        this.dialogue = new Element('div').setStyle({
            display: 'none',
            position: 'fixed',
            zIndex: 2000,
            top: '0px',
            left: '0px',
            backgroundColor: '#333333',
            opacity: '0.8',
            filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=80)',
            borderRadius: '10px',
            boxShadow: '0px 0px 30px #000000',
            textAlign: 'center',
            padding: '20px',
            fontSize: '16px',
            color: '#FFFFFF'
        }).update(this.text);
        document.body.insert(this.dialogue);
    },

    /**
     *
     * @private
     */
    positioning: function () {
        var dim = this.dialogue.getDimensions();
        this.dialogue.setStyle({
            top: 0 - dim.height - 30 + 'px',
            left: Math.floor((document.documentElement.scrollWidth || document.body.scrollWidth) / 2 - dim.width / 2) + 'px',
        });
    },

    /**
     *
     * @private
     */
    advent: function () {
        this.dialogue.show();
        this.topIni = this.dialogue.getStyle('top').replace('px', '') - 0;
        this.down();
    },

    /**
     *
     * @private
     */
    down: function () {
        var self = this;
        var pid = setInterval(function () {
            var top = self.dialogue.getStyle('top').replace('px', '') - 0;
            if (top < 33) {
                top += self.LENGTH_OF_MOVE;
                self.dialogue.setStyle({ top: top + 'px' });
            } else {
                clearInterval(pid);
                self.up.bind(self).delay(self.SWITCHING_DELAY_SEC);
            }
        }, self.INTERVAL_MSEC);
    },

    /**
     *
     * @private
     */
    up: function () {
        var self = this;
        var pid = setInterval(function () {
            var top = self.dialogue.getStyle('top').replace('px', '') - 0;
            if (top > self.topIni) {
                top -= self.LENGTH_OF_MOVE;
                self.dialogue.setStyle({ top: top + 'px' });
            } else {
                clearInterval(pid);
                self.dialogue.remove();
            }
        }, self.INTERVAL_MSEC);
    }
});

Connection: close