C# WindowsFormsでジグソーパズルを作ります。

画像を切り抜くには?

ジグソーパズルを作るにはひとつの画像をピースに分解しなければなりません。ピースのような複雑な画像を生成するにはどうすればいいでしょうか?

Regionクラスを使うと画像を自由な形に切り抜くことができます。

これでジグソーパズルのピースっぽい画像を出力することができます。

ピースを生成する

ピースは辺や角の部分でなければ出っ張っている部分とへこんでいる部分がそれぞれ向かい合っています。そこで以下のような列挙体を定義します。

GraphicsPath.AddEllipseメソッドにRectangleオブジェクトを渡すのですが、左上の座標と辺の長さよりも円の中心と半径で指定したほうがやりやすいので、円の中心と半径を渡すとRectangleオブジェクトを返すメソッドを定義します。

上下左右が凸なのか凹なのか直線なのかを指定するとRegionを取得できるようにメソッドを定義します。最後の引数は境界線分の1ピクセルを取得するのかどうかを指定するためのものです。

もとの画像を指定したらそれをもとにピースに描画される画像を返すメソッドを指定します。

ピースの輪郭部分の画像を返すメソッドを指定します。

ピースはドラッグで移動できるようにすることにします。そこでピースを描画するためのクラスを定義します。

Pieceクラスの定義

ImageプロパティとOutlineプロパティはピースに描画される画像とピースの輪郭部分のイメージを返します。

DrawImageメソッドとDrawOutlineメソッドはピースの内部と輪郭部分を描画するためのものです。Form1.MarginLeftとForm1.MarginTopは左上に描画されるピースとフォームの余白です。

IsClickedメソッドは引数として渡された座標がピースの内部かどうかを調べます。

ピースを描画する

フォームにボタンとチェックボックスを配置します。ボタンはピースをシャッフルするためのものです。チェックボックスはピースの輪郭部分を描画するかどうかを設定するためのものです。

ピースを描画する処理を示します。

描画するとこんな感じになります。

チェックボックスのONOFFが切り替わったら輪郭部分の描画を有効または無効にします。

ピースをシャッフルする

ボタンがクリックされたらピースをシャッフルします。

ピースを移動させる

フォームがクリックされたら対応するピースが存在するか調べます。見つかった場合はフィールド変数内に格納します。

DraggedPieceがnullではないときにマウスが移動したらマウスポインタの座標にピースの中央がくるように移動中のピースを描画できるようにします。

ピースの移動処理が行なわれているときにマウスボタンが離されたら、その座標に別のピースがあるか調べます。ある場合はこれまでドラッグされていたピースと位置を入れ替えます。そしてDraggedPieceをnullにして再描画をおこないます。

課題

やってみると意外に難しいです。隣にあるピースと出っ張っている部分が重なっている場合、必要なピースを見つけにくくなっています。ゲームとして成立させるためには組み合わせていないピースの描画位置を変えるなどの工夫が必要です。