ページ 11

C#とDXライブラリでマップの描画

Posted: 2012年4月15日(日) 13:43
by ぴーまん
]C#とDXライブラリの組み合わせでアクションゲームのサンプルを作っています。
とりあえずマップを描画(マリオのようなブロックの配置)しようと思い,二次元配列とfor文を組み合わせてやってみたのですが,描画されません。
エラーはなく,実行はされるのですが,描画されないのです。
今まで軽いシューティングゲームぐらいしか作ったことがなかったので,二次元配列とfor文を組み合わせたものはちょっと苦手です。
以下にコードを載せました。どこがダメなのでしょうか。。。

コード:

using System;
using System.Windows.Forms;
using DxLibDLL;
namespace SampleGame
{
    public class Game
    {
        [STAThread]
        public static void Main()
        {
            Ziki ziki; //Zikiクラスのziki変数を用意
            Block[] blocks;//Blockクラスのblocks配列を用意
            int input;//自機の操作を割り当てる変数
            int zikigraph;//自機のグラフィック
            int blockgraph;//ブロックのグラフィック
            const int BLOCK_NUM = 300;//ブロックの個数
            int[,] haiti = 
            {
                { 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1},
                { 0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,0,0,0},
                { 0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0},
                { 0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0},
                { 0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0},
                { 0,1,0,0,1,1,0,0,0,0,0,1,1,1,0,1,1,1,0,0},
                { 0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0},
                { 0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1},
                { 0,1,0,0,1,0,1,0,0,0,1,0,0,0,1,1,0,0,0,1},
                { 0,1,1,1,1,0,1,0,1,1,1,0,0,0,1,1,1,1,1,1},
                { 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
                { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
                { 0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0},
                { 0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1},
                { 0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0},
             };//ブロックの配置,1なら配置,0なら配置しない
            DX.ChangeWindowMode(1);
            if (DX.DxLib_Init() == -1) return;
            DX.SetDrawScreen(DX.DX_SCREEN_BACK);
            zikigraph = DX.LoadGraph("data\\自機.png");//画像データの読み込み
            blockgraph = DX.LoadGraph("data\\ブロック.png");//同上
            ziki = new Ziki(zikigraph);
            blocks = new Block[BLOCK_NUM];
            for (int i = 0; i < blocks.Length; i++)
            {
                blocks[i] = new Block(blockgraph);//Blockクラスコンストラクタにblockgraphを渡す
            }

                while (DX.ProcessMessage() == 0)
                {
                    input = DX.GetJoypadInputState(DX.DX_INPUT_KEY);//inputにこのキー操作を割り当てる
                    ziki.Update(input);//Update関数(操作)にinputを渡す
                    foreach (Block bl in blocks)
                    {
                        for (int i = 0; i < 15; i++)
                        {
                            for (int j = 0; j < 20; j++)
                            {
                                bl.blockflag = haiti[i, j];//int型変数blockflagにhaitiの1か0を渡す
                            }
                        }
                    }
                    ziki.gravityflag = true;//重力のフラグをたてる
                    ziki.Gravity();//重力を実際に与える関数
                    DX.ClearDrawScreen();
                    ziki.Draw();//自機の描画
                    foreach (Block bl in blocks)
                    {
                        for (int i = 0; i < 15; i++)
                        {
                            for (int j = 0; j < 20; j++)
                            {
                                bl.Draw(i,j);//ブロックの描画,iとjを渡して,ブロックの大きさをそれぞれにかけて座標を割り当てる
                            }
                        }
                    }
                    DX.ScreenFlip();
                }
            DX.DxLib_End();
        }
    }
    public class Character//後で敵キャラクターを作るためにひとまずキャラクター全体の基盤クラスをつくる
    {
        int graph;
        public int gravity = 2;//重力の大きさ
        public bool gravityflag;//重力がかかっているかないかのフラグ
        protected int x, y;//キャラクターの座標
        protected int width, height;//キャラクターのタテヨコの長さ
        public Character(int gr)
        {
            graph = gr;
        }
        public void Draw()
        {
            DX.DrawGraph(x, y, graph, 1);//描画
        }
        public void Gravity()
        {
            while (gravityflag == true)
            {
                y += gravity;
                break;
            }
        }
    }
    public class Ziki : Character
    {
        public Ziki(int gr)
            : base(gr)
        {
            x = 320;
            y = 0;
            width = 32;
            height = 32;
        }
        public void Update(int input)//自機の操作
        {
            if ((input & DX.PAD_INPUT_UP) != 0) y -= 2;
            if ((input & DX.PAD_INPUT_DOWN) != 0) y += 2;
            if ((input & DX.PAD_INPUT_LEFT) != 0) x -= 2;
            if ((input & DX.PAD_INPUT_RIGHT) != 0) x += 2;
        }
    }
    public class Block
    {
        int x, y;//ブロックの座標
        int graph;
        public int blockflag;//ブロックがあるかないか,haitiのそれぞれの1と0をここに入れる
        const int CHIP_SIZE = 32;//ブロックひとつあたりの大きさ
        const int XKOSUU = 640 / CHIP_SIZE;//ブロックが画面横(X)に埋まる個数
        const int YKOSUU = 480 / CHIP_SIZE;//ブロックが画面縦(Y)に埋まる個数
        public Block(int gr)
        {
            graph = gr;
        }
        public void Draw(int i, int j)
        {
            if (blockflag == 1)
            {
                DX.DrawGraph(j * CHIP_SIZE, i * CHIP_SIZE, graph, 1);
            }
        }
    }
}
自分が思うに,

コード:

foreach (Block bl in blocks)
                    {
                        for (int i = 0; i < 15; i++)
                        {
                            for (int j = 0; j < 20; j++)
                            {
                                bl.blockflag = haiti[i, j];//int型変数blockflagにhaitiの1か0を渡す
                            }
                        }
                    }
と,

コード:

foreach (Block bl in blocks)
                    {
                        for (int i = 0; i < 15; i++)
                        {
                            for (int j = 0; j < 20; j++)
                            {
                                bl.Draw(i,j);//ブロックの描画,iとjを渡して,ブロックの大きさをそれぞれにかけて座標を割り当てる
                            }
                        }
                    }
の部分が間違っているのではないかと思うのですが…
ご教授願います。これができたら次はブロックと自機の当たり判定をとって自機の位置補正の処理を付けようと思っています。

Re: C#とDXライブラリでマップの描画

Posted: 2012年4月15日(日) 13:50
by beatle

コード:

foreach (Block bl in blocks)
{
    for (int i = 0; i < 15; i++)
    {
        for (int j = 0; j < 20; j++)
        {
            bl.blockflag = haiti[i, j];//int型変数blockflagにhaitiの1か0を渡す
        }
    }
}
このプログラムでは、結局すべてのblに対してbl.blockflag = haiti[14, 19];が実行されたのと同じことになってしまいます。

Re: C#とDXライブラリでマップの描画

Posted: 2012年4月15日(日) 15:30
by ぴーまん
なるほど,原因はわかりました。
しかし,それではそれぞれのblocksに対してひとつずつhaiti[2,3]やhaiti[12,13]のように割り当てるにはどのような構文を書けばいいのでしょうか。
また,ご指摘いただいたhaiti[14,19]=1にしてみたところ,実際にすべてのhaitiが1になり,画面がブロックで埋めつくされました。すると,自機の操作の遅さからわかったのですが,プログラムが大変重くなってしまいます。すると,画面にブロックがたくさんあるような場面では困ってしまいます。このやり方は,効率がよくないのでしょうか?

Re: C#とDXライブラリでマップの描画

Posted: 2012年4月15日(日) 15:45
by softya(ソフト屋)
クラス間の情報の持ち方・渡し方に問題があります。
[補足]どう直すかの前に明確にしたいことがあります。

疑問なのですが、背景って15x20ブロックの300画像パーツで構成されているんでしょうか?
blockgraph = DX.LoadGraph( "data\\ブロック.png" ); //同上
と言う読み込みを見ると1パーツしかないように見えます。
15x20ブロックが1パーツでブロックの有無しか条件が無いのなら、

コード:

				foreach ( Block bl in blocks ) {
					for ( int i = 0; i < 15; i++ ) {
						for ( int j = 0; j < 20; j++ ) {
							bl.Draw( i, j ); //ブロックの描画,iとjを渡して,ブロックの大きさをそれぞれにかけて座標を割り当てる
						}
					}
				}
だと300x15x20回のループは300回が無駄なループでblocks配列も必要無いです。

Re: C#とDXライブラリでマップの描画

Posted: 2012年4月15日(日) 15:50
by beatle
ぴーまん さんが書きました:しかし,それではそれぞれのblocksに対してひとつずつhaiti[2,3]やhaiti[12,13]のように割り当てるにはどのような構文を書けばいいのでしょうか。
blocksは1次元配列でhaitiは2次元配列ですので、単純に添字を対応させることはできませんが、両方の要素数が同じなので
haiti[i, j] : blocks[i * 20 + j]
という対応関係を作ればいいと思います。

コード:

for (int i = 0; i < 15; ++i)
{
    for (int j = 0; j < 20; ++j)
    {
        blocks[i * 20 + j].blockflag  = haiti[i, j];
    }
}

Re: C#とDXライブラリでマップの描画

Posted: 2012年4月15日(日) 17:47
by ぴーまん
>>softyaさん
ブロックひとつあたりです。キャラクターと同じ大きさ32*32の正方形で用意しています。

>>beatleさん
できました! とりあえずはこれでできたということにさせていただきます。ありがとうございました。
softyaさんのご意見も聞いてみようと思います。

Re: C#とDXライブラリでマップの描画

Posted: 2012年4月15日(日) 17:59
by softya(ソフト屋)
現状だと300x15x20回で9万回描画ループしていますので遅くなるはずです。
でもほんとうに必要なのは15x20回の描画ループですので、haiti[i,j]が1の時だけDrawすれば良いという訳です。

コード:

	for (int i = 0; i < 15; i++)
	{
		for (int j = 0; j < 20; j++)
		{
        	if( haiti[i, j] == 1 ) {//int型変数blockflagにhaitiの1か0
        		block.Draw(i,j);
        	}
        }
これだとBlock[] blocks;は配列である必然がなくBlock block;で良いです。
それとblockflagも不要ですし、DX.LoadGraph("data\\ブロック.png");自体をclass Blockに管理させたほうが良い気がします。
もっと考えるとhaiti自体をclass Blockに渡して、上のループ自体をclass Blockに内で行ったほうが良いでしょう。

Re: C#とDXライブラリでマップの描画

Posted: 2012年4月17日(火) 01:01
by ぴーまん
>>softyaさん
返信遅れて申し訳ございません。
ご指摘いただいた部分を自分なりに直してみて,それなりに納得のいくものができました(といっても全然でまだ調整が必要なのですが)。ありがとうございました!キャラクターのジャンプとブロックの当たり判定・キャラの位置補正まで実装することができました。
一応ソースコードを載せておきます。

コード:

using System;
using System.Windows.Forms;
using DxLibDLL;
namespace SampleGame
{
    public class Game
    {
        [STAThread]
        public static void Main()
        {
            Ziki ziki; //Zikiクラスのziki変数を用意
            Block block;//Blockクラスのblocks変数を用意
            Haikei haikei;
            int input;//自機の操作を割り当てる変数
            int zikigraph;//自機の画像を割り当てる変数
            DX.ChangeWindowMode(1);
            if (DX.DxLib_Init() == -1) return;
            DX.SetDrawScreen(DX.DX_SCREEN_BACK);
            //画像ハンドルの読み込み
            zikigraph = DX.LoadGraph("data\\自機.png");
            //オブジェクトの生成
            ziki = new Ziki(zikigraph);
            block = new Block();
            haikei = new Haikei();
            block.LoadGraph();
            haikei.LoadGraph();

            while (DX.ProcessMessage() == 0)
            {
                input = DX.GetJoypadInputState(DX.DX_INPUT_KEY);//inputにこのキー操作を割り当てる
                ziki.Update(input);//Update関数(操作)にinputを渡す
                ziki.gravityflag = true;
                ziki.Gravity();//重力を実際に与える関数
                ziki.Jump(input);
                ziki.Genkai();
                DX.ClearDrawScreen();
                haikei.Draw();
                block.Draw();
                block.BlockAtari(ziki.x,ziki.y);
                ziki.Draw();//自機の描画
                ziki.Kakunin();
                DX.ScreenFlip();
            }
            DX.DxLib_End();
        }
    }
    public class Character//後で敵キャラクターを作るためにひとまずキャラクター全体の基盤クラスをつくる
    {
        int graph;
        public int gravity = 3;//重力の大きさ
        public bool gravityflag;//重力がかかっているかないかのフラグ
        public int x, y;//キャラクターの座標
        public int movex, movey;//移動距離
        public int width, height;//キャラクターのタテヨコの長さ
        public int x_temp, y_temp,z_temp;//移動時の仮座標
        public int jump_power;//ジャンプ時の移動距離(初期)
        public bool jump_flag;//ジャンプ中かどうかのフラグ
        public bool run_flag;//走っているかどうかのフラグ
        public bool ontheground_flag = false; // 地面についているかどうかのフラグ
        public Character(int gr)
        {
            graph = gr;
        }
        public void Draw()
        {
            DX.DrawGraph(x, y, graph, 1);//描画
        }
        public void Gravity()
        {
            while (gravityflag == true)
            {
                x_temp = x;
                y_temp = y+gravity+35;
                z_temp = x + 35;
                x_temp = x_temp / 40;
                y_temp = y_temp / 40;
                z_temp = z_temp / 40;
                if (Block.haiti[y_temp, x_temp] == 0 && Block.haiti[y_temp, z_temp] == 0)
                {
                    y += gravity;
                }
                if (Block.haiti[y_temp, x_temp] == 1 && Block.haiti[y_temp, z_temp] == 1)
                {
                    ontheground_flag = true;
                    jump_flag = false;
                    jump_power = 20;
                }
                break;
            }
        }
    }
    public class Ziki : Character
    {
        public Ziki(int gr)
            : base(gr)
        {
            x = 40;
            y = 0;
            width = 40;
            height = 40;
            jump_power = 20;
          jump_flag = false;
          run_flag = false;
            ontheground_flag = false;
            movex = 2;
        }
        public void Update(int input)//自機の操作
        {
            if ((input & DX.PAD_INPUT_LEFT) != 0)
            {
                x_temp = x - movex;
                y_temp = y;
                z_temp = y + 35;
                x_temp = x_temp / 40;
                y_temp = y_temp / 40;
                z_temp = z_temp / 40;
                if (Block.haiti[y_temp, x_temp] == 0 && Block.haiti[z_temp, x_temp] == 0)
                {
                    x -= movex;
                    if (DX.CheckHitKey(DX.KEY_INPUT_LSHIFT) != 0)
                    {
                        run_flag = true;
                        x -= movex;
                    }
                    else
                    {
                        run_flag = false;
                    }
                }
            }
            if ((input & DX.PAD_INPUT_RIGHT) != 0)
            {
                x_temp = x + movex + 35;
                y_temp = y;
                z_temp = y + 35;
                x_temp = x_temp / 40;
                y_temp = y_temp / 40;
                z_temp = z_temp / 40;
                if (Block.haiti[y_temp, x_temp] == 0 && Block.haiti[z_temp, x_temp] == 0)
                {
                    x += movex;
                    if (DX.CheckHitKey(DX.KEY_INPUT_LSHIFT) != 0)
                    {
                        run_flag = true;
                        x += movex;
                    }
                    else
                    {
                        run_flag = false;
                    }
                }
            }
        }
        public void Genkai()
        {
            if (x < 0)
            {
                x = 0;
            }
            if (y < 0)
            {
                y = 0;
            }
        }
        public void Jump(int input)
        {
            if ((input & DX.PAD_INPUT_A) != 0 && jump_flag == false && ontheground_flag == true)
            {
                jump_flag = true;
                ontheground_flag = false;
            }
            while(jump_flag == true)
            {
                x_temp = x;
                y_temp = y - jump_power-10;
                z_temp = x + 35;
                x_temp = x_temp / 40;
                y_temp = y_temp / 40;
                z_temp = z_temp / 40;
                if (Block.haiti[y_temp, x_temp] == 0 || Block.haiti[y_temp, z_temp] == 0)
                {
                    y -= jump_power;
                    if (jump_power > 0)
                    {
                        jump_power--;
                    }
                    else
                    {
                        jump_power = 0;
                    }
                }
                if (Block.haiti[y_temp, x_temp] == 1 || Block.haiti[y_temp, z_temp] == 1)
                {
                    jump_power = 0;
                }
                break;
            }
        }
        public void Kakunin()
        {
            if (ontheground_flag == true)
            {
                DX.DrawString(0, 0, "着地", DX.GetColor(255, 255, 255));
            }
            else
            {
                DX.DrawString(0, 0, "着地していない", DX.GetColor(255, 255, 255));
            }
        }
    }
    public class Block
    {
        int blockgraph1;
        public static int[,] haiti = 
            {
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0},
                {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0},
                {0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0},
                {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0},
                {0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0},
                {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0},
                {0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0},
                {1,1,1,1,1,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},
             };//ブロックの配置,1なら配置,0なら配置しない
        const int CHIP_SIZE = 40;//ブロックひとつあたりの大きさ
        const int XKOSUU = 640 / CHIP_SIZE;//ブロックが画面横(X)に埋まる個数
        const int YKOSUU = 480 / CHIP_SIZE;//ブロックが画面縦(Y)に埋まる個数
        public void LoadGraph()
        {
            blockgraph1 = DX.LoadGraph("data\\ブロック.png");
        }
        public void Draw()
        {
            for (int i = 0; i < YKOSUU; ++i)
            {
                for (int j = 0; j < XKOSUU; ++j)
                {
                    if (haiti[i, j] == 1)
                    {
                        DX.DrawGraph(j * CHIP_SIZE, i * CHIP_SIZE, blockgraph1, 1);
                    }
                }
            }
        }
        public void BlockAtari(int x,int y)
        {
            int px, py;
            px = x / CHIP_SIZE;
            py = y / CHIP_SIZE;
            string pxstring = px.ToString();
            string pystring = py.ToString();
            string zahyou = "x座標のmap:" + pxstring + "     " + "y座標のmap:" + pystring;
            DX.DrawString(160,240,zahyou,DX.GetColor(255,255,255));
        }
    }
    public class Haikei
    {
        int graph;
        int x = 0;
        int y = 0;
        public void LoadGraph()
        {
            graph = DX.LoadGraph("data\\背景.png");
        }
        public void Draw()
        {
            DX.DrawGraph(x, y, graph, 1);
        }
    }
}
]