Цитата:
Сообщение от NEoMASTERR
И Что ждать пока винда сама решит когда игроку видеть соперников?) Я навязываю ей, думаю так и другие движки делают
|
У вас ошибка с тем, что по таймеру надо НЕ рисовать. Вам необходимо вернуться назад в "доDLLелье" и произвести повторную декомпозицию систем (желательно при этом забить на WinAPI - на начальном этапе и без него хватит проблем). Напишу предельно сжато по архитектуре.
Наша игра что-то рисует. Когда? Всегда, в этом её смысл:
Код:
while true do begin
Draw(); // строим и отправляем изображение на экран
end;
Статичная картинка скушная, хотим движения:
Код:
while true do begin
Update(); // пересчитываем физику
Draw(); // строим и отправляем изображение на экран
end;
Теперь хотелось бы получить реакцию от игрока:
Код:
while not FTerminated do begin // должен же игрок как-то выйти из игры
while OperationSystem.HaveMessage do begin // ОС есть что сказать? (мыша, клавиатура, всякие свернуть\развернуть и т.п.)
ProcessMessage(OperationSystem.GiveMeOneMessagePlease); // реагируем на её запрос
end;
Update();
Draw();
end;
Всё отлично, но изображение мерцает (несколько раз меняем цвет одного пикселя):
Код:
while not FTerminated do begin
while OperationSystem.HaveMessage do begin
ProcessMessage(OperationSystem.GiveMeOneMessagePlease);
end;
Update();
Render(); // теперь рисуем в битмап\буфер
Present(); // отправляем буфер на экран
end;
Прекрасно, но персонажи при перемещении дёргаются и вообще при каждом запуске ведут себя по-разному. Это у нас ошибки округления и неравномерные дельты по времени, фиксируем:
Код:
while not FTerminated do begin
while OperationSystem.HaveMessage do begin
ProcessMessage(OperationSystem.GiveMeOneMessagePlease);
end;
someStepCollector += deltaTime;
stepCount := Trunc(someStepCollector / stepTime);
someStepCollector -= stepCount * stepTime;
if stepCount > 0 then
for i := 0 to stepCount - 1 do begin
FixedUpdate(); // физика тут
end;
BeforeRender(); // интерфейс и анимации тут
Render();
Present();
end;
Теперь мы начинаем сжигать ЦПУ, у нас ФПС под 2000, а надо всего 60 - пора снижать нагрузку. Нагрузку может снизить Present, если будет ожидать сигнал монитора\видеокарты "айда рисовать" (VSync). Ммм... но мы не можем доверять этим бездушным железкам:
Код:
while not FTerminated do begin
while OperationSystem.HaveMessage do begin
ProcessMessage(OperationSystem.GiveMeOneMessagePlease);
end;
someStepCollector += deltaTime;
stepCount := Trunc(someStepCollector / stepTime);
someStepCollector -= stepCount * stepTime;
if stepCount > 0 then
for i := 0 to stepCount - 1 do begin
FixedUpdate();
end;
someRenderCollector += deltaTime;
if (someRenderCollector > renderTime) then begin // пропускаем лишние рендеры
someRenderCollector -= Trunc(someRenderCollector / renderTime) * renderTime;
BeforeRender();
Render();
end;
Present(); // всё равно всегда презентуем
SleepEx(5); // ну так-то можно и немного поспать (Ex значит, что нас дёрнут, если для нас будет сообщение от ОС)
end;
Что касается VCL. Сообщения от ОС вместо вас отработает именно он. Вам остаётся написать то, что ниже while OperationSystem.HaveMessage - это Appliction.OnIdle. Туда всё и суём. Связываться с Present'ом не наш путь: у нас он либо уже готовый от OpenGL\DirectX, либо просто рисуем на канву формы и пусть она сама себя презентует (нафиг нам этот WM_PAINT, у нас вон какой VCL есть).
Если нельзя, но очень хочется, то Present должен у нас рисовать в еще один Bitmap (а если точнее, то просто обменять местами указатели - фронт буфера с бэк буфером). После этого он засылает ОС сигнал, что изображение обновлено (а тот же инвалидейт подойдёт). Когда ОС прилетит со своим WM_PAINT мы отдаём ей наш фронт-буфер (мы понятия не имеем сколько раз и когда нас спросят об этом - отдаём всегда, когда просят, а не по таймеру или другим хотелкам).
Можно пошастать по опенсурсному Asphyre и посмотреть, а как оно работает (заодно посмотреть на правильно написанный AsphyreTimer.pas).