兵棋系列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窗体

 


 

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

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

 

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

 

posted @ 2014-11-22 17:54  牧师/preacher  阅读(1880)  评论(0编辑  收藏  举报