Как создать плавную анимацию движения фигур в приложениях форм C # windows? - PullRequest
1 голос
/ 14 октября 2019

Я создаю приложение на C # для оконных форм, в котором хочу сделать плавную анимацию движения черной полосы на белом фоне. Я попробовал следующий код, но движение мерцает и не достигает плавного быстрого движения даже на таймере с интервалом 1 мс.

Я попробовал следующий код:

    public Form1()
    {
        InitializeComponent();
        DoubleBuffered = true;
        System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
        timer1.Interval = 30;
        timer1.Tick += Timer1_Tick;
        timer1.Enabled = true;
    }
    int x_position = 0;
    int bar_width = 40;
    int speed = 1;
    private void Timer1_Tick(object sender, EventArgs e)
    {
        x_position += speed;
        if (x_position + bar_width > this.Width) x_position = 0;
        Invalidate();
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.FillRectangle(Brushes.Black, x_position, 0, bar_width, 500);
    }

Результат не плавное движениевообще. Я нашел в других вопросах что-то вроде Invalidate(true) или DoubleBuffered = true;, но они не решили проблему.

Ожидание помощи. Спасибо

1 Ответ

1 голос
/ 15 октября 2019

Winforms API и встроенный в Windows 32 интерфейс обмена сообщениями Windows Win32 просто не предназначены для "плавной анимации". Будет трудно, если не невозможно, добиться идеально плавной анимации.

Тем не менее, многое можно сделать, чтобы улучшить вашу попытку выше:

  • Не используйте CreateGraphics(). Вместо этого всегда рисуйте в ответ на событие Paint.
  • Не используйте какие-либо реализации таймера .NET для планирования рендеринга. Вместо этого нарисуйте как можно быстрее, принимая во внимание время между кадрами для обновления позиции вашего объекта.

Применяя эти идеи, вот версия вашего кода, которая работает на намного лучше:

const float speedXPerSecond = 1000f / 30;
const int bar_width = 40;

public Form1()
{
    InitializeComponent();
    DoubleBuffered = true;
}

float x_position = 0;
TimeSpan _lastFrameTime = TimeSpan.Zero;
Stopwatch _frameTimer = Stopwatch.StartNew();

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    TimeSpan currentFrameTime = _frameTimer.Elapsed;
    float distance = (float)(currentFrameTime - _lastFrameTime).TotalSeconds * speedXPerSecond;

    x_position += distance;
    while (x_position > this.Width) x_position -= this.Width;
    e.Graphics.FillRectangle(Brushes.Black, x_position, 0, bar_width, 500);
    _lastFrameTime = currentFrameTime;

    Invalidate();
}

Вы можете применить эту общую технику к любому интерактивному сценарию. Настоящая игра будет иметь другие элементы в этом общем «цикле рендеринга», включая пользовательский ввод и обновление состояния игры (например, на основе физической модели). Это добавит накладные расходы к циклу рендеринга и обязательно уменьшит частоту кадров. Но для любой основной игры на любом относительно новом оборудовании (например, построенном в последние пару десятилетий) чистая частота кадров будет все еще намного выше, чем требуется для приемлемо плавного игрового процесса.

Обратите внимание, что вВ управляемом контексте .NET / Winforms всегда будет предел успешности этого подхода по сравнению с использованием низкоуровневого API. В частности, без какой-либо дополнительной работы с вашей стороны сборка мусора будет периодически прерываться, вызывая незначительное заикание частоты кадров, а также неравномерность в планировании потока и очереди сообщений потока.

Но это был мой опыт, что людизадавая подобные вопросы, не нужно, чтобы все было идеально. Им нужно «достаточно хорошо», чтобы они могли перейти к изучению других тем, связанных с разработкой игр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...