Статьи

Изучение структуры Multipeer Connectivity: игровая логика

В этом уроке я покажу вам, как создать простую многопользовательскую игру, используя платформу Multipeer Connectivity, которая была представлена ​​в iOS 7. В первой части этой серии мы заложили основу игры. В этой статье мы реализуем игровую логику.

На этом этапе приложение может обнаружить других соседних игроков, установить соединение и отобразить подключенные узлы в текстовом представлении контроллера представления параметров. Однако реализация класса OptionsViewController еще не закончена.

Контроллер представления параметров содержит текстовое поле в верхней части его представления. Мы будем использовать это текстовое поле, чтобы позволить пользователю изменить отображаемое имя устройства. На данный момент имя устройства используется в качестве отображаемого имени партнера. Давайте рассмотрим, как мы можем настроить это отображаемое имя.

Давайте начнем с принятия протокола OptionsViewController классе OptionsViewController . Откройте OptionsViewController.h и измените интерфейс класса, как показано ниже.

1
@interface OptionsViewController : UIViewController <MCBrowserViewControllerDelegate, UITextFieldDelegate>

В файле реализации класса установите контроллер представления в качестве делегата текстового поля в методе viewDidLoad .

1
2
3
4
— (void)viewDidLoad {
    // … //
    [self.txtPlayerName setDelegate:self];
}

Нам нужно реализовать только один метод протокола textFieldShouldReturn: , textFieldShouldReturn:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
— (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [self.txtPlayerName resignFirstResponder];
 
    if (self.appDelegate.mpcHandler.peerID != nil) {
        [self.appDelegate.mpcHandler.session disconnect];
 
        self.appDelegate.mpcHandler.peerID = nil;
        self.appDelegate.mpcHandler.session = nil;
    }
 
    [self.appDelegate.mpcHandler setupPeerWithDisplayName:self.txtPlayerName.text];
    [self.appDelegate.mpcHandler setupSession];
    [self.appDelegate.mpcHandler advertiseSelf:self.swVisible.isOn];
 
    return YES;
}

Сначала мы вызываем resignFirstResponder в текстовом поле, чтобы закрыть клавиатуру. Далее, если объекты peerID и session не равны nil , мы сбрасываем оба значения, устанавливая их peerID nil . Мы также вызываем disconnect connect для объекта session чтобы отключить устройство от любых одноранговых узлов, к которым оно может подключаться. Затем мы инициализируем объект peerID используя имя, введенное пользователем в текстовое поле, и настраиваем объект session . Попробуйте это, запустив приложение и изменив отображаемое имя. Если все идет хорошо, вместо имени устройства будет использоваться пользовательское имя.

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

Следующим шагом является реализация действия toggleVisibility: мы объявили ранее. Его реализация не может быть проще. Всякий раз, когда переключатель переключается, мы вызываем advertiseSelf: метод объекта mpcHandler и передаем ему состояние переключателя. Запустите приложение, чтобы попробовать.

1
2
3
— (IBAction)toggleVisibility:(id)sender {
    [self.appDelegate.mpcHandler advertiseSelf:self.swVisible.isOn];
}

Нам также необходимо реализовать действие disconnect: action. Отключить устройство от сеанса так же просто. Посмотрите на реализацию disconnect: ниже.

1
2
3
— (IBAction)disconnect:(id)sender {
    [self.appDelegate.mpcHandler.session disconnect];
}

Это завершает его для класса OptionsViewController . Приложение в его текущем состоянии может установить соединение, обновить отображаемое имя устройства и отключиться от активного сеанса. Пришло время сосредоточиться на самой игре, исследуя, как мы можем обмениваться данными между сверстниками.

