1. 效果介绍
屏幕录制 2025-09-01 094804
2.实现
2.1 分割并打乱图片
创建 c# winform 项目 pintu_game

private const int CellSize = 100;//每块拼图的大小
private PictureBox[,] pictureBoxes = new PictureBox[3, 3];//创建3x3个picturebox
private Dictionary<PictureBox, Point> originalPositions = new Dictionary<PictureBox, Point>();//初始位置
private Dictionary<PictureBox, Point> currentPositions = new Dictionary<PictureBox, Point>();//当前位置
用picturebox存放拼图
用Dictionary<PictureBox, Point>存放数据
只要比较 originalPositions和currentPositions就能判断胜利:
private bool IsPuzzleSolved()
{
foreach (var entry in currentPositions)
{
PictureBox pb = entry.Key;
Point currentPos = entry.Value;
Point originalPos = originalPositions[pb];
if (currentPos.X != originalPos.X || currentPos.Y != originalPos.Y)
{
return false;
}
}
return true;
}
entry.Key
表示字典中的键(Key),对应拼图块的控件对象
entry.Value
表示字典中的值(Value),对应拼图块的坐标(Point
类型,包含 X
和 Y
属性)。
调整原图尺寸:
private Image resize_img(Image originalImage)
{
//调整原图的尺寸为300x300
Bitmap resizedImage = new Bitmap(300, 300);
using (Graphics g = Graphics.FromImage(resizedImage))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(originalImage, 0, 0, 300, 300);
}
return resizedImage;
}
- 新建一个300x300像素的空白位图作为目标画布
-
通过
Graphics.FromImage
获取目标位图的绘图对象 -
指定使用双三次插值法(Bicubic Interpolation)
分割图片
private void CutImage()
{
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
// 创建每个部分的位图
Bitmap part = new Bitmap(CellSize, CellSize);
using (Graphics g = Graphics.FromImage(part))
{
// 计算源图像中的矩形区域
Rectangle srcRect = new Rectangle(j * CellSize, i * CellSize, CellSize, CellSize);
// 计算目标图像中的矩形区域
Rectangle destRect = new Rectangle(0, 0, CellSize, CellSize);
// 绘制图像部分
g.DrawImage(originalImage, destRect, srcRect, GraphicsUnit.Pixel);
}
imageParts[i, j] = part;
}
}
}

打乱顺序
// 收集所有拼图块
foreach (PictureBox pb in pictureBoxes)
{
boxes.Add(pb);
}
// 洗牌
int n = boxes.Count;
while (n > 1)
{
n--;
int k = rand.Next(n + 1);
PictureBox temp = boxes[k];
boxes[k] = boxes[n];
boxes[n] = temp;
此阶段代码:
cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace pintu_game
{
public partial class Form1 : Form
{
private const int CellSize = 100;//每块拼图的大小
static private int GridSize = 3;//拼图数
private PictureBox[,] pictureBoxes = new PictureBox[GridSize, GridSize];//创建3x3个picturebox
private Dictionary<PictureBox, Point> originalPositions = new Dictionary<PictureBox, Point>();//初始位置
private Dictionary<PictureBox, Point> currentPositions = new Dictionary<PictureBox, Point>();//当前位置
private Image originalImage = Image.FromFile("rabbit.png");//原图
private Image[,] imageParts = new Image[GridSize, GridSize];//分割后的图片
private const int Spacing = 5;//分割图片之间的留白大小
public Form1()
{
InitializeComponent();
InitializeGame();
}
private void InitializeGame()
{
//调整尺寸
originalImage = resize_img(originalImage);
// 切割图片
CutImage();
// 创建游戏面板
CreateGameBoard();
// 打乱拼图
ShufflePuzzle();
}
//调整尺寸
private Image resize_img(Image originalImage)
{
//调整原图的尺寸为300x300
Bitmap resizedImage = new Bitmap(300, 300);
using (Graphics g = Graphics.FromImage(resizedImage))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(originalImage, 0, 0, 300, 300);
}
return resizedImage;
}
//分割图片
private void CutImage()
{
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
// 创建每个部分的位图
Bitmap part = new Bitmap(CellSize, CellSize);
using (Graphics g = Graphics.FromImage(part))
{
// 计算源图像中的矩形区域
Rectangle srcRect = new Rectangle(j * CellSize, i * CellSize, CellSize, CellSize);
// 计算目标图像中的矩形区域
Rectangle destRect = new Rectangle(0, 0, CellSize, CellSize);
// 绘制图像部分
g.DrawImage(originalImage, destRect, srcRect, GraphicsUnit.Pixel);
}
imageParts[i, j] = part;
}
}
}
private void CreateGameBoard()
{
Controls.Clear();
ClientSize = new Size(CellSize * GridSize + Spacing * (GridSize + 1),
CellSize * GridSize + Spacing * (GridSize + 1) + 30);
// 重新开始按钮
Button restartButton = new Button
{
Text = "重新开始",
Location = new Point(10, ClientSize.Height - 25),
Size = new Size(100, 20)
};
restartButton.Click += (s, e) => ShufflePuzzle();
Controls.Add(restartButton);
// 检查按钮
Button checkButton = new Button
{
Text = "检查",
Location = new Point(150, ClientSize.Height - 25),
Size = new Size(100, 20)
};
Controls.Add(checkButton);
// 创建所有拼图块
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
PictureBox pb = new PictureBox
{
Size = new Size(CellSize, CellSize),
Location = new Point(Spacing + j * (CellSize + Spacing),
Spacing + i * (CellSize + Spacing)),
Image = imageParts[i, j],
SizeMode = PictureBoxSizeMode.StretchImage,
};
Controls.Add(pb);
pictureBoxes[i, j] = pb;
originalPositions.Add(pb, new Point(i, j));
currentPositions.Add(pb, new Point(i, j));
}
}
}
private void ShufflePuzzle()
{
Random rand = new Random();
List<PictureBox> boxes = new List<PictureBox>();
// 收集所有拼图块
foreach (PictureBox pb in pictureBoxes)
{
boxes.Add(pb);
}
// 洗牌
int n = boxes.Count;
while (n > 1)
{
n--;
int k = rand.Next(n + 1);
PictureBox temp = boxes[k];
boxes[k] = boxes[n];
boxes[n] = temp;
}
// 重新排列图片和位置
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
int index = i * GridSize + j;
PictureBox pb = boxes[index];
// 更新物理位置
pb.Location = new Point(Spacing + j * (CellSize + Spacing),
Spacing + i * (CellSize + Spacing));
// 更新位置字典
currentPositions[pb] = new Point(i, j);
pictureBoxes[i, j] = pb;
}
}
}
private bool IsPuzzleSolved()
{
foreach (var entry in currentPositions)
{
PictureBox pb = entry.Key;
Point currentPos = entry.Value;
Point originalPos = originalPositions[pb];
if (currentPos.X != originalPos.X || currentPos.Y != originalPos.Y)
{
return false;
}
}
return true;
}
}
}
运行后:

2.2 拖与放
cs
private void PictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var pb = (PictureBox)sender;
pb.DoDragDrop(pb, DragDropEffects.Move);
}
}
private void PictureBox_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(PictureBox)))
{
e.Effect = DragDropEffects.Move;
}
}
private void PictureBox_DragDrop(object sender, DragEventArgs e)
{
var targetPb = (PictureBox)sender;
var sourcePb = (PictureBox)e.Data.GetData(typeof(PictureBox));
// 交换物理位置
Point tempLoc = targetPb.Location;
targetPb.Location = sourcePb.Location;
sourcePb.Location = tempLoc;
// 交换currentPositions记录
Point tempPos = currentPositions[targetPb];
currentPositions[targetPb] = currentPositions[sourcePb];
currentPositions[sourcePb] = tempPos;
// 同步更新pictureBoxes数组
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
if (pictureBoxes[i, j] == targetPb)
pictureBoxes[i, j] = sourcePb;
else if (pictureBoxes[i, j] == sourcePb)
pictureBoxes[i, j] = targetPb;
}
}
// 强制刷新界面
targetPb.Refresh();
sourcePb.Refresh();
}
完整拖放流程
- 按下鼠标 →
MouseDown
启动拖放。 - 拖入控件边界 →
DragEnter
验证并显示反馈。 - 释放鼠标 →
DragDrop
执行实际交换操作。
3.完整代码
cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace pintu_game
{
public partial class Form1 : Form
{
private const int CellSize = 100;//每块拼图的大小
static private int GridSize = 3;//拼图数
private PictureBox[,] pictureBoxes = new PictureBox[GridSize, GridSize];//创建3x3个picturebox
private Dictionary<PictureBox, Point> originalPositions = new Dictionary<PictureBox, Point>();//初始位置
private Dictionary<PictureBox, Point> currentPositions = new Dictionary<PictureBox, Point>();//当前位置
private Image originalImage = Image.FromFile("rabbit.png");//原图
private Image[,] imageParts = new Image[GridSize, GridSize];//分割后的图片
private const int Spacing = 5;//分割图片之间的留白大小
public Form1()
{
InitializeComponent();
InitializeGame();
}
private void InitializeGame()
{
//调整尺寸
originalImage = resize_img(originalImage);
// 切割图片
CutImage();
// 创建游戏面板
CreateGameBoard();
// 打乱拼图
ShufflePuzzle();
}
//调整尺寸
private Image resize_img(Image originalImage)
{
//调整原图的尺寸为300x300
Bitmap resizedImage = new Bitmap(300, 300);
using (Graphics g = Graphics.FromImage(resizedImage))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(originalImage, 0, 0, 300, 300);
}
return resizedImage;
}
//分割图片
private void CutImage()
{
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
// 创建每个部分的位图
Bitmap part = new Bitmap(CellSize, CellSize);
using (Graphics g = Graphics.FromImage(part))
{
// 计算源图像中的矩形区域
Rectangle srcRect = new Rectangle(j * CellSize, i * CellSize, CellSize, CellSize);
// 计算目标图像中的矩形区域
Rectangle destRect = new Rectangle(0, 0, CellSize, CellSize);
// 绘制图像部分
g.DrawImage(originalImage, destRect, srcRect, GraphicsUnit.Pixel);
}
imageParts[i, j] = part;
}
}
}
private void CreateGameBoard()
{
Controls.Clear();
ClientSize = new Size(CellSize * GridSize + Spacing * (GridSize + 1),
CellSize * GridSize + Spacing * (GridSize + 1) + 30);
// 重新开始按钮
Button restartButton = new Button
{
Text = "重新开始",
Location = new Point(10, ClientSize.Height - 25),
Size = new Size(100, 20)
};
restartButton.Click += (s, e) => ShufflePuzzle();
Controls.Add(restartButton);
// 检查按钮
Button checkButton = new Button
{
Text = "检查",
Location = new Point(150, ClientSize.Height - 25),
Size = new Size(100, 20)
};
checkButton.Click += (s, e) => checksuccess();
Controls.Add(checkButton);
// 创建所有拼图块
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
PictureBox pb = new PictureBox
{
Size = new Size(CellSize, CellSize),
Location = new Point(Spacing + j * (CellSize + Spacing),
Spacing + i * (CellSize + Spacing)),
Image = imageParts[i, j],
SizeMode = PictureBoxSizeMode.StretchImage,
};
// 拖放事件
pb.MouseDown += PictureBox_MouseDown;
pb.DragEnter += PictureBox_DragEnter;
pb.DragDrop += PictureBox_DragDrop;
pb.AllowDrop = true;
Controls.Add(pb);
pictureBoxes[i, j] = pb;
originalPositions.Add(pb, new Point(i, j));
currentPositions.Add(pb, new Point(i, j));
}
}
}
private void ShufflePuzzle()
{
Random rand = new Random();
List<PictureBox> boxes = new List<PictureBox>();
// 收集所有拼图块
foreach (PictureBox pb in pictureBoxes)
{
boxes.Add(pb);
}
// 洗牌
int n = boxes.Count;
while (n > 1)
{
n--;
int k = rand.Next(n + 1);
PictureBox temp = boxes[k];
boxes[k] = boxes[n];
boxes[n] = temp;
}
// 重新排列图片和位置
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
int index = i * GridSize + j;
PictureBox pb = boxes[index];
// 更新物理位置
pb.Location = new Point(Spacing + j * (CellSize + Spacing),
Spacing + i * (CellSize + Spacing));
// 更新位置字典
currentPositions[pb] = new Point(i, j);
pictureBoxes[i, j] = pb;
}
}
}
private void PictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) // 检查是否左键按下
{
var pb = (PictureBox)sender;
pb.DoDragDrop(pb, DragDropEffects.Move);// 开始拖放操作
}
}
private void PictureBox_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(PictureBox))) // 检查拖入的是否是PictureBox
{
e.Effect = DragDropEffects.Move;// 允许移动操作
}
}
//当用户释放鼠标左键(完成拖放)时触发。
private void PictureBox_DragDrop(object sender, DragEventArgs e)
{
var targetPb = (PictureBox)sender;
var sourcePb = (PictureBox)e.Data.GetData(typeof(PictureBox));
// 交换物理位置
Point tempLoc = targetPb.Location;
targetPb.Location = sourcePb.Location;
sourcePb.Location = tempLoc;
// 交换currentPositions记录
Point tempPos = currentPositions[targetPb];
currentPositions[targetPb] = currentPositions[sourcePb];
currentPositions[sourcePb] = tempPos;
// 同步更新pictureBoxes数组
for (int i = 0; i < GridSize; i++)
{
for (int j = 0; j < GridSize; j++)
{
if (pictureBoxes[i, j] == targetPb)
pictureBoxes[i, j] = sourcePb;
else if (pictureBoxes[i, j] == sourcePb)
pictureBoxes[i, j] = targetPb;
}
}
// 强制刷新界面
targetPb.Refresh();
sourcePb.Refresh();
}
private bool IsPuzzleSolved()
{
foreach (var entry in currentPositions)
{
PictureBox pb = entry.Key;
Point currentPos = entry.Value;
Point originalPos = originalPositions[pb];
if (currentPos.X != originalPos.X || currentPos.Y != originalPos.Y)
{
return false;
}
}
return true;
}
private void checksuccess()
{
if (IsPuzzleSolved())
{
MessageBox.Show("恭喜你完成了拼图!", "游戏胜利");
}
else
{
MessageBox.Show("未完成");
}
}
}
}