使用WinForms连续绘制椭圆并保留矩形

3

我正在完成一本关于WinForms、继承、接口和抽象类的习题。这个练习是创建几个以随机方向移动的球,并且有形状如盒子的障碍物与球进行交互。

enter image description here

书中的思路是有一个Ball类和另一个名为Engine的类来处理椭圆的创建。我的任务是创建障碍物并让它们与球进行交互。

以下是要求:

所有障碍都应针对引擎以相同方式处理; 可以使用接口。不要使用完全抽象的类。

所有障碍物应尽可能共享代码,但仅共享有意义的代码; 如必要,请使用抽象类避免继承无意义或空方法。

给出了BallEngine类。

位置:

public class Position
    {
        public float X, Y;

        public Position(float x, float y)
        {
            X = x; Y = y;
        }
    }

向量

public class Vector
{
    public float X, Y;

    public Vector(float x, float y)
    {
        X = x; Y = y;
    }
}

public class Ball
    {
        static Pen Pen = new Pen(Color.Black);

        Position Position;
        Vector Speed;

        float Radius;

        static Random Random = new Random();

        public Ball(float x, float y, float radius)
        {
            Position = new Position(x,y);
            var xd = Random.Next(1, 6);
            var yd = Random.Next(1, 6);
            if (Random.Next(0, 2) == 0) xd = -xd;
            if (Random.Next(0, 2) == 0) yd = -yd;

            Speed = new Vector(xd,yd);
            Radius = radius;
        }

        public void Draw(Graphics g)
        {
            g.DrawEllipse(Pen,Position.X - Radius, Position.Y - Radius, 2 * Radius, 2 * Radius);
        }

        public void Move()
        {
            Position.X += Speed.X;
            Position.Y += Speed.Y;
        }

    }

引擎:

public class Engine
    {
        MainForm Form = new MainForm();
        Timer Timer = new Timer();
        List<Ball> Balls = new List<Ball>();
        Redbox RBox = new Redbox(); //My added code 
        Random Random = new Random();


        public void Run()
        {
            Form.Paint += Draw;
            Timer.Tick += TimerEventHandler;
            Timer.Interval = 1000/25;
            Timer.Start();

            Application.Run(Form);
        }

        private void Form_Paint(object sender, PaintEventArgs e)
        {
            throw new NotImplementedException();
        }

        void TimerEventHandler(Object obj, EventArgs args)
        {
            if (Random.Next(100) < 25)
            {
                var ball = new Ball(400, 300, 10);
                Balls.Add(ball);
            }

            foreach (var ball in Balls)
            {
                ball.Move();
            }

            Form.Refresh();
        }

        void Draw(Object obj, PaintEventArgs args)
        {
            foreach (var ball in Balls)
            {
                ball.Draw(args.Graphics);
            }
            RBox.Draw(args.Graphics); //Testing 
        }
    }

这是我创建的界面: :

interface IObstacles
{
    void Draw(Graphics g);
}

class Redbox : IObstacles
{
    public void Draw(Graphics g)
    {
        Pen Pen = new Pen(Color.Red);
        Random Random = new Random();
        Position Position = new Position(Random.Next(100, 700), Random.Next(100, 700));

        var width = Random.Next(30, 100);
        var height = Random.Next(30, 100);

        g.DrawRectangle(Pen, Position.X , Position.Y, width, height);

    }
}

我现在遇到了一个难题,不知道如何绘制一个矩形(目前只有一个),同时不会让它每次都被刷新。可能我听起来像个白痴,但我还是很新手。我得到的结果是同一个矩形在相同的滴答声中出现和消失。我还注意到Ball类在列表中创建并存储新的“球”,但我认为由于我的接口实现,我不能这样做。我正在为所有Obstacles创建接口,因为它们都是相同的,但形状不同。也许我的实现有误,但至少对我来说听起来很合理。

如果有帮助的话,我作为C#新手将十分感激。


1
Form.Refresh(); 触发了重绘,包括背景。由于你的小球在移动,你可能确实希望在每次表单刷新/失效时重新绘制背景。因此需要重新绘制每个对象。虽然你可以比 Form.Refresh() 更谨慎地逐个无效化每个矩形,但这可能不值得努力。 - Jeremy Lakeman
1
将Draw方法中除g.DrawRectangle()之外的所有内容移到外部,即将这些变量声明为字段。仅在创建障碍物时使用随机尺寸,并不是每次绘制时都创建 - 对画布(这里是窗体)进行双缓冲处理,并用Form.Invalidate()替换Form.Refresh() - 在实际场景中使用时,引擎必须实现IDisposable,如果将绘图工具(仅此处的Pen)视为静态对象,则Ball也要如此 - 您不是在创建“一对球”,而是创建了很多球 - Jimi
1
忘了提一下:从你发布的图片来看,似乎你计划进行碰撞检测,我建议使用GraphicsPath作为形状的容器,并在Ball和ReBox中公开路径或路径点。GraphicsPath类已经提供了基本的(但实质性的)手段来确定路径是否包含一个点或一个点是否超出其外部边界(基于Pen大小)。或者,通过路径点,相对简单地执行区域交集并评估结果。 - Jimi
@Jimi,根据您的评论,我发布了一个答案。我不能更改EngineBall类中已经存在的任何内容,但是我可以像在我的答案中所做的那样添加代码。希望您对我这样做感到满意,我非常感谢您的反馈。 - Darke
1个回答

1

使用Jimi的建议:

事实证明,Draw中声明的随机性不是正确的解决方案,就像他所说的那样,它导致了障碍物每次都以随机的position重新绘制。通过将它们移到外部,所有这些问题都得到了解决,我还修改了类,使得可以选择绘制盒子的位置。

我发布答案,以便其他遇到这个问题的人可以看看我是如何通过Jimi的解决方案解决我的问题的。

界面:

interface IObstacles
{
    void Draw(Graphics g);
}

class Redbox : IObstacles
{
    Pen Pen = new Pen(Color.Red);

    Random Random = new Random();
    Position Position;

    float width;
    float height;

    public Redbox(float x, float y)
    {
        Position = new Position(x, y);

        width = Random.Next(30, 100);
        height = Random.Next(30, 100);
    }


    public void Draw(Graphics g)
    {
        g.DrawRectangle(Pen, Position.X , Position.Y, width, height);
    }
}

引擎

public class Engine
{
    MainForm Form = new MainForm();
    Timer Timer = new Timer();
    List<Ball> Balls = new List<Ball>();

    List<IObstacles> RBox = new List<IObstacles>(); 

    Random Random = new Random();


    public void Run()
    {
        Form.Paint += Draw;
        Timer.Tick += TimerEventHandler;
        Timer.Interval = 1000/25;
        Timer.Start();

        Application.Run(Form);
    }

    private void Form_Paint(object sender, PaintEventArgs e)
    {
        throw new NotImplementedException();
    }

    void TimerEventHandler(Object obj, EventArgs args)
    {
        
        if (Random.Next(100) < 25)
        {
            var ball = new Ball(400, 300, 10);
            Balls.Add(ball);
        }

        if (RBox.Count() < 2) 
        {
            RBox.Add(new Redbox(100 * Random.Next(1, 8), 100 * Random.Next(0, 6)));
        }
        foreach (var ball in Balls)
        {
            ball.Move();
        }

        Form.Refresh();
    }

    void Draw(Object obj, PaintEventArgs args)
    {
        foreach (var ball in Balls)
        {
            ball.Draw(args.Graphics);
        }

        foreach (var rbox in RBox)
        {
            rbox.Draw(args.Graphics);
        }
    }
}

如果还有问题或者任何建议,欢迎留言评论。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接