Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Игры";
Текущий архив: 2005.03.13;
Скачать: [xml.tar.bz2];

Вниз

Привязка ко времени ( как это осуществляется )   Найти похожие ветки 

 
Zak3D[@Tm] ©   (2004-12-10 22:38) [0]

Подскажите плз. как осуществеть привязку ко времени ( что бы на разных машинах, при разном количестве fps скорость игры была одинаковой ).
Уже кучу велосипедов насочинял, но ни один нормально не работает... может кто сталкивался с этим и поделится опытом?
Заранее огромное спасибо.


 
Zak3D[@Tm] ©   (2004-12-11 13:16) [1]

Неужели никто не знает как это сделать?


 
Sphinx   (2004-12-11 14:31) [2]

1) Можно использовать таймер,
2) Можно считать тики (GetCurrentTick кажется),
3) Можно что-нибудь придумать с потоками, чтобы считалось в одном, а отрисовывалось в другом.

Много чего можно.


 
WandR   (2004-12-11 14:32) [3]

Скорость игры в плане игравое время
Или сам игравой процес чтоб не тормозил?


 
Zak3D[@Tm] ©   (2004-12-11 21:05) [4]

Sphinx
А не можешь рассказать, как это осуществить через
2) Можно считать тики (GetCurrentTick кажется)
WandR
Да, в плане игрового времени, например у меня есть движущийся танк, так вот я хочу, что бы при разных fps танк двигался с одинаковой скоростью.


 
Sphinx   (2004-12-11 22:29) [5]

Во-первых извени ошибся, должно быть GetTickCount

У меня выглядело примерно так:

 FPS:          Cardinal = 0;   // количество кадров в секунду
 FPSCount:     Cardinal = 0;   // счетчик обновления кадров
 ThisTickCount:  DWORD;        // дополнительные переменные для
 LastTickCount:  DWORD;        //   подсчета FPS
 AniSpeed:     DWORD = 95;     // скорость анимации

в процедуре перерисовки экрана

   Inc(FPSCount);
   ThisTickCount:=GetTickCount;
   if appActive then
     if ThisTickCount-LastTickCount > AniSpeed then
      begin
         Hero.DoMove;      
      end;

   if ThisTickCount-LastTickCount > 600 then
     begin
       FPS:=FPSCount;
       FPSCount:=0;
       LastTickCount:=GetTickCount;
       AniCount:=0;
     end;

 while appRun do
   begin
     PeekMessage(aMSG, mainHandle, 0, 0, PM_REMOVE);
     TranslateMessage(aMsg);
     DispatchMessage(aMsg);
     DrawScreen;
   end;


Но есть одно НО...при очень громозких вычислениях на медленных машинах будет тормозить...


 
Zak3D[@Tm] ©   (2004-12-12 00:33) [6]

МММ... что-то не работает, т.е. работает, но не так как следовало бы...
короче вот в чём беда,как только разница между this и last становится больше 95, герой начинает постоянно двигаться, но раз в 600 милисекунд, он замирает ровно на 95 милисекунд...
Не мог бы ты подправить код? Может стоит использовать не один таймер? (хотя при использовании двух, тоже ничего хорошего не получается(наверно не правильно использую.. : )))
МЛИН, только что заметил, у тебя ещё какая-то переменная aniCount в коде обнуляется..., а где она возрастает???


 
Sphinx   (2004-12-12 01:07) [7]

СОРРИ :)

У меня все работает...лови полный код чтоб не мучаться, а то aniCount случайно удалил, вдруг еще чАво вот так подотру :)

(* перерисовка экрана *)
procedure DrawScreen;
begin
 case ScreenCount of
   ScreenCountTitle: begin
        if appActive then
          begin
            DrawTitleSelect;
            if ReDrawTitle=DD_OK then
              FlipSurf;
          end;
      end;
   ScreenCountGame: begin
      if appActive then
        begin
          if mouseY<5 then
            begin
              if GroundY>0 then
              GroundY:=GroundY-2;
            end;
          if mouseY>(SCREEN_HEIGHT-40) then       // DInput
            begin
              if GroundY<(GameFlur.Height-screen_height) then
                GroundY:=GroundY+2;
            end;
          if mouseX<5 then
            begin
              if GroundX>0 then
                GroundX:=GroundX-2;
            end;
          if mouseX>(SCREEN_WIDTH-40) then        // DInput
            begin
              if GroundX<(GameFlur.Width-screen_width) then
                GroundX:=GroundX+2;
            end;
               if ReDrawGameScreen=DD_OK then
                 FlipSurf;
               end;
        end;
   ScreenCountInfo: begin
      if appActive then
        begin
          if ReDrawInfoScreen=DD_OK then
            FlipSurf;
        end;
      end;
   end;
   KeyboardReadData;
   MouseReadData;
   Inc(FPSCount);
   ThisTickCount:=GetTickCount;
   if appActive then
     if ThisTickCount-LastTickCount > AniSpeed*(AniCount) then
      begin
       if ScreenCount=2 then
         Hero.DoMove;      
       Inc(AniCount);
      end;
   if ThisTickCount-LastTickCount > 600 then
     begin
       FPS:=FPSCount;
       FPSCount:=0;
       LastTickCount:=GetTickCount;
       AniCount:=0;
     end;

