簡體   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