Home > Enterprise >  Using WinForms to Draw Ellipses Continuously while Preserving a Rectangle
Using WinForms to Draw Ellipses Continuously while Preserving a Rectangle

Time:12-18

I am doing an exercise from my book on WinForms, inheritance, interfaces and abstract classes. The exercise is to create couple of balls moving in random directions and having obstacles of shapes like a box interact with the balls.

enter image description here

The thought process in the book was that there is a Ball class and another class that is called Engine to handle the creations of the ellipses. My job is to create the obstacles and have them interact with the balls.

This are the requirements

All obstacles should be treated the same w.r.t. the engine; perhaps by using an interface. Do not use fully abstract classes

All obstacles should share as much code as possible, but only code that makes sense to share; use abstract classes if necessary to avoid inheriting methods that do not make sense or empty methods.

The Ball and Engine, class where given.

Position :

public class Position
    {
        public float X, Y;

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

Vector :

public class Vector
{
    public float X, Y;

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

Ball :

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;
        }

    }

Engine :

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 
        }
    }

And this is the interface I created :

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);

    }
}

I am stuck figuring out how would my one (for now) rectangle would be drawn without it being refreshed every time. I might sound like an idiot, but I am fairly new. The result that I get is same rectangle appearing and disappearing with the same tick. I have also noticed that the Ball class creates and stores new "balls" in the list, but I cant do the same I think because of my interface implementation. I am creating interface for all the Obstacles as they are the same but with different shape. I might be wrong in my implementation but it sound logical to me at least.

Any help would be appreciated, as I am newbie in C#.

CodePudding user response:

Using Jimi's Suggestios :

It turns out that he was right the randomness declared in Draw wasn't the right solution and it caused like he said for the obstacles to be redrawn every time with random position. This is all fixed by moving them outside I also modified the class so that it was possible to chose where the position of the box would be drawn.

I am posting the answer so that others that stumble on this can have a look on how I solved my problem thanks to Jimi's solution.

Interface :

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);
    }
}

Engine :

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);
        }
    }
}

If there are still issues or some suggestion that anyone would like to add feel free to comment.

  • Related