[英]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
以避免闪烁。protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
aquarium.Paint(e.Graphics); // instead of your Init() method
}
Aquarium
类中不需要bitmap
、 graphics
和aquarium_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;
}
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.