「CKJigsaw」プログラムの紹介

トップページへ、 「CKJigsaw」ページへ

 プログラムの使用言語・グラフィックス

使用言語:Visual C# 2008 (.NET Framework バージョン2.0使用)
グラフィックス処理方法:GDI+

 プログラムのポイント紹介

「CKJigsaw」で使用する画面の中で、「ジグソーパズル」画面以外のものは一般的な手法を組合わせて作成したものなので、 この欄では「ジグソーパズル」画面を作成する中で工夫を要したポイントを紹介します。

ピースオブジェクト「JPiece」
ジグソーパズルを構成する中で最も重要なポイントは、ピースをどのように構成するかという点です。
開発の初期には、PictureBoxを継承したカスタムコントロールAPicBoxを作って、 これにピースの形状情報、画像情報を与えてゲームを構成してみましたが、 描画に時間がかかって、動きの悪いピースになってしまいました。
コントロールをLabelを継承したものに代えても、状況は同じでした。
既存コントロールの継承では無理そうだとわかったので、 JPieceクラスを作り、そのインスタンスをグラフィックスで描画する手法をとることにしました。

JPieceクラス

    public class JPiece
    {
        int _argX, _argY;
        int _dep;
        int _listNo;
        int _mh;
        int _pleft, _ptop;
        int _width, _height;
        int _x, _y;
        string[] _spt;
        Bitmap _bmp;
        Region _region1;     // [DrawPiece]で取得

        // コンストラクター
        public JPiece(int vi, int vj, string vline, Bitmap vbmp, int vmh)
        {
            string[] data = vline.Split(',');      // ピース情報データ
            _pleft = Int32.Parse(data[2]);
            _ptop = Int32.Parse(data[3]);
            _width = Int32.Parse(data[4]);
            _height = Int32.Parse(data[5]);
            _spt = data[6].Split('+');

            // 画像を切り抜いて、_bmpに保持させる
            Rectangle rect = new Rectangle(_pleft, _ptop, _width, _height);
            _bmp = vbmp.Clone(rect, vbmp.PixelFormat);

            _mh = vmh;       // 呼出し元FormのMenuStripのHeight
            _argX = vi;
            _argY = vj;
            _listNo = -1;
        }

        // [IsContain]メソッド:引数である[vi, vj]座標が、ピースの_region1内に存在するかを返す
        public bool IsContain(int vi, int vj)
        {
            if (_region1.IsVisible(new Point(vi, vj)))
                return true;
            else
                return false;
        }

        // [ArgX]プロパティ:二次元配列のJPieceで、横要素のインデックス
        public int ArgX
        {
            get { return _argX; }
        }

        // [ArgY]プロパティ:二次元配列のJPieceで、縦要素のインデックス
        public int ArgY
        {
            get { return _argY; }
        }

        // [Dep]プロパティ:ピースの深度(マウスクリックの際に最も深度の小さいピースを選ぶ)
        public int Dep
        {
            get { return _dep; }
            set { _dep = value; }
        }

        // [ListNo]プロパティ:結合したピース群を識別するための番号
        public int ListNo
        {
            get { return _listNo; }
            set { _listNo = value; }
        }

        // [PL]プロパティ:完成図中のピースのLeft値
        public int PL
        {
            get { return _pleft; }
        }

        // [PT]プロパティ:完成図中のピースのTop値
        public int PT
        {
            get { return _ptop; }
        }

        // [Width]プロパティ:ピースに外接する長方形の幅
        public int Width
        {
            get { return _width; }
        }

        // [Height]プロパティ:ピースに外接する長方形の高さ
        public int Height
        {
            get { return _height; }
        }

        // [X]プロパティ:呼出し元Form内のピースのLeft値
        public int X
        {
            get { return _x; }
            set { _x = value; }
        }

        // [Y]プロパティ:呼出し元Form内のピースのTop値
        public int Y
        {
            get { return _y; }
            set { _y = value; }
        }

        // [MoveX]メソッド
        public void MoveX(int vadd)
        {
            _x += vadd;
        }

        // [MoveY]メソッド
        public void MoveY(int vadd)
        {
            _y += vadd;
        }

        // [DrawPiece]メソッド
        public void DrawPiece(Graphics vg)
        {
            Point[] pt = new Point[_spt.Length];
            int x, y;
            for (int k = 0; k < _spt.Length; k++)
            {
                x = Convert.ToInt32(_spt[k].Substring(0, 4)) + _x;
                y = Convert.ToInt32(_spt[k].Substring(4)) + _y;
                pt[k] = new Point(x, y);
            }
            var path = new GraphicsPath();
            path.StartFigure();
            path.AddPolygon(pt);
            path.CloseFigure();

            _region1 = new Region(path);
            vg.SetClip(_region1, CombineMode.Replace);
            vg.DrawImage(_bmp, _x, _y);
        }
    }
   

