前几天写了一个六边形阵列的算法,今天周末比较闲,下午没事就做了做兵棋的地图操作,一点一点的做吧,总会做好,毕竟我也经常玩各种棋,对做一个这类型的小游戏非常感兴趣。
首先来解释下,下面要出现代码的操作。如上图,当鼠标指针移动到地图的四个边时,地图会自动左右上下滑动(地图比这个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 }
时间有限,篇幅有限,今天下午就敲了这么多,下次继续。(不过上午把设计文档写好了,按照步骤一点点的做吧)
这几段代码手打后没详细测试,可能会有问题,欢迎斧正。
差点忘记了,背景图片用的这个: