Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 02.02.2017, 09:23   #1
fanlis
Пользователь
 
Регистрация: 13.05.2007
Сообщений: 60
Лампочка Неправильно работает таймер?

Скорее всего глупый вопрос, но при написании игры и перестроении концепции времени в ней заметил интересную штуку. Далее решил поэкспериментировать и вот что получил: на форме есть обычный дельфовый таймер, кнопка и мемо.
Код:
procedure TForm1.sButton1Click(Sender: TObject);
begin
  Timer1.Interval:=strtoint(sEdit1.Text);
  sMemo1.Lines.Add('Timer1.Interval='+inttostr(Timer1.Interval));
  sMemo1.Visible:=false;

  QueryPerformanceFrequency(Freq);
  QueryPerformanceCounter(Coun1);
  Timer1.Tag:=1;
  Timer1.Enabled:=true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  QueryPerformanceCounter(Coun2);
  sMemo1.Lines.Add(Format('%8.3f c (%6.3f мс)',[(Coun2-Coun1)/Freq,1000*(Coun2-Coun1)/Freq]));

  Timer1.Tag:=Timer1.Tag+1;
  if Timer1.Tag=20 then
  begin
    Timer1.Enabled:=false;
    sMemo1.Visible:=true;
  end;
  QueryPerformanceCounter(Coun1);
end;
Короче делаю таймером 20 тактов и в каждом такте вывожу измеренное значение времени. Получаю, что при задании интервала таймера, например, 20, проходит времени 30; при задании 50 - проходит 60. Там конечно с погрешностью, но +10мс просматривается всегда. Что тут не так и чего я не понимаю?
fanlis вне форума Ответить с цитированием
Старый 02.02.2017, 09:55   #2
Arigato
Высокая репутация
СуперМодератор
 
Аватар для Arigato
 
Регистрация: 27.07.2008
Сообщений: 15,551
По умолчанию

Таймер не считает с точностью до 1 мс, у него дискретные интервалы. Не помню точно, какой период, допустим, 20 мс. Это означает, что чаще 20 мс срабатывать он не сможет (например, поставите 1 мс, а срабатывать будет каждый 20 мс). В итоге если ставите 50 мс, то срабатывает каждый 60 мс, то есть ближайшее значение, попадающее в период.

Провел эксперимент, вот код, позволяющий вычислить период:
Код:
var
  T, i, n, S: Cardinal;

procedure TForm1.Button1Click(Sender: TObject);
begin
  n := 1000;
  i := 0;
  S := 0;
  Timer1.Interval := 1;
  Timer1.Enabled := true;
  T := GetTickCount;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Timer1.Enabled := False;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  inc(S,GetTickCount - T);
  inc(i);
  //caption := inttostr(i);
  if i >= n then begin
    timer1.Enabled := false;
    s := s div i;
    showmessage(inttostr(s));
  end;
  T := GetTickCount;
end;
При Interval = 1 выдает 15, при Interval = 20 - 31. То есть период даже не целый, он примерно 15.5

Последний раз редактировалось Arigato; 02.02.2017 в 10:07.
Arigato вне форума Ответить с цитированием
Старый 02.02.2017, 10:31   #3
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,709
По умолчанию

1 мс было на старых системах, с 8.1 0.5; так же стоит учесть, что на серверных ОС квант все рано оставили большим.
Если нужна такая точность то вам в сторону https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
p51x вне форума Ответить с цитированием
Старый 02.02.2017, 10:58   #4
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

fanlis
Это особенность Windows таймер на сообщениях работает как захочется левой пятки ведущего разработчика майкрософт.

С деланно это для оптимизации работы системы в сторону энергопотребления. В параллели на одном ядре у вас крутиться много программ. Чем меньше раз сработает таймер тем меньше времени съест ваше приложение и его можно будет потратить на остальные приложения. С ростом памяти стало больше приложений и в современных ОС таймер этот притормаживается программно.

Показания таймера на сообщениях(стандартный дельфийский). Зависит от числа запущенных программ. производительности вашего процессора и числа ядер. А также от разных ОС и более того от того главный поток или побочный. В побочном быстрее в 2 раза.

Для игр стоит использовать мультимедийный таймер. Он уже будет гарантированно работать с точностью 15,16 мс. И при настройке системы 1 мс.
timeGetTime, timeKillEvent, and timeSetEvent

timeBeginPeriod(1); // Устанавливаем время срабатывания мультимидийного таймера в 1мс
Этот параметр влияет на все программы в ОС. Так что не удивляйтесь если другая программа сделала это раньше вас.

Arigato
А ваш пример неверный так как вы используете менее точный таймер чем ТС.
GetTickCount - всегда даёт гранулярность 15.16 мс

QueryPerformanceFrequency - как повезёт но обычно либо 3 579 545 Гц либо 14 318 180 Гц либо порядка 2,4 ГГц

fanlis
При большой загрузки ОС может переносить программу с ядра на ядро и у вас будут разные значения QueryPerformanceCounter. В плоть до получения отрицательной разницы Coun2-Coun1

p51x
Подтверждения про квант времени я не нашёл, не наблюдалось. Если параметр и влиял, то только в NT3 или NT4 или Win95.
Win98 Win2000 я проверял - там этот параметр не влияет там влияют другие параметры описанные в предыдущем сообщении.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .

Последний раз редактировалось Вадим Мошев; 02.02.2017 в 11:53.
Pavia вне форума Ответить с цитированием
Старый 02.02.2017, 11:28   #5
fanlis
Пользователь
 
Регистрация: 13.05.2007
Сообщений: 60
По умолчанию

А как вообще лучше использовать таймер в игре? Вот, например, есть 2D игра, персонаж идет с одной скоростью, бежит с другой, картинки анимации движения меняются с третьей,падает перс с четвертой скоростью и т.д. Есть еще анимация, не связанная с персонажем.
Для всего этого:
1. делать отдельные таймеры?
2. Один таймер и менять у него интервал?
3. Один таймер с постоянным интервалом, но для каждой анимации вычислять прошедшее время.

По первому пункту получается много таймеров и возникает путаница когда какой включать, какой выключать.
По второму пункту я пробовал, но в конец запутался со сменой интервалов. И для разных объектов со своей анимацией это не подходит.
Попробовал третий пункт и столкнулся с проблемой данной темы. Т.е. допустим у меня картинки анимации перса меняются каждые 80 мс. Таймер у меня с интервалом 20 мс. И есть счетчик последней смены картинки. Каждый такт таймера я увеличиваю счетчик. Но сначала я увеличивал его на интервал (20), потому что думал, что таймер срабатывает каждые 20 мс (а на самом деле 30 мс), т.е. на каждом такте я получал погрешность в 10 мс и суммарная задержка сильно выросла. Потом я стал увеличивать счетчик на рассчитанное время такта (QueryPerformanceCounter), но в этом случае картинки сменяются не равномерно.
Как вообще это делается?
fanlis вне форума Ответить с цитированием
Старый 02.02.2017, 11:57   #6
Serge_Bliznykov
Старожил
 
Регистрация: 09.01.2008
Сообщений: 26,229
По умолчанию

Цитата:
Сообщение от fanlis Посмотреть сообщение
3. Один таймер с постоянным интервалом, но для каждой анимации вычислять прошедшее время.
я не спец. но, на мой взгляд, именно третий пункт!

Цитата:
Сообщение от fanlis Посмотреть сообщение
Попробовал третий пункт и столкнулся с проблемой данной темы. Т.е. допустим у меня картинки анимации перса меняются каждые 80 мс. Таймер у меня с интервалом 20 мс. И есть счетчик последней смены картинки.
А не надо использовать счётчики.
в таймере получайте текущее время из системы и отталкивайтесь от него. а в каждом объекте храните время следующей обработки (ну или скорость изменения координат - если это движение).
Serge_Bliznykov вне форума Ответить с цитированием
Старый 02.02.2017, 12:28   #7
Arigato
Высокая репутация
СуперМодератор
 
Аватар для Arigato
 
Регистрация: 27.07.2008
Сообщений: 15,551
По умолчанию

Цитата:
Сообщение от Pavia Посмотреть сообщение
GetTickCount - всегда даёт гранулярность 15.16 мс
Да, действительно, сам GetTickCount не выдает разность меньше 15. Значит мой тест неверный.

В качестве примера:
Код:
var
  T, DT: Cardinal;
begin
  T := GetTickCount;
  repeat
    DT := GetTickCount - T;
  until DT > 0;
  showmessage(inttostr(DT));
end;
Получаем ответ: 15 или 16.
Arigato вне форума Ответить с цитированием
Старый 02.02.2017, 16:48   #8
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Лучше один таймер. Тобишь 3 вариант.

При каждом срабатывание таймера вычисляете разницу времени dt. Её умножаете на скорость персонажа и соответственно на столько и смещаем его
dx:=dt*V;
x:=x+dx;

Номер кадра тоже вычисляете исходя из dt, round(dt) mod Count. Если процесс будет притормаживать соответственно это позволит при анимации пропустить ряд кадров.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 02.02.2017, 18:40   #9
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

https://m.habrahabr.ru/post/136878/
Отличная статья, только нечитайте примечания глупого переводчика.
И статья не всё охватывает, есть куда рости что доделать.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 03.02.2017, 03:12   #10
northener
ПШП
Участник клуба
 
Регистрация: 15.07.2013
Сообщений: 1,872
По умолчанию

Цитата:
Сообщение от Pavia Посмотреть сообщение
Arigato
А ваш пример неверный так как вы используете менее точный таймер чем ТС.
ТС в топике использует тот же самый таймер.
northener на форуме Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Программа неправильно работает HaKeRR Помощь студентам 1 05.12.2014 23:51
Неправильно работает таймер Юшков Компоненты Delphi 9 07.11.2011 14:42
TextRect работает неправильно bbk_serg БД в Delphi 16 10.08.2010 21:11
Скрипт неправильно работает aleksa76 JavaScript, Ajax 4 21.05.2008 17:47
Запрос неправильно работает... yulia БД в Delphi 0 27.05.2007 20:41