JPieceクラスでは、コンストラクターでピース情報を取得して、ピースの画像を_bmpに保持させます。
また、DrawPieceメソッドでピースの外周を形成して、画像をはめこみます。
ListNoプロパティは結合したピース群を識別するための番号であり、 結合されていない単独のピースのListNoは-1とします。
「ジグソーパズル」画面のForm(以下、主Form)でピースオブジェクト(JPieceクラスのインスタンス)の二次元配列 _apiece[i, j] を作成して、これをジグソーパズルのピースとして、ゲームを構成します。

ピースリスト配列 「_lstPC」
List<JPiece>[] _lstPc;
主Formでは、結合したピースを ピースオブジェクトのジェネリックリスト (以下、ピースリスト)に格納して取り扱います。
また、ピースの結合体は複数できるので、この複数のピースリストを配列 _lstPC として取り扱います。
一つのピースリストに格納される複数のピースオブジェクトには、0以上の同一のListNoを持たせます。
結合したピースをマウスドラッグして動かす場合、そのピースが属するピースリスト中のピースオブジェクト (同一のListNoを持つもの)をマウスの移動量分だけ動かすことにより、 ピース結合体を一体として動かすことができます。

ピースの結合処理方法
ピースをマウスドラッグし、マウスを離したときに、そのピースと隣接する(通常は上下左右の) ピースとの距離を求めます。
この距離が予め決められた範囲に入ったときに、ピース同士の結合処理に入ります。
ピースの結合処理は、該当するピースが結合済みか、未結合であるかに応じて、以下のように実施します。

(1) 該当するピースがいずれも未結合の場合
ピースリスト配列に要素を一つ追加し、その追加された要素に該当するピースオブジェクトを格納する。
該当するピースオブジェクトのListNoに、追加された要素のインデックスを代入する。

(2) 該当するピースのいずれか一方が結合済みの場合
未結合のピースオブジェクトを結合済みピースオブジェクトが属するピースリスト配列要素に追加して、 未結合のピースオブジェクトのListNoに、結合済みピースオブジェクトのListNoを代入する。

(3) 該当するピースの両方が結合済みの場合
ドラッグしている側の全ピースオブジェクトを、結合される側(ドラッグされていない側) のピースオブジェクトが属するピースリスト配列要素に追加した後、ドラッグ側ピースオブジェクトのListNo値を、 結合される側ピースピースオブジェクトのListNo値に変更する。 同時に、ドラッグ側ピースオブジェクトが元々属していたピースリスト配列要素から、 ドラッグ側ピースオブジェクトを削除する。

 プログラム開発に際して参考にした情報

・ たかな工房さんのサイト MultiPatternJigsaw シリーズ
    http://www.takana.info/index.htm
    「CKJigsaw」で使用しているジグソーピース1、ジグソーピース2の形状は、たかな工房さんのご了解をいただいた上で、
    このサイトに掲載されていたものを利用して作成しました。
・ 「ドットネットマガジン2002年11月〜2003年2月号」(翔泳社)の「GDI+ではじめるグラフィックス」
・ DOBON.NETさんの.NET Tips − 画像、印刷の部分
     http://dobon.net/vb/dotnet/graphics/index.html

 ソースコードのダウンロード

本プログラムのソースコードは、こちらからダウンロードすることができます。

 ご質問・ご意見・ご感想

ご質問、ご意見、ご感想、バグ等のご連絡は、 こちらへ

トップページへ、 「CKJigsaw」ページへ