自己绘制的仪表盘

  一时兴起,想写个模拟地铁驾驶的游戏,但是很多东西都不会,资源哪里有?例如列车前进时周围景物的动态效果怎么做出来,速度控制杆上各个幅度代表了多大的加速度。我很佩服也很羡慕《申城脉动》的作者,能写出那样的一个游戏和获得那么多的资源。地铁族上面资料不少的,但不会找出来,废话不多说。

  里面要用到的速度仪表盘,上网看见别人画了不少很炫的,但提供下载没有。对GDI+不太了解的我只能自己写。幸亏看到一篇博文,是别人学生时代的实验报告的(呵呵!这就是差别)。里面的代码我没看,我只看了一幅图片就够了。之前只困惑于仪表盘上面的刻度是怎么画出来的,那篇文章里面有一幅图就是一幅三角函数的图。之前一直没想到这里要用回初中时学的三角函数。

  下面将从控件的属性,还有绘制的过程做一个介绍。从而介绍完这个仪表盘的制作。

数据类型

属性名称

描述

int

LongGraduateLength

长刻度长度

int

ShortGraduateLength

短刻度长度

float

Short2Long

短刻度转长刻度进率

float

ShortgraduateSpan

短刻度间隔,实际数值的间隔

float

Range

量程

int

Angle

整个刻度的角度,角度制的度数

int

Width

控件宽度,与高度相等

int

Height

控件高度,与宽度相等

float

Value

当前值

  绘制控件都是在OnPaint事件里执行GDI+的代码。

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            DrawGraduation(e.Graphics);
            DrawArc(e.Graphics);
        }

第一个方法是画表盘的刻度,以及上面的示数;第二个方法是画表盘的指针,包括当前的值。

先介绍第一个方法

 1         private void DrawGraduation(Graphics g)
 2         { 
 3             int startDec = 270 - (360 - Angle) / 2;
 4             int endDec = 270 + (360 - Angle) / 2 - 360;//计算出起始角度和终止角度
 5             float dealta = Angle * ShortgraduateSpan / Range; //计算出每个刻度间的角度间隔
 6             StringFormat sf=new StringFormat(){ Alignment= StringAlignment.Center,LineAlignment= StringAlignment.Center};
 7             
 8             for (float i = startDec; i >= endDec; i -= dealta)//通过循环画出表盘上的刻度
 9             {
10                 if ((i - startDec)*Range/Angle % Short2Long == 0) //判断此位置画的是大刻度还是小刻度
11                 {
12                     //画大刻度
13                     g.DrawLine(Pens.White,
14                         Radius + (float)Math.Cos(Degree2Radian(i)) * Radius,
15                         Radius - (float)Math.Sin(Degree2Radian(i)) * Radius,
16                         Radius + (float)Math.Cos(Degree2Radian(i)) * (Radius - LongGraduateLength),
17                         Radius - (float)Math.Sin(Degree2Radian(i)) * (Radius - LongGraduateLength)
18                         );
19                     //写刻度上的文字
20                     g.DrawString(((startDec - i) * Range / Angle).ToString(), this.Font, Brushes.White,
21                         Radius + (float)Math.Cos(Degree2Radian(i)) * (Radius - LongGraduateLength-15),
22                         Radius - (float)Math.Sin(Degree2Radian(i)) * (Radius - LongGraduateLength-15),
23                         sf
24                         );//sf是文字的格式,这里设置成居中
25                 }
26                 else
27                     //画小刻度
28                     g.DrawLine(Pens.White,
29                     Radius + (float)Math.Cos(Degree2Radian(i)) * Radius,
30                     Radius - (float)Math.Sin(Degree2Radian(i)) * Radius,
31                     Radius + (float)Math.Cos(Degree2Radian(i)) * (Radius - ShortGraduateLength),
32                     Radius - (float)Math.Sin(Degree2Radian(i)) * (Radius - ShortGraduateLength)
33                     );
34             }
35         }

画刻度线还考了一回初中的三角函数。

只要有刻度线所在的角的角度,刻度线的起点和终点的坐标都可以确定下来。起点就是半径上圆心的另一端。通过sin*R可以得到起点的相对圆心的纵坐标;cos*R可以得到起点相对圆心的横坐标。终点的同样道理,通过sin*(R-L)可以得到终点的相对圆心的纵坐标;cos*(R-L)可以得到终点相对圆心的横坐标。

.NET Framework提供的三角函数有点坑,方法的描述上是说角度值,但是实际上要传是弧度值。所以我这里还要做一个角度制和弧度制的转换。

        private double Degree2Radian(double degree)
        {
            return degree * Math.PI / 180;
        }

