华容道问题求解第一部分_详细设计(二)之棋子和游戏类_棋盘和棋子渲染

本文详细介绍了HrdGame类中关于图像输出的部分,包括逻辑布局的定义,如何通过GameState类表示棋子状态,以及物理布局函数,如棋子位置、大小的计算和绘制过程。后续将探讨DFS和Dijkstra算法的应用。
摘要由CSDN通过智能技术生成

(续上篇)
HrdGame 类中的图像输出部分的函数,包括两部分,布局定义和绘制。

布局定义

广义上讲,布局只是棋子众多排列组合中一个快照,或者说是一个状态,因此引入了一个GameState 类,用来刻画一个布局。

逻辑布局定义

  逻辑布局是UI的内部数据结构,在其定义中主要通过使用HrdPoint 刻画 棋子的位置和大小,数据记录在一个数组中。
  该类主要属性定义如下:
public class GameState
{
    public Piece[,] layoutOfObj = new Piece[6, 7];// for the efficiency of searching the blocks beside the blank area 
    public int[,] layoutOfIdx = new int[6, 7];//  use index to represent the piece that can save space for hash and stack. 
    public Piece[] pieces = new Piece[12];
    public List<OpenPiece> openPieces = new List<OpenPiece>();

    public Piece selPcs { get; set; }
    ......
}

其中 layoutOfObj , 是一个Piece的二维数组,数组下标就是棋子的位置(左上角),数组元素的值刻画了棋子的属性。理论上,只要记住了元素的位置和类型,它的位置和形状(区域)就可以确定了。为了计算的高效,除了描述位置和类型之外,在棋子的其余位置也进行了描述,记录了棋子的基本属性,包括类型,大小以及位置信息。
为了查询方便,同时引入了一个索引数组,相当于每个棋子的基本属性的实例刻画。这样重复记录的意义在于,查找棋子的位置关系时,比较高效。它和算法本身没有直接联系。这个数组定义如下:

public int[,] layoutOfIdx = new int[6, 7];
棋子定义数组
public Piece[] pieces = new Piece[12];

索引布局数据中的索引即来自上面的棋子定义的索引,为了便于理解,我们不妨把这个索引叫做棋子的ID或者主键,这样一个索引值就表示对应的一个具体的棋子(实例)。

 public List<OpenPiece> openPieces = new List<OpenPiece>();

上面的List 用来记录当前布局下可以移动的棋子,其中 OpenPiece 定义如下:

public class OpenPiece
{
    public Piece piece { get; set; }


    public String Dir;
    public Piece MoveToPcs;// record the blank pieces to move, usually one element, the max is 2 

}

其中Dir 用来表示棋子的可以移动的方向,MoveToPcs 用来描述移到到具体哪个空白区域(这个区域也是用Piece来刻画)。

逻辑布局函数

函数用于对上面的属性进行初始化,代码如下

       public void FillLayoutArr(Piece pcs)
       {
           var pos = pcs.GetHrdPos();
           var size = pcs.GetHrdSize();
           var type = pcs.GetHrdType();
           gameState.layoutOfObj[pos.X, pos.Y] = pcs;
           gameState.layoutOfObj[pos.X + size.X - 1, pos.Y + size.Y - 1] = pcs;
           
           gameState.layoutOfIdx[pos.X, pos.Y] = pcs.idx;
           gameState.layoutOfIdx[pos.X + size.X - 1, pos.Y + size.Y - 1] = pcs.idx;

           if (type == 4)
           {

               gameState.layoutOfObj[pos.X + size.X - 1, pos.Y] = pcs;
               gameState.layoutOfObj[pos.X, pos.Y + size.Y - 1] = pcs;
               gameState.layoutOfIdx[pos.X + size.X - 1, pos.Y] = pcs.idx;
               gameState.layoutOfIdx[pos.X, pos.Y + size.Y - 1] = pcs.idx;
            
           }
           
       }

上面代码中,对除了左上角的数组元素之外也进行了处理,这样该棋子覆盖区域内的每个数组元素都可踹表征这个棋子的信息。

物理布局函数

物理布局函数将根据逻辑布局以及外部函数传入的控件尺寸,将逻辑布局转化为像素为单位的客户区位置和大小(像素单位),然后调用GDI函数将棋盘和旗子画在输入的控件上。

控件客户区位置和大小映射

函数如下:

       public void SetLocAndSize(Piece pcs)
       {

           int tW = _ctrl.Width / 4;
           int tH = _ctrl.Height / 5;
           int tX = pcs.GetHrdPos().X - 1;
           int tY = pcs.GetHrdPos().Y - 1;
           int pcsW = pcs.GetHrdSize().X;
           int pcsH = pcs.GetHrdSize().Y;
           var x = tX * tW;
           var y = tY * tH;
           var w = pcsW * tW;
           var h = pcsH * tH;
           //MessageBox.Show("debug");
           pcs.PcsLoc = new Point(x, y);
           pcs.PcsSize = new Size(w, h);
       }

