Pull to refresh

Как бросить кости без OpenGL

Reading time3 min
Views24K
image

Необязательное вступление
Разработчики приложений под iOS зарабатывают не на собственных творениях, а на сторонних заказах. Создав себе имя славного парня, который творит чудеса с iPad-ом, рано или поздно Вы будете получать предложения от знакомых своих знакомых.
-Алло! Напиши что-нибудь под iOS.


Позвольте несколько советов.
Если, по Вашему мнению, работы более чем на две недели, отказывайтесь.
Если на неделю — соглашайтесь за $5000.
Если на 2 дня — за $1000.

Еще одно правило — чем ближе круг знакомств с заказчиком — тем выше гонорар. С близкими друзьями — 100% предоплата.
Поверьте, в этом случае число мусорных проектов резко уменьшится, а уважение к Вам резко возрастет.


Один хороший человек захотел сделать электронную книгу под iOS, коллекцию афоризмов. Фразы вылетают случайно, данные предоставлены в формате комма сепарейтед валью. С флешкой и устным ТЗ он пришел к другу-программисту. Программист оценил примерный объем работы
  • Конвертируем данные в sqlite;
  • Заводим три UIView (левый, правый и центральный);
  • В каждый UIView добавляем UITextView и UILabel;
  • Обрабатываем нажатие touchesBegin для листания афоризмов вправо-влево;
  • Добавляем кнопку — показать случайный афоризм.
  • Добавляем закладки.
  • Получаем 1000 долларов США


Работы на 2 дня, программист согласился.
Однако в ТЗ было еще одно условие — при случайном выборе афоризма по экрану должен кататься игральный кубик. Самый обыкновенный, из шести граней.

Всю работу, кроме кубика, программист сделал быстро. Афоризмы выскакивали, как чертик из табакерки, страницы листались, как живые. Остался кубик.

Стон программиста
-OpenGL,- с тоской подумал программист. 150 строк кода только для инициализации GLKView и чтения текстур, а если возиться с шейдерами, то лучше Вольфенштейн 2048 доделать. Неужели нельзя простой трехмерный кубик нарисовать без OpenGL? Использовать обыкновенный UIImageView?..
Оказалось, можно.


Как нарисовать трехмерный кубик без использования OpenGL


В обычных играх UIImageView *p использует собственный набор функций и полей для

  • перемещения в точку (x,y), например p.center = CGPoint(x,y);
  • масштабирования, например p.transform = CGAffineTransformMakeScale(sx,sy);
  • вращения, например p.transform = CGAffineTransformMakeRotate(alpha);

Однако, существует прямой доступ к матрице двумерного преобразования UIImageView

             UIImageView *p = [dice objectAtIndex:i];
             CGAffineTransform diceTransform = CGAffineTransformMake(a, b, c, d, tx, ty);
             p.transform = diceTransform;


Матрица двумерного преобразования CGAffineTransform имеет 6 параметров

struct CGAffineTransform {
  CGFloat a, b, c, d;
  CGFloat tx, ty;
};


Любая точка изображения преобразуется согласно формуле

CGPointApplyAffineTransform(CGPoint point, CGAffineTransform t)
{
  CGPoint p;
  p.x = a * point.x + c * point.y + tx;
  p.y = b * point.x + d * point.y + ty;
  return p;
}


Для примера, чтобы повернуть картинку на угол angle, матрица преобразования примет вид


   t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] 


Для перемещения картинки-изображения на вектор (tx,ty), матрица преобразования примет вид

  t' = [ 1 0 0 1 tx ty ] 


И так далее.

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

Рассмотрим грань кубика. Квадратную картинку необходимо разбить на два треугольника. Пустые пикселы делаем прозрачными. Сохраняем изображения в файлы dice_1.png и dice_2.png
image

В проекте делаем создаем две переменные с указанными картинками.
UIImageView *p1 =  [[UIImageView alloc]  initWithImage:[UIImage imageNamed:@"dice_1"] ];
UIImageView *p2 =  [[UIImageView alloc]  initWithImage:[UIImage imageNamed:@"dice_2"] ];


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

Рассмотрим верхний треугольник
image

который должен преобразоваться на экране в треугольник произвольного вида с вершинами в точках (x1,y1), (x2,y2), (x3,y3)
image

Таким образом, мы имеем 6 линейных уравнений с 6-тью неизвестными.
В результате несложных преобразований получаем решение для 6-ти неизвестных a,b,c,d,tx,ty;

                float ddt = 1.0/xsize;
 
                float a = ddt*(x3-x2);
                float b = ddt*(y3-y2);
                float c= ddt*(x2-x1);
                float d= ddt*(y2-y1);
                
                float tx = 0.5*(x1+x3);
                float ty = 0.5*(y1+y3);


Здесь xsize — линейный размер в пикселах исходной картинки. Например xsize = 100 для картинки размером 100 на 100.
Задача решена.

Короткое видео
Видео приложения, демонстрирующее алгоритм.
Кубик отображается при помощи 6*2 = 12 треугольников.



А вот версия алгоритма, внедренная в старую, классическую игру для сглаженных кубиков.
Каждый кубик отображается при помощи 18*2 + 4 = 40 треугольников (сглажены грани и вершины).



Tags:
Hubs:
Total votes 59: ↑50 and ↓9+41
Comments22

Articles