• 兵棋系列2----兵棋游戏中地图滑动和委托消息


      前几天写了一个六边形阵列的算法,今天周末比较闲,下午没事就做了做兵棋的地图操作,一点一点的做吧,总会做好,毕竟我也经常玩各种棋,对做一个这类型的小游戏非常感兴趣。

         首先来解释下,下面要出现代码的操作。如上图,当鼠标指针移动到地图的四个边时,地图会自动左右上下滑动(地图比这个from要大很多,不这么做地图显示不完整了,不要跟我说用滚动条,那个给人感觉不好,这也是兵棋里不会缺少的操作吧),同时下面的消息框会记录鼠标的位置,这个消息框前期为我开发时显示一些测试信息用的,后期应该会把它改成一个功能区(部队参数、将领参数、环境参数、消息显示等等吧)

      下面把源代码放出来,懂得大大们可以指点下怎么做。


      C#开发中,控件和控件之间的消息传递有多种方式,我一般选择委托,毕竟灵活方便。以下是两种传递消息的模式,我选择了第一个,放弃了第二个;其实个人觉得第二个方法更优秀,它作为一个继承基类,可以很安全的把消息传递给它的上层类;第一种采用静态,优势是灵活方便,弊端是一个委托几乎只在一个功能上使用。

    选择的传递消息模式

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 
      6 namespace UI
      7 {
      8     public enum MsgType
      9     {
     10         /// <summary>
     11         /// 常规消息显示(黑色)
     12         /// </summary>
     13         Show,
     14         /// <summary>
     15         /// 提示信息(绿色)
     16         /// </summary>
     17         Info,
     18         /// <summary>
     19         /// 状态栏显示
     20         /// </summary>
     21         State,
     22         /// <summary>
     23         /// 错误消息,可识别,可控类型(蓝色)
     24         /// </summary>
     25         Error,
     26         /// <summary>
     27         /// 警告消息,程序异常,不可处理(红色)
     28         /// </summary>
     29         Warn,
     30         /// <summary>
     31         /// 他人发送消息的颜色
     32         /// </summary>
     33         OtherMessage,
     34         /// <summary>
     35         /// 本人发送消息的颜色
     36         /// </summary>
     37         OwnerMessage
     38     }
     39 
     40     class MsgEventArgs
     41     {
     42         private string _message;
     43 
     44         public string Message
     45         {
     46             get { return _message; }
     47             set { _message = value; }
     48         }
     49 
     50         private MsgType _type;
     51 
     52         public MsgType Type
     53         {
     54             get { return _type; }
     55             set { _type = value; }
     56         }
     57 
     58         public MsgEventArgs(string msg)
     59         {
     60             this._message = msg;
     61 
     62             this._type = MsgType.Show;
     63         }
     64         public MsgEventArgs(string msg, MsgType type)
     65         {
     66             this._message = msg;
     67             this._type = type;
     68         }
     69     }
     70 
     71     class MsgEvnet
     72     {
     73         public delegate void MsgEvent(object sender, MsgEventArgs msg);
     74         public static event MsgEvent msgEvent;
     75 
     76         public static void SendMsg(string msg)
     77         {
     78             if (MsgEvnet.msgEvent != null)
     79             {
     80                 MsgEvnet.msgEvent(null, new MsgEventArgs(msg));
     81             }
     82         }
     83 
     84         public static void SendMsg(string msg, MsgType type)
     85         {
     86             if (MsgEvnet.msgEvent != null)
     87             {
     88                 MsgEvnet.msgEvent(null, new MsgEventArgs(msg, type));
     89             }
     90         }
     91 
     92         public static void SendMsg(object sender, string msg)
     93         {
     94             if (MsgEvnet.msgEvent != null)
     95             {
     96                 MsgEvnet.msgEvent(sender, new MsgEventArgs(msg));
     97             }
     98         }
     99 
    100         public static void SendMsg(object sender, string msg, MsgType type)
    101         {
    102             if (MsgEvnet.msgEvent != null)
    103             {
    104                 MsgEvnet.msgEvent(sender, new MsgEventArgs(msg, type));
    105             }
    106         }
    107     }
    108 }
    消息委托类

    放弃的传递消息模式

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace UI
     7 {
     8     public enum MsgType
     9     {
    10         /// <summary>
    11         /// 常规消息显示(黑色)
    12         /// </summary>
    13         Show,
    14         /// <summary>
    15         /// 提示信息(绿色)
    16         /// </summary>
    17         Info,
    18         /// <summary>
    19         /// 状态栏显示
    20         /// </summary>
    21         State,
    22         /// <summary>
    23         /// 错误消息,可识别,可控类型(蓝色)
    24         /// </summary>
    25         Error,
    26         /// <summary>
    27         /// 警告消息,程序异常,不可处理(红色)
    28         /// </summary>
    29         Warn,
    30         /// <summary>
    31         /// 他人发送消息的颜色
    32         /// </summary>
    33         OtherMessage,
    34         /// <summary>
    35         /// 本人发送消息的颜色
    36         /// </summary>
    37         OwnerMessage
    38     }
    39 
    40     public class MsgEventArgs : EventArgs
    41     {
    42         public string Message;
    43         public MsgType Type;
    44 
    45         public MsgEventArgs(string msg)
    46         {
    47             this.Message = msg;
    48 
    49             this.Type = MsgType.Show;
    50         }
    51         public MsgEventArgs(string msg, MsgType type)
    52         {
    53             this.Message = msg;
    54             this.Type = type;
    55         }
    56     }
    57 
    58     public class MsgEvent
    59     {
    60         public event EventHandler<MsgEventArgs> msgEvent;
    61 
    62         public void SendMsg(string msg)
    63         {
    64             if (this.msgEvent != null)
    65             {
    66                 this.msgEvent(this, new MsgEventArgs(msg));
    67             }
    68         }
    69 
    70         public void SendMsg(string msg, MsgType type)
    71         {
    72             if (this.msgEvent != null)
    73             {
    74                 this.msgEvent(this, new MsgEventArgs(msg, type));
    75             }
    76         }
    77     }
    78 }
    放弃的消息传递模式

      下面这段代码之前放出来过(http://www.cnblogs.com/preacher/p/4105810.html),做了几点修改,计算的方式:根据每个正六边形的中心点,计算出下三边的相对位置然后绘制下三边。以下算法中,把之前的基类Control改为了Label,主要方便控件透明,Control为基类是不支持控件透明。

    六边形阵列绘制算法

      1 using System;
      2 using System.Drawing.Drawing2D;
      3 using System.Drawing;
      4 using System.Windows.Forms;
      5 using System.Collections.Generic;
      6 
      7 namespace UI.Controls
      8 {
      9     public class SixSidesControl : Label
     10     {
     11         double G3 = Math.Sin(60 * Math.PI / 180);//二分之根号三
     12         private int m_sideLength = 20;
     13 
     14         public int SideLength
     15         {
     16             get { return m_sideLength; }
     17             set
     18             {
     19                 m_sideLength = value;
     20                 Invalidate();
     21             }
     22         }
     23 
     24 
     25         private float m_lineThickness = 1;
     26 
     27         public float LineThickness
     28         {
     29             get { return m_lineThickness; }
     30             set
     31             {
     32                 m_lineThickness = value;
     33                 Invalidate();
     34             }
     35         }
     36 
     37 
     38         private Color m_lineColor = Color.Black;
     39 
     40         public Color LineColor
     41         {
     42             get { return m_lineColor; }
     43             set
     44             {
     45                 m_lineColor = value;
     46                 Invalidate();
     47             }
     48         }
     49 
     50         public SixSidesControl()
     51         {
     52             SetStyle(ControlStyles.UserPaint, true);
     53             SetStyle(ControlStyles.AllPaintingInWmPaint, true);
     54             SetStyle(ControlStyles.DoubleBuffer, true);
     55         }
     56 
     57         protected override void OnPaint(PaintEventArgs pe)
     58         {
     59             //横线,三被的边长
     60             //纵线,根号三倍的边长
     61             List<float> xList = new List<float>();
     62             List<float> yList = new List<float>();
     63 
     64             int maxx = this.Width / (3 * m_sideLength);
     65             int maxy = (int)(this.Height / (G3 * m_sideLength));
     66 
     67             for (int y = -1; y <= maxy; y++)
     68             {
     69                 float curHeight =(float)( y * G3 * m_sideLength);
     70                 for (int x =-1; x <= maxx; x++)
     71                 {
     72                     float curWidth;
     73                     if (y % 2 == 0)
     74                         curWidth = (float)(x * 3 * m_sideLength);
     75                     else
     76                         curWidth = (float)((x * 3 + 1.5) * m_sideLength);
     77 
     78                     yList.Add(curHeight);
     79                     xList.Add(curWidth);
     80                 }
     81             }
     82 
     83             using (Pen pen = new Pen(new SolidBrush(m_lineColor), m_lineThickness))
     84             {
     85                 pe.Graphics.SmoothingMode = SmoothingMode.HighQuality;
     86                 pen.StartCap = LineCap.Round;
     87                 pen.EndCap = LineCap.Round;
     88 
     89                 OnPaint(pen, pe, xList.ToArray(), yList.ToArray());
     90             }
     91 
     92             base.OnPaint(pe);
     93         }
     94 
     95         private void OnPaint(Pen pen, PaintEventArgs pe, float[] x, float[] y)
     96         {
     97             for (int i = 0; i < x.Length; i++)
     98             {
     99                 //9点方向的点
    100                 float px1 = (float)(x[i] - m_sideLength);
    101                 float py1 = (float)(y[i]);
    102 
    103                 //3点方向的点
    104                 float px2 = (float)(x[i] + m_sideLength);
    105                 float py2 = (float)(y[i]);
    106 
    107                 //5点方向的点
    108                 float px3 = (float)(x[i] + 0.5 * m_sideLength);
    109                 float py3 = (float)(y[i] + G3 * m_sideLength);
    110 
    111                 //7点方向的点
    112                 float px4 = (float)(x[i] - 0.5 * m_sideLength);
    113                 float py4 = (float)(y[i] + G3 * m_sideLength);
    114 
    115                 pe.Graphics.DrawLines(pen, new PointF[]
    116                     {   
    117                         new PointF(px2, py2),
    118                         new PointF(px3, py3), 
    119                         new PointF(px4, py4),
    120                         new PointF(px1, py1)
    121                     });
    122             }
    123         }
    124     }
    125 }
    六边形阵列控件

       下面这段代码主要控制鼠标操作的事件,以及通过委托把消息传递出去。当鼠标指针靠近控件边缘并在1/8的相对区域内,会触发滑动地图的事件。

    兵棋地图控件

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Drawing;
      5 using System.Data;
      6 using System.Linq;
      7 using System.Text;
      8 using System.Windows.Forms;
      9 using UI.Properties;
     10 using System.Threading;
     11 
     12 namespace UI.Controls
     13 {
     14     public class MyMap : Panel
     15     {
     16         SixSidesControl ctl = null;
     17 
     18         readonly int _movePx = 10;
     19 
     20         public MyMap()
     21         {
     22             ctl = new SixSidesControl();
     23             ctl.BackgroundImage = Resources.dt;
     24             ctl.BackgroundImageLayout = ImageLayout.Stretch;
     25             ctl.Size = new Size((int)(1024 * 2), (int)(600 * 2));//设置地图大小
     26             ctl.LineThickness = 2;//线够不够胖
     27             ctl.LineColor = Color.Gray;//线的颜色够不够深
     28             ctl.SideLength = 25;//线的边够不够长
     29             this.Controls.Add(ctl);
     30             this.AutoScroll = false;//是否开启滚动条
     31             this.BackColor = Color.Blue;
     32 
     33             ctl.MouseMove += new MouseEventHandler(ctl_MouseMove);
     34             this.MouseMove += new MouseEventHandler(MyMap_MouseMove);
     35         }
     36 
     37         void ctl_MouseMove(object sender, MouseEventArgs e)
     38         {
     39             MyMap_MouseMove(sender, new MouseEventArgs(e.Button, e.Clicks, e.X + ctl.Location.X, e.Y + ctl.Location.Y, e.Delta));
     40         }
     41 
     42         void MyMap_MouseMove(object sender, MouseEventArgs e)
     43         {
     44             int mx = e.X;
     45             int my = e.Y;
     46 
     47             if (mx > 0 && my > 0 && mx < this.Width && my < this.Height)
     48             {
     49                 int x = this.Width / 8;
     50                 int y = this.Height / 8;
     51                 int x2 = this.Width - x;
     52                 int y2 = this.Height - y;
     53 
     54                 if (mx > x && my > y && mx < x2 && my < y2)
     55                     return;
     56 
     57                 if (mx < x && my < y)
     58                 {
     59                     MoveMap(_movePx, _movePx);
     60                     MsgEvnet.SendMsg("左上移动,鼠标位置:(" + mx + "," + my + ")");
     61                 }
     62                 else if (mx < x && my > y && my < y2)
     63                 {
     64                     MoveMap(_movePx, 0);
     65                     MsgEvnet.SendMsg("向左移动,鼠标位置:(" + mx + "," + my + ")");
     66                 }
     67                 else if (mx < x && my > y2)
     68                 {
     69                     MoveMap(_movePx, -_movePx);
     70                     MsgEvnet.SendMsg("左下移动,鼠标位置:(" + mx + "," + my + ")");
     71                 }
     72                 else if (mx > x && mx < x2 && my < y)
     73                 {
     74                     MoveMap(0, _movePx);
     75                     MsgEvnet.SendMsg("向上移动,鼠标位置:(" + mx + "," + my + ")");
     76                 }
     77                 else if (mx > x && mx < x2 && my > y2)
     78                 {
     79                     MoveMap(0, -_movePx);
     80                     MsgEvnet.SendMsg("向下移动,鼠标位置:(" + mx + "," + my + ")");
     81                 }
     82                 else if (mx > x2 && my < y)
     83                 {
     84                     MoveMap(-_movePx, _movePx);
     85                     MsgEvnet.SendMsg("右上移动,鼠标位置:(" + mx + "," + my + ")");
     86                 }
     87                 else if (mx > x2 && my > y && my < y2)
     88                 {
     89                     MoveMap(-_movePx, 0);
     90                     MsgEvnet.SendMsg("向右移动,鼠标位置:(" + mx + "," + my + ")");
     91                 }
     92                 else if (mx > x2 && my > y2)
     93                 {
     94                     MoveMap(-_movePx, -_movePx);
     95                     MsgEvnet.SendMsg("右下移动,鼠标位置:(" + mx + "," + my + ")");
     96                 }
     97             }
     98         }
     99 
    100         void MoveMap(int x, int y)
    101         {
    102             int nx = Math.Min(Math.Max(this.Width - ctl.Width, ctl.Location.X + x), 0);
    103             int ny = Math.Min(Math.Max(this.Height - ctl.Height, ctl.Location.Y + y), 0);
    104 
    105             if (ctl.Location.X == nx && ny == ctl.Location.Y)
    106                 return;
    107 
    108             ctl.Location = new Point(nx, ny);
    109 
    110             Thread.Sleep(50);
    111         }
    112 
    113         ~MyMap()
    114         { 
    115         
    116         }
    117     }
    118 }
    兵棋地图

      from窗体控件里面的东西相对来说比较简单了,主要是把通过委托的消息展示下,没什么难度,把设计器和后台代码合并了。

     form窗体控件

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Data;
      5 using System.Drawing;
      6 using System.Linq;
      7 using System.Text;
      8 using System.Windows.Forms;
      9 
     10 namespace UI
     11 {
     12     public partial class frmMap : Form
     13     {
     14         public frmMap()
     15         {
     16             InitializeComponent();
     17 
     18             MsgEvnet.msgEvent += (s, e) => { AppendText(this.rtxShowMsg, e); };
     19         }
     20 
     21         private void AppendText(RichTextBox tb, MsgEventArgs e)
     22         {
     23             if (!this.InvokeRequired)
     24             {
     25                 Color color = Color.Black;
     26                 switch (e.Type)
     27                 {
     28                     case MsgType.Info:
     29                         color = Color.Green;
     30                         break;
     31                     case MsgType.Error:
     32                         color = Color.Blue;
     33                         break;
     34                     case MsgType.Warn:
     35                         color = Color.Red;
     36                         break;
     37                     case MsgType.OtherMessage:
     38                         color = Color.DarkSeaGreen;
     39                         break;
     40                     case MsgType.OwnerMessage:
     41                         color = Color.DarkSlateBlue;
     42                         break;
     43                 }
     44                 tb.SelectionColor = color;
     45                 tb.AppendText(e.Message + Environment.NewLine);
     46             }
     47             else
     48             {
     49                 tb.Invoke(new MethodInvoker(delegate { AppendText(tb, e); }));
     50             }
     51         }
     52 
     53 
     54         private System.ComponentModel.IContainer components = null;
     55 
     56         protected override void Dispose(bool disposing)
     57         {
     58             if (disposing && (components != null))
     59             {
     60                 components.Dispose();
     61             }
     62             base.Dispose(disposing);
     63         }
     64 
     65         private void InitializeComponent()
     66         {
     67             this.rtxShowMsg = new System.Windows.Forms.RichTextBox();
     68             this.myMap1 = new UI.Controls.MyMap();
     69             this.SuspendLayout();
     70             // 
     71             // rtxShowMsg
     72             // 
     73             this.rtxShowMsg.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
     74                         | System.Windows.Forms.AnchorStyles.Right)));
     75             this.rtxShowMsg.Location = new System.Drawing.Point(0, 484);
     76             this.rtxShowMsg.Name = "rtxShowMsg";
     77             this.rtxShowMsg.Size = new System.Drawing.Size(767, 62);
     78             this.rtxShowMsg.TabIndex = 2;
     79             this.rtxShowMsg.Text = "";
     80             // 
     81             // myMap1
     82             // 
     83             this.myMap1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
     84                         | System.Windows.Forms.AnchorStyles.Left)
     85                         | System.Windows.Forms.AnchorStyles.Right)));
     86             this.myMap1.BackColor = System.Drawing.Color.Blue;
     87             this.myMap1.Location = new System.Drawing.Point(0, 2);
     88             this.myMap1.Name = "myMap1";
     89             this.myMap1.Size = new System.Drawing.Size(767, 476);
     90             this.myMap1.TabIndex = 3;
     91             // 
     92             // frmMap
     93             // 
     94             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
     95             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
     96             this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
     97             this.ClientSize = new System.Drawing.Size(767, 547);
     98             this.Controls.Add(this.myMap1);
     99             this.Controls.Add(this.rtxShowMsg);
    100             this.Name = "frmMap";
    101             this.Text = "frmMap";
    102             this.ResumeLayout(false);
    103 
    104         }
    105 
    106         private System.Windows.Forms.RichTextBox rtxShowMsg;
    107         private UI.Controls.MyMap myMap1;
    108     }
    109 }
    from窗体

      时间有限,篇幅有限,今天下午就敲了这么多,下次继续。(不过上午把设计文档写好了,按照步骤一点点的做吧)

      这几段代码手打后没详细测试,可能会有问题,欢迎斧正。

    差点忘记了,背景图片用的这个:

  • 相关阅读:
    如何去除行内元素之间的间隙
    js判断字符串是否为空,多个空格也算为空
    android4.4的Webview的getCookie有兼容性有问题
    配置xampp搭建简单的web服务器环境
    Okhttp传递cookie给Webview的解决方法
    linux下使用nodejs和lessc编译器
    某盘下载链接提取脚本
    Android项目开发过程常用的工作流工具以及平台
    Android开发检测App从后台进入前台的解决方法
    NetworkImageView
  • 原文地址:https://www.cnblogs.com/preacher/p/4115451.html
Copyright © 2020-2023  润新知