Интеграция Facebook SDK (iOS) в мобильные free-to-play игры

в 10:53, , рубрики: Facebook, free-to-play, game development, iOS, Блог компании «Alawar Entertainment», разработка под iOS, метки: , ,

imageМобильный free-to-play уже практически не обходится без использования социальных сетей в играх. Социальные сети обеспечивают то, что называют виральностью (от англ. viral – вирусный, т.е. способный распространяться как вирус, от одного человека к другому), что позволяет привлекать новых игроков с минимальными затратами. В данной статье мы поделимся опытом интеграции Facebook в игры Alawar на платформе iOS.

В типичной мобильной free-to-play-игре интеграция Facebook предоставляет следующие возможности:

  1. Возможность авторизации через аккаунт пользователя в Facebook. Такая авторизация хороша тем, что мы формируем уникальный идентификатор пользователя игры с минимальными усилиями с его стороны. Игроку нет необходимости придумывать логин, вводить адрес электронной почты и т.д. Ведь речь идет о мобильном приложении, где постоянно вводить текстовые данные несколько утомительно. Достаточно однажды нажать на кнопку с хорошо узнаваемым логотипом “f”. Есть преимущества и для разработчиков – прогресс игрока может быть синхронизован между различными устройствами на разных платформах, главное чтобы на этих платформах был Facebook.
  2. Возможность отправлять запросы (app requests) друзьям. Запросы – это своего рода сообщения, которые появляются на Facebook-странице игрока (и в приложении Facebook) и содержат относящуюся к игре информацию. К типичным запросам можно отнести приглашения друзей в игру, просьбы вида «отправь/подари мне что-либо» или «помоги пройти уровень». В конечном счёте, запрос нужен только для того, чтобы человек нажал на него и перешел в игру (или на страницу игры в App store). Использование запросов – это мощный механизм для бесплатного привлечения новых игроков и возвращения старых игроков в игру.
  3. Возможность отправлять посты в таймлайн (timeline). Такой постинг позволяет сообщать об игре всем, кто читает таймлайн. Игры обычно помещают туда информацию о достижениях игрока, что добавляет соревновательную составляющую.
  4. Возможность получать информацию об игроке из его аккаунта в Facebook. Чаще всего в такую информацию входит имя игрока, его аватарка и, возможно, аватарки его друзей. Пример использования аватарок в реально существующей free-to-play-игре под спойлером.
    Игра с аватарками

    image

Для того чтобы упростить интеграцию Facebook, мы создали демо-приложение, реализующее практически все необходимое с клиентской стороны.

Демо-приложение

Демо-приложение написано на C++ (как и большинство наших free-to-play-игр) со вставками на Objective-C и представляет собой простейшее приложение на движке Cocos2d-x (скачать можно здесь). По экрану перемещается прямоугольник и отражается от границ экрана при достижении этих границ.
image
Управление осуществляется при помощи 4 кнопок: f – авторизация, REQ – отправка запроса друзьям, POST – постинг в таймлайн и LOGOUT. После авторизации на место прямоугольника помещается аватарка пользователя, и добавляется приветственная надпись.
image
Для компиляции необходимо скачать и установить Facebook SDK, добавить фреймворк Facebook SDK в проект в XCode. Мы использовали Facebook SDK версии 3.9.
Для запуска демо-приложения вы должны создать собственное Facebook-приложение на вашем портале разработчика, прописать его идентификатор в plist нашего демо-приложения, а также проделать ряд других несложных операций. Подробнее про это можно прочитать здесь.

Технические детали реализации

Права доступа

Первое, с чем сталкиваешься при интеграции Facebook на клиенте – это создание сессии с запросом прав доступа (permissions), разные варианты которых предлагает Facebook. Здесь важно сдержать порыв и не запросить права доступа на все сразу. Хорошим тоном считается запрос прав доступа в момент, когда они действительно необходимы. При создании сессии запросим только базовые права.

+ (NSArray*) getBasicPermissions
{
    NSArray* permissions = [[NSArray alloc] initWithObjects: @"user_birthday", nil];
    return permissions;
}

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

image

В противном случае iOS попытается использовать приложение Facebook (если оно было установлено из App Store) или откроет Facebook в браузере.
Когда нам потребуются права для опубликования чего-либо в таймлайн или при запросе друзьям, запросим права конкретно на это.

