【问题标题】:What would be the best way to simulate Radar in C#?在 C# 中模拟雷达的最佳方法是什么?
【发布时间】:2010-11-20 01:25:47
【问题描述】:

我的表单中的 Groupbox 中有一个图片框,其中雷达图片设置为背景图片。我的意图是在运行时在雷达区域(覆盖)内动态加载微小的 Jpeg 图像,但我不确定实现这一目标的最佳方法。 欢迎所有疯狂的想法(但我更喜欢理智容易做的想法)。 谢谢大家。

【问题讨论】:

    标签: c# .net winforms graphics


    【解决方案1】:

    这在很大程度上取决于您的“雷达”需要是什么样子,但几乎可以肯定的是,您需要实现 Paint 事件处理程序,并自己绘制雷达显示的内容。图片框只能让你走这么远(“不是很”)。

    GDI+ 非常容易用于绘制圆圈、线条、文本和图像,并且可以让您完全控制显示器的外观。

    【讨论】:

      【解决方案2】:

      以实际为例:

        // Among others
        using System.Collections.Generic;
        using System.Drawing;
        using System.IO;
      
        class TinyPic {
          public readonly Image Picture;
          public readonly Rectangle Bounds;
      
          public TinyPic(Image picture, int x, int y) {
            Picture = picture;
            Bounds = new Rectangle(x, y, picture.Width, picture.Height);
          }
        }
      
        class MyForm : Form {
      
          Dictionary<String, TinyPic> tinyPics = new Dictionary<String, TinyPic>();
      
          public MyForm(){
            InitializeComponent(); // assuming Panel myRadarBox
                                   // with your background is there somewhere;
            myRadarBox.Paint += new PaintEventHandler(OnPaintRadar);
          }
      
          void OnPaintRadar(Object sender, PaintEventArgs e){
            foreach(var item in tinyPics){
              TinyPic tp = item.Value;
              e.Graphics.DrawImageUnscaled(tp.Picture, tp.Bounds.Location);
            }
          }
      
          void AddPic(String path, int x, int y){
            if ( File.Exists(path) ){
              var tp = new TinyPic(Image.FromFile(path), x, y);
              tinyPics[path] = tp;
              myRadarBox.Invalidate(tp.Bounds);
            }
          }
      
          void RemovePic(String path){
            TinyPic tp;
            if ( tinyPics.TryGetValue(path, out tp) ){
              tinyPics.Remove(path);
              tp.Picture.Dispose();
              myRadarBox.Invalidate(tp.Bounds);
            }
          }
        }
      

      这当然是非常基本的,假设图像源是路径并且不处理许多复杂的事情,但这是你当然可以建立的快速而肮脏的 jist。

      【讨论】:

      • 干杯.....还没有完成 GDI+ 或任何图形编程在 C# 中,并且截止日期迫在眉睫(这是我在截止日期前
      【解决方案3】:

      Click here 运行一个示例应用程序,演示如何做雷达的基础知识(或至少一种方式)。注意:此应用程序不对小图像进行双缓冲或透明处理。

      该项目的源代码是here

      更新代码:

      public partial class Form1 : Form
      {
          private Bitmap _canvas;
          private float _sweepStartAngle = -90;
          private float _sweepAngle = 15;
          private SolidBrush _sweepBrush = new SolidBrush(Color.Red);
          private Rectangle _sweepRect;
          private Timer _sweepTimer = new Timer();
          private Bitmap _submarine;
          private Point _submarinePosition = new Point(0, 0);
          private Random rnd = new Random();
      
          public Form1()
          {
              InitializeComponent();
      
              _canvas = new Bitmap(pbScope.Width, pbScope.Height);
              pbScope.Image = _canvas;
              _sweepRect = new Rectangle(0, 0, pbScope.Width, pbScope.Height);
      
              _submarine = (Bitmap)pbSubmarine.Image;
      
              RedrawScope();
      
              _sweepTimer.Interval = 100;
              _sweepTimer.Tick += new EventHandler(_sweepTimer_Tick);
              _sweepTimer.Start();
          }
      
          void _sweepTimer_Tick(object sender, EventArgs e)
          {
              _sweepStartAngle += _sweepAngle;
              RedrawScope();
          }
      
          private void RedrawScope()
          {
              using (Graphics g = Graphics.FromImage(_canvas))
              {
                  // draw the background
                  g.DrawImage(pbBackground.Image, 0, 0);
      
                  // draw the "sweep"
                  GraphicsPath piepath = new GraphicsPath();
                  piepath.AddPie(_sweepRect, _sweepStartAngle, _sweepAngle);
                  g.FillPath(_sweepBrush, piepath);
                  //g.FillPie(_sweepBrush, _sweepRect, _sweepStartAngle, _sweepAngle);
      
                  // move the submarine and draw it
                  _submarinePosition.X += rnd.Next(3);
                  _submarinePosition.Y += rnd.Next(3);
                  // check if submarine intersects with piepath
                  Rectangle rect = new Rectangle(_submarinePosition, _submarine.Size);
                  Region region = new Region(piepath);
                  region.Intersect(rect);
                  if (!region.IsEmpty(g))
                  {
                      g.DrawImage(_submarine, _submarinePosition);
                  }
              }
              pbScope.Image = _canvas;
          }
      
          private void Form1_FormClosing(object sender, FormClosingEventArgs e)
          {
              _sweepTimer.Stop();
              _sweepTimer.Dispose();
          }
      
          private void Form1_Load(object sender, EventArgs e)
          {
              //GraphicsPath piepath = new GraphicsPath();
              //piepath.AddPie(
      
          }
      
      }
      
         private void InitializeComponent()
          {
              System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
              this.pbScope = new System.Windows.Forms.PictureBox();
              this.pbBackground = new System.Windows.Forms.PictureBox();
              this.pbSubmarine = new System.Windows.Forms.PictureBox();
              ((System.ComponentModel.ISupportInitialize)(this.pbScope)).BeginInit();
              ((System.ComponentModel.ISupportInitialize)(this.pbBackground)).BeginInit();
              ((System.ComponentModel.ISupportInitialize)(this.pbSubmarine)).BeginInit();
              this.SuspendLayout();
              // 
              // pbScope
              // 
              this.pbScope.Location = new System.Drawing.Point(12, 12);
              this.pbScope.Name = "pbScope";
              this.pbScope.Size = new System.Drawing.Size(300, 300);
              this.pbScope.TabIndex = 0;
              this.pbScope.TabStop = false;
              // 
              // pbBackground
              // 
              this.pbBackground.Image = ((System.Drawing.Image)(resources.GetObject("pbBackground.Image")));
              this.pbBackground.Location = new System.Drawing.Point(341, 12);
              this.pbBackground.Name = "pbBackground";
              this.pbBackground.Size = new System.Drawing.Size(300, 300);
              this.pbBackground.TabIndex = 1;
              this.pbBackground.TabStop = false;
              this.pbBackground.Visible = false;
              // 
              // pbSubmarine
              // 
              this.pbSubmarine.Image = ((System.Drawing.Image)(resources.GetObject("pbSubmarine.Image")));
              this.pbSubmarine.Location = new System.Drawing.Point(658, 45);
              this.pbSubmarine.Name = "pbSubmarine";
              this.pbSubmarine.Size = new System.Drawing.Size(48, 48);
              this.pbSubmarine.TabIndex = 2;
              this.pbSubmarine.TabStop = false;
              this.pbSubmarine.Visible = false;
              // 
              // Form1
              // 
              this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
              this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
              this.ClientSize = new System.Drawing.Size(326, 328);
              this.Controls.Add(this.pbSubmarine);
              this.Controls.Add(this.pbBackground);
              this.Controls.Add(this.pbScope);
              this.Name = "Form1";
              this.Text = "Radar";
              this.Load += new System.EventHandler(this.Form1_Load);
              this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
              ((System.ComponentModel.ISupportInitialize)(this.pbScope)).EndInit();
              ((System.ComponentModel.ISupportInitialize)(this.pbBackground)).EndInit();
              ((System.ComponentModel.ISupportInitialize)(this.pbSubmarine)).EndInit();
              this.ResumeLayout(false);
      
          }
      

      【讨论】:

      • 您可以在代码中进行编辑吗?今天的标准需要的不仅仅是外部链接。
      • 当然,尽管发布依赖于控件初始化和布局等的代码并不是很实用。
      【解决方案4】:

      最简单的方法是将微小的 JPEG 加载到微小的 PictureBox 中,并在运行时将它们添加到主 PictureBox 的 Controls 集合中(即将它们放在 PictureBox 上)。

      由于这可能会产生闪烁,稍微复杂一点的方法是将主图片和小图片保留在类级别的 Bitmap 对象中,并在主 PictureBox 的 Paint 事件中,复制主图片,然后复制每个小图片使用 DrawImage 方法复制到第二个类级别位图(名为 _doubleBuffer 或类似名称)上,然后将 _doubleBuffer 复制到您的 PictureBox(也使用 DrawImage)上。每当您需要更新显示并重绘所有内容时,只需调用 PictureBox 的 Invalidate 方法即可。

      这里有很多关于 SO 的示例,展示了如何使用这些方法。祝你好运,这听起来很有趣(如果你正在重写经典的街机游戏 Submarine,请告诉我 - 我喜欢那个游戏)。

      【讨论】:

      • 该死!!这听起来很复杂......我想我会按照上面 JW 的建议为初学者阅读 GDI+。不过谢谢。没玩过潜艇;我更喜欢 NES 版的“The Hunt for The Red October”。
      • 这并不复杂,实际上只是对杰森答案的阐述。我也在谈论 GDI+ 和处理 Paint 事件。
      • 双缓冲部分对于避免闪烁是必不可少的。
      • 当雷达图像设置为我的主图片框的背景图像时,为什么我需要双缓冲。我正在考虑在绘图区域内绘制一个容器(在这种情况下是一个面板),使其不可见,然后创建一个 rectf 并将其边界设置为容器并在 rectf 对象边界内绘制......你怎么看场景?
      • 你会闪烁 - 相信我这个。这很复杂,但闪烁基本上来自 GDI+ 绘图操作在显示器刷新过程中被“捕获”(以及其他问题)。避免这种情况的唯一方法是在不可见的画布上执行所有复杂的多步骤绘图操作,然后将这个画布一次性渲染到可见表面。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-03
      • 1970-01-01
      • 2012-01-09
      • 1970-01-01
      • 2014-08-22
      相关资源
      最近更新 更多