LINUX.ORG.RU

Как лучше организовать таймеры?

 ,


1

3

В общем организую внутриигровые таймеры.(для анимации и всякой другой лабуды)
Есть сотня тысяч объектов у них есть метод который надо вызывать с определенной периодичностью (у каждого период разный в рамках 0.1 секунды до +inf). Как это лучше организовать?
Спасибо.

★★★★★

Последнее исправление: deterok (всего исправлений: 1)

Зависит от внутренней организации кода. Если у тебя есть один основной цикл, и его итерация гарантированное меньше 0.1 сек, вызывай методы прямо из основного цикла.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Есть пачка потоков:
События
Рендеринг
И теперь думал накидать поток для обработки данных( думал сделать петлю с некоторым периодом, которая будет обходить все объекты сверясь с временной меткой и вызывать их методы при надобности), но решил проконсультироваться.

deterok ★★★★★
() автор топика
Ответ на: комментарий от note173

А системными функциями никак нельзя организовать?
Уж очень не хочется тащить boost из-за одной либы.

deterok ★★★★★
() автор топика

В твоей игре должна быть функция, которая вызывается для просчета следующего «тика» игры. Имеется в виду обработка состояния всех объектов, влияния на них «законов физики» и т.п. Так вот, этот игровой тик обычно делается постоянным, например 100 мс, а дальше от этого времени уже танцуется все остальное. Во время просчета следующего тика игры ты в любом случае будешь обходить некоторое множество объектов, твоя задача сделать так, чтобы в это множество попали те, у которых установлены таймеры. Ну а сравнивая (время установки таймера + длительность) >= текущее время ты понимаешь, что пора запускать обработчик таймера.

m0rph ★★★★★
()
Ответ на: комментарий от m0rph

Вот я и хотел организовать подобное(наверное не так выразился в позапрошлом посте), но засомневался в скорости и правильности такого метода.
Т.е. так делать в порядке вещей?

deterok ★★★★★
() автор топика
Ответ на: комментарий от deterok

Руками из одного таймера делается event loop на произвольное кол-во. Сортированный список событий (сортировка по времени истечения). Каждый элемент хранит абсолютное время, когда его вызвать.

Далее в цикле: если время первого пришло - вызвать и проверить опять новый первый. В результате очередь или станет пустая, или первый будет указывать на какое-то время в будущем. Подождать этого времени и все повторить. Когда очередь пустая - ждать появления задачи в очереди.

Pavval ★★★★★
()
Ответ на: комментарий от deterok

У меня была такая реализация, но для 10-100 объектов:

Есть основной поток, в нем цикл. На каждом шаге делается проход (в том числе) по всем активным «таймерам». Для каждого вычисляется время до срабатывания, начиная от текущего момента. При отрицательном времени таймер срабатывает и сбрасывается. При положительном сравнивается с выделенной переменной, значение которой в начале примерно +INF, если меньше, то переменная перезаписывается. Поток засыпает на время, указанное в этой переменной (получается, что он проснется как раз к следующему событию).

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

(Да, сортированный список тут сильно поможет оптимизировать цикл.)

note173 ★★★★★
()
Последнее исправление: note173 (всего исправлений: 1)
Ответ на: комментарий от m0rph

Сразу задам такой вопрос: 100 мс достаточно?

deterok ★★★★★
() автор топика
Ответ на: комментарий от deterok

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

m0rph ★★★★★
()
Ответ на: комментарий от note173

А причем здесь fps и игровой тик?
(У меня отдельно реализована графическая петля)

deterok ★★★★★
() автор топика
Ответ на: комментарий от note173

Игровой тик никогда постоянным не делается

Не путай FPS и игровой тик. Рендерер не должен влиять на внутреннее время игры. И кстати насчет «никогда»: некто Джон Кармак в Quake 2 применял постоянный игровой тик длительностью кажется как раз в 100 мс. Я слышал он специалист в подобных вопросах ;-).

m0rph ★★★★★
()
Ответ на: комментарий от note173

А как часто надо сортировать список и когда лучше это делать?

deterok ★★★★★
() автор топика
Ответ на: комментарий от deterok

Есть пачка потоков:

Если у тебя сложная многонитевая структура без основного цикла (удачи в отладке, бугага), то создавай по системному таймеру для каждого события с некратными квантами, нить, которая ждет на этом таймере, и вызывает методы.

А системными функциями никак нельзя организовать?

Можно - список callable-объектов для каждого таймера.

tailgunner ★★★★★
()
Ответ на: комментарий от m0rph

некто Джон Кармак в Quake 2 применял постоянный игровой тик длительностью кажется как раз в 100 мс

То есть изменение состояния мира 10 раз в секунду? Ну-ну.

tailgunner ★★★★★
()
Ответ на: комментарий от m0rph

