繁体   English   中英

如何绘制动画矩形?

[英]How to draw an animated rectangle?

如何绘制一个可以移动到 PictureBox 边缘的矩形,当它到达边缘时,它会展开。 可能有几个这样的矩形

下面是带有 Draw 函数的类的代码 (Pike.cs):

using System.Drawing;

namespace course_project
{
    internal class Pike : Fish
    {
        private Point coordinates;
        private Size size = new Size(40, 40);
        private Size speed;

        public Pike(Point data, AquariumForm aquariumForm) : base(Color.Green, aquariumForm)
        {
            Data = data;

            speed = new Size();
        }

        public Pike Next { get; set; }

        public override void Draw(Graphics graphics)
        {
            graphics.FillEllipse(brush, coordinates.X, coordinates.Y, size.Width, size.Height);
        }

        public void UpdateLocation(Rectangle bounds)
        {
            if (!bounds.Contains(coordinates + speed))
            {
                if (coordinates.X + speed.Width < bounds.Left || coordinates.X + speed.Width > bounds.Right - size.Width)
                {
                    speed.Width *= -1;
                }

                if (coordinates.Y + speed.Height < bounds.Top || coordinates.Y + speed.Height > bounds.Bottom - size.Height)
                {
                    speed.Height *= -1;
                }
            }

            coordinates += speed;
        }
    }
}

链表(PikeFlock.cs):

using System.Collections;
using System.Collections.Generic;
using System.Drawing;

namespace course_project
{
    internal class PikeFlock : IEnumerable<Pike>
    {
        Pike head;
        Pike tail;
        int count;

        public void Add(Point data, AquariumForm aquariumForm)
        {
            Pike pike = new Pike(data, aquariumForm);

            if (head == null)
                head = pike;
            else
                tail.Next = pike;

            tail = pike;

            count++;
        }

        public bool Remove(Point data)
        {
            Pike current = head;
            Pike previous = null;

            while (current != null)
            {
                if (current.Data.Equals(data))
                {
                    if (previous != null)
                    {
                        previous.Next = current.Next;

                        if (current.Next == null)
                        {
                            tail = previous;
                        }
                    }
                    else
                    {
                        head = head.Next;

                        if (head == null)
                            tail = null;
                    }

                    count--;
                    return true;
                }

                previous = current;
                current = current.Next;
            }

            return false;
        }

        public int Count { get { return count; } }

        public bool IsEmpty { get { return count == 0; } }

        public void Clear()
        {
            head = null;
            tail = null;
            count = 0;
        }

        public bool Contains(Point data)
        {
            Pike current = head;

            while (current != null)
            {
                if (current.Data.Equals(data))
                    return true;

                current = current.Next;
            }

            return false;
        }

        public void AppendFirst(Point data, AquariumForm aquariumForm)
        {
            Pike pike = new Pike(data, aquariumForm)
            {
                Next = head
            };

            head = pike;

            if (count == 0)
            {
                tail = head;
            }

            count++;
        }

        public IEnumerator<Pike> GetEnumerator()
        {
            Pike current = head;

            while (current != null)
            {
                yield return current;
                current = current.Next;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)this).GetEnumerator();
        }
    }
}

AquariumForm.cs(表单本身):

using System;
using System.Drawing;
using System.Windows.Forms;

namespace course_project
{
    public partial class AquariumForm : Form
    {
        readonly Aquarium aquarium;

        public AquariumForm()
        {
            InitializeComponent();

            DoubleBuffered = true;

            aquarium = new Aquarium(ClientRectangle);
            aquarium_timer.Interval = 100;
            aquarium_timer.Tick += Timer_Tick;
            aquarium_timer.Enabled = true;
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            foreach (Pike pike in aquarium.pikeFlock)
            {
                pike.UpdateLocation(ClientRectangle);
            }

            foreach (Carp carp in aquarium.carpFlock)
            {
                carp.UpdateLocation(ClientRectangle);
            }

            Invalidate();
        }

        private void Add_carp_button_Click(object sender, EventArgs e)
        {
            aquarium.carpFlock.Add(new Point(), this);
        }

        private void Add_pike_button_Click(object sender, EventArgs e)
        {
            aquarium.pikeFlock.Add(new Point(), this);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            aquarium.Paint(e.Graphics);
        }
    }
}

水族馆.cs:

using System.Drawing;

namespace course_project
{
    internal class Aquarium
    {
        readonly public PikeFlock pikeFlock;
        readonly public CarpFlock carpFlock;
        readonly Color waterColor;

        public Aquarium(Rectangle clientRectangle)
        {
            waterColor = Color.LightSkyBlue;
            pikeFlock = new PikeFlock();
            carpFlock = new CarpFlock();
        }

