Шайбу вбросим в iOS восемь

    Прежде чем создавать казуальную игру для iOS, хорошо бы ответить на вопрос: — А зачем?
    Вариантов три:
    • Срубить денег;
    • Порадовать родственников;
    • Хрен его знает, но мысль жжет организм изнутри.


    image

    Думаю, в ближайшие годы, правильный ответ — третий.
    А, не буду спорить и учить — расскажу, как я делаю приложения.
    Гуру разработки молча нажимают плюс и уходят в сторону. Остальные следуют за мной, чтобы вспомнить школу и настольный хоккей.
    И да, уникальность топика, что в каждом предложении слова начинаются разными буквами.
    В статье девять картинок и пол-минуты забавной анимации.

    1. Идея


    От неё зависит выбор инструментов. Мои идеи, как пользователи смартфонов — незамысловатые. Потому — никаких лишних инструментов для разработки, кроме библиотеки воспроизведения звуков. Ресурсы заимствую из сети. Мелодии, дизайн, изображения, примеры кода. Все уже создано до нас. Авторское право — как в хоккее или футболе. Допустим, Месси придумал новый финт. Вы можете его скопировать, не отчисляя автору денег.
    Ладно. В качестве примера игры я выбрал настольный хоккей.
    Очень личное
    80-ые годы. На мех-мате эта забава была популярна. Мы играли в умывалке ФДС-6. Похвастаюсь звездным моментом — толпа народу соревновалась в ночь. Я зашел, дождался очереди, победил местного чемпиона 10-0 и скромно удалился. Если честно — у меня два брата и шестнадцать лет азартной практики.


    2. Начало проекта


    image
    Рисунок 2. В Xcode завожу новый проект.

    Кстати, у меня толстые пальцы. Значит, для игры подходит лишь iPad режим. Это существенно облегчает работу. Кроме того, запрещаю вращение экрана, допуская лишь портретную ориентацию. Да здравствует портрет, тут Вам хоккей, а не балет.
    С выбором имени приложения затруднений нет — Hockey 2015. Названия видов спорта — прибыльны. Насчет денег, я, конечно, слукавил. Хочется, чтобы будущий опус принес удовлетворение как душевное, так и материальное. Я уже писал здесь о своей игре Biathlon 2014. Каждый год — почти тысяча долларов навара. Покупают в дни чемпионата мира, олимпиады, кубковых этапов. Кто? Норвежцы, германцы, чехи, французы, русские, итальянцы.
    Думаю, мой будущий хоккей ждет что-то похожее всего лишь из-за названия.

    Выбираю иконку. Не так важно, как имя. Главное — чтобы самому нравилось.
    image
    Рисунок 3. Кто угадает, что это за хоккеист, тому- приз 1 доллар.

    В результате, получается заготовка будущей игры, которая отображает ярко-серый экран. Проект имеет два основных файла, которые я могу редактировать.
    • ViewController.xib
    • ViewController.m

    А не буду, плохой тон. Хороший тон не полениться и создать другой класс вида ViewController. Например, с именем PlayViewController.

    image
    Рисунок 4. Добавляю новый контроллер PlayViewController.

    Проект имеет два новых файла, которые я всегда могу редактировать.
    • PlayViewController.xib
    • PlayViewController.m


    3. Статичные изображения


    Неподвижные изображения размещаю с помощью редактора в файле PlayViewController.xib.
    Вначале картинки надо создать, украсть или позаимствовать. Изображения ледового поля, хоккеистов, а также вратарей мне сделал Milfgard. В Мосигре их есть. За что я без спросу разместил именной лейбл Мосигры в центре поля. Шайба — из сети. Фотографии лиц хоккеистов — nhl.com. Кнопки — от дизайнеров Зептолаб. Хоккейные звуки — из приложения Ice Rage.
    Теперь можно разместить изображения в редакторе xib-файлов. Это просто — все картинки отображается элементом UIImageView.

    image
    Рисунок 5. Хоккейное поле и табло в редакторе Xcode.

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

    Пример, объявляем элемент scoreBoard (черное табло на картинке) в файле PlayViewController.m
       IBOutlet UIImageView *scoreBoard;
    

    Ключевое слово IBOutlet означает, что в редакторе XIB любому элементу можно присвоить идентификатор scoreBoard.
    Присваиваю мышкой в редакторе.

    Теперь я могу сдвинуть картинку глубоко наверх, чтобы оно не загораживало хоккейное поле.
      scoreBoard.center = CGPointMake(384, -1000);
    

    Напоминаю, 384 — центр экрана iPad по ширине, -1000 — что-то вне устройства. После исполнения команды табло улетит вверх за границы экрана.

    Если требуется двигать группу картинок — их надо объединить. Завести элемент типа UIView, чтобы переместить в него группу картинок и надписей.
    image
    Рисунок 6. Итак, ненужные элементы убраны со льда.

    Теперь разместим здесь хоккеистов, чтобы научить их двигаться.


    4. Трансформируемые изображения


    Размещаем одного хоккеиста программно в файле PlayViewController.m
       UIImageView *player =  [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"player_1.png"]];
        float rPlayer = 150.0;
       player.frame = CGRectMake(0, 0, rPlayer, rPlayer);
        player.center = CGPointMake(100, 500);
        [self.view addSubview:player];
     


    image
    Рисунок 7. На корте появился хоккеист.

    В дальнейшем я его буду перемещать и вращать. Вот так.
        player.center = CGPointMake(xnew, ynew); // перемещаю в точку (xnew=100, ynew=440)
        player.transform = CGAffineTransformMakeRotation(alpha); // кручу хоккеиста на угол (alpha=2.0) в радианах
     


    image
    Рисунок 8. Хоккеист уехал в другое место и развернулся.

    Хоккеистов много, а я один. Размещаем все 12 игроков с помощью массива.
        NSMutableArray *players;
    
        players = [[NSMutableArray alloc] init];
        shadows = [[NSMutableArray alloc] init];
     
        for (int k=0; k<12; k++) {
            UIImageView *p =  [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"player_1"]];
            p.frame = CGRectMake(0, 0, cellDx, cellDx);
            float x = xp[k];
            float y = yp[k];
            p.center = CGPointMake(x, y);
            p.transform = CGAffineTransformMakeRotation(ap[k]);
            [players addObject:p];
            p = [players objectAtIndex:k];   // так можем получить доступ к любому из 12 хоккеистов
            [self.view addSubview:p];
        }
      


    5. Численные методы в хоккее


    Хоккей, как жизнь, зависит от времени.
    Необходимо завести счетчик временем. Событие, которое программа будет вызывать 3000 раз за минуту.

         NSTimer *pauseTimer;
    
        time = 0;
        deltaTime = 1.0/50.0;
        pauseTimer = [NSTimer scheduledTimerWithTimeInterval:deltaTime  target:self selector:@selector(timerFunction) userInfo:nil repeats:YES];
     
    
    - (void) timerFunction
    {
    // мы здесь бываем 50 раз в секунду
        time = time + deltaTime;
        [self renderPuck];
        [self renderPlayers];
        [self puckMoving:deltaTime];
    }
        


    Итак, все что мне осталось — расписать функцию puckMoving. В ней проверю столкновение шайбы и хоккеистов. Математически шайба задается окружностью радиусом 21 пиксел. Каждый хоккеист есть две окружности — побольше (радиус 25 пикселов) тело. Поменьше (радиус 5 пикселов) — клюшка.

    Функция ниже, комментарии внутри.
    puckMoving
           for (int k=1; k<33; k++) {
                    if ( [self checkCollision:i With:k] ) {
                        [self resolve:i With:k];
                    }
            }
    
    -(int) checkCollision:(int) k1 With:(int) k2
    {
        float d2 = [self distance2:k1 With:k2];
        float dr = rp2[k1] + rp2[k2];
        return ( d2 < dr*dr ? 1 : 0);
    }
    
    -(float) distance2:(int) k1 With:(int) k2
    {
        float dxx = xp2[k1] - xp2[k2];
        float dyy = yp2[k1] - yp2[k2];
        return dxx*dxx + dyy*dyy;
    }
    
    
    
    -(void) resolve:(int) k1 With:(int) k2
    {
        float x1 = xp2[k1];
        float y1 = yp2[k1];
        float x2 = xp2[k2];
        float y2 = yp2[k2];
        float u2 = up2[k2];
        float v2 = vp2[k2];
        float u1 = up2[k1];
        float v1 = vp2[k1];
        Vector *b1Velocity = [Vector alloc];
        [b1Velocity  initX:u1 initY:v1];
        Vector *b2Velocity = [Vector alloc];
        [b2Velocity  initX:u2 initY:v2 ];
        float b1Mass   = mp2[k1];
        float b2Mass   = mp2[k2];
        
        Vector *vv = [Vector alloc];
        [vv  initX:x1-x2 initY:y1-y2];
        float distance = [vv magnitude];
        float min_distance = rp2[k1] + rp2[k2];
        if (distance < min_distance) {
            [vv mulScalar: ((0.1+min_distance-distance)/(distance)) ];
            x1 += vv.x;
            y1 += vv.y;
            xp2[k1] = x1;
            yp2[k1] = y1;
        }
        
        
        Vector *lineOfSight = [Vector alloc];
        [lineOfSight initX:x1-x2 initY:y1-y2];
        Vector *v1Prime = [b1Velocity vectorProjectionOnto:lineOfSight];
        Vector *v2Prime = [b2Velocity vectorProjectionOnto:lineOfSight];
        
        Vector *v1Prime2 = [Vector alloc];
        [v1Prime2 copyVector:v2Prime];
        [v1Prime2 mulScalar:(2*b2Mass)];
        [v1Prime2 addVector:[v1Prime getMulScalar:(b1Mass - b2Mass)] ];
        [v1Prime2 mulScalar:(1.0/(b1Mass + b2Mass))];
        
        
        Vector *v2Prime2 = [Vector alloc];
        [v2Prime2 copyVector:v1Prime];
        [v2Prime2 mulScalar:(2*b1Mass)];
        [v2Prime2 subVector: [v2Prime getMulScalar:(b1Mass - b2Mass)] ];
        [v2Prime2 mulScalar:(1.0/(b1Mass + b2Mass))];
        
        [v1Prime2 subVector:(v1Prime)];
        [v2Prime2 subVector:(v2Prime)];
        
        [b1Velocity addVector:v1Prime2];
        [b2Velocity addVector:v2Prime2];
        
        float a = 0.999;
        
        up2[k1] = a*b1Velocity.x + (1.0-a)*b2Velocity.x;
        vp2[k1] = a*b1Velocity.y + (1.0-a)*b2Velocity.y;
       
        a = 1.0 - a;
        
        //    NSLog(@"new speed %f", hypotf(u, v)) );
    }
     


    Внутри функции используется класс Vector
    класс Vector
    #import "Vector.h"
    
    @implementation Vector
    @synthesize x,y;
    
    
    -(void) initX:(float) setX  initY:(float) setY  {
    	x = setX;
    	y = setY;
    }
    
    -(void) copyVector:(Vector*) v{
    	x = v.x;
    	y = v.y;
    }
    
    -(void) addVector:(Vector*) v {
    	x += v.x;
    	y += v.y;
    }
    -(void) subVector:(Vector*) v {
    	x -= v.x;
    	y -= v.y;
    }
    -(void) mulScalar:(float) f {
    	x *= f;
    	y *= f;
    }
    
    -(float) magnitude {
    	return sqrt( x*x + y*y );
    }
    -(float) magnitude2 {
    	return  x*x + y*y;
    }
    
    
    -(Vector *)getMulScalar:(float) f {
    	Vector *v = [Vector alloc];
    	[v initX:x*f initY:y*f];
    	return v;
    }
    
    -(float) scalarProjectionOnto:(Vector*) v {
    	
    	return (x* v.x + y*v.y)/ [v magnitude];
    }
    
    -(Vector *) vectorProjectionOnto:(Vector*) v {
    	Vector *res = [v getUnitVector];
    	[res mulScalar: [self scalarProjectionOnto:v]];
    	return res;
    }
    	 
    -(Vector *) getUnitVector {
    		float len = [ self magnitude];
    		Vector *res = [Vector alloc];
    		[res initX:x initY:y];
    		if (len>0) {
    			len = 1.0/len;
    			[res mulScalar:len];
    		}
    		return res;
    	}
    @end
    



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

    - (void) timerFunction
    {
    // мы здесь бываем 50 раз в секунду
        time = time + deltaTime;
        [self renderPuck];
        [self renderPlayers];
        for (int k=0; k<10; k++)  [self puckMoving:deltaTime/10.0];
    }
        


    Смотрим, что получилось



    6. Статистика знает все


    Русские и американцы любят статистику. Поэтому я организовал турнир шести лучших команд мира.
    Реальные игроки с nhl.com, голы, шайбы, очки, все звезды турнира.

    7. Тестирование


    Apple допускает 1000 тестеров на стадии ревизии приложения.

    Не заходите, реклама
    Ночью приложение одобрили в магазине Apple. Неделю игра будет платной, затем расскажу о количестве скачиваний.


    Спасибо, что дочитали.
    Papa Buba Diop
    Company
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 62

      +12
      Интересная статья. Хоть я и не разработчик, но прочитал с удовольствием. А на картинке Mike Modano.
      Image
      image
        +4
        Автор молоток)
        откуда идеи берутся?)
          +13
          Тоже интересовал этот вопрос. Мне кажется, автор их как-то придумывает.
            +4
            Если вы выросли в 90х, то должны знать автора и догадываться откуда у него берутся идеи :)
              0
              И все же, расскажите тем, кто родился в 90-х… Интересно ведь)
                0
                Эта картинка заставляет плакать целое поколение. Автор топика имеет к ней непосредственное отношение.
                image
                  0
                  А, теперь понял)
              +1
              Присмотритесь идеи везде! Вот это, к примеру:
              «Рисунок 7. На корте появился хоккеист.»
              это же идея новой игры «Теннис на льду»)
              +1
              Спасибо, очень понравилась статья.
              Не соглашусь пожалуй с введением. Да, на мобильные платформы сегодня уже есть практически все, что душе угодно, но помимо трех возможных мотиваций на создание игры есть и другие. Это подтверждает достаточно свежий пример разработчиков Threes! (казуальнейшая игра, на основе идеи которой потом очень быстро появилась знаменитая 2048). Я к тому, что не все идеи исчерпаны, и порой что-то совсем простое в App Store оказывается настоящим произведением, оригинальным и изящным, которым разработчики могут гордиться.

              За настольный хоккей отдельное спасибо. Большой любитель игры Stinger Table Hockey, которая к несчастью уже два года не обновляется. С удовольствием попробую вашу игру.
                +1
                Рисунок 3. Кто угадает, что это за хоккеист, тому- приз 1 доллар.

                Mike Modano

                Спасибо, ничего не нужно :)
                  0
                  Майк Модано же!
                    +2
                    Ага, в альте так и написано.
                    0
                    Вот жеж как, ответил на вопрос в статье и получил минус :(
                    Что за беспредел, товарищи?

                    PS
                    «Рисунок 3. Кто угадает, что это за хоккеист, тому- приз 1 доллар» — это слова из статьи, а не мои.
                      +6
                      Другие комментарии не читали? В первом же комментарии та же фотография и ответ
                    +1
                    Автор добавь мультиплеер и заработок увеличится в разы :)
                      +4
                      Будет не один доллар в месяц, а три!
                        +1
                        Это больная тема. Необходима мгновенная реакция, задержка в 0.1 секунды фатальна. Где взять такую связь?
                          +1
                          Wi-Fi хотспот, очевидно?
                            0
                            Сделай ее более казуальной, тогда небольшая задержка будет приемлемой… ну и юзер не будет же замечать задержку, для каждого будет как будто так и должно быть :)
                              0
                              Вы же реализовывали общение по Bluetooth 4, это не подойдет?
                                0
                                Там случаются задержки, еще какие.
                                +3
                                Даешь пошаговый настольный хоккей с блэкдж кубиком D12.
                              +8
                              уникальность топика, что в каждом предложении слова начинаются разными буквами.

                              И следующее предложение:

                              В статье девять картинок и пол-минуты видео.


                              Фу таким быть.
                                +7
                                Аргументируйте минусы, плиз. Только потому что я зануда?
                                  0
                                  Спасибо за замечание. Предложение исправил)
                                  0
                                  Вставил это предложение наспех последним, сейчас придумаю, как исправить.
                                    +2
                                    Топик будет содержать девять картинок и пол минуты видео.

                                    Вадим, пойдет?
                                      +1
                                      Еще лучше
                                      Текст содержит девять картинок и пол-минуты видео.
                                        +4
                                        Простите, но текст не может содержать картинок и видео.
                                        Да, я ЗАНУДА!
                                          +1
                                          Значит MSWord не текстовый редактор, кругом обман.
                                            0
                                            Да. Обман. Это текстовый процессор, который занимает свою нишу между текстовыми редакторами и издательскими системами.

                                            Как насчёт:

                                            В статье будет девять картинок и ролик на пол-минуты.
                                              +1
                                              Может хватит уже несчастные «полминуты» и в хвост, и в гриву шпынять? :) У вас, кстати, тогда буква «м» освободится. А настоящее «фу» — это MSWord, если он такое не подчеркивает.
                                                +1
                                                Так я же неграмотный. У меня три класса церковно-приходской. :)
                                      0
                                      Еще одно: «В Этом случае элемент можно программно двигать, гасить, трансформировать, что угодно.»

                                      Я тоже зануда, извините… Но раз уж объявлено в самом начале, непроизвольно проверяю буквы…
                                        +1
                                        «Завести элемент типа UIView, чтобы переместить в него группу картинок и надписей.»
                                        и тут же:
                                        Рисунок 6. Итак, я освободил поле от лишних элементов.
                                  +8
                                  В заголовок прямо ещё одно число четырехзначное в рифму просится.
                                  И я узнал про то, куда пошли картинки, спасибо )
                                    0
                                    Да ладно, был уверен, что ваш совместный проект.
                                    0
                                    обалдеть, заглядывая в подкат ожидал увидеть Unity или cacos2d, а тут всё так просто!
                                    Автор молодец.
                                      +1
                                      Ай красавчик! Папа буба как всегда молодец! Даже не столько читал реализацию, сколько сама идея понравилась!
                                        +1
                                        Игруха — улёт! В первом матче слил, но во втором надрал финам задницы 10:3 :D
                                          0
                                          С канадцами будет сложно — у них мгновенная реакция и мощные броски.
                                          Кроме того к концу турнира палец потеет и меняет емкостные свойства.
                                          Пришлось уделить время процессу ловли быстрого движения пальцем для мощного щелчка по воротам. Если при сухом пальце iOS ловит движение пальца с хорошим шагом (40-80 пикселей), то при влажном пальце все сильно сглаживается или разрывается.
                                          –1
                                          А дайте, пожалуйста, ссылку на игру. Хватит боятся рекламы. Уверен, что 99% прочитавших пост интересуются что это за игра.
                                          +1
                                          Искать это так
                                          удобно...
                                          image
                                            0
                                            У меня по запросу «hockey 2015» искомая игра в первой десятке, а на пятом месте внезапно футбол :)
                                            +1
                                            Нормальная статья, хотя и расстроило, что маловато математики.

                                            Если когда-нибудь будете ещё улучшать, то читы для шайбы с увеличением фрейм-рейта можно заменить на Continuous Collision, считая коллизии через пересечения отрезков.
                                              0
                                              Я, пожалуй, перепишу механизм соударений. Заменю модель клюшек с окружности на прямоугольники. Хоккеиста поменяю на два прямоугольника (два лезвия коньков).
                                              Хоккейная коробка у меня описывается 8-ми угольником, надо сделать закругления в углах, чтобы все по-честному.
                                              Кроме того я не анимирую щелчок соперника. Здесь столько забавной геометрии, с какой руки ему лучше бросать в зависимости от взаимного расположения шайбы — игрока — ворот.
                                                0
                                                А не думали ещё что будете использовать для анимации? Я в своё время использовал CoreAnimation, но очень давно не имел дело с iOS. Какие там последние штуки используются?
                                                  0
                                                  Я минималист. UIImageView.transform(); — наше все.
                                                +2
                                                Если хотите математики, посмотрите в сторону цикл статей, где описывают процесс моделирования «рулевого управления». Например, посмотрите здесь.
                                                  +1
                                                  Б… ть, ну все придумано до нас!
                                                +4
                                                Добавьте в тэги «Довлатов» тогда уж.
                                                0
                                                Классная идея! Если удасться геймплей, может стать хитом. По моему скромному мнению нужна добавить немного тактики. Как то в далеком прошлом с другом играли под досом в простецкий волейбол, две головы с ногами сетка и мяч. Играли всю ночь до утра, были у нас игрушки и поинтересней, но та простая игра позволяла проводить разные приемы, атака с низу сетки, атака от стены и т.д. что несомненно затягивало и давало сначала перевес одному игроку, тогда оппонент вырабатывал тактику противодействия и все по кругу!
                                                Кстати в процессе можно добавить хоккеистам финты, за отдельную плату :)
                                                  +1
                                                  Да, волейбол был очень популярен, как в режиме против компьютера, так и против живого игрока. В моем хоккее обнаружено несколько классных моментов — типа финт левого нападающего с броском в дальний угол, но все еще очень сыро. Надо доделывать. Неделю-другую переварю, подумаю и доделаю.
                                                  Насчет успеха сразу могу предсказать ( если игра без издателя) будущее. Для приличных платных игр — в первый день скачек 10-15. За неделю — 30. За месяц — 50. За год 100.
                                                  Причем от цены игры число загрузок не зависит (цену смело можно менять в диапазоне 1-4 долларов, проверено лично несколько раз). Плюс всплески на период ЧМ-2015. Короче, $600-$1000 за год будет капать.
                                                  +1
                                                  Сотрудничаете ли вы сейчас с издателем? Вроде бы пару лет назад издатель хорошо продвинул вашу игру. Что с тех пор изменилось?
                                                    0
                                                    4 года назад это было- время летит. Затем издатель ушел в Google play, а под андроид я не в силах творить. С тех пор поддерживаем дружеские отношения и делим ios доходы пополам.

                                                  Only users with full accounts can post comments. Log in, please.