難易度の調整と改造について

iPhone+Safariでは予想よりもタップの反応が悪いので隕石のサイズを48×48ピクセルとしています。ただ、プログラム内では出現位置などを除いて、ある程度自由なサイズにすることができます。隕石のサイズを大きくすればタップしやすくなります。逆に小さくするとタップしにくくなりゲームが難しくなります。32×32ピクセル以下にするとゲームにならないでしょう。

隕石のサイズを変えずにゲームの難易度を変えることもできます。ゲームが難しすぎる場合には関数startMeteo()内の以下の1行を修正してください。

            mSpeed[n] = Math.random() * cnt + 1;    // 落下速度を設定

例えば以下のようにするとゲームの難易度と落下速度が比例しないため簡単になります。

            mSpeed[n] = Math.random() * 3 + 1;  // 落下速度を設定

逆にゲームが簡単だ、という人は以下のようにすると落下速度が速くなるため難易度があがります。

            mSpeed[n] = Math.random() * cnt * 2 + 1;    // 落下速度を設定

今回作成したゲームを見る限りではiPhone+Safariでも、ある程度のリアルタイム性をもったゲームは可能だと言えます。また、今回のゲームはもともとMacOS XのDashboard用で動作していたものをiPhone用に改良したものです。Dashboardで作成したゲームがあればiPhone用に移植するのも面白いのではないでしょうか。

