C# Winform实现拼图游戏

C# Winform实现拼图游戏,包含图片切割、拖拽交互、胜利判断等核心功能

一、项目架构设计

MainForm
GamePanel
ControlPanel
GameLogic
GraphicsRenderer
PieceManager
MapManager
PieceRenderer
InputHandler


二、核心代码实现

1. 主窗体设计 (MainForm.cs)
csharp 复制代码
public partial class MainForm : Form
{
    private PuzzleGame game;
    private Image originalImage;
    
    public MainForm()
    {
        InitializeComponent();
        InitializeComponents();
        LoadDefaultImage();
    }

    private void InitializeComponents()
    {
        // 初始化控件
        this.Size = new Size(800, 600);
        this.Text = "C# 拼图游戏 v1.0";
        
        // 创建游戏面板
        game = new PuzzleGame(this);
        game.Dock = DockStyle.Fill;
        this.Controls.Add(game);
        
        // 创建控制面板
        var controlPanel = new FlowLayoutPanel();
        controlPanel.Dock = DockStyle.Bottom;
        
        // 添加功能按钮
        var loadButton = new Button { Text = "加载图片", Size = new Size(100, 30) };
        loadButton.Click += (s, e) => LoadImage();
        controlPanel.Controls.Add(loadButton);
        
        var resetButton = new Button { Text = "重新开始", Size = new Size(100, 30) };
        resetButton.Click += (s, e) => game.Reset();
        controlPanel.Controls.Add(resetButton);
        
        this.Controls.Add(controlPanel);
    }

    private void LoadImage()
    {
        using (var ofd = new OpenFileDialog())
        {
            ofd.Filter = "图片文件|*.jpg;*.png";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                originalImage = Image.FromFile(ofd.FileName);
                game.LoadImage(originalImage, 3); // 3x3拼图
            }
        }
    }
}
2. 游戏逻辑核心 (PuzzleGame.cs)
csharp 复制代码
public class PuzzleGame : Panel
{
    private const int CellSize = 150; // 每个拼图块大小
    private Image[,] puzzlePieces;
    private Point[,] originalPositions;
    private Point[,] currentPositions;
    private int gridSize = 3;
    private int moveCount = 0;
    
    public event EventHandler<GameOverEventArgs> GameOver;

    public PuzzleGame(Control parent) : base()
    {
        this.DoubleBuffered = true;
        InitializeGame();
    }

    public void LoadImage(Image img, int gridSize)
    {
        this.gridSize = gridSize;
        this.Size = new Size(CellSize * gridSize + 20, CellSize * gridSize + 20);
        originalPositions = new Point[gridSize, gridSize];
        currentPositions = new Point[gridSize, gridSize];
        
        // 创建拼图块矩阵
        puzzlePieces = new Image[gridSize, gridSize];
        for (int i = 0; i < gridSize; i++)
        {
            for (int j = 0; j < gridSize; j++)
            {
                originalPositions[i, j] = new Point(j * CellSize, i * CellSize);
                currentPositions[i, j] = new Point(j * CellSize, i * CellSize);
            }
        }
        
        // 切割图片
        CutImage(img);
        RefreshPieces();
    }

    private void CutImage(Image img)
    {
        using (var bmp = new Bitmap(img, CellSize * gridSize, CellSize * gridSize))
        {
            for (int i = 0; i < gridSize; i++)
            {
                for (int j = 0; j < gridSize; j++)
                {
                    var rect = new Rectangle(j * CellSize, i * CellSize, CellSize, CellSize);
                    puzzlePieces[i, j] = new Bitmap(rect.Width, rect.Height);
                    using (var g = Graphics.FromImage(puzzlePieces[i, j]))
                    {
                        g.DrawImage(bmp, rect, rect, GraphicsUnit.Pixel);
                    }
                }
            }
        }
    }

    public void Reset()
    {
        moveCount = 0;
        ShufflePieces();
        RefreshPieces();
    }

    private void ShufflePieces()
    {
        var random = new Random();
        for (int i = 0; i < gridSize * gridSize; i++)
        {
            int x = random.Next(gridSize);
            int y = random.Next(gridSize);
            SwapPieces(new Point(x, y), new Point(0, 0));
        }
    }

    public void RefreshPieces()
    {
        this.Controls.Clear();
        for (int i = 0; i < gridSize; i++)
        {
            for (int j = 0; j < gridSize; j++)
            {
                var pb = new PictureBox
                {
                    Size = new Size(CellSize, CellSize),
                    Location = originalPositions[i, j],
                    Image = puzzlePieces[i, j],
                    SizeMode = PictureBoxSizeMode.StretchImage,
                    BorderStyle = BorderStyle.FixedSingle,
                    Tag = new Point(i, j)
                };
                
                pb.MouseDown += Pb_MouseDown;
                pb.MouseMove += Pb_MouseMove;
                pb.MouseUp += Pb_MouseUp;
                
                this.Controls.Add(pb);
            }
        }
    }

    private void Pb_MouseDown(object sender, MouseEventArgs e)
    {
        var pb = sender as PictureBox;
        if (pb == null) return;
        
        var tag = pb.Tag as Point;
        currentPositions[tag.X, tag.Y] = new Point(tag.X, tag.Y);
        
        // 开始拖拽
        var dragImage = new Bitmap(pb.Width, pb.Height);
        using (var g = Graphics.FromImage(dragImage))
        {
            g.DrawImage(pb.Image, 0, 0);
        }
        pb.DoDragDrop(dragImage, DragDropEffects.Move);
    }

