enchant.js を使って 0 から 48 分以内にゲームを作ってみました! んで, その様子をビデオキャプチャーしてみました!!

phiary に引っ越しました. 毎日プログラミングやWebに関する情報を発信しています! RSS 登録してたまに覗いたり, tweet やハテブして拡散してもらえると幸いです.

Pocket

enchant.js を使って 48 分縛りでゲームを作ってみました. んでその様子をビデオキャプチャーしてみました. もちろん 48 ってのは GGJ の 48 時間からきていますw

どんなゲームを作るか?ってとこから完成までほぼ 48 分ぴったりでできたので, GGJ に参加できなかった鬱憤を晴らすことができました.

キャプチャーした動画は youbute にアップして, 下に貼りつけています. HD 画質でアップしているので, HD モードでフルスクリーンにしてもらえればコードもクッキリ見えるかと思います.

また, 47:14 もあるので HTML5 モードにして 2倍速で見るのをオススメします. 暇な人はゆっくり見てね♪

作ったゲームは少し修正して(音を足したりなど) 9leap にアップしています. 下記の “Play Game” ボタンを押してもらえればプレイできます.

Play Game Download

About

上の “Play Game” ボタンを押すと 9leap さんのサイトでプレイできます.

数値の順番に球を押していき, 1-9まで全てを消すタイムを競うシンプルなゲームです.

Code

今回作ったゲームのスクリプト部分です.

/**
 * phi
 */

// ----------------------------------------------------------------
// 定数
// ----------------------------------------------------------------
var SCREEN_SIZE = 320;
var BALL_SIZE   = 30;
var BALL_RADIUS = BALL_SIZE/2;
var COLOR_LIST  = {
    1: "yellow",
    2: "blue",
    3: "red",
    4: "purple",
    5: "orange",
    6: "green",
    7: "brown",
    8: "black",
    9: "yellow"
};
// ブラウザ対応 Audio 拡張子取得
var AUDIO_EXT = (function(){
    var ext = "";
    var audio = new Audio();
    if      (audio.canPlayType("audio/ogg") == "maybe") { ext="ogg"; }
    else if (audio.canPlayType("audio/mp3") == "maybe") { ext="mp3"; }
    else if (audio.canPlayType("audio/wav") == "maybe") { ext="wav"; }
    return ext;
})();
var SE_LIST = {
    "pinpon": new Audio("sound/pinpon." + AUDIO_EXT),
    "boo"   : new Audio("sound/boo." + AUDIO_EXT),
    "clap"  : new Audio("sound/clap." + AUDIO_EXT),
};

// ----------------------------------------------------------------
// グローバル変数
// ----------------------------------------------------------------
var game    = null;
var ballList= [];

// ----------------------------------------------------------------
// enchant 関連
// ----------------------------------------------------------------
enchant();

Object.defineProperty(Game.prototype, "seconds", {
    get: function() {
        return game.frame / this.fps;
    }
});


// ----------------------------------------------------------------
// util
// ----------------------------------------------------------------
var playSE = function(name, volume)
{
    SE_LIST[name].volume = volume || 0.5;
    SE_LIST[name].play();
    SE_LIST[name] = new Audio(SE_LIST[name].src);
};

// ----------------------------------------------------------------
// main
// ----------------------------------------------------------------
window.onload = function()
{
    game = new Game(SCREEN_SIZE, SCREEN_SIZE);
    game.fps = 30;
    
    game.onload = function() {
        var scene = game.rootScene;
        scene.backgroundColor = "black";
        
        var timerLabel = new Label();
        timerLabel.color = "white";
        timerLabel.text = "";
        scene.addChild(timerLabel);
        
        // ゲーム開始時の処理
        scene.onenter = function() {
            game.frame = 0;
            game.touchIndex = 1;
            
            for (var i=0; i<9; ++i) {
                var ball = new Ball(i+1);
                var pos = getUniquePos();
                ball.x = pos.x;
                ball.y = pos.y;
                ballList.push(ball);
                scene.addChild(ball);
            }
        };
        
        // 更新処理
        scene.onenterframe = function() {
            game.time = Number(game.seconds.toFixed(2));
            timerLabel.text = "Timer : " + game.time;
        };
    };
    game.start();
};