end;


 
Sphinx   (2004-12-12 02:34) [8]

Перепроверил, вроде всё, даже много лишнего накидал, но думаю разберешься. Единственное НО. Если Hero.Move будет выполняться более AniSpeed по эффекту это будет как снижение fps вдвое...или больше, поэтому для ОЧЕНЬ медленных машин - тоже не выход.


 
Zak3D[@Tm] ©   (2004-12-12 09:38) [9]

Вот как делаю я, в чём загвоздка???

//Считаем fps и задержку\\
procedure FPSTimer;
begin
 inc(Frames);
 ThisTickCount := GetTickCount();
 if ThisTickCount - LastTickCount > AniSpeed*(AniCount) then
 begin
   WorldMove;                       //Движение всего мира
   Inc(AniCount);
 end;
 if (ThisTickCount - LastTickCount > 1000) then
 begin
   FPS := Frames;
   Frames := 0;
   LastTickCount := ThisTickCount;
   AniCount:=0;
 end;
end;

В процедуре отрисовки
procedure UpdateFrame;
--//--//--//--//
 ReadKeys;           //Читаем нажатия клавишь
 FPSTimer;            
--//--//--//
end;

Вроде также, но где ошибка?


 
Бульдозер   (2004-12-12 14:29) [10]

Короче, всё намного проще:

....
Var
 TimeL:int64;
 Fps:Integer;

Procedure Update;
.....
Var Freq,t:Int64;
Begin
 t:=TimeL;
 queryperformancefrequency(freq);
 queryperformancecounter(TimeL);

...

 x:=x+10/fps;
 y:=y+1/fps;
..
...

 FPS:=freq div(TimeL-t);
end;

координаты x,y будут меняться равномерно, не зависимо от скорости компа


 
Zer0 ©   (2004-12-14 02:46) [11]

все кружится по спирали, про квертитаймер уже была куча ноутов. странно почему модераторы не объединят кой-какие ответы в FAQ...

---
k3wl


 
П7   (2004-12-14 11:22) [12]

Ответы на твой вопрос есть почти во всех конкурсных работах. Например: я использую GetTickCount...


 
FUNKy   (2004-12-14 18:16) [13]

Помню делал курсач на паскале по движению физических тел так там просто смотрел изминились ли миллисикунды в системном времени если да то запускал обновление картинки .


 
Omar2002 ©   (2004-12-15 15:22) [14]

>>Бульдозер   (12.12.04 14:29) [10]

Слушай, а что за функции-то такие?

queryperformancefrequency(freq);
queryperformancecounter(TimeL);


 
BlackTr   (2004-12-15 15:36) [15]

Win API (kernel32) MSDN


 
NailMan ©   (2004-12-15 17:13) [16]

http://delphimaster.net/view/9-1091386709/
смотри топик и в каком-то моем посте я давал ссылку на реализацию. Кажись она в архивах. Если очень надо могу еще раз накатать(не сразу), но объяснения работы по вышеприведенной сцыле.

---
WBR, NailMan aka 2:5020/3337.13


 
Zak   (2004-12-15 22:06) [17]

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


 
NailMan ©   (2004-12-16 17:42) [18]

Zak

Описываем структуру:

                           TSyncronize = packed record
                               LastHPC : Int64;
                            TimeOffset : Cardinal;
                        CorrMultiplier : Extended;
                           End;


В которой:
LastHPC - предыдущее значение счетчика
TimeOffset - время задержки. Это то время ожидания, через которое будет обновляться состояние. 1/TimeOffset - есть частота обновлений состояния.
CorrMultiplier - коэффициент масштабирования процесса(самая главная часть).

У каждого объекта, который может изменять свое состояние, делаем поле этого типа. Скажем Sync: TSyncronize;

Объявляем функции:

1.
Заносим в поле предыдущее значение счетчика, взяв его значение из системы
и указываем через какое время будет проведен процесс обновления состояния(в мс):

Function SetSync(msecs:Cardinal):TSyncronize;
begin
QueryPerformanceCounter(result.LastHPC);
result.TimeOffset:=TimerMSecResolution * msecs;
Result.CorrMultiplier:=1;
end;


2.
Проверяем произошло ли событие(текущее значение HPC превысило или равно предыдущему значению + время задержки). В случае положительного результата вычисляется коффициент поправки(отношение реально прошедшего времени задержки к идеальному):