完成したプログラムコード

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
        <meta name="viewport" content="initial-scale=1">
        <meta name="viewport" content="user-scalable=no">
    <title>隕石破壊ゲーム</title>
    <style type="text/css"><!--
        body { margin:0px; }
        #m0,#m1,#m2,#m3,#m4,#m5,#m6,#m7,#m8,#m9 { position:absolute; top:-10px;left:0px; margin:0px; }
        #bak { position:absolute; left:0px;top:-100px; }
        #dispSC { position:absolute; left:5px;top:10px; font-size:24px; color:white; font-family:Optima-Bold; }
        #msg { position:absolute; left:90px;top:90px; font-size:24px; color:white; font-family:Optima-Bold; }
        #gForm { position:absolute; left:100px;top:160px; margin:0px; padding:0px; }
        #gForm input { font-size:20pt; }
    --></style>
    <script type="text/javascript"><!--
        // =================================================================
        // 隕石情報の初期化
        // =================================================================
        var mx = new Array();   // 隕石のX座標
        var my = new Array();   // 隕石のY座標
        var mOffset = new Array();  // 隕石の移動方向(オフセット)
        var mSpeed = new Array();   // ゲーム速度
        var mFlag = new Array();    // 隕石が存在するかどうかのフラグ
        // =================================================================
        // ゲーム開始
        // =================================================================
        function startGame(){
            document.getElementById("townImage").src = "images/bg.png";
            document.getElementById("gForm").style.visibility = "hidden";
            document.getElementById("msg").innerHTML = "";
            document.getElementById("bak").style.top = -100;
            document.getElementById("dispSC").innerText = "Score 0";
            for (var i=0; i<10; i++){
                mFlag[i] = false;   // 最初の時点では隕石は存在しないことにする
                mx[i] = 0;  // 隕石の開始X座標
                my[i] = -50;    // 隕石の開始Y座標
                var mID = "m"+i;    // 隕石のIDを求める
                document.getElementById(mID).style.left = mx[i];    // 指定した横位置に隕石の座標を設定
                document.getElementById(mID).style.top = my[i]; // 指定した縦位置に隕石の座標を設定
            }
            gameRank = 0;   // ゲームの難易度
            cnt = 3;    // 出現させる隕石の数。最初は3つにする
            score = 0;  // スコアを0点にする
            gameFlag = true;    // ゲーム中どうかのフラグ (trueならゲーム中)
            bakFlag = false;    // 爆発表示フラグ
            for (i=0; i<cnt; i++) startMeteo(i);    // 隕石を生成する
            timerID = setInterval("moveMeteo()",100);   // 0.1秒ごとに隕石を移動させる関数を呼び出す
        }
        // =================================================================
        // 隕石出現
        // =================================================================
        function startMeteo(n){
            mx[n] = Math.floor(Math.random() * 220) + 50;   // X座標を設定
            my[n] = Math.floor(Math.random() * 30) - 30;    // Y座標を設定
            mOffset[n] = (Math.random() - 0.5 ) / 4;    // 左右の移動量(オフセット)を設定
            mSpeed[n] = Math.random() * cnt + 1;    // 落下速度を設定
            mFlag[n] = true;    //  隕石が存在することを示す
        }
        // =================================================================
        // 隕石の移動処理
        // =================================================================
        function moveMeteo(){
            for (var i=0; i<cnt; i++){  // 最大表示数(cnt)分繰り返す
                mx[i] = mx[i] + mOffset[i]; // X座標を加算する
                my[i] = my[i] + mSpeed[i];  // Y座標を加算する
                var mID = "m"+i;    // ミサイルのIDを求める
                document.getElementById(mID).style.left = mx[i];    // 指定した位置に横隕石の座標を設定
                document.getElementById(mID).style.top = my[i]; // 指定した位置に縦隕石の座標を設定
                if (my[i] > 240){   // 240は地平線でこれ以上になったら隕石が街に落下したとみなす
                    clearInterval(timerID); // タイマーを停止する
                    gameFlag = false;   // ゲームフラグをfalseにしゲームを継続できないようにする
                    document.getElementById("msg").innerHTML = "Game Over!!<br>Score = "+score; // スコアを表示
                    document.getElementById("townImage").src = "images/end.png";    // 街が炎上した画像にする
                    document.getElementById("gForm").style.visibility = "visible";  // ゲーム開始ボタンを表示する
                }
            }
            bakuhatsu();    // 爆発の表示
        }
        // =================================================================
        // 爆発処理
        // =================================================================
        function bakuhatsu(){
            if (!bakFlag) return;   // ゲーム中でなければ以後の処理を行わない
            bakCount--; // 爆発時間を示すカウンタを減らす
            var num = bakCount & 1; // 0または1にする
            document.getElementById("bak").src = "images/bak"+num+".gif";
            if (bakCount < 0 ){
                document.getElementById("bak").style.top = -100;
                bakFlag = false;
            }
        }
        // =================================================================
        // 隕石にヒット
        // =================================================================
        function hit(n){
            if (!gameFlag) return;  // ゲーム中でなければ以後の処理を行わない
            score = score + Math.floor(my[n]);  // スコアを加算する
            document.getElementById("dispSC").innerText = "Score "+score;
            bakFlag = true; // trueとし爆発中であることを示す
            bakCount = 10;  // 爆発される時間(カウント)
            bakX = mx[n];   // 爆発画像の横位置は現在の隕石の位置と同じ
            bakY = my[n];   // 爆発画像の縦位置は現在の隕石の位置と同じ
            document.getElementById("bak").style.left = bakX;
            document.getElementById("bak").style.top = bakY;
            startMeteo(n);  // 新たな隕石を出現させる
            gameRank++; // ゲームの難易度を上げる
            if ((gameRank & 7) == 0){   // 8個破壊するごとに、一度に表示される隕石の数を増やす
                cnt++;
                if (cnt > 10) cnt = 10; // 隕石の最大表示数を超えないようにする
                startMeteo(cnt);    // 新たな隕石を出現させる
            }
        }
    // --></script>
    </head>
    <body>
        <img src="images/bg.png" id="townImage">
        <img src="images/meteo.png" id="m0" onMouseDown="hit(0)">
        <img src="images/meteo.png" id="m1" onMouseDown="hit(1)">
        <img src="images/meteo.png" id="m2" onMouseDown="hit(2)">
        <img src="images/meteo.png" id="m3" onMouseDown="hit(3)">
        <img src="images/meteo.png" id="m4" onMouseDown="hit(4)">
        <img src="images/meteo.png" id="m5" onMouseDown="hit(5)">
        <img src="images/meteo.png" id="m6" onMouseDown="hit(6)">
        <img src="images/meteo.png" id="m7" onMouseDown="hit(7)">
        <img src="images/meteo.png" id="m8" onMouseDown="hit(8)">
        <img src="images/meteo.png" id="m9" onMouseDown="hit(9)">
        <img src="images/bak0.gif" id="bak">
        <img src="images/bak1.gif" width="1" height="1">
        <img src="images/end.png" width="1" height="1">
        <div id="dispSC">Score </div>
        <div id="msg"></div>
        <form id="gForm">
            <input type="button" value="Start" onClick="startGame()">
        </form>
    </body>
</html>

*なお、このプログラムは自由に改造して配布しても問題ありません。画像やプログラム書き換えて楽しんでください。