        public void Paint(Graphics graphics)
        {
            graphics.Clear(waterColor);

            foreach (Pike pike in pikeFlock)
                pike.Draw(graphics);

            foreach (Carp carp in carpFlock)
                carp.Draw(graphics);
        }
    }
}

鱼.cs

using System.Drawing;

namespace course_project
{
    internal class Fish
    {
        private protected Brush brush;
        private protected AquariumForm aquarium_form;

        public Fish(Color color, AquariumForm aquariumForm)
        {
            aquarium_form = aquariumForm;

            brush = new SolidBrush(color);
        }

        public virtual void Draw(Graphics graphics) { }

        public Point Data { get; set; }
    }
}

只是在您需要为所有动画制作动画时感到困惑。 通过单击按钮,一切都按原样创建,但动画不起作用,即使我不知道如何:(

您没有发布fish.cs ,因此未经测试我建议进行以下更改:

  • 不要使用PictureBox ,如果您以这种方式执行自定义动画,它只会使事情复杂化。 因此,不要从其分配的Image属性中提取Graphics ,该属性由与表单大小相同的Bitmap支持(顺便说一句。如果调整表单大小会发生什么?)只需使用表单时已经存在的Graphics被重绘。
  • PictureBox不同, Form默认情况下不使用双缓冲,因此您可能希望在表单构造函数中设置DoubleBuffered = true以避免闪烁。
  • 覆盖表单中的 OnPaint 并从那里启动整个重绘会话:
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    aquarium.Paint(e.Graphics); // instead of your Init() method
}
  • 这意味着您的Aquarium类中不需要bitmapgraphicsaquarium_form字段。 相反,您可以将表单的Graphics传递给Paint方法:
// in Aquarium.cs:
public void Paint(Graphics graphics)
{
    // Please note that graphics can have any size now so it works even if you
    // resize the form. Please also note that I treat pikeFlock as a Pike
    // enumeration instead of just coordinates
    g.Clear(waterColor);
    foreach (Pike pike in pikeFlock) // instead of int[] items
        pike.Draw(graphics); // instead of Draw(int[])
}
  • 您的PikeFlock只是IEnumerable ,我会将其更改为IEnumerable<Pike>以使其具有强类型。
  • 这也意味着PikeFlock.GetEnumerator应该产生Pike实例而不是 int 数组: yield return current而不是current.Data
  • Pike现在有int[]作为坐标。 我会将其更改为Point 并且不要在每次绘图迭代中传递新的随机坐标,因为每条鱼都会在这里和那里随机“传送”。 相反,保持当前的水平和垂直速度。
// in Pike.cs:
private Point coordinates;
private Size size = new Size(40, 40);

// speed is declared as Size because there is an operator overload for Point + Size
// Do not forget to initialize speed in constructor
private Size speed; 

public override void Draw(Graphics graphics)
{
    // no Refresh is needed because graphics comes from the form's OnPaint now
    graphics.FillEllipse(brush, coordinates.X, coordinates.Y, size.Width, size.Height);
}

public void UpdateLocation(Rectangle bounds)
{
    // if a fish would go out of bounds invert its vertical or horizontal speed
    // TODO: if you shrink the form an excluded fish will never come back
    // because its direction will oscillate until you enlarge the form again.
    if (!bounds.Contains(coordinates + speed))
    {
        if (coordinates.X + speed.Width < bounds.Left
            || coordinates.X + speed.Width > bounds.Right - size.Width)
        {
            speed.Width *= -1;
        }
        if (coordinates.Y + speed.Height < bounds.Top
            || coordinates.Y + speed.Height > bounds.Bottom - size.Height)
        {
            speed.Height *= -1;
        }
    }

    coordinates += speed;
}


  • 现在唯一缺少的是动画本身。 您可以从 Toolbox 将Timer组件添加到表单中。
// form:
public AquariumForm()
{
    InitializeComponent();
    DoubleBuffered = true;

    // No need to pass the Form anymore. Pass the bounds instead to
    // generate the initial coordinates within the correct range.
    // No Random field is needed here, use one in Aquarium constructor only.
    aquarium = new Aquarium(ClientRectangle);
    timer.Interval = 100;
    timer.Tick += Timer_Tick;
    timer.Enabled = true;
}

private void Timer_Tick(object sender, EventArgs e)
{
    // Updating coordinates of all fish. We just pass the current bounds.
    foreach (Pike pike in aquarium.pikeFlock)
        pike.UpdateLocation(ClientRectangle);

    // Invalidating the form's graphics so a repaint will be automatically
    // called after processing the pending events.
    Invalidate();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM