Loading...

Във форума е въведено ограничение, което позволява на потребителите единствено да разглеждат публикуваните въпроси.

Smeshan avatar Smeshan 89 Точки

Timers

Здравейте,

ясно как с един таймер да местя един обект, но какво правя ако имам няколко от този обект?
Да предположим примерно, че искам да направя падащи снежинки:
Имам си мембър:
std::vector<SnowFlake> _snowFlakes;
и в инициализацията на Game:

_snowFlakes.resize(SNOWFLAKE_COUNT);
for (int32_t i = 0; i < SNOWFLAKE_COUNT; ++i) {
    _snowFlakes[i].init(i, config.fallingTimerId);
    _snowFlakes[i].startAnim();
}

 В обекта съответно:

int32_t SnowFlake::init(const int32_t indx, int32_t timerId) {
    _fallingTimerId = timerId;
    _snowFlakeImg.create(TextureId::SNOWFLAKE_ID, Point(0 + indx* 50, 0));
    return EXIT_SUCCESS;
}
void SnowFlake::startAnim() {
    Timer::startTimer(25, _fallingTimerId, TimerType::PULSE);
}

И съответно само първата тръгва да пада, а за другите изписва грешка "Error, trying to start an already existing timer with ID: " 0 .. Което е логично, но как тогава да го направя да работи за примерно 100 такива обекта? Ако трябва в CommonDefines да напиша 100 ID-та и после да ги попълня в EngingeConfigLoader-a, a след това да ги подавам за всеки обект ми се струва нелогично?

Нещо не ми става ясно как да процедирам при повече обекти.. Ще се радвам ако някой ми помогне.

Поздрави,
Илиян

Тагове:
1
C++ Applications Development
MartinBG avatar MartinBG 4803 Точки

Трябва ти автоматична генерация на timerId-тата.

Вариант е да овърлоуднеш:

void TimerManager::startTimer(const int32_t timerId, TimerData&& timerData)

с:

int32_t TimerManager::startTimer(TimerData&& timerData)

В TimerManager при извикването на овърлодната функция ще се генерира timerId, който се връща обратно и може да го запазиш и използваш по-късно за манипулиране на таймера.

 

Самото генериране може да е прост инкреметиращ брояч, но имай предвид следни особености:

1. Броячите могат да се препълнят. На практика това не би трябвало да се случи - int32_t има над 2 млрд. стойности без да броим отрицателните. 

2. Генерираните ID-та не трябва да съвпадат с тези, които сме задали ръчно отвън. Набързо се сещам за 2 варианта:

  • фиксираме range за user defined ID-та (напр. 0 - 1000) и генерираните такива започват след него (напр. от 1001)
  • при генериране на ID се проверява дали вече не съществува таймер с такова и ако има се генерира нaново, докато не намерим свободно. Това може да е по-бавно, но подобен подход би решил и проблема с препълването от т.1 - при препълване просто ресетваме брояча и търсим следващата свободна стойност.
1
10/11/2021 18:31:19
j.petrov_90 avatar j.petrov_90 373 Точки

Привет, Илиян,

MartinBG ти е дал страхотен отговор.
Аз лично обичам да си имам user defined range от timerId според броя на специфичните обекти.
По този начин имам видимост и пълен контрол на коя част от системата ми, кои id-та използвам.

Това може да се постигне по следния начин:

enum Internals {
  MAX_SNOWFLAKE_TIMERS = 100
}

enum TimerId {
  SOME_TIMER_ID,
  ANOTHER_TIMER_ID,
  SNOWFLAKE_FIRST_TIMER_ID,

  //reserved

  SNOWFLAKE_LAST_TIMER_ID = SNOWFLAKE_FIRST_TIMER_ID + MAX_SNOWFLAKE_TIMERS,
  YET_ANOTHER_TIMER_ID
}

Като ще спазвам ограничението, че мога да имам максимум MAX_SNOWFLAKE_TIMERS активни снежинки.

Относто самите снежинки:
Не е проблем всяка да си има собствен таймер.
Просто трябва да се погрижиш, че този таймер е уникален. Също обекта правилно да стартира/пуска този таймер.

Тук вече си пред архитектурна дилема:
Ако имаш 10-20 снежинки - направи 10-20 обекта с индивидуални таймери.

Ако обаче имаш 50, 100, 1000 обекта - не е ОК да пускаш толкова таймери.
Да приемем, че всичките ти обекти работят на една и съща честота.
Тогава можеш да имаш един обект, който управлява всички снежинки. Той ще има само 1 таймер и когато тикне - ще update-ва всички активни снежинки.

class ParticleHandler {
  void onTimeout(int32_t timerId) {
    if (notMyTimer) return;
    
    for (auto& snowflake : _activeSnowflakes) {
      snowflake.process();
    }
  }

  std::vector<Snowflake> _activeSnowflakes;
}


Ако пък имаш нужда от няколко таймера с различни честоти - просто твоя обект може да управлява няколко таймера като предварително си групира обектите си в контейнери спрямо честотите.
Например:

class Particle {};
class SnowFlake : public Particle {};
class Raindrop : public Particle {};

class ParticleHandler {
  void onTimeout(int32_t timerId) {
    if (timerId == frequentId) {
      for (auto& particle : _frequentParticles) {
        particle->process();
      }
    }
    
    else if (timerId == notSoFrequentId) {
      for (auto& particle : _notSoFrequentParticles) {
        particle->process();
      }
    }
  }

  std::vector<std::unique_ptr<Particle>> _frequentParticles;
  std::vector<std::unique_ptr<Particle>> _notSoFrequentParticles;
}


Поздрави

1
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.