下面到另一个方法

 1         private void DrawArc(Graphics g)
 2         {
 3             //示数和外部的圆圈
 4             for(float i=0;i<3;i+=0.1f)
 5                 g.DrawEllipse(Pens.White, Radius - 25+i, Radius - 25+i, 50-2*i, 50-2*i);
 6             g.DrawString(this.Value.ToString(), new Font(this.Font.FontFamily, 20,FontStyle.Bold), Brushes.White, Radius, Radius, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
 7 
 8             //旋转画布,形成一定的倾角
 9             Matrix matrix = new Matrix();
10             matrix.RotateAt(Value * Angle / Range - (270 - (360 - Angle) / 2), new PointF(Radius, Radius), MatrixOrder.Append);
11             g.Transform = matrix;
12             
13             //绘制指针上面的主体矩形
14             Rectangle rec = new Rectangle(Radius + 25, Radius - 3,Radius-55-ShortGraduateLength, 6);
15             g.DrawRectangle(Pens.White, rec);
16             g.FillRectangle(Brushes.White, rec);
17             
18             //绘制指针上的三角形
19             g.DrawPolygon(Pens.White,CreateTriangle(Radius*2 -30-ShortGraduateLength,Radius ,6,10,false));
20             g.FillPolygon(Brushes.White, CreateTriangle(Radius*2 -30-ShortGraduateLength, Radius , 6, 10, false));
21 
22             //绘制指针末端的小矩形
23             rec = new Rectangle(Radius * 2 - 25 - ShortGraduateLength,Radius-1,20,2);
24             g.DrawRectangle(Pens.White, rec);
25             g.FillRectangle(Brushes.White, rec);
26         }

这里指针实际上有四个图形组成,指针起始的一个圆形,主体的那个粗的矩形,达到渐细效果的三角形和末端的一个小矩形

  .NET Framework又没有提供画三角形的方法,所以画三角形它只能通过画多边形的方法来绘制。

  我这里定义了一个只符合这里使用的方法,用于绘制一个等腰三角形,传入底边的中点坐标,底边和高的长度,是水平还是垂直,来构造一个三角形,最后返回三个顶点的坐标

 1         private Point[] CreateTriangle(int x, int y,int bottom, int height, bool isHorizontal)
 2         {
 3             Point p1, p2, ph;
 4             if (isHorizontal)
 5             {
 6                 p1 = new Point(x - bottom/2, y);
 7                 p2 = new Point(x + bottom/2, y);
 8                 ph = new Point(x, y + height);
 9             }
10             else
11             {
12                 p1 = new Point(x, y - bottom/2);
13                 p2 = new Point(x, y + bottom/2);
14                 ph = new Point(x + height, y);
15             }
16             return new Point[] {p1,p2,ph };
17         }

附一幅完成的效果图,最后把整个控件的源码粘上来。

觉得这个控件一点儿也不通用,只能满足极个别的需求。大家尽管提一下意见,让我改进改进。

  1     public class Dashboard:Control
  2     {
  3         public Dashboard()
  4         {
  5             this.Width = 300;
  6             this.Height = 300;
  7             this.BackColor = Color.Black;
  8             DoubleBuffered = true;
  9             this.Font = new Font(this.Font.FontFamily, 15f);
 10         }
 11 
 12         private int longGraduateLength=20,shortGraduateLength=10,angle=300;
 13         private float short2Long=2,shortgraduateSpan=5,range=100;
 14         private Color lineColor=Color.White;
 15 
 16         public int LongGraduateLength
 17         {
 18             get { return longGraduateLength; }
 19             set 
 20             {
 21                 longGraduateLength = value < 1 ? 1 : value;
 22             }
 23         }
 24 
 25         public int ShortGraduateLength
 26         {
 27             get { return shortGraduateLength; }
 28             set
 29             {
 30                 shortGraduateLength = value < 1 ? 1 : value;
 31             }
 32         }
 33 
 34         public float Short2Long
 35         {
 36             get { return short2Long; }
 37             set
 38             {
 39                 short2Long = value < 1 ? 1 : value;
 40             }
 41         }
 42 
 43         public float ShortgraduateSpan
 44         {
 45             get { return shortgraduateSpan; }
 46             set
 47             {
 48                 shortgraduateSpan = value < 1 ? 1 : value;
 49             }
 50         }
 51 
 52         public float Range
 53         {
 54             get { return range; }
 55             set
 56             {
 57                 range = value < 1 ? 1 : value;
 58             }
 59         }
 60 
 61         public int Angle
 62         {
 63             get { return angle; }
 64             set
 65             {
 66                 angle = value < 1 ? 1 : value;
 67             }
 68         }
 69 
 70         public Color LineColor
 71         {
 72             get { return lineColor; }
 73             set { lineColor = value; }
 74         }
 75 
 76         public new int Width
 77         {
 78             get { return base.Width; }
 79             set 
 80             {
 81                 if (value != base.Height) base.Height = value;
 82                 base.Width = value;
 83             }
 84         }
 85 
 86         public new int Height
 87         {
 88             get { return base.Height; }
 89             set
 90             {
 91                 if (value != base.Width) base.Width = value;
 92                 base.Height = value;
 93             }
 94         }
 95 
 96         private float currValue;
 97         public float Value
 98         {
 99             get { return currValue; }
100             set
101             {
102                 float preValue = currValue;
103                 if (value < 0) currValue = 0;
104                 else if (value > Range) currValue = Range;
105                 else currValue = value;
106                 if (preValue != currValue)
107                 {
108                     if(ValueChanged!=null) ValueChanged(this, new EventArgs());
109                     this.Refresh();
110                 }
111                 
112             }
113         }
114 
115         protected override void OnPaint(PaintEventArgs e)
116         {
117             base.OnPaint(e);
118             //刻度 示数 指针 示数
119             DrawGraduation(e.Graphics);
120             DrawArc(e.Graphics);
121         }
122 
123         private int Radius
124         {
125             get { return Width / 2; }
126         }
127 
128         private Point core;
129         private Point Core
130         {
131             get 
132             {
133                 if (core.X != this.Radius || core.Y != this.Radius)
134                     core = new Point(this.Radius, this.Radius);
135                 return core;
136             }
137         }
138 
139         private void DrawGraduation(Graphics g)
140         { 
141             //换算角度范围
142             //半径*sin=外点
143             //(半径-刻度长)*sin=内点
144             //内点标数
145             int startDec = 270 - (360 - Angle) / 2;
146             int endDec = 270 + (360 - Angle) / 2-360;
147             float dealta=Angle*ShortgraduateSpan/Range;
148             StringFormat sf=new StringFormat(){ Alignment= StringAlignment.Center,LineAlignment= StringAlignment.Center};
149             
150             for (float i = startDec; i >= endDec; i -= dealta)
151             {
152                 if ((i - startDec)*Range/Angle % Short2Long == 0)
153                 {
154                     g.DrawLine(Pens.White,
155                         Radius + (float)Math.Cos(Degree2Radian(i)) * Radius,
156                         Radius - (float)Math.Sin(Degree2Radian(i)) * Radius,
157                         Radius + (float)Math.Cos(Degree2Radian(i)) * (Radius - LongGraduateLength),
158                         Radius - (float)Math.Sin(Degree2Radian(i)) * (Radius - LongGraduateLength)
159                         );
160                     g.DrawString(((startDec - i) * Range / Angle).ToString(), this.Font, Brushes.White,
161                         Radius + (float)Math.Cos(Degree2Radian(i)) * (Radius - LongGraduateLength-15),
162                         Radius - (float)Math.Sin(Degree2Radian(i)) * (Radius - LongGraduateLength-15),
163                         sf
164                         );
165                 }
166                 else
167                     g.DrawLine(Pens.White,
168                     Radius + (float)Math.Cos(Degree2Radian(i)) * Radius,
169                     Radius - (float)Math.Sin(Degree2Radian(i)) * Radius,
170                     Radius + (float)Math.Cos(Degree2Radian(i)) * (Radius - ShortGraduateLength),
171                     Radius - (float)Math.Sin(Degree2Radian(i)) * (Radius - ShortGraduateLength)
172                     );
173             }
174         }
175 
176         private void DrawArc(Graphics g)
177         {
178             for(float i=0;i<3;i+=0.1f)
179             g.DrawEllipse(Pens.White, Radius - 25+i, Radius - 25+i, 50-2*i, 50-2*i);
180             g.DrawString(this.Value.ToString(), new Font(this.Font.FontFamily, 20,FontStyle.Bold), Brushes.White, Radius, Radius, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
181 
182 
183             Matrix matrix = new Matrix();
184             matrix.RotateAt(Value * Angle / Range - (270 - (360 - Angle) / 2), new PointF(Radius, Radius), MatrixOrder.Append);
185             g.Transform = matrix;
186             
187             Rectangle rec = new Rectangle(Radius + 25, Radius - 3,Radius-55-ShortGraduateLength, 6);
188             g.DrawRectangle(Pens.White, rec);
189             g.FillRectangle(Brushes.White, rec);
190             
191             g.DrawPolygon(Pens.White,CreateTriangle(Radius*2 -30-ShortGraduateLength,Radius ,6,10,false));
192             g.FillPolygon(Brushes.White, CreateTriangle(Radius*2 -30-ShortGraduateLength, Radius , 6, 10, false));
193 
194             rec = new Rectangle(Radius * 2 - 25 - ShortGraduateLength,Radius-1,20,2);
195             g.DrawRectangle(Pens.White, rec);
196             g.FillRectangle(Brushes.White, rec);
197         }
198 
199         private Point[] CreateTriangle(int x, int y,int bottom, int height, bool isHorizontal)
200         {
201             Point p1, p2, ph;
202             if (isHorizontal)
203             {
204                 p1 = new Point(x - bottom/2, y);
205                 p2 = new Point(x + bottom/2, y);
206                 ph = new Point(x, y + height);
207             }
208             else
209             {
210                 p1 = new Point(x, y - bottom/2);
211                 p2 = new Point(x, y + bottom/2);
212                 ph = new Point(x + height, y);
213             }
214             return new Point[] {p1,p2,ph };
215         }
216 
217         private double Degree2Radian(double degree)
218         {
219             return degree * Math.PI / 180;
220         }
221 
222         protected override void OnResize(EventArgs e)
223         {
224             base.OnResize(e);
225             this.Refresh();
226         }
227 
228         public delegate void OnValueChanged(object sender, EventArgs e);
229 
230         public event OnValueChanged ValueChanged;
231     }
仪表盘完整代码
原文地址:https://www.cnblogs.com/HopeGi/p/3329885.html