Как реализовать цикл с фиксированным шагом?

StackOverflow https://stackoverflow.com/questions/647974

  •  22-07-2019
  •  | 
  •  

Вопрос

Я пытаюсь создать фиксированный пошаговый цикл в моей программе, но по какой-то причине я просто не могу заставить его работать должным образом. По сути, мне нужен цикл, который выполняет:

while(!over) {
    Update(elapsedtime);
    Draw(elapsedtime);
}

или что-то подобное, с. Я пытался использовать Thread.Sleep, но я не уверен, что он дал мне реальный фиксированный пошаговый цикл, и когда я пытался реализовать решение на этот веб-сайт У меня были проблемы из-за того, что я не смог найти способ сохранить состояние объектов, затронутых циклом. Это заставило меня исключить эту часть, что привело к замедлению, когда в цикле было много объектов.

Как я могу получить фиксированный пошаговый цикл без необходимости постоянно сохранять состояние каждого объекта в цикле?

Это было полезно?

Решение

Если вы работаете над игровым движком, таймер, вероятно, не будет работать хорошо. Таймеры не имеют достаточной точности для обработки цикла с фиксированным шагом и поддержания его регулярности.

Вам нужно будет реализовать это с помощью высокоточного таймера. В этом случае вы захотите использовать класс Секундомер. Это позволит вам иметь достаточно точности для точного отслеживания вашего elapsedTime.

Вам просто нужно отслеживать, как часто вы хотите обновлять (в тиках), а затем использовать секундомер, чтобы измерить время, необходимое для каждого из ваших обновлений. После этого вы можете потенциально использовать вызов Sleep () для блокировки, но при этом понимаете, что в большинстве систем sleep будет иметь точность только 14-15 мс, даже если вы спите только 0 или 1 мс. Это может слишком сильно замедлить ваш цикл, поэтому вам может понадобиться реализовать спин-блокировку или аналогичную (в то время как (ожидание) {сделать что-то}). Sleep () хорош, потому что он экономит процессор, если вы можете, спин-блокировка будет потреблять циклы процессора, пока вы ждете, но дает вам большую точность.

Другие советы

Вы запрашиваете цикл, который будет работать в основном каждые n тиков? Это похоже на таймер . В BCL есть пара таймеров. таймер для серверов или один для оконных форм .

Вы можете использовать их в этом направлении. Ниже приведен код psuedo, предназначенный для общего представления о том, как это делается. Другими словами, он, вероятно, не скомпилируется, если вы просто скопируете и вставите.

public class RepeatingTask
{
    public MyObjectState _objectState;

    public RepeatingTask(Timespan interval)
    {
       Timer timer=new Timer(Timer_Tick); //This assumes we pass a delegate. Each timer is different. Refer to MSDN for proper usage
       timer.Interval=interval;
       timer.Start();

    }
    private DateTime _lastFire;
    private void Timer_Tick()
    {
       DateTime curTime=DateTime.Now();
       DateTime timeSinceLastFire = curTime-lastFireTime;
       _lastFire=DateTime.Now(); //Store when we last fired...

       accumulatedtime+=timeSinceLastFire
       while(accumulatedtime>=physicsInterval)
       {
          Update(physicsInterval);
          accumulated-=physicInterval;
       }

               }

}

Вы также можете использовать замыкание, чтобы завершить состояние метода, в котором определен таймер.

Изменить

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

Используете ли вы WPF, у них есть некоторые события, которые, я полагаю, запускаются с постоянной скоростью для обычных анимаций.

Изменить

Я обновил мой пример кода, чтобы показать вам мою интерпретацию, но в основном вы определяете интервал для вашего физического движка, а затем то, что вам нужно делать при каждом проходе через ваш «цикл / таймер», что угодно " определить, сколько реального времени прошло с момента последней итерации. Затем вы сохраняете эту дельту в Accumalator, который будете использовать для обратного отсчета до тех пор, пока вы не вызовете свой физический движок для всех интервалов, которые вы пропустили с момента последнего вызова.

С чем я борюсь, так это с тем, чтобы лучше использовать таймер, а затем иметь спящий выделенный поток или какую-то другую реализацию.

Thread.Sleep не даст вам фиксированный пошаговый цикл, он просто будет ждать заданное количество времени, прежде чем позволит вашей программе продолжить работу. Фактическая частота будет зависеть от того, насколько точен сон и насколько последовательно ваша программа запускает одинаковое количество часов на каждом шаге. Однако, если вы можете приспособиться к некоторому дрейфу, это может быть достаточно и, безусловно, является самым простым.

Чтобы получить настоящий фиксированный пошаговый цикл, вам нужен интервальный таймер , который выполняет ваш код с помощью обратного вызова по истечении таймера. Обратите внимание, что это больше не цикл, но ваш код выполняется периодически, что, я думаю, то, что вы хотите. Опять же, может быть некоторый сдвиг, и поэтому вам может понадобиться настроить интервал на каждом шаге, чтобы следующее событие запускалось в нужное время, если вам нужна большая точность. На самом деле невозможно сделать его абсолютно точным - в конце концов, даже аппаратные интервальные таймеры имеют точность - но, надеюсь, вы сможете сделать его достаточно точным для своих целей.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top