Function CheckSync(Var Sync:TSyncronize):boolean;
var tickcount,subtraction:int64;
begin
result:=false;
QueryPerformanceCounter(Tickcount);
subtraction:=tickcount - Sync.LastHPC;
If subtraction>=Sync.TimeOffset then
 begin
  If Sync.TimeOffset=0 then  Sync.CorrMultiplier:=1 else
  Sync.CorrMultiplier:=subtraction / (Sync.TimeOffset);
  result:=true;
 end;
end;


Таким образом чтобы использовать синхронизацию на любой машине, нужно предварительно сделать некоторые подстройки игры к конкретной машине, а конкретней надо вычислить пресловутое разрешение HPC счетчика TimerMSecResolution:

Procedure CalculateHPCResolution;
var z,z2:int64;
Begin
QueryPerformanceCounter(z);
 Sleep(1000);
QueryPerformanceCounter(z2);
 TimerResolution:=z2 - z;
 TimerMSecResolution:= round((z2 - z) / 1000);
end;

TimerResolution - это число HPC тиков в секунду(иногда нужно но не принципиально),
TimerMSecResolution - это число тиков в миллисекунду(так как задержки указываем в мс).

Это вычисление разрешения понятно делается один раз в начале программы.

Теперь использование:

так как у нас у каждого изменяющегося объекта есть свой синхронизатор(поле Sync), надо его использовать. При создании экземпляра можно задать время синхро, например так: ляляля.Sync:=SetSync(50); Т.е. Следующее изменение состояния объекта будет не ранее чем через 50 миллисекунд(20 раз в сек). Делать в принципе не обязательно, но рекомендуется.

В основном цикле программы делаем пробег по всем таким объектам и определяем случилось ли событие для каждого:

->MainCycle begin
...

for i:=0 to ActorsCount-1 do
begin
 ...
 If CheckSync(Actor[i].Sync) then Actor[i].Processing;
 ...
end;

...
->MainCycle end


Где процесс обновления состояния актера будет иметь(пример из моего движка) следующий вид:

Procedure TActor.Processing;
...
begin
...
 //roll Rotation
    If not RollActive then
      MoveCaps.RollAngularVelocity:=MoveCaps.RollAngularVelocity * 0.98
      else
        MoveCaps.RollAngularVelocity:=MoveCaps.RollAngularVelocity +
                                      MoveCaps.RollAngularAcceleration * Sync.CorrMultiplier * 0.05;

...

//Трастинг
     If not ThrustActive then
      begin
       MoveCaps.LinearVelocity:=MoveCaps.LinearVelocity * 0.98;
       if not LockThrustDecrement then
        begin
         If thrusters>0 then DecEnginesThrust(ENGINE_THRUSTER,0.015 * Sync.CorrMultiplier);
         If brakethrusters>0 then DecEnginesThrust(ENGINE_BRAKETHRUSTER,0.03 * Sync.CorrMultiplier);
         If Controlthrusters>0 then DecEnginesThrust(ENGINE_CONTROLTHRUSTER,0.03 * Sync.CorrMultiplier);
        end;
      end
      else
        MoveCaps.LinearVelocity:=MoveCaps.LinearVelocity +
                                 MoveCaps.LinearAcceleration * Sync.CorrMultiplier;

...
end;


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

Если коэфициент больше единицы, т.е время прошедшее с последнего изменения больше теоретического, то изменение "растянется" пропорционально этой разнице, если время совпало, то будет точно заданным(как правило коэффицинт слегка[0.01% или около того] больше единицы в силу объективных причин). Меньше единицы понятно быть не может.

Т.о. если комп внезапно "задумался" или просто банально начал тормозить, то скажем движение или вращение будет как бы с пропуском промежуточных положений, но величина изменения будет сохраняться на любой по производительности машине. Что нам и нужно.

---
WBR, NailMan aka 2:5020/3337.13


 
Zak   (2004-12-17 18:46) [19]

NailMan
Огромное спасибо. Постараюсь реализовать : )



Страницы: 1 вся ветка

Форум: "Игры";
Текущий архив: 2005.03.13;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.038 c
3-1108018605
Mars
2005-02-10 09:56
2005.03.13
Как сделать выборку из базы с помощью SQL,


4-1107264874
stud
2005-02-01 16:34
2005.03.13
проблема с протоколом MODBUS


1-1109601238
hgd
2005-02-28 17:33
2005.03.13
TColor


3-1108477912
}|{yk
2005-02-15 17:31
2005.03.13
Работа с нулем в FB 1.5


14-1109158819
olookin
2005-02-23 14:40
2005.03.13
Мелодия (музыка) из рекламы про ТонкоМото





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский