もぐら叩きゲーム(もどき)

  1. 概要

    出てくる「モグラ」をクリックして得点を稼ぐゲームを作ります。このプログラムを作るにあたって、 http://bitarts.jp/tech/java/mogura/に掲載されているソースを参考にさせていただきました。

  2. 手法

    1. ゲーム

       下がゲームの画面です。「スタート」すると箱が開きます。開いている箱をクリックすると得点になります。右が文字盤で、残り時間と得点を表示します。


    2. 素材を用意する

       「もぐら叩き」ゲームは画像や効果音など視覚、聴覚にかかわる要素が大きな役割を持ちます。したがって、画像を変更すれば、クリックするものをモグラではなく箱から出てきた宝石や爆弾にしたりもっと独特な生き物にしたりすることも可能です。
       今回は開いた宝箱の中に入ってきた宝石をクリックすると加点し、爆弾をクリックすると減点するゲームをつくりますから、途中のアニメーションとあわせて3×3=9種類の画像を用意します。(下図参照、48×48px)

      画像一覧
      閉まっている箱 開きかけの箱1 開きかけの箱2
      宝石の箱1 宝石の箱2 得点時
      爆弾の箱1 爆弾の箱2 減点時

    3. 音ファイル

      効果音ファイルとして、「Pikari.au」、「Dokan.au」の2つのAUファイルを用意します。.auは web で標準の音ファイルで、フリーソフトの 「Paseri」などで、Windows 標準の wav ファイルを変換して作成することができます。
       音ファイルは AudioClip atari; として
        atari = getAudioClip(getCodeBase(),"Pikari.au");
      で読み込み、
       atari.play()
      で再生できます。

    4. 乱数

      Math.random() は0~1.0 の乱数(でたらめな数)を返します。「開く」箱の位置やあいている時間を乱数で設定すると、変化をつけることができます。

    5. 配列
      箱の数を可変にするため、2次元でなく1次元の配列を利用します。matsplash を仮想2次元の配列のサイズとすると、matrix[mx+my*matsplash] で、配列にアクセスします。

  3. プログラム

    1. 読み込み

      以下のファイルを読み込みます。getImage()とgetAudioClip()を使います。画像の読み込みにはMediaTrackerもセットします(簡単のため、0番のファイルのみチェックします)。

          gra    = getGraphics();
                      
      //画像の用意
         imgbox0 = getImage(getDocumentBase(), "box0.gif");//閉まっている箱
         mt.addImage(imgbox0, 0);
         imgbox1 = getImage(getDocumentBase(), "box1.gif");//開きかけの箱1
         mt.addImage(imgbox1, 0);
        imgbox2 = getImage(getDocumentBase(), "box2.gif");//開きかけの箱2
        mt.addImage(imgbox2, 0);
        ......
      //効果音の用意
          atari = getAudioClip(getCodeBase(),"Pikari.au");//当たりの音
         // atari = getAudioClip(getCodeBase(),"tone1.au");
          hazure = getAudioClip(getCodeBase(),"Dokan.au");//はずれの音
          batan = getAudioClip(getCodeBase(),"Batan.au");//現在未使用

    2. 文字盤の用意

      画面右に残り時間、最高得点、現在の得点を表示する文字盤を用意します。ここでは、文字のサイズ、表示位置を指定して描画しています。文字枠は背景を黒とし、文字を白で表示します。また、setFont()で文字のフォントとサイズを指定します。

           //文字枠を用意する
          g.setColor(Color.black);//背景色
          g.fillRect(Width,0,300,Height);
          
          g.setColor(Color.white); //文字色
                         
          g.setFont(new Font("TimesRoman", Font.PLAIN, 20));//時間のフォント指定
          g.drawString("Time : ", Width + 10, 30);
                           
          g.setFont(new Font("TimesRoman", Font.PLAIN, 14));//得点のフォント
          g.drawString("HiScore : ", Width + 15, 60);
          g.drawString("Score : ", Width + 15, 80);
        .....
         //ボタンの案内
         if (run_mode == 0) {
               g.setColor(Color.yellow);
               g.setFont(new Font("MS ゴシック", Font.PLAIN, 11));
               g.drawString("- ボタンを 押してください -", Width + 10, 100);
           }

       これらの表示は、ゲームが進行すると順次書き換えられていきます。なお、ここでの Width、Heightはゲーム画面(宝箱が表示される領域)の幅と同じ数値になるよう設定されています。

    3. アニメーション−宝箱を開けて中身を見せる

       画面上には3×3=9個の箱があります。この箱の状態記憶に matrix[] を使います。この値が負のときは、閉まっている状態です。各箱がまずそれぞれに乱数 Math.random() によって発生した乱数によって開く/開かないを決定します。
       さらに開くことになった箱はその表示時間を乱数によって決定します。この乱数設定によって当たりと外れの比率、宝箱が開く頻度が変化します。箱の中身(宝石/爆弾)は state[] で記録します。箱の中身が1種類の場合は2つ目の if ステートメントは不要です。

            int pos = (int)(Math.random() * 1000 % (matsplash * matsplash));
      
            if (matrix[pos] <= 0 && (int)(Math.random() * 100) > Boxes) {
              //Score:得点で寿命の値を変更する
              matrix[pos] =1 + (int)(Math.random() * 1000 % SpeedLen + SpeedMin);//寿命設定
              state[pos] = 0;
               if( 31 <= (int)( Math.random() * 100) && (int)( Math.random() * 100) <= 40)
                        { state[pos] = 1; }//爆弾の箱にする
            //  System.out.println(String.valueOf(matrix[pos]));
              gra.drawImage(imgbox1, matsize_x*(pos%matsplash),
                         matsize_x*(pos/matsplash), this);
                         
              continue;
            }

       開いた箱はmatrix[pos]の値が1以上になります。この値が箱の中身が表示される時間となり、開いている内は少しずつ減っていきます。値が0になると箱は閉じて、また開く/開かないの判定を始めます。これを制限時間の間くり返します。

            for (int i=0,ly=0; ly < matsplash; ly++) {
              for (int lx=0; lx < matsplash; lx++, i++) {
                if (matrix[i] > 0) {
                  matrix[i]--;
                  if (matrix[i] > 1) {
                    if (matrix[i] % 8 == 0){
                      drawbox = imgboxA1;//中身画像1
                      if (matrix[i] % 8 == 0 && state[i] == 1)
                      drawbox = imgboxB1;}
      
                    else {  
                        drawbox = imgboxA2;//中身画像2
                        if(state[i] ==1) 
                        {drawbox = imgboxB2;}
                                    }
                        } else {
                         if (matrix[i] == 0)
                         drawbox = imgbox0;
                        else
                         drawbox = imgbox1;
              
                  drawbox = imgbox2;
                  }
                } else if (matrix[i] == -1) {
                  drawbox = imgboxA3;//クリック画像宝石
      
                  if (state[i] == 1){
                   drawbox = imgboxB3;}//クリック画像爆弾
                   matrix[i] = 0;
                  
                } else {
                  drawbox = imgbox0;
                }
                gra.drawImage(drawbox, matsize_x*lx, matsize_x*ly, this);
              }
            }
          }
        }
      }
    4. クリック時のイベントと得点処理

       マウスクリックがあった場所の値が0以上であれば(開いていれば)、点数処理を行います。単純に1点ずつ増えていくものであれば、3つ目の if は要らず「Score++;」の1文で済みます。今回は(爆弾の場合)減点するので2択になります。減点が重なってもマイナス点を発生させたくなければ、0点以下になったときの処理が必要です。
       最後に算出された点数を文字盤に表示します。gra.setColor 以下の記述です。背景と同じ色の四角形で塗りつぶしてから新しい点数を表示させないと、文字が見えなくなります。

        public boolean this_mousePressed(MouseEvent e) {
        //マウス押し下げ処理
          int x = (e.getPoint()).x;
          int y = (e.getPoint()).y;
          //System.out.println("pressed:"+x+":"+y);
          if (run_mode == 1 && x <= Width) {
            int mx = x / matsize_x;
            int my = y / matsize_y;
            //System.out.println("pressed:"+mx+":"+my);
      
            if (matrix[mx+my*matsplash] > 0){//得点処理
              int i = mx+my*matsplash;
              matrix[mx+my*matsplash] = -1;
              if(state[i] == 0){
               Score = Score + 1;//加点
               atari.play();}//効果音
            else {
              Score = Score - 2;//減点
              hazure.play();//効果音
            } 
       
            //得点表示
            gra.setColor(Color.black);
            gra.fillRect(Width+60, 65, 150, 30);
            gra.setFont(new Font("Helvetica", Font.PLAIN, 18));
            gra.setColor(Color.white);
            gra.drawString(String.valueOf(Score), Width + 75, 80);
            if (Score >40) {Boxes = 95;}
            }
          }
          return true;
        }
      ソースはこちらを参照してください。画像、音ファイルはこちらです(ダブルクリックで解凍します)。

  4. 実行

    こんな感じです。画面が小さいので難易度はかなり低いです。環境によっては音も出ます。爆弾を1つくらい取ってしまっても、宝石を集めていけば40点くらいはいきます。


  5. 今後の発展とアンケート

    1. 発展

      このもぐら叩き系ゲームを発展させる案として、いくつか紹介します。
      • BGMを流す←常に音がある状態を作る
      • 宝箱の背景画像を用意する←どこかの洞窟や宝物庫の中とか。
      • ハイスコアを記録しランキングする←誰がどれくらい得点できたか、気になりません?
      • 宝石、爆弾アイテムを追加する←優先順位をつけてプレイする
      • アニメーションを複雑にする←クリックしたとき本当は画像を点滅させる
    2. アンケート

      1.プログラムの複雑さ 1.思ったより簡単 2:こんなものかな 3:けっこうたいへん 4:理解できん
      2.プログラムの操作性 1.楽しめる 2.こんなまものかな 3.:わかりづらい
      3.ゲームプログラム 1.作ってみたい 2.できればつくりたい 3.できそうもない
      4:電子ゲーム 1.すきなほう 2.たまにはする 3.あきた/しない