Форум: "Игры";
Текущий архив: 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.036 c