Skip to content

shi3z式ゲームプログラミング入門 #9 同じソースから全く別のゲームを作り出す

やあshi3zだ。
5月1日にスタートした9leap 9Days Challengeも、いよいよ6日目。後半戦に入ったところだ。

毎日1本か2本の新作ゲームがアップされていて、しかもどれもが独創性に富んでいて非常に面白い。

さて、9leapに投稿されているゲームのなかには、「ZIPファイルをダウンロード」できるようになっているものがある。

これは、ゲームのソースコードを見て勉強したり、改造したりして新しいゲームを作り出せるようになっているということだ。

しかし、実際、既存のゲームを改造するとどんな新しいゲームができるのか?
今日はそのテクニックを紹介したいと思う。

題材は、enchant.jsの開発者である田中諒くんが制作した人気のワンボタンアクションゲーム「cave!!」だ。

このソースをベースとして、僕が「暴走スケボーくまさん@タクラマカン」というゲームを作ってみたぞ。

cave!!は、ボタンを離していると落ちて来て、押している間は上がり続けるという、ごく単純なゲームになっている。

上と下の壁どちらかに触れたらゲームオーバー。

さらに厄介なことに、邪魔なブロックも登場する。

これ、実はあまりenchant.jsらしくないゲームだ。

enchant.jsの特徴的な機能であるマップも、スプライトも使っていない。
ただし、イベントの管理やキー入力とタッチ入力の互換性の部分や、スタート画面とゲームオーバー画面、ハイスコア登録などはenchant.jsの機能を使っている。

僕はこのcaveのように、どんどん横へ横へスクロールしていく自動生成のゲームが作りたかった。

しかも、なだらかな斜面を利用して、ピョンピョン飛んでいくようなゲームだ。

ただし、斜面には切れ目があり、うまくジャンプしないと落ちてしまう。

流れるようなスピード感を出したかったので、スケボーに乗ったクマの素材を使うことにした。

で、タイトルは「暴走スケボーくまさん@タクラマカン」というわけ。

最後のタクラマカンは、中国にある砂漠の名前。黒い地面じゃつまらないなーと思って適当に色をつけたら砂漠っぽくなったのでつけてみた。

そのうち雪山バージョンとか作るかもしれないし。

さて、改造は、まずソースを読むところから始まる。

と言っても、基本はenchant.jsなので、読むべきはenterframeのイベント処理コールバック(無名関数)の中身がメインだ。

cave!!の場合、毎フレームごとに画面を左へスクロールさせて、右端に新しい地形を書き込むという方式がとられていた。

        buffer.clear();
        buffer.draw(sprite.image);
        sprite.image.clear();
        sprite.image.draw(buffer, -1, 0);

これだと1ドットずつしかスクロールしないのでスピード感が出ない。そこで、ここを1ドットではなくvx(x方向への速度)という変数でスクロールするようにした。

        buffer.clear();
        buffer.draw(sprite.image);
        sprite.image.clear();
    sprite.image.context.fillStyle="rgb(0,128,255)";
    sprite.image.context.fillRect(0, 0, 320, 320);
    sprite.image.draw(buffer,Math.floor( -vx), 0);

また、cave!!では地面や壁に触れること=即死だったけど、スケボーくまさんでは、むしろ地面に接していないと落ちて死んでしまう。

そのあたりの処理も書き換えた。
意外と面倒だったのは、実はスケボーに乗ったクマさんの傾きを求める部分だ。

もともと、cave!!の当たり判定はgetPixelを利用して画面上のピクセルの色を調べて当たってるかどうかを判定している。

これは非常にシンプルかつすぐれた方式なので、ここをそのまま利用した。

cave!!では衝突したらゲームオーバーなので一回しか衝突判定を行っていないが、スケボーくまさんでは、衝突するまでどのくらいの距離になるのかループによって地面スレスレのy座標がどこになるのか探している。

    if (sprite.image.getPixel(103, y)[2] ==0) {//クマの足元が空中だったら
        for(var i=y;i>0;i--){//クマの現在地の高さを求める
                if(sprite.image.getPixel(103, i)[2]!=0){
                    if( y-i > 30){//ゲーム終了:中略}
                    }else{ //地面に設置しているので高さをあわせる
                                    jumpFrame=0;
                        canJump=true;
                        y=i;
                        tmp_y=y;
                        break;
                    }
                }
            }
            for(var i=y;i>0;i--){//16ドット先の高さを調べる
                if(sprite.image.getPixel(103+16, i)[2]!=0){
                    tmp16dotAfter=i;
                    break;
                }
            }
            if(vx<10)vx+=0.1; //地上にいる間は速度10まで加速する
        }else{
            vx=vx*0.99; //空中にいる間は減速する
        }


また、角度を求めるためにクマさんの位置よりもさらに16ドット先の地面の高さを計測し、現在の地面の高さとの差をTという変数に格納している。

ここでまた、16ドットの底辺と、Tの高さを持つ直角三角形が出てくる。

直角三角形といえば、三角関数だ。
前回はcos(コサイン)とsin(サイン)を使った。この二つは三角関数の顔と言ってもいい。非常に扱いやすく、使い勝手が良い。

しかし今回は違う。
θ(シータ)が決まっているとき、cosとsinはそれぞれx,y座標を求めるのに使える。

しかし、θがわからないときはどうするか。
そう。tan(タンジェント)を使うのだ。

tanθの定義は、tanθ=高さ/底辺だ。

しかし、今は高さと底辺は解っている。つまり、tanθの値は、T/16だ。

こうして求めたtanθの値から、再びθを得るにはどうするか?

逆関数を使えばいい。
そこでatan(アークタンジェント)の登場だ。
atanは、tanθの値を渡すと、θを返してくれる便利な関数で、まさにこのような時にこそ使う。

atanを数式で書くと、逆関数の表現である-1が肩に乗って図のような表記になる。

atanを使うことで簡単に角度を求めることができた。
ただし、sinとcosに渡したのがラジアン角だったことを思い出してほしい。

ここで得られるθも、当然、ラジアン角だ。
これをenchant.jsに渡すには、ラジアン角から普通の一周360度となる角度への変換が必要になる。

これもおまじないのようなものなので以下のようにすればいい。

角度 = ラジアン角 × 180 ÷ 3.14159(円周率)

そしてあいかわらず回転角はちょっとズレてるのでちょっとずらしたりしながらあわせれば完成だ。

        rot=Math.atan( (tmp16dotAfter-prev_y)/16 ); //アークタンジェントを求める
        bear.rotation = ((rot+270)*180/3.14359-bear.rotation)/5 + bear.rotation+5;

また、ここで求めた角度は、ときたま極端な値になることがある。
フレームとフレームのつながりを考えると、あまり急にクマの角度が変わると気持ち悪い。

そこで、「あるべき角度」に「現在の角度」が近づいていく、ということにしよう。

下の式は、毎フレーム計算する場合に、少しずつ近づかせたいという場合にもちいるイディオムだ。

x = (Target - x) / Delay + x

と書くと、たとえばxが10でTargetが50、Delayが10の場合、以下のようにフレームごとにxの値が変化していく。

目的とする数、この場合は50に近づけば近づくほど、移動量が減るのがわかるだろう。

このイディオムを使うと、手軽に滑らかな動きをつくりだせるため、UIの実現などにも多用する。

ウィンドウが出てくるとき、画面にちょっとした変化をつけるとき、この動きをかなり使うのだ。

また、衝突の判定だけど、今回は地面の傾きが30ドット以上差があったらぶつかることにした。
妥当なところだと思う。
そのせいで、地面との高さに差がない時はうまく乗り越えてしまう。ま、いいか。

最後にもうひとひねり。
いまの9lepaの仕様だと、Twitterにスコアを登録できるようになっているけど、そのままだしても味気ない。

そこで、スコアを判定したら、resultの文字列を自分で作ることにした。
たとえば500メートルなら「ぜんぜんダメだな」とか、7000メートルなら「あんた天才!」、絶対無理だと思う10000メートルまで行ったら・・・こんなふうにクリア時のメッセージにひとひねりするだけでゲームをもう一度遊ぼうと言う意欲が湧いてくるものだ。

                        comment="もっと頑張れよ";
                        if(distance>500)comment="ヘナチョコだな";
                        if(distance>1000)comment="まだまだだな";
                        if(distance>1500)comment="真面目にやってるの?";
                        //中略
                        if(distance>7000)comment="あんた天才!";
                        if(distance>8000)comment="もういっそプロになっちゃえば?";
                        game.end(distance,distance+"m走りました。"+comment);

「もっと頑張ったら、次はどんなこと言うんだろう?」

そう思ってもらえたら、しめたもの。
ゲームオーバー=終わりだと思って気を緩めずにここでひと工夫しよう。
こんな感じでソースコードを改造することで、まるで別のゲーム性を持ったゲームが完成した。

9leapで公開されているソースコードをこんなふうに弄るところから始めるのも、ゲームプログラミング上達の近道だぞ

ただし、ソースを改造したゲームを投稿する場合は少し注意が必要だ。
改造ソースの再配布を禁止しているかどうかソースを読んで確かめるのも忘れずに。

とりあえず僕のゲームはどう使ってもOKだ。

また、大人(26歳以上)の人が作ったゲームは、ソース公開と再利用がOKなものが多いぞ。

ではまた次回!

文・shi3z

このエントリーをはてなブックマークに追加
はてなブックマーク - shi3z式ゲームプログラミング入門 #9 同じソースから全く別のゲームを作り出す
Post to Google Buzz
Share on GREE

Related posts:

  1. shi3z式ゲームプログラミング入門 #8 enchant.jsでプチ物理シミュレースゲームを作ってみた
  2. [enchant.js]shi3z式ゲームプログラミング #7 タイムアタックは神!
  3. shi3z式ゲームプログラミング入門 #1 72時間でゲームプログラマになる方法
  4. shi3z式ゲームプログラミング入門 #2 あと48時間でゲームプログラマになる方法
  5. shi3z式ゲームプログラミング入門 #3 “ゲームらしく”する

Facebook comments:

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*