+ (void) requestPublishPermissions
{
    if (hasPublishPermissions) return;
    
    NSArray *permissions = [[NSArray alloc] initWithObjects:
                            @"publish_actions", @"publish_stream", nil];
    
    [[FBSession activeSession] requestNewPublishPermissions:permissions
                               defaultAudience:FBSessionDefaultAudienceFriends
                               completionHandler:^(FBSession *session, NSError *error)
    {
        hasPublishPermissions = [[FBSession activeSession].permissions containsObject:@"publish_actions"] &&
                                [[FBSession activeSession].permissions containsObject:@"publish_stream"];
        if (g_handler) { g_handler->OnGetPublishPermissions(hasPublishPermissions); }
    }];
}

В этом случае будет показан следующий диалог.
image
Важно помнить, что игрок может отказать в правах на опубликование от своего имени, но разрешить авторизацию. Эти случаи необходимо аккуратно обрабатывать.

Запросы к друзьям

Реализация запросов к друзьям (app requests) достаточно подробно изложена в документации, однако есть ряд тонкостей. При базовой реализации запросов будет показано диалоговое окно, приведенное ниже.
image
Код этой реализации находится под спойлером.

Реализация

+ (void) requestFriend
{
    // more details here
    // https://developers.facebook.com/docs/ios/send-requests-using-ios-sdk/
    
    if (!friendsCache)
    {
        friendsCache = [[FBFrictionlessRecipientCache alloc] init];
    }
    
    [friendsCache prefetchAndCacheForSession:nil];
    
    [FBWebDialogs presentRequestsDialogModallyWithSession:nil
                  message:@"Help me, friend!"
                  title:@"Help me!"
                  parameters:nil
                  handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error)
    {
        if (error)
        {
            NSLog(@"Error sending request");
        }
        else
        {
            if (result == FBWebDialogResultDialogNotCompleted)
            {
                NSLog(@"User canceled request");
            }
            else
            {
                NSDictionary *urlParams = [FacebookController parseURLParams:[resultURL query]];
                if (![urlParams valueForKey:@"request"])
                {
                    NSLog(@"User canceled request");
                }
                else
                {
                    NSLog([NSString stringWithFormat: @"Request Sent: %@", [urlParams valueForKey:@"request"]]);
                }
            }
        }
    }
    friendCache:friendsCache];
}

Поведением окна запросов можно управлять при помощи параметров, передаваемых после parameters: при вызове диалога. Например, следующий набор параметров позволяет отправить запрос одному известному заранее другу.

NSString *friendId=@"100006530868327";
NSMutableDictionary* params = [[NSMutableDictionary alloc] init];
params[@"to"] = friendId;

Facebook SDK в этом случае покажет видоизменённое окно App requests и даже предложит не показывать окно при следующих запросах этому другу.

Постинг в таймлайн

Facebook позволяет размещать в таймлайн так называемые истории (stories). Для того чтобы сформировать историю необходимо на странице вашего приложения на портале Facebook-разработчика выбрать в меню слева «Open Graph». Вы увидите 3 вкладки: Stories (Истории), Object Types (Типы объектов) и Action Types (Типы действий). Тип объекта обобщает некоторую группу объектов, которые могут встретиться пользователям вашей игры, например, бейджи. Теперь нужны действия, которые можно применить к объекту «Бейдж», например, действие «Найти». Тип действия и тип объекта могут быть объединены в историю «Find a Badge». Facebook имеет широкие возможности по настройке историй. Подробнее об этом можно узнать здесь.
Чтобы получить код для публикации созданной истории из приложения, необходимо кликнуть на надпись «Get Code» справа от названия истории. Выбираем требуемую платформу (iOS SDK в нашем случае) и вкладку «Code for Object». Система сгенерирует код подобный этому:

NSMutableDictionary<FBGraphObject> *object =
    [FBGraphObject openGraphObjectForPostWithType:@"aw_test:badge"
                                            title:@"Sample Badge"
                                            image:@"https://fbstatic-a.akamaihd.net/images/devsite/attachment_blank.png"
                                              url:@"http://samples.ogp.me/473380876115865"
                                      description:@""];;

[FBRequestConnection startForPostWithGraphPath:@"me/objects/aw_test:badge"
                                   graphObject:object
                             completionHandler:^(FBRequestConnection *connection,
                                                 id result,
                                                 NSError *error) {
                                 // handle the result
                             }];

Во вкладке «Code for Action» можно получить код для действия. Для формирования истории в таймлайн сначала необходимо выполнить создание объекта затем создание действия с этим объектом.
Как ни странно, этот код не работает. Если включить его в проект без модификаций, то будет крэш по необнаруженному селектору в одном из классов. Для того чтобы это исправить, необходимо добавить пару строк в параметры создаваемого объекта Open Graph.

object[@"create_object"] = @"true";
object[@"fbsdk:create_object"] = @"true";

Эта «магия» исправляет крэш, однако постинг в таймлайн по-прежнему не работает. Хорошим признаком того, что все заработало, является возвращенный Facebook идентификатор операции в коллбэке. Идентификатор представляет собой набор цифр (например, 586146891470767). В ответ на код, полученный с портала, приходит строка «true». Это можно исправить, еще немного модифицировав код. В частности, предлагаемый кодогенератором вызов startForPostWithGraphPath необходимо заменить на startForPostOpenGraphObject при создании объекта.
Полный код для постинга в таймлайн можно увидеть под спойлером.

Реализация

+ (void) createOpenGraphObjectWithType:(NSString *) type
                                 title:(NSString *) title
                                   url:(NSString *) url
                                 image:(NSString *) image
                                 handler: (OpenGraphObjectCreationHandler) handler
{
    NSMutableDictionary<FBOpenGraphObject> *object =
    [FBGraphObject openGraphObjectForPostWithType:type
                                            title:title
                                            image:image
                                              url:url
                                      description:@""];
    object[@"create_object"] = @"true";
    object[@"fbsdk:create_object"] = @"true";
    
    [FBRequestConnection startForPostOpenGraphObject:object
                                   completionHandler:^(FBRequestConnection *connection, id result, NSError *error)
     {
         if (!error && result != nil)
         {
             NSLog([NSString stringWithFormat:@"Posting object '%@' (id=%@) is created!",
                    title, [result objectForKey:@"id"]]);
             
             handler([result objectForKey:@"id"]);
         }
         else
         {
             NSLog([NSString stringWithFormat:@"Posting object creation error: %@", error]);
         }
     }];
}

+ (void) postStory
{
    NSString* badge_title = @"Blue Badge";
    NSString* badge_url = @"http://demo.tom3.html5.services.alawar.com/images/tester/blue_badge.htm";
    NSString* badge_image = @"http://demo.tom3.html5.services.alawar.com/images/tester/blue_badge.png";
    int rnd = arc4random() % 2;
    if (rnd == 0)
    {
        badge_title = @"Red Badge";
        badge_url = @"http://demo.tom3.html5.services.alawar.com/images/tester/red_badge.htm";
        badge_image = @"http://demo.tom3.html5.services.alawar.com/images/tester/red_badge.png";
    }
    
    [FacebookController createOpenGraphObjectWithType:@"aw_test:badge"
                                                title:badge_title
                                                url:badge_url
                                                image:badge_image
                                                handler:^(NSString *objectId)
    {
        // action
        NSMutableDictionary<FBGraphObject> *action = [FBGraphObject graphObject];
        action[@"badge"] = objectId;
        action[@"fb:explicitly_shared"] = @"1";
        
        [FBRequestConnection startForPostWithGraphPath:@"me/aw_test:find"
                                           graphObject:action
                                     completionHandler:^(FBRequestConnection *connection,
                                                         id result,
                                                         NSError *error)
         {
             if (!error && result != nil)
             {
                 NSLog([NSString stringWithFormat:@"Posted (id=%@)!", [result objectForKey:@"id"]]);
             }
             else
             {
                 NSLog([NSString stringWithFormat:@"Posting error: %@", error]);
             }
         }];
    }];
}

Внимательный читатель обратит внимание, что при выполнении кода для действия был добавлен дополнительный параметр.

action[@"fb:explicitly_shared"] = @"1";

Если этот параметр не выставлен, то созданная история попадет в Recent Activities пользователя, а не в таймлайн. Если все сделано правильно, то в таймлайн пользователя можно будет наблюдать нечто, похожее на рисунок ниже.

image

Последний момент, с которым необходимо разобраться: где и как хранятся описания объектов. Описание объекта хранится как специальным образом размеченный html-документ на каком-либо открытом сервере. Изображения также размещаются на открытых серверах, откуда их Facebook и загружает.

Вместо заключения

Если вы решились на интеграцию в игру Facebook помните, что это не минутное дело. Обязательно продумайте, как игра будет взаимодействовать с пользователем через Facebook. В противном случае вы получите вместо полезного инструмента для привлечения игроков очередное приложение досаждающие игрокам. Жалобы от игроков на спам приведут к тому, что Facebook забанит приложение, чего, разумеется, никому не хочется.
Как показывает практика, взять Facebook с наскока получается далеко не у всех. Мы надеемся, что приведенный нами опыт по интеграции этой социальной сети будет вам полезен.

Автор: rokuz

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js