[英]How to draw an animated rectangle?
How to draw a rectangle that could move to the edge of the PictureBox and when it reached the edge, it would unfold.如何绘制一个可以移动到 PictureBox 边缘的矩形,当它到达边缘时,它会展开。 And there could be several such rectangles
可能有几个这样的矩形
Here is the code of the class with the Draw function (Pike.cs):下面是带有 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;
}
}
}
Linked List (PikeFlock.cs):链表(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 (The form itself): 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);
}
}
}
Aquarium.cs:水族馆.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);
}
}
}
Fish.cs鱼.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; }
}
}
Confused just at the point where you need to animate it all.只是在您需要为所有动画制作动画时感到困惑。 By clicking the button everything is created as it should be, but animation does not work, even I do not know how :(
通过单击按钮,一切都按原样创建,但动画不起作用,即使我不知道如何:(
You didn't post fish.cs
so without testing I would suggest the following changes:您没有发布
fish.cs
,因此未经测试我建议进行以下更改:
PictureBox
, it just complicates things if you perform custom animations this way.PictureBox
,如果您以这种方式执行自定义动画,它只会使事情复杂化。 So instead of extracting the Graphics
from its assigned Image
property, which is backed by a Bitmap
that has the same size as your form (btw. what happens if you resize your form?) simply just use the Graphics
that is already there when your form is redrawn.Image
属性中提取Graphics
,该属性由与表单大小相同的Bitmap
支持(顺便说一句。如果调整表单大小会发生什么?)只需使用表单时已经存在的Graphics
被重绘。PictureBox
, a Form
does not use double buffering by default so you might want to set DoubleBuffered = true
in the form constructor to avoid flickering.PictureBox
不同, Form
默认情况下不使用双缓冲,因此您可能希望在表单构造函数中设置DoubleBuffered = true
以避免闪烁。protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
aquarium.Paint(e.Graphics); // instead of your Init() method
}
bitmap
, graphics
and aquarium_form
fields in your Aquarium
class.Aquarium
类中不需要bitmap
、 graphics
和aquarium_form
字段。 Instead, you can pass the form's Graphics
to a Paint
method: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
is just IEnumerable
, I would change it to IEnumerable<Pike>
to make it strongly typed.PikeFlock
只是IEnumerable
,我会将其更改为IEnumerable<Pike>
以使其具有强类型。PikeFlock.GetEnumerator
should yield Pike
instances instead of int arrays: yield return current
instead of current.Data
PikeFlock.GetEnumerator
应该产生Pike
实例而不是 int 数组: yield return current
而不是current.Data
Pike
has now int[]
as coordinates. Pike
现在有int[]
作为坐标。 I would change it to Point
.Point
。 And do not pass new random coordinates in every drawing iteration because every fish will just randomly 'teleport' here and there.// 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
component to the form from the 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.