Теперь давайте сосредоточимся на реализации класса ViewController . Начните с объявления свойства делегата приложения и установки его в методе viewDidLoad контроллера представления. Это включает в себя три шага.

  1. Откройте ViewController.m и добавьте оператор импорта для класса AppDelegate .

    1
    #import «AppDelegate.h»
  2. AppDelegate свойство для объекта AppDelegate .

    1
    2
    3
    4
    5
    @interface ViewController ()
     
       @property (nonatomic, strong) AppDelegate *appDelegate;
     
       @end
  3. В viewDidLoad сохраните ссылку на делегат приложения в свойстве appDelegate .

    1
    2
    3
    4
    5
    — (void)viewDidLoad {
         [super viewDidLoad];
     
         self.appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
       }

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

1
2
3
4
5
6
7
8
9
@interface ViewController ()
 
@property (nonatomic, strong) AppDelegate *appDelegate;
 
@property (nonatomic) int secretNumber;
@property (nonatomic) BOOL hasCreatedGame;
@property (nonatomic) BOOL isGameRunning;
 
@end
  • Свойство secretNumber будет хранить секретный номер, выбранный игроком, принимающим игру.
  • Флаг hasCreatedGame указывает, является ли текущий игрок тем, кто начал текущую игру.
  • Флаг isGameRunning указывает, идет ли игра в данный момент.

Чтобы начать новую игру, игрок, который начинает игру, должен установить секретный номер. Основное внимание в этом руководстве уделяется инфраструктуре Multipeer Connectivity, поэтому мы не будем беспокоиться о сложном механизме установки секретного номера. А что проще, чем просмотр предупреждений, запрашивающий у игрока секретный номер?

Новая игра начинается, когда игрок нажимает кнопку « Пуск» на панели навигации, а это значит, что нам нужно реализовать действие startGame: . Как вы можете видеть в его реализации ниже, мы сначала проверяем, идет ли уже игра, перед тем, как представить представление предупреждений. Мы не хотим начинать новую игру, пока идет другая.

01
02
03
04
05
06
07
08
09
10
11
12
13
— (IBAction)startGame:(id)sender {
    if (!self.isGameRunning) {
        UIAlertView *newGameAlert = [[UIAlertView alloc] initWithTitle:@»MPCDemo»
                                                               message:@»Please enter a number between 1 and 100:»
                                                              delegate:self
                                                     cancelButtonTitle:@»Cancel»
                                                     otherButtonTitles:@»Start Game», nil];
 
        newGameAlert.alertViewStyle = UIAlertViewStylePlainTextInput;
        [[newGameAlert textFieldAtIndex:0] setKeyboardType:UIKeyboardTypeNumberPad];
        [newGameAlert show];
    }
}

Если для параметра alertViewStyle значение UIAlertViewStylePlainTextInput , в представление предупреждений добавляется текстовое поле.


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

  1. Нам нужно обработать нажатие игроком кнопки « Начать игру» .
  2. Нам также необходимо проверить, находится ли выбранное число между 1 и 100 .
  3. Другие игроки в игре должны быть уведомлены о том, что игра началась и был установлен секретный номер.

Давайте начнем с первой задачи с реализации alertView:clickedButtonAtIndex: метод делегата протокола UIAlertViewDelegate . Откройте файл заголовка контроллера представления и UIAlertViewDelegate протокол UIAlertViewDelegate , как показано ниже.

1
@interface ViewController : UIViewController <UIAlertViewDelegate>

Затем реализуйте alertView:clickedButtonAtIndex: метод протокола UIAlertViewDelegate . Чтобы убедиться, что представление предупреждений содержит текстовое поле, мы сначала проверяем его свойство alertViewStyle чтобы увидеть, равно ли оно UIAlertViewStylePlainTextInput . Мы также удостоверимся, что кнопка « Начать игру» была нажата, проверив, что свойство buttonIndex нажатой кнопки равно 1 .

1
2
3
4
5
— (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput && buttonIndex == 1) {
 
    }
}

Затем мы извлекаем ввод текстового поля и преобразуем его в целое число, которое сохраняем в свойстве secretNumber контроллера представления.

1
2
3
4
5
6
7
— (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput && buttonIndex == 1) {
        UITextField *textField = [alertView textFieldAtIndex:0];
 
        self.secretNumber = [textField.text intValue];
    }
}

Следующим шагом является проверка, является ли выбранное число от 1 до 100 , что очень легко сделать. Если число не попадает в требуемый диапазон, мы выводим на экран предупреждение для игрока. Однако, если секретный номер пройдет наш тест, мы создадим сообщение для других игроков и отправим его подключенным партнерам, используя инфраструктуру Multipeer Connectivity. Как это работает?

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
— (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput && buttonIndex == 1) {
        UITextField *textField = [alertView textFieldAtIndex:0];
        self.secretNumber = [textField.text intValue];
 
        // Make sure that the given number is between 1 and 100.
        if (self.secretNumber >= 1 && self.secretNumber <= 100) {
            // Create a message to tell other players that a new game has been created,
            // convert it to a NSData object and send it.
            NSString *messageToSend = @»New Game»;
            NSData *messageAsData = [messageToSend dataUsingEncoding:NSUTF8StringEncoding];
            NSError *error;
 
            [self.appDelegate.mpcHandler.session sendData:messageAsData
                                                  toPeers:self.appDelegate.mpcHandler.session.connectedPeers
                                                 withMode:MCSessionSendDataReliable
                                                    error:&error];
 
            // If any error occurs, just log it.
            // Otherwise set the following couple of flags to YES, indicating that the current player is the creator
            // of the game and a game is in progress.
            if (error != nil) {
                NSLog(@»%@», [error localizedDescription]);
 
            } else{
                self.hasCreatedGame = YES;
                self.isGameRunning = YES;
 
                [self.tvHistory setText:@»»];
            }
 
        } else{
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@»MPCDemo»
                                                            message:@»Please enter a valid number.»
                                                           delegate:nil
                                                  cancelButtonTitle:nil
                                                  otherButtonTitles:@»Okay», nil];
            [alert show];
        }
    }
}

Мы создаем экземпляр NSString , устанавливаем его значение в New Game и кодируем строку в объект NSData . Помните, что экземпляр NSString не может быть отправлен с использованием инфраструктуры Multipeer Connectivity. Сначала он должен быть преобразован в объект NSData .

Самая интересная строка кода показана ниже. В этой единственной строке мы отправляем объект NSData всем подключенным узлам. Мы передаем адрес указателя NSError чтобы перехватить любые ошибки, которые могут возникнуть в процессе.

1
2
3
4
[self.appDelegate.mpcHandler.session sendData:messageAsData
                                     toPeers:self.appDelegate.mpcHandler.session.connectedPeers
                                    withMode:MCSessionSendDataReliable
                                       error:&error];

Как вы уже могли догадаться, sendData:toPeers:withMode:error: метод объявлен в платформе многопользовательского подключения. Поскольку мы хотим, чтобы каждый self.appDelegate.mpcHandler.session.connectedPeers получил сообщение, мы передаем self.appDelegate.mpcHandler.session.connectedPeers в качестве второго аргумента. Третий аргумент метода, MCSessionSendDataReliable , указывает режим передачи. Если вы помните из введения , структура Multipeer Connectivity может отправлять данные одним из двух способов, надежным или ненадежным. В этом примере это ключ к надежной отправке данных, чтобы каждый игрок получал сообщение без икоты.

Если не было hasCreatedGame никакой ошибки, мы устанавливаем hasCreatedGame и isGameRunning в YES . Мы также очищаем текстовое представление, чтобы подготовить пользовательский интерфейс для новой игры.

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

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

1
2
3
4
5
— (void)toggleSubviewsState:(BOOL)shouldEnable {
    self.btnCancel.enabled = shouldEnable;
    self.txtGuess.enabled = shouldEnable;
    self.btnSend.enabled = shouldEnable;
}

В toggleSubviewsState: мы toggleSubviewsState: или отключаем кнопки и текстовое поле в зависимости от состояния игры. Давайте toggleSubviewsState: в методе viewDidLoad контроллера представления.

1
2
3
4
— (void)viewDidLoad {
    // … //
    [self toggleSubviewsState:NO];
}

Каждый раз, когда одноранговый узел получает объект NSData , session:didReceiveData:fromPeer: метод session:didReceiveData:fromPeer: делегат платформы Multipeer Connectivity. Это также приводит к публикации уведомления, как вы, возможно, помните из ранее в этом руководстве. Чтобы получать и обрабатывать эти уведомления, нам нужно добавить контроллер представления в качестве наблюдателя, как показано ниже. Всякий раз, когда публикуется уведомление с именем MPCDemo_DidReceiveDataNotification , handleReceivedDataWithNotification: метод.

1
2
3
4
5
6
7
— (void)viewDidLoad {
    // … //
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleReceivedDataWithNotification:)
                                                 name:@»MPCDemo_DidReceiveDataNotification»
                                               object:nil];
}

Реализация handleReceivedDataWithNotification: может показаться сложной, поэтому давайте handleReceivedDataWithNotification: на handleReceivedDataWithNotification: куски.

01
02
03
04
05
06
07
08
09
10
11
12
— (void)handleReceivedDataWithNotification:(NSNotification *)notification {
    // Get the user info dictionary that was received along with the notification.
    NSDictionary *userInfoDict = [notification userInfo];
     
    // Convert the received data into a NSString object.
    NSData *receivedData = [userInfoDict objectForKey:@»data»];
    NSString *message = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
     
    // Keep the sender’s peerID and get its display name.
    MCPeerID *senderPeerID = [userInfoDict objectForKey:@»peerID»];
    NSString *senderDisplayName = senderPeerID.displayName;
}

Мы получаем словарь userInfo уведомления, извлекаем объект NSData , воссоздаем объект NSString и сохраняем его в переменной с именем message . Следующее действие зависит от значения объекта message . Мы также извлекаем имя партнера, который отправил сообщение, чтобы мы могли использовать его для обновления пользовательского интерфейса игры.

Если значение message равно New Game , новая игра была запущена. Затем мы уведомляем пользователя об этом событии, обновляем значение isGameRunning и обновляем интерфейс пользователя, чтобы игрок мог делать предположения. Взгляните на обновленную реализацию handleReceivedDataWithNotification:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
— (void)handleReceivedDataWithNotification:(NSNotification *)notification {
    // Get the user info dictionary that was received along with the notification.
    NSDictionary *userInfoDict = [notification userInfo];
 
    // Convert the received data into a NSString object.
    NSData *receivedData = [userInfoDict objectForKey:@»data»];
    NSString *message = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
 
    // Keep the sender’s peerID and get its display name.
    MCPeerID *senderPeerID = [userInfoDict objectForKey:@»peerID»];
    NSString *senderDisplayName = senderPeerID.displayName;
 
    if ([message isEqualToString:@»New Game»]) {
        // In case the message is about a new game, then show an alert view telling that the sender of the message
        // has just started a new game.
        NSString *alertMessage = [NSString stringWithFormat:@»%@ has started a new game.», senderDisplayName];
 
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@»MPCDemo»
                                                        message:alertMessage
                                                       delegate:nil
                                              cancelButtonTitle:nil
                                              otherButtonTitles:@»Done», nil];
 
        [alert show];
 
        // Also, indicate that a game is in progress.
        self.isGameRunning = YES;
 
        // Enable all subviews.
        [self toggleSubviewsState:YES];
 
        // Clear all previous history from the text view.
        [self.tvHistory setText:@»»];
    }
}

Интересно отметить две детали. Во-первых, имя игрока, запустившего игру, упоминается в сообщении с предупреждением, чтобы убедиться, что другие игроки знают, кто является хозяином игры. Во-вторых, мы вызываем toggleSubviewsState: для обновления пользовательского интерфейса. Если вы еще раз протестируете приложение, вы заметите, что при запуске новой игры каждому подключенному игроку показывается предупреждение.


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

Перед тем, как отправлять предположение другим игрокам в игре, мы проверяем, является ли предположение игрока действительным числом и находится ли оно в требуемом диапазоне. Если это так, мы преобразуем содержимое текстового поля txtGuess в объект NSData и отправляем его подключенным узлам. На устройстве игрока, который сделал предположение, мы добавляем предположение к содержимому текстового представления.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
— (IBAction)sendGuess:(id)sender {
    // Check if a number has been entered or not, and if it’s valid.
    if (self.txtGuess.text.length == 0 || [self.txtGuess.text intValue] < 1 || [self.txtGuess.text intValue] > 100) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@»MPCDemo»
                                                        message:@»Please enter a valid number.»
                                                       delegate:nil
                                              cancelButtonTitle:nil
                                              otherButtonTitles:@»Okay», nil];
        [alert show];
 
    } else{
        // Convert the guess string to a NSData object and send it to all peers (players).
        NSData *guessAsData = [self.txtGuess.text dataUsingEncoding:NSUTF8StringEncoding];
        NSError *error;
        [self.appDelegate.mpcHandler.session sendData:guessAsData
                                          toPeers:self.appDelegate.mpcHandler.session.connectedPeers
                                         withMode:MCSessionSendDataReliable
                                            error:&error];
 
        // If any error occurs just log its description.
        if (error != nil) {
            NSLog(@»%@», [error localizedDescription]);
        }
 
        // Add to the history text view the number value given by the current player.
        NSString *history = [NSString stringWithFormat:@»I guessed the number: %@\n\n», self.txtGuess.text];
        [self.tvHistory setText:[history stringByAppendingString:self.tvHistory.text]];
    }
 
    self.txtGuess.text = @»»;
    [self.txtGuess resignFirstResponder];
}

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

1
2
3
4
5
6
7
8
9
— (void)handleReceivedDataWithNotification:(NSNotification *)notification {
    // … //
 
    if ([message isEqualToString:@»New Game»]) {
        // … //
    }
 
    // … //
}

Пришло время добавить предложение else для обработки других входящих сообщений. На данный момент мы хотим обработать только предположение, сделанное другим игроком, что означает, что нам нужно проверить, содержит ли сообщение верный номер. Следующий фрагмент кода определяет, содержит ли объект NSString число.

1
2
3
4
5
6
NSCharacterSet *numbersSet = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *messageSet = [NSCharacterSet characterSetWithCharactersInString:message];
 
if ([numbersSet isSupersetOfSet:messageSet]) {
 
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
— (void)handleReceivedDataWithNotification:(NSNotification *)notification {
    // … //
 
    if ([message isEqualToString:@»New Game»]) {
        // … //
 
    } else {
        // Check if the message contains only digits.
        // that means that it contains a guess from the player who sent it.
        NSCharacterSet *numbersSet = [NSCharacterSet decimalDigitCharacterSet];
        NSCharacterSet *messageSet = [NSCharacterSet characterSetWithCharactersInString:message];
 
        if ([numbersSet isSupersetOfSet:messageSet]) {
            // The message contains the guess from another player.
            // Convert it to a number.
            int guess = [message intValue];
 
            // Add this guess to the history text view.
            NSString *history = [NSString stringWithFormat:@»Player %@ guessed the number: %d\n\n», senderDisplayName, guess];
            [self.tvHistory setText:[history stringByAppendingString:self.tvHistory.text]];
 
            // If self is the game creator, then show all available options regarding this guess.
            if (self.hasCreatedGame) {
                NSString *optionsMessage = [NSString stringWithFormat:@»%@\n\nThe secret number is %d.\n\nWhat’s your answer?», history, self.secretNumber];
                UIAlertView *optionsAlert = [[UIAlertView alloc] initWithTitle:@»MPCDemo»
                                                                       message:optionsMessage
                                                                      delegate:self
                                                             cancelButtonTitle:nil
                                                             otherButtonTitles:@»Correct Guess!», @»Give a greater number», @»Give a lower number», nil];
                [optionsAlert show];
            }
        }
    }
}

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


Чтобы обработать ответ хоста, нам нужно обновить alertView:clickedButtonAtIndex: метод протокола UIAlertViewDelegate , как показано ниже. Все, что мы делаем, это отправляем заголовок кнопки в виде сообщения другим игрокам в игре.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
— (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput && buttonIndex == 1) {
        // … //
 
    } else {
        // Get the tapped button’s title as the answer, convert it to a NSData object and send it to other players.
        NSString *selectedAnswer = [alertView buttonTitleAtIndex:buttonIndex];
        NSData *answerAsData = [selectedAnswer dataUsingEncoding:NSUTF8StringEncoding];
        NSError *error;
        [self.appDelegate.mpcHandler.session sendData:answerAsData
                                              toPeers:self.appDelegate.mpcHandler.session.connectedPeers
                                             withMode:MCSessionSendDataReliable
                                                error:&error];
 
        if (error != nil) {
            NSLog(@»%@», [error localizedDescription]);
        }
 
        // In case of correct guess then turn the flags to off.
        if (buttonIndex == 0) {
            self.hasCreatedGame = NO;
            self.isGameRunning = NO;
        }
    }
}

Когда противник делает правильное предположение, мы заканчиваем игру, устанавливая hasCreatedGame и isGameRunning в NO . На следующем этапе мы обработаем сообщение, отправленное другим игрокам в игре.

Я уверен, что вы начинаете понимать, как разные части игры сочетаются друг с другом. Это немного похоже на игру в теннис, в которой игроки бьют друг друга по мячу, пока один не уронит мяч. Чтобы завершить игру, нам нужно вернуться к handleReceivedDataWithNotification: еще раз, чтобы обработать ответ, отправленный хостом игры после того, как игрок сделал правильное предположение.

Мы начнем с добавления предложения else ко второму оператору if как показано ниже. Все, что мы делаем, это добавляем сообщение, заголовок кнопки, которую нажал хост, к содержимому текстового представления. Если сообщение равно Correct Guess , мы заканчиваем игру и отключаем управление игрой, вызывая toggleSubviewsState: и передавая NO .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
— (void)handleReceivedDataWithNotification:(NSNotification *)notification {
    // … //
 
    } else {
        // Check if the message contains only digits.
        // that means that it contains a guess from the player who sent it.
        NSCharacterSet *numbersSet = [NSCharacterSet decimalDigitCharacterSet];
        NSCharacterSet *messageSet = [NSCharacterSet characterSetWithCharactersInString:message];
 
        if ([numbersSet isSupersetOfSet:messageSet]) {
            // … //
 
        } else {
            // If the message doesn’t contain digits, then it contains the answer from the player who started the game.
            // For starters, just show answer to the history text view.
            NSString *history = [NSString stringWithFormat:@»%@ says:\n%@\n\n», senderDisplayName, message];
            [self.tvHistory setText:[history stringByAppendingString:self.tvHistory.text]];
 
            // Check if the game creator answered that the last guess was the correct one.
            // should stop.
            if ([message isEqualToString:@»Correct Guess!»]) {
                self.isGameRunning = NO;
 
                [self toggleSubviewsState:NO];
            }
        }
    }
}

Приложение почти закончено. Нам осталось только реализовать действие cancelGuessing: . В этом методе мы скрываем клавиатуру, вызывая resignFirstResponder в текстовом поле txtGuess .

1
2
3
— (IBAction)cancelGuessing:(id)sender {
    [self.txtGuess resignFirstResponder];
}

Наша простая игра готова к игре. Имейте в виду, что реализация игры проста, так как основное внимание в этом уроке было уделено изучению платформы Multipeer Connectivity, которая была представлена ​​в iOS 7. На следующем снимке экрана должно быть дано представление о различных состояниях, в которых может находиться игра.


Надеюсь, я убедил вас, что платформа Multipeer Connectivity является отличным новым дополнением к iOS SDK. Из этого туториала вы узнали, что отправка данных от однорангового узла очень проста с платформой Multipeer Connectivity. Фреймворк может предложить гораздо больше, поэтому я советую вам изучить документацию Apple для более глубокого понимания ее возможностей.