Я бы использовал растровые изображения вместо иконок, как говорили другие. Вот как бы я это сделал:
Прежде всего, создайте класс, который содержит растровое изображение и делегат, который будет указывать на метод, который получит положение растрового изображения как функцию времени.
delegate Point PositionFunction(int time);
class MovingBitmap
{
private Bitmap bitmap;
private PositionFunction positionFunction;
public MovingBitmap(Bitmap bitmap, PositionFunction positionFunction)
{
if (bitmap == null)
{
throw new ArgumentNullException("bitmap");
}
else if (positionFunction == null)
{
throw new ArgumentNullException("bitmap");
}
this.bitmap = bitmap;
this.positionFunction = positionFunction;
}
public Bitmap Bitmap
{
get { return this.bitmap; }
}
public PositionFunction PositionFunction
{
get { return this.positionFunction; }
}
}
Для функции положения вам нужно решить, как вы хотите перемещать растровые изображения. (Примечание: время выражается в миллисекундах.) Это может быть просто:
private Point SimpleTimeFunction(int time)
{
return new Point(time / 5, time / 5);
}
private Point ParabolaFunction(int time)
{
return new Point(time / 5, (time / 5) * (time / 5));
}
Или это может быть кусочная функция, составленная из нескольких уравнений, например:
if (time < 5000)
{
return new Point(time / 5, 2 * (time / 5));
}
else
{
return new Point(time / 5, time / 5) * time);
}
Все сводится к тому, как вы хотите, чтобы это двигалось. Надеюсь, вам нравится математика. :)
Затем в элемент управления, который будет содержать растровые изображения, добавьте поле List<MovingBitmap>
.
private List<MovingBitmap> bitmaps = new List<MovingBitmap>();
Затем вам нужно растровое изображение размера родительского элемента управления, чтобы использовать его в качестве буфера, чтобы пользовательский интерфейс был без мерцания. Вы нарисуете все движущиеся растровые изображения в буфере, а затем в свою очередь нарисуете это растровое изображение на элементе управления в OnPaint
.
private int startTime; // set this to System.Environment.TickCount when you start
// Add this line to the constructor
this.UpdateBufferSize();
private void UpdateBufferSize()
{
if (this.buffer != null)
{
this.buffer.Dispose();
}
this.buffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
}
private void RefreshBuffer()
{
int timeElapsed = Environment.TickCount - this.startTime;
using (Graphics g = Graphics.FromImage(this.buffer))
{
g.Clear(this.BackColor);
foreach (MovingBitmap movingBitmap in this.bitmaps)
{
Rectangle destRectangle = new Rectangle(
movingBitmap.PositionFunction(timeElapsed),
movingBitmap.Bitmap.Size);
g.DrawImage(movingBitmap.Bitmap, destRectangle);
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawImage(this.buffer, Point.Empty);
}
Для достижения эффекта анимации вам понадобится таймер. Когда таймер истекает, обновите буфер, а затем элемент управления.
private System.Timers.Timer timer;
private ParameterlessVoid refreshMethod;
private delegate void ParameterlessVoid();
// Add these four lines to the constructor
this.timer = new System.Timers.Timer(1000 / 20); // 20 times per second
this.timer.AutoReset = true;
this.timer.Elapsed += this.HandleTimerElapsed;
this.refreshMethod = new ParameterlessVoid(this.Refresh);
private void HandleTimerElapsed(object sender, EventArgs e)
{
this.RefreshBuffer();
this.Invoke(this.refreshMethod);
}
private void Start()
{
this.startTime = System.Environment.TickCount;
this.timer.Start();
}
Я думаю, что это в основном все, что вам нужно сделать. Это не полностью проверено и отлажено, но оно должно указать вам правильное направление.