    private void Pb_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left) return;
        
        var pb = sender as PictureBox;
        if (pb == null) return;
        
        var dragImage = new Bitmap(pb.Width, pb.Height);
        using (var g = Graphics.FromImage(dragImage))
        {
            g.DrawImage(pb.Image, 0, 0);
        }
        Cursor.Clip = new Rectangle(this.PointToScreen(pb.Location), pb.Size);
    }

    private void Pb_MouseUp(object sender, MouseEventArgs e)
    {
        Cursor.Clip = null;
        var pb = sender as PictureBox;
        if (pb == null) return;
        
        var targetPoint = this.PointToClient(Cursor.Position);
        var targetCell = new Point(
            (targetPoint.X - CellSize / 2) / CellSize,
            (targetPoint.Y - CellSize / 2) / CellSize
        );
        
        if (IsValidMove(targetCell))
        {
            SwapPieces(targetCell, (Point)pb.Tag);
            moveCount++;
            CheckWinCondition();
        }
    }

    private bool IsValidMove(Point target)
    {
        var (x, y) = (Point)pb.Tag;
        return (Math.Abs(x - target.X) + Math.Abs(y - target.Y)) == 1;
    }

    private void SwapPieces(Point a, Point b)
    {
        var tempImg = puzzlePieces[a.X, a.Y];
        puzzlePieces[a.X, a.Y] = puzzlePieces[b.X, b.Y];
        puzzlePieces[b.X, b.Y] = tempImg;
        
        RefreshPieces();
    }

    private void CheckWinCondition()
    {
        for (int i = 0; i < gridSize; i++)
        {
            for (int j = 0; j < gridSize; j++)
            {
                if (currentPositions[i, j] != originalPositions[i, j])
                    return;
            }
        }
        
        GameOver?.Invoke(this, new GameOverEventArgs(moveCount));
    }
}

public class GameOverEventArgs : EventArgs
{
    public int MoveCount { get; }
    
    public GameOverEventArgs(int moves)
    {
        MoveCount = moves;
    }
}

三、关键功能扩展

1. 计时器功能
csharp 复制代码
public class GameTimer
{
    private System.Timers.Timer timer;
    private int elapsedTime = 0;
    
    public event EventHandler<TimeEventArgs> TimeUpdated;
    
    public GameTimer()
    {
        timer = new System.Timers.Timer(1000);
        timer.Elapsed += (s, e) => 
        {
            elapsedTime++;
            TimeUpdated?.Invoke(this, new TimeEventArgs(elapsedTime));
        };
    }
    
    public void Start() => timer.Start();
    public void Stop() => timer.Stop();
}

public class TimeEventArgs : EventArgs
{
    public int Seconds { get; }
    
    public TimeEventArgs(int seconds)
    {
        Seconds = seconds;
    }
}
2. 音效系统
csharp 复制代码
public class SoundManager
{
    private SoundPlayer clickSound;
    private SoundPlayer successSound;
    
    public SoundManager()
    {
        clickSound = new SoundPlayer("click.wav");
        successSound = new SoundPlayer("success.wav");
    }
    
    public void PlayClick() => clickSound.Play();
    public void PlaySuccess() => successSound.Play();
}

四、界面优化技巧

抗锯齿处理

csharp 复制代码
this.SetStyle(ControlStyles.AllPaintingInWmPaint | 
             ControlStyles.UserPaint | 
             ControlStyles.DoubleBuffer, true);

拖拽特效

csharp 复制代码
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    if (dragging)
    {
        using (var g = this.CreateGraphics())
        {
            g.DrawRectangle(Pens.LightGray, dragRect);
        }
    }
}

动态难度选择

csharp 复制代码
private void UpdateDifficulty(int level)
{
    gridSize = level + 2; // 简单=3x3, 普通=4x4, 困难=5x5
    Reset();
}

参考代码 C# 拼图游戏 源码 www.youwenfan.com/contentcsp/93149.html

五、完整项目结构

csharp 复制代码
PuzzleGame/
├── MainForm.cs
├── PuzzleGame.cs
├── PuzzlePiece.cs
├── Resources/
│   ├── default.jpg
│   └── sounds/
├── Controls/
│   ├── GameTimer.cs
│   └── SoundManager.cs
└── Properties/
    └── Settings.settings

六、扩展建议

  1. AI对战模式 实现基于A*算法的自动求解 添加难度分级系统
  2. 网络对战功能 使用Socket实现联机对战 开发匹配系统和排行榜
  3. 成就系统 记录最快通关时间 解锁特殊拼图模板
  4. 3D效果增强 使用OpenGL实现立体旋转 添加粒子消散特效
相关推荐
唐青枫14 小时前
深入理解 System.Lazy<T>:C#.NET 延迟初始化与线程安全
c#·.net
十五年专注C++开发1 天前
CMake进阶:vcpkg中OpenSSLConfig.cmake详解
c++·windows·cmake·openssl·跨平台编译
多多*1 天前
2026年1月3日八股记录
java·开发语言·windows·tcp/ip·mybatis
世洋Blog1 天前
AStar算法基础学习总结
算法·面试·c#·astar·寻路
nnsix1 天前
Windows 将桌面 路径 从C盘 移动到D盘
windows
何中应1 天前
windows安装python环境
开发语言·windows·python
nnsix1 天前
【WinDirStat】Windwos查看文件夹占用空间大小
windows
、、、、南山小雨、、、、1 天前
LCEL基本使用和高级使用
android·服务器·windows
海盗儿1 天前
(一)TensorRT-LLM 初探(version: 1.0.0)
linux·运维·windows