You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WinForms版2048开发:如何判断方块的移动与合并逻辑?

嘿,我刚好也折腾过WinForms版的2048,对你这个向上移动的逻辑梳理太有共鸣了!咱们一步步拆解清楚,从单列的核心逻辑入手,再扩展到整个棋盘,顺便把移动和合并的判断也说透。

核心思路:先搞定单列,再覆盖全棋盘

向上移动的本质是每一列独立处理——毕竟方块只会在列内上下移动,不会跨列。所以先把单列的移动+合并逻辑跑通,再循环处理4列(标准2048是4x4棋盘)就行。

第一步:清理单列的空方块(0)

先把列里所有非0的方块往上“挤”,空方块(0)全留在下方。这一步要记录哪些方块发生了移动——只要方块的位置变了,就算移动。

举个例子:原列是[0, 1, 0, 3](对应空、2、空、8),清理后变成[1, 3, 0, 0](2、8、空、空)。

实现代码(C# WinForms环境):

// 假设原棋盘是int[4,4]的二维数组,col是当前处理的列索引
List<int> tempColumn = new List<int>();
// 先收集所有非0元素(从上到下遍历列)
for (int row = 0; row < 4; row++)
{
    int currentVal = oldBoard[row, col];
    if (currentVal != 0)
    {
        tempColumn.Add(currentVal);
        // 这里可以记录移动:原位置(row, col),新位置暂时是tempColumn.Count-1(后续补0后位置不变)
    }
}
// 补0到4个元素,填满列
while (tempColumn.Count < 4)
{
    tempColumn.Add(0);
}

第二步:处理单列的合并逻辑

清理完空方块后,就可以处理合并了。规则是:相邻且值相同的方块,上面的合并为下一级(比如1+1=2,对应2→4),下面的变为0;且每个方块在单次移动中只能合并一次

比如清理后的列是[1,1,1,1](四个2),合并后应该变成[2,2,0,0](两个4),而不是[3,0,0,0](一个8)。

实现步骤:

  1. 从上到下遍历清理后的列,比较当前元素和下一个元素
  2. 若两者相同且非0,就合并:当前元素+1,下一个元素设为0,同时标记这两个方块发生了合并
  3. 合并完成后,再重复一次“清理空方块”的操作,把合并产生的0挪到下方

代码片段:

// 第一步:处理合并
for (int i = 0; i < 3; i++)
{
    if (tempColumn[i] != 0 && tempColumn[i] == tempColumn[i+1])
    {
        tempColumn[i] += 1; // 等级+1,比如1(2)→2(4)
        tempColumn[i+1] = 0;
        // 记录合并:原列中对应的两个原位置(需要之前记录的原row映射)
        // 比如原位置是rowA和rowB,现在合并成一个,位置在i
    }
}

// 第二步:再次清理合并产生的空方块
List<int> finalColumn = new List<int>();
foreach (int val in tempColumn)
{
    if (val != 0)
    {
        finalColumn.Add(val);
    }
}
while (finalColumn.Count < 4)
{
    finalColumn.Add(0);
}

第三步:扩展到整个棋盘的向上移动

遍历每一列(col从0到3),对每一列执行上面的两步操作,然后把处理后的列放回新棋盘的对应位置。

注意:一定要用深拷贝的新棋盘来存储结果,不要直接修改原棋盘,否则会影响后续的“是否有有效移动”判断。比如:

// 深拷贝原棋盘,避免修改原数据
int[,] newBoard = (int[,])oldBoard.Clone();

// 遍历每一列处理
for (int col = 0; col < 4; col++)
{
    // 执行上面的单列处理逻辑,得到finalColumn
    // ...
    // 将finalColumn赋值给newBoard的对应列
    for (int row = 0; row < 4; row++)
    {
        newBoard[row, col] = finalColumn[row];
    }
}

第四步:判断移动/合并是否发生

有时候用户点击向上,但棋盘没有任何变化(既没有方块移动,也没有合并),这时候不应该生成新方块。可以通过比较原棋盘和新棋盘来判断:

bool IsMoveValid(int[,] oldBoard, int[,] newBoard)
{
    for (int row = 0; row < 4; row++)
    {
        for (int col = 0; col < 4; col++)
        {
            if (oldBoard[row, col] != newBoard[row, col])
            {
                return true; // 有变化,移动有效
            }
        }
    }
    return false; // 无变化,移动无效
}

关键细节提醒

  • 合并唯一性:因为合并后把下一个元素设为0,后续循环不会再处理这个位置,自然保证了每个方块单次移动只合并一次。
  • 动画追踪:如果要做WinForms的方块滑动动画,在第一步清理空方块时,要记录每个非0元素的原row和新row,这样才能知道方块从哪个位置滑到哪个位置。
  • 边界处理:遍历列的时候注意不要越界(比如合并时只遍历到索引2,避免i+1超出列长度)。

内容的提问来源于stack exchange,提问作者Sweeper

火山引擎 最新活动