Есть еще такой вопрос
Так как я использую многопоточную модель, то мне придется иногда блокировать потоки, думаю сделать так:
Когда графическая петля натыкается на занятый другим потоком объект, то она перебрасывает его в конец очереди и продолжает, тоже самое можно сделать если тикер наткнется на занятый другим потоком объект.(не знаю насколько это целесообразно и быстро)

deterok ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

То есть изменение состояния мира 10 раз в секунду? Ну-ну.

Языком почесать заглянул?

g_local.h:

#define	FRAMETIME		0.1

g_main.c:

/*
================
G_RunFrame

Advances the world by 0.1 seconds
================
*/
void G_RunFrame (void)
{
	int		i;
	edict_t	*ent;

	level.framenum++;
	level.time = level.framenum*FRAMETIME;

// skipped... }

m0rph ★★★★★
()
Ответ на: комментарий от m0rph

Языком почесать заглянул?

Профи геймдева в треде, все в машину!

void G_RunFrame (void)

Сколько раз в секунду она вызывается, чудо?

tailgunner ★★★★★
()
Ответ на: комментарий от deterok

Не понял, что значит «некратными квантами»

Если у тебя есть события, например, с частотой 100Гц и 110Гц, и 2 программируемых таймера, выгоднее разнести их по разным таймерам. Но на обычном ПК так, наверное, не получится - лучше взводить таймер на ближайшее событие (и при каждом срабатывании проходить по списку событий в поиске следующего ближайшего).

tailgunner ★★★★★
()
Ответ на: комментарий от deterok

Уж очень не хочется тащить boost из-за одной либы.

Буст хорош тем, что можно тащить только нужную часть.

Dudraug ★★★★★
()
Ответ на: комментарий от tailgunner

Сколько раз в секунду она вызывается, чудо?

Внезапно 10.

void SV_Frame (int msec)
{
        // .... SKIPPED ....

    svs.realtime += msec;

        // .... SKIPPED ....

	// move autonomous things around if enough time has passed
	if (!sv_timedemo->value && svs.realtime < sv.time)
	{
		// never let the time get too far off
		if (sv.time - svs.realtime > 100)
		{
			if (sv_showclamp->value)
				Com_Printf ("sv lowclamp\n");
			svs.realtime = sv.time - 100;
		}
		NET_Sleep(sv.time - svs.realtime);    // <<< --- Пауза до начала следующего 100 мс кванта времени
		return;
	}

        // .... SKIPPED ....

	// let everything in the world think and move
	SV_RunGameFrame ();    // <<< --- Вызывает ge->RunFrame(), что и есть G_RunFrame()

        // .... SKIPPED ....
}

Профи геймдева в треде, все в машину!

Нет, я не специалист в сфере геймдева. А ты судя по иронии профессионал, так просвети темных неучей.

m0rph ★★★★★
()

Можно реализовать C++ класс, где стартуется поток выполнения (thread), который периодически засыпает на квант таймера, потом просыпается, определяет, по каким зарегистрированным в таймере задачам истек очередной период их выполнения и производит их запуск (шаблон проектирования Command). Далее в программе нужно создать необходимое кол-во таких таймеров и раскидать по ним свои задачи для балансировки нагрузки по thread-ам. Вариант реализации см. тут.

illy
()
Ответ на: комментарий от tailgunner

Понял, спасибо за разъяснение.
У меня есть еще 3 мини вопросик к вам и окружающим:
1) Какой функцией лучше получать время с точностью до 0.01 секунды?(пока решил использовать clock())
2) Нормально ли для каждого таймера хранить время следующего запуск и период?(в общем, как лучше и эффективней хранить эти данные? (Например можно еще хранить как время предыдущего запуска и период, но тогда нужно будет высчитывать )
3) Нужно ли вычитать из времени следующего запуска время работы функции?(Какая из формул лучше: текущее_время + период_выполнения - время_выполнения_функции или текущее_время + период_выполнения? )

deterok ★★★★★
() автор топика
Последнее исправление: deterok (всего исправлений: 1)
Ответ на: комментарий от deterok

Какой функцией лучше получать время с точностью до 0.01 секунды?(пока решил использовать clock())

clock_gettime(CLOCK_MONOTONIC, &tspec)

Нормально ли для каждого таймера хранить время следующего запуск и период?

Да, если под «таймером» ты имеешь в виду периодическое внутриигровое событие.

в общем, как лучше и эффективней хранить эти данные?

Я думаю, это не слишком важно :) У тебя же не миллионы событий и даже не десятки тысяч.

Нужно ли вычитать из времени следующего запуска время работы функции?

Нет. Но код должен разумно обрабатывать пропущенные сроки.

tailgunner ★★★★★
()

Таймер можно сделать и один, а динамические объекты - «квантовать» тиками.

slackwarrior ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.