Molingo 发表于 2023-12-3 17:02

用C# Windows窗体实现一个俄罗斯方块

本帖最后由 Molingo 于 2023-12-3 17:06 编辑

先简单说一下实现思路
1.画板
游戏画面是有10*20的小方格组成,用代码在窗体中创建10*20个label控件,将长宽设为一样大小且统一设置黑色背景;
每个图形都是由四个小方格组成,通过改变画板上label的背景颜色,即可在绘制出图形;


2.图形的移动和变型

游戏中有7中图形,每个图形都是有4个小方格组成,可以左右移动或变形;在每个图形中选择其中一个方块,作为整个图形中心点,并记录这个图形点在整个画布中的(X,Y)坐标;通过中心点的坐标,绘制不同的图形;在移动图形时,只需要改变中心点坐标;例:左右移动时,只需要对中心的X轴-1或+1即可;移动之后,先记录上一次坐标位置,并通过坐标擦除掉图形(将label背景色设为黑色);并根据移动后的坐标进行绘制方块,图形有四种变化,每一次做变型时,根据中心点坐标先擦除在绘制;


3.图形的绘制和移动旋转判断


label是用10*20的数组进行存放,其数组索引即为小方格的坐标; 创建一个10*24的二维数组(需要在画布上方外腾出位置,绘制下落的图形),记录画布中每个方格坐标中是否存在方格,当图形在做移动和旋转时,可通过图形坐标点检查周围是否存在方块,来判定是否可以旋转或移动或下落;在窗体中增加一个定时器,让图形的中心点Y轴-1,来达到自动下落的功能;如图中,根据中心点绘制出红色图形;根据中线点判断灰色部分是否存在方块,来判定图形能否做旋转;


4.满行消除与游戏结束

当图形无法下落时,通过用来记录方块是否存在的数组,判断画布每一行是否满行;满行后,擦除当行,让上面每一行都往下覆盖,实现消除;当方块无法下落且Y轴坐标高于画布最高的点时,游戏结束;

5.游戏截图



