В качестве эксперимента мы решили попробовать сделать такой 3Д фон в программе Deluxe Moon Pro.
Вот пример того, что у нас получилось:
Итак начнем.
Для того, чтобы достичь 3Д эффекта необходимо изображение разбить на «слои» и в зависимости от показаний акселерометра смещать каждый слой на определённую величину.
Казалось бы всё просто, но есть несколько проблем.
- Дрожание рук вызывает мерцание картинки
- Телефон держится под наклоном. Поэтому, при запуске программы лучше считать, что в этом положении взгляд пользователя был направлен по нормали к плоскости телефона, а все изменения сенсоров отсчитывать от этого начального значения.
Таким образом, общий подход к решению данной задачи такой:
Разбить фон на неско
- лько слоёв, которые будут двигаться друг относительно друга. Это может быть текст, фоновые изображения или же специальные элементы созданные для придания «глубины»
- Подписаться на события акселерометра.
- При изменении акселерометра:
- Подкоректировать показатели сенсоров относительно начальных значений.
- Сгладить колебания от дрожания рук
- Сдвигать каждый слой на свою величину.
- Сделать сенсоры опциональными: не всем пользователям может понравится такой сюрприз.
Итак, в нашем случае у нас есть изображение фона, + 2 слоя звезд, + слои различных элементов экрана.
Для нужд фильтрации устанавливаем некоторые константы:
#define kUpdateFrequency 240.0 //частота обновления фильтра #define kCutoffFrequency 5.0 //частота фильтрации #define kAccDataScale 2.2 //масштаб данных
При запуске программы мы инициализируем низкочастотный фильтр для того чтобы убрать дребезжание фона, вызванное дрожанием рук.
про него можно почитать вот здесь
и скачать пример реализации у Apple здесь
-(void)awakeFromNib { filter = [[LowpassFilter alloc] initWithSampleRate:kUpdateFrequency/10 cutoffFrequency:kCutoffFrequency]; //собственно фильтр низких частот if ([Options instance].useSensors) { [self performSelector:@selector(startAcc) withObject:nil afterDelay:4]; // стартуем с запаздыванием чтобы программа успела запуститься и заполнить нормальные начальные значения акселерометра } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didGoToBG:) name:UIApplicationDidEnterBackgroundNotification object:nil]; //программа ушла в фон [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didResume:) name:UIApplicationDidBecomeActiveNotification object:nil]; //Программа стала активна [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(useSensorsChanged) name:@"useSensors" object:nil]; //подписываемся на изменения опции }
Реакцию на изменение опции по вкл/выкл 3д эффекта фона и на сворачивание программы в фон описывать думаю не стоит, там все банально
Теперь рассмотрим функцию которая запускает всю эту красоту
P.S. Используется ARC потому, никаких retain или release
-(void)startAcc { @try { if (!acc) { startY = 100; //это стартовое положение сенсоров по Y чтобы не приходилось постоянно держать устройство в горизонтальном положении acc = [UIAccelerometer sharedAccelerometer]; acc.updateInterval = 1/kUpdateFrequency; acc.delegate = self; } } @catch (NSException *exception) { NSLog(@"startAcc %@",exception); } }
Чтобы остановить все это дело
-(void)stopAcc { @try { if (acc) { acc.delegate = nil; acc = nil; [self resetState]; } } @catch (NSException *exception) { NSLog(@"stopAcc %@",exception); } }
Дальше нам остается только ловить событие обновления акселерометра
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { @try { [filter addAcceleration:acceleration]; //фильтруем данные чтобы избежать «тряски» фона double x = filter.x*kAccDataScale; //масштабируем данные чтобы увеличить частоту double y = filter.y*kAccDataScale; if (startY == 100) { startY = acceleration.y*kAccDataScale; // запоминаем начальные данные по Y } // убираем лишнее по X if (x > 1) { x = 1; } // учитываем начальное значение y = (y - startY); // убираем лишнее по Y if (y > 1) { y = 1; } //Сдвигаем необходимые элементы [self setOffsetElementForX:x Y:y]; } @catch (NSException *exception) { NSLog(@"accelerometer %@",exception); } } @end
И, наконец, в функции сдвига мы передвигаем разные слои на разную фиксированную величину. Поэтому, верхние элементы двигаются с меньшей скоростью чем фон что и создает эффект 3Д. При этом, все элементы должны сдвигаться в сторону поворота устройства.
-(void)setOffsetElementForX:(double)x Y:(double)y { @try { allEll.transform = CGAffineTransformMakeTranslation(-8.5*x, 11.5*(y - startY)); movingButtonsView.transform = CGAffineTransformMakeTranslation(-8.5*x, 11.5*(y - startY)); arcView.transform = CGAffineTransformMakeTranslation(-8.5*x, 11.5*(y - startY)); bgView.transform = CGAffineTransformMakeTranslation(-17*x, 23*(y - startY)); bgStar1View.transform = CGAffineTransformMakeTranslation(-13*x, 18*(y - startY)); bgStar2View.transform = CGAffineTransformMakeTranslation(-10*x, 13*(y - startY)); allEll2.transform = CGAffineTransformMakeTranslation(-17*x, 21*(y - startY)); } @catch (NSException *exception) { NSLog(@"offsetElementForX %@",exception); } }
Эти 4 простых шага позволят сделать программы привлекательнее, интереснее и инновационее. 21-й век всё-таки. Помните, что эффектный и стильный дизайн может положительно повлиять на оценку вашего приложения ревьюверами, а также выделить их из сотен тысяч конкурентов. Мы в Lifeware Solutions (http://www.lifewaresolutions.com/) активно занимаемся исследованиями и будем рады, если наш опыт будет вам полезен. Буду очень благодарен за ваши отзывы.
ссылка на оригинал статьи http://habrahabr.ru/post/162081/
Добавить комментарий