// 生成済みのボールと重複しない位置情報を作成する
var getUniquePos = function()
{
    var pos = {};
    
    while (true) {
        pos.x = Math.random()*(SCREEN_SIZE-BALL_SIZE);
        pos.y = Math.random()*(SCREEN_SIZE-BALL_SIZE);
        
        var ishit = false;
        for (var i=0,len=ballList.length; i<len; ++i) {
            var ball = ballList&#91;i&#93;;
            var lengthSq = Math.pow(ball.x - pos.x, 2) + Math.pow(ball.y - pos.y, 2);
            // 重複チェック
            if (lengthSq < BALL_SIZE*BALL_SIZE) {
                ishit = true;
                break;
            }
        }
        // 重複していなければ抜ける
        if (ishit == false) {break;}
    }
    
    return pos;
};


// ----------------------------------------------------------------
// ボールクラス
// ----------------------------------------------------------------
var Ball = Class.create(Group, {
    initialize: function(index) {
        Group.call(this);   // 忘れないようにね♪
        
        this.index = index;
        
        // 円表示用スプライトをグループに追加
        var sprite  = new Sprite(BALL_SIZE, BALL_SIZE);
        var surface = new Surface(BALL_SIZE, BALL_SIZE);
        var context = surface.context;
        context.fillStyle = COLOR_LIST&#91;index&#93;;
        context.strokeStyle = "white";
        context.beginPath();
        context.arc(BALL_RADIUS, BALL_RADIUS, BALL_RADIUS-2, 0, Math.PI*2, false);
        context.fill();
        context.stroke();
        sprite.image = surface;
        this.addChild(sprite);
        
        // 数値を表示
        var label = new Label();
        label.text = index;
        label.color = (COLOR_LIST&#91;index&#93;=="yellow") ? "black" : "white";
        label.font  = "20px 'Consolas'";
        label.width = BALL_SIZE;
        label.height= BALL_SIZE;
        label.x = 9;
        label.y = 3;
        label._element.style.cursor = "pointer";
        this.addChild(label);
    },
    
    ontouchstart: function() {
        if (game.touchIndex == this.index) {
            this.parentNode.removeChild(this);
            ++game.touchIndex;
            // TODO: SE を鳴らす
            playSE("pinpon", 1.0);
            
            if (game.touchIndex > 9) {
                // 称号
                var title   = null;
                switch ( Number(game.time.toFixed()) ) {
                    case 0 : case 1 : case 2 : case 3 : case 4 : case 5 : 
                        title = "ウサイン・ボルトなう!!";
                        playSE("clap");
                        break;
                    case 6  : title = "ドーピング中のベン・ジョンソン"; break;
                    case 7  : title = "2003年の末續慎吾"; break;
                    case 8  : title = "インターハイ出場"; break;
                    case 9  : title = "成人男性"; break;
                    default : title = "女子高生"; break;
                }
                var score   = (120-game.time)*100;
                var message = game.time + "秒でクリア!! あなたの称号は『" + title + "』です.";
                console.log(score, message);
                game.end(score, message);
            }
        }
        else {
            // TODO: SE を鳴らす
            playSE("boo");
            console.log("ブー!!");
        }
    }
});

いやぁ~楽しかった. ありがたいことに最近, 『どういう環境で作ってるんですか?』 とか, 『作っている流れを見たい』ってメールを頂くことが何度かあったので, それも兼ねてやってみました.

自分がコーディングしている感じを晒すのは少し恥ずかしいけど, Web プログラムは(いまのところは)あくまで趣味としてやっているのでオープンな感じでいこうと思っています.

今後もこういった変態企画をちょこちょこ挟んで行こうかなと思っています. 少しでも参考になれば幸いです.

TRACK BACK URL

POST COMMENT

メールアドレスが公開されることはありません。

COMMENT