BG を二枚重ねる

BG を二枚重ねて、その間にキャラクタを置くことにより、背景に奥行きを持たせます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. BG に使用する一般的な画像と、透けて見える部分に透明色(黒)を設定した上から重ねる画像を並べてみました。
    通路や草原の上から、花や木の背景を透明にした画像を重ねて、二枚の BG の間を通るように描画します。

  2. BG も工夫次第で、例えば上と下の BG のスクロール速度を変えて遠近感を持たせたり、三枚以上の BG を 組み合わせて、より立体感を持たせることが出来ます。
    またゲームの進行状況によりマップチップを変える方法もあります。
    例えば、最初は宝箱が閉じていますが、宝を取ると蓋が開いた画像に切り替えたりします。
  3. BG で描画された背景の画像を認識してキャラクタを動かすのですが、マップチップが多くなると直接マップチップの番号で判定することが難しくなります。
    そこでマップチップの属性を定義したテーブルを用いて、背景画像の属性を認識します。
    次の数字の並びは、マップチップの属性を定義したテキストファイル(MapAtt.txt) の一部で、マップチップと一対一に対応しています。
    0 が通ることの出来ないセルで、1 が通ることが出来るセルです。
    透明色を設定した木の属性などは、木の裏側を通るので「通行可能」を設定しています。
    2 は宝箱で、最初は閉じていますが、宝を取ると蓋が開いた画像に切り替えてみましょう。
    3 はダンジョンなどの入り口ですが、今回は使いません。
    4 はドアでアイテムを持っていると開く画像に切り替えるのですが、今回は使いません。
    //Map.png の属性(0:通行不可  1:通行可  2:宝箱  3:ワープ 4:ドア)  27*16
      1,  1,  1,  1,  1,  1,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1,
      1,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
                      :
      0,  0,  0,  0,  3,  3,  0,  0,  1,  1,  2,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  3,  0,  0,  1,  1,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  3,  0,  0,  1,  1,
                      :
    
  4. Game1.cs を編集して二枚の BG の間で、キャラクタが歩き回るプログラムを作成します。
    プログラムの最初に Image Object Class と BG Object Class を定義します。
    Image Object Class の説明は Image Class でアニメーション を参照して下さい。
    BG Object Class の説明は Mapchip を組み合わせて背景を作成 を参照して下さい。
    Draw メソッドを呼び出す度に SpriteBatch をパラメータで渡すのはセンスが悪いと思い、コンストラクタで渡すようにしてみました。
    但しその分だけ領域が増えるので、シューティングゲームのように沢山の Object を生成するゲームには向きません。
    namespace Main
    {
        #region ★Image Object Class
        public class Image
        {
            //Image Object Class を定義する
        }
        #endregion
    
        #region ★BG(Back Ground) Object Class
        public class BG
        {
            //BG(Back Ground) Object Class を定義する
        }
        #endregion
    
  5. class Game1 の最初に次の領域を宣言して下さい。
    myTexture; が BG に使用するマップチップ画像です。
    charTexture; が BG 上を動き回るキャラクタの画像です。
    Image bunny; でキャラクタの Image Class を定義します。
    direct はキャラクタの向いている方向(0〜3)です。
    BG bg; が Back Ground Object Class の定義です。
    mapx, mapy には宝箱の画像を切り替えるために、上のマップ(mapu)の Index を保存します。
    mapd と mapu が二枚重ねる上側と下側のマップチップの並び情報です。
    att がマップチップの属性を定義したテーブルです。
            Texture2D   myTexture;     // 描画に使用するテクスチャ
            Texture2D   charTexture;   // キャラクタのテクスチャ
            Image       bunny;         // Image Class のバニーガール
            int         direct = 2;    // キャラクタの向き
            BG          bg;            // Back Ground Class
            int         mapx, mapy;    // BG Map Index
            //BG Map Data (縦=25個 800ピクセル, 横=30個 960ピクセル)
            int[,]      mapd = new int[25, 30]; // 下のマップ
            int[,]      mapu = new int[25, 30]; // 上のマップ
            int[]       att  = new int[27*16];  // Mapchip の属性(27*16)
        
  6. LoadContent() メソッドで画像をロードして、BG Object Class と Image Object Class を初期化します。
    Chr17.png はキャラクタが前後左右に動き回る様子を2枚のペアでアニメーションする画像です。
    Image(spriteBatch,charTexture,1,8,960,800) の 1,8 は縦に並んだ8枚の画像を切り分けます。
    960,800 はウインドウのサイズです。
    キャラクタの座標は32ドットの境界に設定して下さい。今回は 32,0 からスタートします。
                charTexture = Texture2D.FromFile(graphics.GraphicsDevice, "c:\\data\\test\\Chr17.png");
                bunny = new Image(spriteBatch,charTexture,1,8,960,800);
                bunny.sp_no = 0;
                bunny.pos = new Vector2(32, 0);
        

  7. Map.png がマップチップの画像です。
    BG(spriteBatch,myTexture,32) の 32 はマップチップのサイズです。
    BG32D.txt と BG32U.txt がマップチップの並び情報がタイプされたテキスト形式のファイルです。
    MapAtt.txt は先に説明したマップチップの属性がタイプされたファイルです。
                myTexture = Texture2D.FromFile(graphics.GraphicsDevice, "c:\\data\\test\\Map.png");
                bg = new BG(spriteBatch,myTexture,32);
                bg.GetTxt("C:\\data\\test\\BG32D.txt", mapd);
                bg.GetTxt("C:\\data\\test\\BG32U.txt", mapu);
                bg.GetTxt("C:\\data\\test\\MapAtt.txt", att);
        
  8. BG32U.txt の最初の部分です。
    このファイルは MapChip を組み合わせて背景画像を作成するツール(Mapedit)で作成することが出来ます。
    //Map  Ver 3.0  前田 稔
    //Map.png
    //  30,   25,   32,   32,  Map の幅と高さ, Mapchip の幅と高さ
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 299, 0, 
    0, 0, 0, 0, 0, 0, 166, 167, 208, 209, 209, 209, 209, 210, 166, 167, 0, 0, 259, 0, 0, 0, 0, 217, 218, 0, 0, 0, 315, 0, 
    0, 0, 0, 0, 0, 0, 166, 167, 224, 225, 225, 225, 225, 226, 166, 167, 0, 0, 259, 0, 202, 196, 197, 233, 234, 196, 197, 198, 0, 0, 
                  ・・・・・
    
  9. Update() メソッドでキャラクタの移動と、キー操作を検出して進行方向の切り替えを行います。
    キャラクタの移動は Image Class の Move() メソッドを呼ぶだけです。
    KeyCheck() がキーが押された方向の BG を調べて、進行方向を切り替えるメソッドです。
                // TODO: Add your update logic here
                bunny.Move();       //キャラクタの移動
                KeyCheck();         //キーの操作と進行方向のチェック
        
  10. Draw() メソッドで BG とキャラクタを描画します。
    bg.Draw(mapd); で下側の背景画像を描画します。
    bunny.Draw(); で下側の背景画像の上からキャラクタを描画します。
    次の2行のコードで、進行方向に合わせて2枚の画像を切り替えながらアニメーションします。
    bunny.Loop(gameTime, 200);
    bunny.sp_no = (bunny.sp_no%2) + direct + direct;
    bg.Draw(mapu); で、キャラクタの上から上側の背景画像を描画します。
                spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
                //bg.Draw(spriteBatch);
                bg.Draw(mapd);
                bunny.Draw();
                bunny.Loop(gameTime, 200);
                bunny.sp_no = (bunny.sp_no%2) + direct + direct;
                bg.Draw(mapu);
                spriteBatch.End();
        
  11. キーが押された方向の BG を調べて、進行方向を切り替える KeyCheck() メソッドです。
    BG Map に沿ってキャラクタが動くようにセル(32ドット)の切れ目でキーを調べます。
    bunny.pos が32の倍数になるように、初期値や増分を設定して下さい。
    bunny.vect をクリアして、とりあえずキャラクタの移動を止めます。
    方向キーを調べて direct を設定します。
    キーが押されていないときは、そのまま return して、その場で立ち止まります。
            private void KeyCheck()
            {   KeyboardState keyState;
                int     sel;
    
                //セルの切れ目でキーを調べる
                if ((int)bunny.pos.X % 32 != 0 || (int)bunny.pos.Y % 32 != 0) return;
                bunny.vect.X = 0;
                bunny.vect.Y = 0;
                keyState = Keyboard.GetState();
                if (keyState.IsKeyDown(Keys.Up))         direct = 0;
                else if (keyState.IsKeyDown(Keys.Right)) direct = 1;
                else if (keyState.IsKeyDown(Keys.Down))  direct = 2;
                else if (keyState.IsKeyDown(Keys.Left))  direct = 3;
                else return;
        
  12. GetAtt() メソッドで、direct の方向の BG の属性(通行可能か否か)を取得します。
    att[sel] == 2 のときは、宝箱の前に立っているので、蓋を開きます。
    mapu[mapy, mapx] に 2 を加えて蓋が開いた画像に切り替えます。
    当然のことながら、2を加えれば蓋が開いた画像になるように、マップチップの画像を作成しておかなければなりません。
    mapy, mapx は、グローバル領域(BG の所)で定義していて、GetAtt() メソッドで設定されています。
    att[sel] が 1 のときは、通行可能なセルなので、進行方向に合わせて bunny.vect を設定します。
                sel = GetAtt(direct, bunny.pos);
                if (sel==-1)    return;     //画面の外
                if (att[sel] == 2)          //宝箱
                {
                    mapu[mapy, mapx] += 2;
                    return;
                }
                if (att[sel] != 1) return;  //その方向には進めない
                switch(direct)
                {
                    case 0:     //上
                        bunny.vect.Y = -2;  break;
                    case 1:     //右
                        bunny.vect.X = 2;   break;
                    case 2:     //下
                        bunny.vect.Y = 2;   break;
                    case 3:     //左
                        bunny.vect.X = -2;  break;
                }
            }
        
  13. direct の方向の BG を調べて、属性(通行可能か否か)を取得する GetAtt() メソッドです。
    宝箱の画像を切り替えるために、mapy, mapx をグローバル領域(BG の所)で定義しています。
    Index が配列の大きさを超えると実行時にプログラムが中断してしまいます。
    そこで mapy, mapx のリミットチェックをしています。
    mapu[mapy, mapx] が 0 のときは、透明のセルです。
    上側のセルが透明のときは、下側のセルの属性を取得します。
            public int GetAtt(int houko, Vector2 pos)
            {
                mapx = (int)(pos.X / 32);
                mapy = (int)(pos.Y / 32);
                switch (houko)
                {
                    case 0:     //上
                        mapy -= 1;  break;
                    case 1:     //右
                        mapx += 1;  break;
                    case 2:     //下
                        mapy += 1;  break;
                    case 3:     //左
                        mapx -= 1;  break;
                }
                if (houko>3 || mapx<0 || mapx>=mapu.GetLength(1) || mapy<0 || mapy>=mapu.GetLength(0))
                {   Console.WriteLine("Map Index Limit Error X={0} Y={1}",pos.X,pos.Y);
                    mapx = 0;
                    mapy = 0;
                    return -1;
                }
                if (mapu[mapy, mapx] != 0) return (mapu[mapy, mapx]);
                return (mapd[mapy,mapx]);
            }
        

【演習】

  1. BG とキャラクタの画像を作成してプログラムを動かしてみて下さい。
  2. 宝箱だけで無く、扉やアイテムの画像などを切り替えてみて下さい。

超初心者の方のためにソースコードを掲載します。 (^_^;)
ソースコード抜粋

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(XNA(C#) game program)