Анимации на лямбдах в C++11

от автора

Компании-разработчики, как правило, не особо спешат переходить на новый Си++. Главным образом из-за поддержки его компиляторами, а точнее ее полного или частичного отсутствия. Недавно я решил узнать, что же есть новенького в плане поддержки C++11 компилятором GCC, и понял, что пора начинать. Благо, у нас в Ivideon лояльно относятся к новым технологиям и дают пробовать что-то новое.
Начал, конечно же, с самого вкусного — с лямбда-выражений! И с потоков.

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

Заголовок:

#ifndef ANIMATION_ENGINE_H #define ANIMATION_ENGINE_H  #include <functional>  namespace animation {  // Actor function has one parameter: time delta since previous frame // It should return true if animation has finished, //      or false if it should go on typedef std::function<bool(float)> actor_func;  // Handler is called after the animation is complete typedef std::function<void(void)> handler_func;  const handler_func doNothing = [] {};  void start(unsigned intervalMs, actor_func, handler_func = doNothing);  } // end of namespace animation  #endif // ANIMATION_ENGINE_H 

Интерфейс простой: в start() мы передаём интервал вызовов, функцию и необязательный обработчик завершения.

И реализация:

#include "animation_engine.h" #include <thread>  namespace animation {  void start(unsigned intervalMs, actor_func actor, handler_func handler) {     std::thread t([=]() {         auto timeStart = std::chrono::system_clock::now();         float lastInterval = 0;         while(1) {             std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));             float timeDelta = std::chrono::duration_cast<std::chrono::milliseconds>                     (std::chrono::system_clock::now() - timeStart).count();             if (actor(timeDelta - lastInterval)) {                 handler();                 break;             }             lastInterval = timeDelta;         }     });     t.detach(); }  } // end of namespace animation 

Вот здесь уже интереснее: на основе функций, которые передали аргументами, и интервала, строится новая функция (замыкание), куда переданные аргументы копируются ([=] означает захват по значению всех доступных переменных). Дальше эта новая функция запускается в отдельном потоке, где она в бесконечном цикле гоняет нашу несчастную функцию actor, покуда она не вернет true. Ну, или приложение не закроется. Далее start() завершается, оставляя анимацию крутиться в своём потоке.

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

animation::start (30,      [=] (float dt) {         this->adjustBallDirection(dt); // bouncing, maybe gravitation         this->updateBallPosition(dt);         return (this->ballMissed()) // if player missed the ball -> stop     },     [this] {          resetBall();     } ); 

Компилировать это всё, как и водится, надо с ключом -std=c++0x или -std=c++0x. Кроме того, на этапе линковки нужно передать ключ -pthread. Если вы, как и я, работаете с QMake, просто добавьте эти две строчки в .pro-файл:

CONFIG += c++11 QMAKE_LFLAGS += -pthread 

Возможное улучшение №1: для реальных задач неплохо бы улучшить расчет интервала между вызовами. Для этого следует учитывать время, пока отрабатывает функция-актор. А то сейчас выходит, что интервал становится чуть больше. Но я не стал заморачиваться на этот раз с этим: просто не об этом заметка.

Возможное улучшение №2: можно ввести дополнительный функционал, который бы позволил прерывать анимации. Сделать это несложно (да хотя бы введением флага, который бы все потоки с анимациями проверяли при работе), но, опять же, код примера распух бы от этого.

Вполне вероятно, что вы знаете более лаконичный способ решения подобной задачи — будет круто, если вы изложите его в комментариях. Или вы знаете более интересный кейс, где лямбды упростили бы жизнь… в общем, вы знаете, что делать.

Спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/company/ivideon/blog/230501/


Комментарии

Добавить комментарий

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