ブロック崩しゲーム

  1. ゲーム機の構成


    1. 表示

      16*16のLEDアレイを利用する。これを。PIC16F876を利用して各画素をドライブ(点灯)します。

    2. 点灯方法

      LEDアレイは縦16,、横16の配列になっています。横線16本をx0〜x15とし縦線をy0〜y15とします。各線の交点には、縦線から横線にLEDが接続されています。今、y7に電圧を加え、x8をグランドに接続すれば、交点のLEDが点灯します。y0からy15に適当に電圧を加えx8をグランドに接続すれば、x8の行の選択されたLEDが発光します。
       同様にy0〜y16に電圧を加え、x0をグランドに接続すれば、最下行のLEDが点灯します。
      もう、わかりましたね。16*16 のすべてのLEDを点灯するには、上の行から順にy0〜y7 に点灯したいパターンにしたがい電圧を加え、X15のみを0にします。次に、下の行の点灯パターンで電圧を加え、x14をグランドにする。これをくりかえして、画面全体を表示します。

       下図は4*4のLEDアレイの説明図です。R0,R1,R2,R3 に行のデータを設定し、C1ラインを0にすると行信号がHになっているC1列のLEDが点灯します。



    3. デコーダ

       x0からX15はどれか1本のみをグランドにすればよく、複数の信号を同時に接続する必要はありません。このような場合、デコーダ回路を用いると、接続に必要な信号線が節約できます。上の図で左のデコーダには、D,C,B,A の4本の信号線しか接続していません。(D,C,B,A )=(0,0,0,0) のときx0 が、(D,C,B,A )=(1,1,1,1) のときx15のみが1となる回路をデコーダ回路と呼びます。このようなデコーダ回路は、標準型の論理ICとして市販されています。

  2. 回路構成


    1. 入力回路

       ここでは、ブロック崩し専用回路を設計します。ブロック崩しのパッドの位置の入力はスライダ型の可変抵抗器を利用します。この電圧をAD変換し、この値により、パッドの位置を定めます。AD変換の入力端子にはポートAのA0を利用します。可変抵抗器の変化は直線的ではないので、プログラムで補正をしています。

    2. デコーダ回路

       LEDアレイの接続で、row1〜row8はポートBを、row9〜row16はポートCを利用します。D,C,B.AはポートAのA1〜A4を接続します。
       4入力16出力のデコーダICはないので3入力8出力のデコーダ(型番 74138)を二つ利用することにします。A,B,Cを各デコーダに入力し、Dの信号で二つのデコーダを切り替えて使用します。

    3. トランジスタアレイ

       他の問題は、デコーダ回路の許容電流です。デコーダ回路の信号には、y0〜y15 の16個ののLEDが接続されており、これらをすべて点灯すると、一つのLEDの電流が10mAとすると、160mAの電流を流すことが必要です。しかし、論理回路で流せる電流は多くても10mAです。
       そこで、電流増幅のため、トランジスタが8個集積されたICを利用します。デコーダの出力はまず、この、トランジスタアレイの入力に接続しこの出力をx0〜x15に接続します。

    4. 回路図




    5. 実装

       アレイLEDはコネクタで接続しています。下に2本のドライバ、スライダ入力のバッファ用オペアンプとPIC、2本のシフトレジスタ、スライダ抵抗の順に組み込んでいます。
       配線は手配線です。



  3. プログラム


    1. プログラムの構成

      main()は以下のようです。AD変換の設定と、ABC各ポートの入出力を設定し、Loopでループ処理を行います。
      void main( void )
      {
          // とりあえず待つ
          delay_ms( 100 );
          
          // 入出力ポートの設定
          setup_adc_ports( RA0_ANALOG );
          setup_adc( ADC_CLOCK_DIV_32 );
          set_tris_a( 0b00000001 );    // in:RA0 out:RA1-4
          set_tris_b( 0b00000000 );    // out:RB0-7
          set_tris_c( 0b00000000 );    // out:RC0-7
      
          // 初期化処理
          Init();
      
          // 無限ループ
          while( 1 )
          {
              Loop();
          }
      }
      Loopの処理は、まず、パッドの位置をスライダのAD変換からPad()で定め、次にCollision()で 衝突判定をします。衝突は、壁、ボール、ブロック、床、などを個別に判定します。床に衝突したら、終了です。
       Moveでボールの位置を移動します。 Move( &x, &y, vx, vy ); で、(x,y) はボールの位置、vx,vy はボールの速度です。たとえば、壁などの衝突が検知されると、vx の符号が変化します。
       Show() は画面表示を行います。2回表示するのは時間稼ぎです。
      void    Loop( void )
      {
          // パッド移動
          Pad();
      
          // 衝突判定と向き調整
          Collision();
      
          // ボール移動
          Move( &x, &y, vx, vy );
      
          // 表示
          //Showには時間遅延なし、したがってshow1回程度の時間余裕
          //ただし、縦方向のバイト合成をしたためで、縦横のドライブ方向を変えれば、
          //時間余裕が出る
          Show();
          Show();
      }

    2. データ構造

       ゲームのフィールドは16*16 の画素から構成されますが、可能なメモリ容量を超えるので、1画素を2ビットで構成し、16*4 バイトで画面を記録します。1画素は障害無し(00)、壁(01)、ブロック(10)、の2bitで表現します。(x,y)画素の情報は、matrix[y*4+x/4] のバイト内の 2bit に記録されます。
       createBlock(x,y) は (x,y)の画素に ブロック のマーク(10)を記録し、この位置のLEDを点灯します。この関数は、フィールド上部にブロックを配置したり、画面に文字やパターンを表示するために利用します。vreateWall(x,y)は、(x,y)の画素に 壁のマーク(10)を記録します。初期のフィールドは CreateRound( ) で作成しています。
       ボールの位置は変数 x,y 、速度は vx,vy で記録し、小数点で計算を行います。IsWall(),IsBlock() などの関数は指定位置に障害があるかどうかをしらべます。Show()は画面表示関数で、ポートBに縦上部、ポートCに縦下部のデータを作成し、横方向はポートAの4bitをハードウエアでデコードして走査しています。

    3. ソース

      ここにソースがあります。

  4. 今後の問題点など

    1. 汎用化
      このシステムを他のゲームでも利用できるよう、汎用化を考えています。一つは、音の問題で、B,CポートをすべてLEDアレイの制御に利用してしまったため、余分の端子がなくなってしまいました。そこで、シフトレジスタを追加し、データとクロックの2本でデータを送り込むよう変更します。余裕がでたピンで、音やシリアル接続が可能になります。

    2. 入力
       このシステムは入力はスライダだけです。これでは他のゲームが利用しにくいので、4個の方向キーと2個の制御キーに改めるつもりです。