画布类:
   public class Canvas
   {
       privateLabel[,] canvals = new Label;
       public Label[,] canvals2 = new Label;
       public enum MoveDirection
       {
         left,
         right,
         Variant
       }
       private readonly Control Control;
       private readonly Control Control2;
       private Random Random;
       public Score score;
       private Blocks Blocks { get; set; }
       private Blocks NextBlocks { get; set; }
       /// <summary>
       /// 记录是否存在方块
       /// </summary>
       public bool[][] isExitBlock = new bool[];

       /// <summary>
       /// 记录方块的颜色
       /// </summary>
       public Color[][] blockColor = new Color[];


       /// <summary>
       /// 初始化画板
       /// </summary>
       /// <param name="c"></param>
       /// <param name="c2"></param>
       public Canvas(Control c, Control c2)
       {
         Control = c; Control2 = c2;
         DrawCanvas();
         InitializeGame();
       }
       /// <summary>
       /// 初始化游戏
       /// </summary>
       public void InitializeGame()
       {
         for (int i = 0; i < isExitBlock.Length; i++)
         {
               isExitBlock = new bool;
               blockColor = new Color;
         }
         Random = new Random((int)DateTime.Now.Ticks);
         Blocks = CreateNextBlock();
         score = new Score();
       }
       /// <summary>
       /// 创建下一个方块
       /// </summary>
       public Blocks CreateNextBlock()
       {
         NextBlocks = CreateBlock();
         NextBlocks.BlockType = Random.Next(1, 5);
         NextBlocks.BlockPoint = new Point(4, 21);
         NextBlocks.NextBlockType = NextBlocks.BlockType;
         return NextBlocks;
       }

       /// <summary>
       /// 随机创建方块
       /// </summary>
       /// <returns></returns>
       private Blocks CreateBlock()
       {
         int type = Random.Next(1, 8);
         switch (type)
         {
               case 1:
                   return new Block1(this);
               case 2:
                   return new Block2(this);
               case 3:
                   return new Block3(this);
               case 4:
                   return new Block4(this);
               case 5:
                   return new Block5(this);
               case 6:
                   return new Block6(this);
               case 7:
                   return new Block7(this);
               default:
                   return new Block1(this);
         }
       }

       /// <summary>
       /// 构建画板
       /// </summary>
       private void DrawCanvas()
       {
         for (int i = 0; i < canvals.GetLength(0); i++)
         {
               for (int j = 0; j < canvals.GetLength(1); j++)
               {

                   Label label = canvals = new Label();
                   label.Width = 30;
                   label.Height = 30;
                   label.BorderStyle = BorderStyle.FixedSingle;
                   label.BackColor = Color.Black;
                   label.Location = new Point(j * 30, Control.Height - 30 - i * 30);
                   Control.Controls.Add(label);
               }
         }
         for (int i = 0; i < canvals2.GetLength(0); i++)
         {
               for (int j = 0; j < canvals2.GetLength(1); j++)
               {
                   Label label = canvals2 = new Label();
                   label.Width = 30;
                   label.Height = 30;
                   label.BorderStyle = BorderStyle.FixedSingle;
                   label.BackColor = Color.White;
                   label.Location = new Point(j * 30, i * 30);
                   Control2.Controls.Add(label);
               }
         }
       }

       /// <summary>
       /// 默认方块下落
       /// </summary>
       /// <param name="block"></param>
       public bool Freefall()
       {
         if (Blocks.IsFall())
         {
               Blocks.PreBlockPoint = Blocks.BlockPoint;
               Blocks.PreBlockType = Blocks.BlockType;
               Blocks.EraseBlock();
               Blocks.SetPointY(Blocks.BlockPoint.Y - 1);
               Blocks.Drawblock();
               ShowBlock();
               if (Blocks.BlockPoint.Y == 20)
               {
                   EraseNextBlock();
                   CreateNextBlock().DrawNextblock();
               }
               return true;
         }
         else
         {
               if (Blocks.BlockPoint.Y == 21)
               {
                   score.GameOverTime = DateTime.Now;
                   score.SaveGamelog();
                   return false;
               }
               FullLineDispel();
               Blocks = NextBlocks;
               return true;
         }
       }

       /// <summary>
       /// 方块显示
       /// </summary>
       /// <param name="blocks"></param>
       public void ShowBlock()
       {
         for (int i = 0; i < canvals.GetLength(0); i++)
         {
               for (int j = 0; j < canvals.GetLength(1); j++)
               {
                   if (isExitBlock)
                     canvals.BackColor = blockColor;
                   else
                     canvals.BackColor = Color.Black;
               }
         }
       }

       /// <summary>
       /// 方块的移动或变形
       /// </summary>
       /// <param name="move"></param>
       public void MoveOrVariantBlock(MoveDirection move)
       {
         switch (move)
         {
               case MoveDirection.left:
                   if (Blocks.LeftMove())
                   {
                     Blocks.PreBlockType = Blocks.BlockType;
                     Blocks.PreBlockPoint = Blocks.BlockPoint;
                     Blocks.EraseBlock();
                     Blocks.SetPointX(Blocks.BlockPoint.X - 1);
                     Blocks.Drawblock();
                     ShowBlock();
                   }
                   break;
               case MoveDirection.right:
                   if (Blocks.rightMove())
                   {
                     Blocks.PreBlockType = Blocks.BlockType;
                     Blocks.PreBlockPoint = Blocks.BlockPoint;
                     Blocks.EraseBlock();
                     Blocks.SetPointX(Blocks.BlockPoint.X + 1);
                     Blocks.Drawblock();
                     ShowBlock();
                   }
                   break;
               case MoveDirection.Variant:
                   if (Blocks.IsVariant())
                   {
                     Blocks.PreBlockType = Blocks.BlockType;
                     Blocks.PreBlockPoint = Blocks.BlockPoint;
                     Blocks.EraseBlock();
                     Blocks.BlockType += 1;
                     Blocks.Drawblock();
                     ShowBlock();
                   }
                   break;
               default:
                   break;
         }

       }

/// <summary>
/// 满行消除
/// </summary>
public void FullLineDispel()
{
      int row = -1;
      for (int i = 0; i <= canvals.GetLength(0); i++)
      {
          int count = 0;
          for (int j = 0; j < canvals.GetLength(1); j++)
          {
            if (isExitBlock) count++;
          }

          if (count == canvals.GetLength(1))
          {
            if (row == i)
            {
                  score.GameScore += 2;
            }
            else
            {
                  score.GameScore += 1;
            }
            score.Rows+=1;
            row = i;
            if (score.GameScore % 20 == 0)
            {
                  score.Leave = score.GameScore / 20;
                  score.FallingVelocity -= score.Leave * 50;
            }

            for (int k = i; k < 23; k++)
            {
                  isExitBlock = isExitBlock;
                  blockColor = blockColor;
            }
            isExitBlock = new bool;
            blockColor = new Color;
            i--;
          }

      }

}
       /// <summary>
       /// 擦除显示下一方块的画板
       /// </summary>
       public void EraseNextBlock()
       {
         foreach (var item in canvals2)
         {
               item.BackColor = Color.White;
         }
       }
   }


方块抽象类:
public abstract class Blocks
{
   public Blocks(Canvas canvas)
   {
         c = canvas;
   }
   protected Canvas c;

   protected static Random random = new Random((int)DateTime.Now.Ticks);
   protected static Color[] Colors = new Color[] {
   Color.Magenta,
   Color.DeepPink,
   Color.Red,
   Color.Orange,
   Color.DarkTurquoise,
   Color.Coral,
   Color.Yellow,
   Color.MediumSpringGreen,
   Color.Cyan,
   Color.DodgerBlue,
   Color.Aqua,
   Color.MediumVioletRed,
   Color.DarkMagenta,
   Color.OrangeRed,
   Color.Aquamarine,
   Color.SeaGreen,
   Color.PaleVioletRed,
   Color.PaleGreen,
   Color.PaleTurquoise,
   Color.SpringGreen,
   Color.Tomato,
   Color.Turquoise,
   Color.Violet,
   Color.LightCoral,
   Color.YellowGreen,
   Color.Plum,
   Color.MediumTurquoise,
   Color.DarkCyan,
   Color.GreenYellow
   };

   
   /// <summary>
   /// 设置当前方块的X坐标
   /// </summary>
   /// <param name="x"></param>
   public void SetPointX(int x) => BlockPoint = new Point(x, BlockPoint.Y);
   /// <summary>
   /// 设置当前方块的y坐标
   /// </summary>
   /// <param name="y"></param>
   public void SetPointY(int y) => BlockPoint = new Point(BlockPoint.X, y);


   private int _blockType;

   /// <summary>
   /// 当前方块的类型
   /// </summary>
   public int BlockType
   {
         get => _blockType;
         set => _blockType = value > 4 ? 1 : value;
   }
   /// <summary>
   /// 当前方块中心点的位置
   /// </summary>
   public Point BlockPoint { get; set; }
   /// <summary>
   /// 当前方块的颜色
   /// </summary>
   publicColor CurrentBlockColor { get; protected set; } = Colors;
   /// <summary>
   /// 方块上一次的中心点位置
   /// </summary>
   public Point PreBlockPoint { get; set; }
   /// <summary>
   /// 上一次方块的类型
   /// </summary>
   public int PreBlockType { get; set; }


   /// <summary>
   /// 下一个方块类型
   /// </summary>
   public int NextBlockType { get; set; }



   /// <summary>
   /// 是否可以继续下落
   /// </summary>
   /// <returns></returns>
   public abstract bool IsFall();

   /// <summary>
   /// 是否可以左移
   /// </summary>
   /// <returns></returns>
   public abstract bool LeftMove();


   /// <summary>
   /// 是否可以右移
   /// </summary>
   /// <returns></returns>
   public abstract bool rightMove();


   /// <summary>
   /// 是否可以变形
   /// </summary>
   /// <returns></returns>
   public abstract bool IsVariant();

   /// <summary>
   /// 擦除方块
   /// </summary>
   public abstract void EraseBlock();

   /// <summary>
   /// 绘制方块
   /// </summary>
   public abstract void Drawblock();

   /// <summary>
   /// 绘制下一个方块
   /// </summary>
   public abstract void DrawNextblock();
}


其中一个方块的实现类:
class Block3 : Blocks
{
    public Block3(Canvas c) : base(c)
    {
    }

    public override void Drawblock()
    {
      switch (BlockType)
      {
            case 1:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = true;
                c.blockColor = c.blockColor = c.blockColor = c.blockColor = CurrentBlockColor;break;
            case 2:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = true;
                c.blockColor = c.blockColor = c.blockColor = c.blockColor = CurrentBlockColor; break;
            case 3:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = true;
                c.blockColor = c.blockColor = c.blockColor = c.blockColor = CurrentBlockColor; break;
            case 4:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = true;
                c.blockColor = c.blockColor = c.blockColor = c.blockColor = CurrentBlockColor; break;


      }
    }

    public override void DrawNextblock()
    {
      switch (NextBlockType)
      {
            case 1:
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                break;
            case 2:
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                break;
            case 3:
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                break;
            case 4:
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                c.canvals2.BackColor = CurrentBlockColor;
                break;
      }
    }

    public override void EraseBlock()
    {
      switch (PreBlockType)
      {
            case 1:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = false; break;
            case 2:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = false; break;
            case 3:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = false; break;
            case 4:
                c.isExitBlock = c.isExitBlock = c.isExitBlock = c.isExitBlock = false; break;

      }
    }

    public override bool IsFall()
    {
      bool b = false;
      switch (BlockType)
      {
            case 1:
                if (BlockPoint.Y > 1 && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 2:
                if (BlockPoint.Y > 1 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 3:
                if (BlockPoint.Y > 1 && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 4:
                if (BlockPoint.Y > 0 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
      }
      return b;
    }

    public override bool IsVariant()
    {
      bool b = false;
      switch (BlockType)
      {
            case 1:
                if (BlockPoint.X > 0 && BlockPoint.X < 9 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 2:
                if (BlockPoint.X > 0 && BlockPoint.X < 9 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 3:
                if (BlockPoint.X > 0 && BlockPoint.X < 9 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 4:
                if (BlockPoint.X > 0 && BlockPoint.X < 9 && BlockPoint.Y > 0 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
      }
      return b;
    }

    public override bool LeftMove()
    {
      bool b = false;
      switch (BlockType)
      {
            case 1:
                if (BlockPoint.X > 0 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 2:
                if (BlockPoint.X > 1 && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 3:
                if (BlockPoint.X > 1 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 4:
                if (BlockPoint.X > 1 && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
      }
      return b;
    }

    public override bool rightMove()
    {
      bool b = false;
      switch (BlockType)
      {
            case 1:
                if (BlockPoint.X < 8 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 2:
                if (BlockPoint.X < 8 && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 3:
                if (BlockPoint.X < 9 && !c.isExitBlock && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
            case 4:
                if (BlockPoint.X < 8 && !c.isExitBlock && !c.isExitBlock) b = true;
                break;
      }
      return b;
    }
}

完整源代码:

xintian 发表于 2023-12-4 09:28

牛啊,我就写不出.

wzbarley 发表于 2023-12-4 10:14

不错不错

Molingo 发表于 2023-12-4 11:16

xintian 发表于 2023-12-4 09:28
牛啊,我就写不出.

思路捋清楚了,你也可以写出来的:lol

xenost 发表于 2023-12-5 11:31

这个好东西啊,感谢分享

ycs 发表于 2023-12-5 12:49

这个是认真学习和思考的结果,该表扬。

BI3NWQ 发表于 2023-12-12 11:27

学习一下,顺便体验体验
页: [1]
查看完整版本: 用C# Windows窗体实现一个俄罗斯方块