函数中先获取控件的大小,然后根据获取逻辑布局大小,然后进行适当的比例缩放,设置棋子的实际大小和位置。

棋子绘制

调用上面的函数之后,棋子的实际位置和大小已经确定,在控件将其进行绘制。实际绘制时除了大小位置之外,还有棋子的颜色,名字等属性。函数主要代码如下

      public void DrawRegularBox(Graphics g, Piece pcs, bool bDrawTp = false)
      {
          var tRec = new Rectangle((int)pcs.PcsLoc.X, (int) pcs.PcsLoc.Y, pcs.PcsSize.Width,pcs.PcsSize.Height);
          Brush brush = new SolidBrush(PieceClr[pcs.GetHrdType()]);

          if (pcs.Highlighted)//获取高亮颜色,用于鼠标滑过时显示
          {
              // Highlight the block when it's marked as highlighted

              Color originalColor = pcs.HrdColor; // Replace with your original light color

              int darkenFactor = 32; // Adjust this factor to control the darkness

              int red = Math.Max(0, originalColor.R - darkenFactor);
              int green = Math.Max(0, originalColor.G - darkenFactor);
              int blue = Math.Max(0, originalColor.B - darkenFactor);

              Color darkerColor = Color.FromArgb(originalColor.A, red, green, blue);

              brush = new SolidBrush(darkerColor);
              //e.Graphics.DrawRectangle(new Pen(Color.Yellow), blockRect);
          }

 
          if (System.IO.File.Exists(pcs.avatarFile))//如果存在图标文件,则绘制棋子的图标
          {
              try
              {
                  var img = Image.FromFile(pcs.avatarFile);
                  g.DrawImage(img, tRec);
                  img.Dispose();
              }
              catch (Exception ex)
              {
                  MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
              }
              //g.DrawRectangle(new Pen( PieceClr[pcs.GetHrdType()]), tRec); ;
              g.DrawRectangle(new Pen(Color.White), tRec);
              brush = new SolidBrush(Color.White);
          }
          else //如果图标文件不在,则绘制简单的矩形框
          {
              g.FillRectangle(brush, tRec); ;
              g.DrawRectangle(new Pen(Color.White), tRec);
          }
          // 创建Font对象,你可以改变字体样式、大小等
          var emSize = 24;
          Font font = new Font("Arial", emSize);

          // 创建Brush对象,你可以改变颜色等  
          brush = new SolidBrush(Color.Black);
          if (pcs.Highlighted)
          {
              brush = new SolidBrush(Color.Yellow);
          }
          if (pcs == gameState.selPcs)
          {

              brush = new SolidBrush(_selFgColor);



              var fontW = GetStringSize(g, pcs.Name, font.FontFamily, emSize).Width;
              var fontH = GetStringSize(g, pcs.Name, font.FontFamily, emSize).Height;

              var x = pcs.PcsLoc.X + (int)(pcs.PcsSize.Width - fontW) / 2;
              var y = pcs.PcsLoc.Y + (int)(pcs.PcsSize.Height - fontH) / 2 - 3;


              var w = (int)fontW;
              var h = (int)fontH;
              Rectangle blockRect = new Rectangle((int)x, (int)y, w, h);
              //_layout.DrawRegularBox(e.Graphics, block, true);

              g.DrawRectangle(new Pen(Color.Yellow), blockRect);

          }

          PointF NameLoc = new Point(0, 0);
          var hrdType = pcs.GetHrdType();
     
          // draw the name of the block 
          {
              NameLoc.X = pcs.PcsLoc.X + (int)(pcs.PcsSize.Width - GetStringSize(g, pcs.Name, font.FontFamily, emSize).Width) / 2;
              NameLoc.Y = pcs.PcsLoc.Y + (int)(pcs.PcsSize.Height - GetStringSize(g, pcs.Name, font.FontFamily, emSize).Height) / 2;
          }
          brush = new SolidBrush(Color.White);
          g.DrawString(pcs.Name, font, brush, NameLoc);
          if (bDrawTp)
          {
              font = new Font("Arial", 10);

              // 使用DrawString方法写字  

              g.DrawString(pcs.GetHrdType().ToString() + "->" + pcs.DirFactor.ToString() + "," + pcs.MoveDir, font, brush, pcs.PcsLoc);
          }
    }

至此,就可以再UI上输出一个当前的布局了。

下面默认的横刀立马布局的绘制情况

在这里插入图片描述
说明:图片来自百度 文心一言。

下一节,将讲述本软件的第一个查找算法, 即DFS结合Dijkstra的算法。(待续)
MaraSun 2024-03-05 BJFWDQ
PS:《周处除三害》非常好看啊,记录一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值