首页 > 代码库 > Airplay 教程: 一个 Apple TV 多人竞答游戏(1)

Airplay 教程: 一个 Apple TV 多人竞答游戏(1)

原文http://twitter.com/share?url=http%3A%2F%2Fbit.ly%2F1iRy7Gq&via=rwenderlich&text=AirplayTutorial%3A An Apple TV Multiplayer QuizGame&related=gpambrozio&lang=en&count=horizontal&counturl=http%3A%2F%2Fwww.raywenderlich.com%2F57161%2Fairplay-tutorial

 

教你如何制作一个多人竞答游戏,iPhone 上和 AppleTV 上显示不同的内容!

从 iOS 5 开始引入了 AirPlay,允许 iOS 设备向AppleTV 上显示内容。这促使了许多游戏的诞生,比如将 AppleTV 当做 iOS 设备的附加显示设备。

GameKit 在 iOS 3(那时 iOS 还叫做 iPhoneOS)时就出现了,它有一个有趣的的特性“p2p连接”。这个特性常用于在多人游戏中充当数据通信的通道。

在本文中,将演示如何使用 AirPlay 和 p2p 连接创建一个通过AppleTV 显示问题和答案的小游戏。每个参与答题者都可以用自己的 iOS 设备回答问题,由最先抢答并答对的玩家得分。游戏界面采用了 Sprite Kit 框架。本文不会过多涉及Sprite Kit 的内容,如果你不太熟悉 Sprite Kit 也没关系。如果你想了解 Sprite Kit,可以参考本站的 Sprite Kit 教程

你也不需要 AppleTV——你甚至可以用模拟器来模拟外接显示器。

开始

此处下载zip文件,解压缩,得到一个本教程使用的初始项目。

先浏览一下项目内容,你会发现除了 UI 和游戏的启动逻辑外,并没有任何关于Airplay 或多玩家逻辑的代码。

现在,就让我们开始 AirPlay 和 GameKit 的学习吧。

创建第2个界面

首先,默认情况下 iOS 设备已经支持将屏幕镜像(mirroring)显示到外接显示器(例如AppleTV),这不需要你写任何代码。从屏幕底部向上拉,点击 AirPlay 按钮,你就可以选择任何一个外接显示器:

但是,在游戏中我们需要在 iOS 设备和外接显示器上显示不同的内容。例如,在这个竞答游戏中,AppleTV显示游戏中要提的问题,而 iOS 设备上显示答题者可选答案。这当然需要编写代码,这就是本文的重点。

但是 AirPlay 没有任何关于向 AppleTV 进行输出的 API。我们能想到的只有一般的外接显示器的API。也就是说,无论是通过 AirPlay 无线连接到 AppleTV,还是用苹果专用的线缆连接到 AppleTV 和显示器,都只能使用这个 API。

因此,如果想在不同的屏幕(例如非 mirroring 的屏幕)显示不同的内容,其实跟外接的显示器类型(无论AppleTV 或者其他)毫无干系,最重要的事情是能否检测到有效的外接显示器。

打开 ATViewController.m,添加如下方法:

 

#pragma mark - AirPlay and extended display   - (void)setupOutputScreen {

   // Register for screen notifications

   NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

   [center addObserver:self selector:@selector(screenDidConnect:) name:UIScreenDidConnectNotification object:nil];

   [center addObserver:self selector:@selector(screenDidDisconnect:) name:UIScreenDidDisconnectNotification object:nil];

   [center addObserver:self selector:@selector(screenModeDidChange:) name:UIScreenModeDidChangeNotification object:nil];

   // Setup screen mirroring for an existing screen

   NSArray *connectedScreens = [UIScreen screens];

   if ([connectedScreens count] > 1) {

     UIScreen *mainScreen = [UIScreen mainScreen];

     for (UIScreen *aScreen in connectedScreens) {

       if (aScreen != mainScreen) {

         // We‘ve found an external screen !

         [self setupMirroringForScreen:aScreen];

         break;

       }

     }

   }

}  

- (void)screenDidConnect:(NSNotification *)aNotification { }  

- (void)screenDidDisconnect:(NSNotification *)aNotification { }  

- (void)screenModeDidChange:(NSNotification *)aNotification { }  

- (void)setupMirroringForScreen:(UIScreen *)anExternalScreen { }  

- (void)disableMirroringOnCurrentScreen { }

setupOutputScreen方法监听 3 种通知:连接外接显示器、断开外接显示器或者外接显示器改变。但是这些通知只能告诉你显示器状态改变——它们不会告诉你是否你已经连着一个外接显示器了。

为了包括已经连接外接显示器的情况,你需要遍历[UIScrennscreens]数组,该数组包括了所有设备上已连接的显示器。只要不是主显示器之外的显示器,我们都可以假设是外接显示器。这样你就可以用 setupMirroringForScreen: 方法来发送一个不同的界面给它。

接下来的任务是实现所有空方法。

在 screenDidConnect: 方法中加入:

  NSLog(@"A new screen got connected: %@", [aNotification object]);

   [self setupMirroringForScreen:[aNotification object]];

notification 的 object 属性包含了新连接的显示器的UIScreen 对象。一旦收到了 notification 对象,简单地打印 Log 并调用 setupMirroringForScreen开启 mirroring。

在 screenDidDisconnect 方法中加入下列代码:

  NSLog(@"A screen got disconnected: %@", [aNotification object]);   [self disableMirroringOnCurrentScreen];

简单打印Log 并关闭 mirroring。disableMirroringOnCurrentScreen仅仅是一个空实现,我们将在后面来实现它。

接下来,在 screenModeDidChange:加入以下代码:

  NSLog(@"A screen mode changed: %@", [aNotification object]);

   [self disableMirroringOnCurrentScreen];

   [self setupMirroringForScreen:[aNotification object]];

这个方法通过先关闭后打开来重置 mirroring,确保新接入的屏幕模式和设置能生效。

在实现 setupMirroringForScreen:方法之前,需要保存某些对象的状态。

ATViewController.m中加入

#import "ATAirPlayScene.h"

找到 import 语句下的这一行:

@property (nonatomic, strong) ATMyScene *scene;

加入以下属性:

@property (nonatomic, strong) UIWindow *mirroredWindow;

@property (nonatomic, strong) UIScreen *mirroredScreen;

@property (nonatomic, strong) SKView *mirroredScreenView;

@property (nonatomic, strong) ATAirPlayScene *mirroredScene;

这些属性分别用于第二显示器的 UIWindow 和 UIScreen,对应的SKView 和 SpriteKit Scene(分别用于显示问题和及选项)。

 

These threeproperties store your secondary UIWindowand UIScreen objects, thecorresponding SKView for thatscreen, and the Sprite Kit scene with the interface that displays the questionand answers to the external display.

在 setupMirroringForScreen:方法中加入:

  self.mirroredScreen = anExternalScreen;

     // Find max resolution

   CGSize max = {0, 0};

   UIScreenMode *maxScreenMode = nil;

     for (UIScreenMode *current in self.mirroredScreen.availableModes) {

     if (maxScreenMode == nil || current.size.height > max.height || current.size.width > max.width) {

       max = current.size;

       maxScreenMode = current;

     }

   }

     self.mirroredScreen.currentMode = maxScreenMode;

在上面的代码中,首先将方法参数的 screen 存到mirroredScreen 属性以便后面使用。接着遍历 screen 的 availableModes ,找出最大的屏幕分辨率,将其用于设置 screen 的currentMode。