TAC文件现在怎么fm2015tac导入不出来到游戏里

开始建立游戏
本文所属图书&>&
作为一名初出茅庐的iOS开发人员,如何着手编写iPhone和iPad游戏呢?您可以借助 iOS游戏开发入门经典 来奠定良好基础。无论您是否拥有iOS编程经验,都可以借助本书来了解开始创建有趣iOS游戏所需的技术。本书首...&&
在此之前,本书建立的所有游戏都只是单人游戏,您的对手还仅限于计算机而已。而在本章中,您将建立一个双人游戏,您可以和另一个真人玩家进行对战。这就是井字棋游戏(Tic-Tac-Toe)。在现实生活中,这并不是一个令人兴奋的游戏。但众所周知,在讨论对等网络这一令人兴奋的主题时,这款游戏往往会被作为起点。
在本章的第一部分,您将首先以&传递互动&的方式建立游戏。也就是说,下了一步棋之后,把设备传递给对手让对方下棋。游戏运行起来后,您将学习有关GameKit框架的特性,为在游戏中添加局域网络游戏功能做一些准备。掌握了基本原理后,您将在井字棋游戏中添加网络对战功能,从而实现两个玩家使用各自的设备彼此对战。
与本书的其他大多数示例不同,本章要求您准备一台支持iOS的设备,例如iOS支持iOS的iPhone、iPod Touch或者iPad。究其原因,是因为现在无法在同一时间同时运行两个iOS模拟器。而您需要能够同时运行网络游戏的两个实例,以了解其工作原理。此外,要运行这个网络游戏的示例,您还需要成为iOS开发者计划的付费成员,因为如果不是开发者计划的付费成员,则无法将程序安装到设备上。
不过,无论是否满足上述条件,都不会影响您现在开始创建&传递互动&方式的井字棋游戏。
&试一试&&&&&& 开始井字棋游戏
可从代码文件TicTac
在本节中,将建立一个简单版本的井字棋游戏。您将以&传递互动&的方式建立游戏,即每个玩家下了一步棋之后,把设备传递给对手让对方下棋。在本章的后续内容中,您将学习如何实现对等网络来提升游戏,使两个玩家使用各自的设备彼此对战。
您可能还从未玩过井字棋游戏,尽管这种可能性很小,这是一个在井字形格子上玩的游戏,如图10-1所示。每个玩家各有一种形状的棋子,叉叉(X)或者圈圈(O)。每个玩家轮流在格子上的空白位置摆上自己的棋子。最先以横、直或者斜线将形状连成一线的玩家获胜。在图10-1的游戏中,叉叉(X)和圈圈(O)玩家每人各走了一步。
(1) 首先打开Xcode,选择File | New | New Project。
(2) 熟悉的对话框再次出现,其中显示用于创建各种iPhone和Mac OS X应用程序的模板。在对话框中选择Single View Application,然后单击Next按钮。
(3) 在Product Name文本框中输入项目名称TicTac。从Device Family下拉框中选择iPhone。确认没有选中Use Storyboard和Include Unit Tests复选框。同时确认选中了Use Automatic Reference Counting复选框。单击Next按钮。
(4) 选择保存项目的位置,然后单击Create按钮。Xcode将创建项目,并呈现项目窗口。
(5) 接下来,需要将图像文件导入到项目中。您可以从本书的Web站点这些图形文件。
(6) 从菜单栏选择File | New | New Group,新建一个File Group来保存图像。将新建的分组命名为Images。
(7) 从菜单栏选择File | Add files to TicTac,将图像文件(Background.png、o.png和x.png)添加到Images文件夹。您可以从本书的Web站点下载此项目使用的图像文件。确认选中Copy items into destination group's folder (if needed)复选框。单击Add按钮将图像文件添加到项目中。
在本程序中,您将实现MVC设计模式。建立一个名为GameState的类用于保存游戏状态,这个类用作为模型(Model)。使用Interface Builder建立视图(View)。最后,使用来自项目模板默认创建的ViewController来实现控制器(Controller)。
第一件事情您需要做的是使用Interface Builder建立如图10-1所示的视图。视图包括一个带渐变效果的井字形格子的Image View作为背景;9个按钮,玩家可以点击这些按钮来摆放他们的棋子;和一个标签,用于显示游戏状态。
(8) 打开ViewController.xib文件。如果没有显示实用程序(Utilities)窗格,可单击View工具栏中最右侧的图标。在窗格下方,切换到对象库(Object Library),可以看到能够在视图中摆放的对象。从对象库中拖曳一个UIImageView对象并将其放在视图上。调整其位置使其覆盖整个视图。在实用程序(Utilities)窗格顶部,选择Attributes inspector(属性检查器)。单击Image View选区中的Image下拉列表,选择Background.png。这样,便将图像视图中的图像设置为Background.png了。
(9) 现在准备添加按钮。从对象库中拖曳一个UIButton到视图中。调整其位置及大小使其尽可能地占满井字格中的第一个方块。从Attributes inspector(属性检查器)中的Type下拉列表中选择Custom。然后,在Image下拉列表中选择x.png。这样,您便可以看到这些按钮并在视图上调整它们的布局了。单击View选区中的Mode下拉列表,选择Aspect Fill。
(10) 切换到Size inspector(大小检查器)将按钮的宽度和高度都设置为80。请检查Interface Builder的对象树,确定在视图层次结构中,是将按钮作为主视图的子视图摆放的。
(11) 现在,单击按钮并按下CMD-C复制它。因为您还需要另外8个按钮,而最简单的方法就是使用复制和粘贴。复制按钮后,按下CMD-V粘贴它,并且将其移动到第一个按钮的右侧。第三个按钮同样如此操作。重复上述操作,再做三个按钮放在第一行三个按钮的下方,继续再做最后三个按钮放在第二行三个按钮的下方。至此,您的井字格应该已经被叉叉(X)按钮填满了。
(12) 接下来,将按照图10-2中的标识,为每一个按钮基于其在井字格中所在的位置设置它们的Tag属性。在Attributes inspector(属性检查器)的View选区中,在Mode下拉列表的下方可以看到Tag字段。每个按钮的Tag属性现在应该都是0。选中图10-2中位置1对应的按钮并将其Tag属性设置为1。按照图10-2中标识的值依次设置对应位置按钮的Tag属性。
(13) 从对象库中将一个UILabel拖曳到视图中。将其放在靠近视图顶部的位置,调整大小使其宽度与视图宽度相近。使用Attributes inspector(属性检查器)将标签的文本对齐方式设置为居中对齐。
(14) 打开助理编辑器,这样可以同时看到ViewController的头文件和界面。在按住Control键的同时将标签拖入头文件,并放在@interface代码块中,为UILabel在头文件中创建一个Outlet。在弹出的窗口中,将UILabel的名称设置为statusLabel,然后单击Connect按钮。
(15) 接下来,为每个按钮创建操作。在按住Control键的同时将第一个按钮拖曳到ViewController头文件,创建一个名为spaceButtonTapped的操作。然后,在按住Control键的同时将其他剩余按钮拖曳连接到新建的spaceButtonTapped方法上。当鼠标拖曳到spaceButtonTapped方法上方时,会看到方法签名周围出现一个蓝色的框,同时头文件中会弹出提示文字Connect Action。现在,玩家点击其中任何一个按钮,该方法都将运行了。
(16) 要验证所有按钮是否都已正确连接到spaceButtonTapped操作方法,需要在space- ButtonTapped方法中添加一条NSLog语句来记录玩家点击的按钮。打开ViewController.m实现文件并找到- (IBAction)spaceButtonTapped:(id)sender方法。在方法中添加以下代码:
NSLog(@&Player tapped: %i&,[sender tag]);
(17) 生成并运行应用程序。依次单击每一个按钮确认输出窗口中记录输出的Tag值正确无误。您的输出结果应该如下所示:
02:05:42.691 TicTac[3] Player tapped: 0
02:05:43.377 TicTac[3] Player tapped: 1
02:05:44.931 TicTac[3] Player tapped: 2
02:05:45.815 TicTac[3] Player tapped: 3
02:05:46.277 TicTac[3] Player tapped: 4
02:05:46.907 TicTac[3] Player tapped: 5
02:05:47.657 TicTac[3] Player tapped: 6
02:05:48.119 TicTac[3] Player tapped: 7
02:05:48.645 TicTac[3] Player tapped: 8
接下来,要为所有按钮创建Outlet,因为需要根据不同玩家放置棋子的位置,修改对应位置的按钮图像。由于将以同样方式处理所有按钮,您将创建一个Outlet集合。
(18) 在按住Control键的同时将第一个按钮拖曳到ViewController头文件。在弹出窗口的Connection下拉列表中选择Outlet Collection。将Outlet Collection的名称设置为spaceButton,然后单击Connect按钮。然后,在按住Control键的同时将其他剩余按钮拖曳到spaceButton Outlet集合属性上,以便将这些按钮连接到同一个Outlet集合。当鼠标拖曳到spaceButton属性上方时,会看到属性周围出现一个蓝色的框,同时头文件中会弹出提示文字Connect Outlet Collection。现在,所有按钮都已经是Outlet集合的一部分了。
(19) 清空Attributes inspector(属性检查器)中的Image字段,删除每个按钮的图像。因为在游戏启动时,您不会希望所有按钮都被预先填充上叉叉(X)形状。
(20) 现在已经准备好开始编写游戏代码了。首先建立模型类。从菜单栏选择File | New | New File&。在模板选择对话框中,在左侧iOS部分选择Cocoa Touch。在右侧窗格中选择Objective-C类,然后单击Next按钮。将类命名为GameState。在Subclass of下拉列表中确认选择的是NSObject,然后单击Next按钮。在接下来的对话框中单击Create按钮。在项目导航区域可以看到两个新建的文件:GameState.h和GameState.m。
(21) 在GameState.h头文件中,在@interface模块之外添加一个枚举表示玩家的回合:
typedef enum {
&&& TTxPlayerTurn = 1,&&&&& // The x player's turn
&&& TToPlayerTurn = 2&&&&&& // The o player's turn
} TTPlayerT
(22) 在@interface代码块中添加两个成员变量来记录玩家的回合和棋盘状态,如下所示:
TTPlayerTurn playersT
NSMutableArray* boardS
(23) 在@interface代码块下方添加两个属性来公开刚才建立的成员变量:
@property TTPlayerTurn playersT
@property (strong, nonatomic) NSMutableArray* boardS
完成后的头文件应该如下所示:
#import &Foundation/Foundation.h&
typedefenum {
&&& TTxPlayerTurn = 1,&&&&& // The x player's turn
&&& TToPlayerTurn = 2&&&&&& // The o player's turn
} TTPlayerT
@interface GameState : NSObject
&&& TTPlayerTurn playersT
&&& NSMutableArray* boardS
@property TTPlayerTurn playersT
@property (strong, nonatomic) NSMutableArray* boardS
(24) 现在切换到GameState.m实现文件。在@implementation指令行下方合成属性:
@synthesize playersTurn,boardS
(25) 接下来添加init方法:
-(id) init
&&& self = [super init];
&&& if (self) {
&&&&&&& // Alloc and init the board state
&&&&&&& boardState = [[NSMutableArray alloc] initWithCapacity:9];
&&&&&&& playersTurn=TTxPlayerT
(26) 切换到ViewController.h头文件。添加一条#import语句来导入GameState.h头文件:
#import &GameState.h&
(27) 在@interface代码块之前创建另一个枚举,用于表示游戏状态:
typedef enum {
&&& TTGameNotOver = 0,&&&&&&&&&&&&& // The game is not over
&&& TTGameOverxWins = 1,&&&&&&&&&&& // The x player won
&&& TTGameOveroWins = 2,&&&&&&&&&&& // The o player won
&&& TTGameOverTie = 3,&&&&&&&&&&&&& // The game is a tie
} TTGameOverS
(28) 在@interface代码块中添加以下成员变量:
&&& UIImage* xI
&&& UIImage* oI
&&& // The game state
&&& GameState* theGameS
(29) 在@interface代码块闭括号的下方,为新建的成员变量添加属性:
@property (strong, nonatomic) UIImage* xI
@property (strong, nonatomic) UIImage* oI
@property (strong, nonatomic) GameState* theGameS
(30) 在属性下方添加以下方法声明:
- (void) initG
- (void) updateB
- (void) updateGameS
- (TTGameOverStatus) checkGameO
- (BOOL) didPlayerWin: (NSString*)
- (void) endGameWithResult:(TTGameOverStatus)
完成后的头文件应该如下所示:
#import &UIKit/UIKit.h&
#import &GameState.h&
typedefenum {
&&& TTGameNotOver = 0,&&&&&&&&&&&&& // The game is not over
&&& TTGameOverxWins = 1,&&&&&&&&&&& // The x player won
&&& TTGameOveroWins = 2,&&&&&&&&&&& // The o player won
&&& TTGameOverTie = 3,&&&&&&&&&&&&& // The game is a tie
} TTGameOverS
@interface ViewController : UIViewController
&&& UIImage* xI
&&& UIImage* oI
&&& // The game state
&&& GameState* theGameS
@property (strong, nonatomic) UIImage* xI
@property (strong, nonatomic) UIImage* oI
@property (strong, nonatomic) GameState* theGameS
@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *spaceB
@property (strong, nonatomic) IBOutletUILabel *statusL
- (IBAction)spaceButtonTapped:(id)
- (void) initG
- (void) updateB
- (void) updateGameS
- (TTGameOverStatus) checkGameO
- (BOOL) didPlayerWin: (NSString*)
- (void) endGameWithResult:(TTGameOverStatus)
(31) 切换到ViewController.m实现文件。在顶部的@implementation代码块中合成新建的属性:
@synthesize xImage,oImage,theGameS
(32) 在viewDidLoad方法中,在调用super viewDidLoad的代码行下方添加如下代码:
// Load the images
xImage = [UIImage imageNamed:@&x.png&];
oImage = [UIImage imageNamed:@&o.png&];
&&& // Create the game state
&&& theGameState = [[GameState alloc] init];
(33) 在viewWillAppear方法中,在调用super viewWillAppear的代码行下方添加以下代码:
&&& // Initialize the game
&&& [self initGame];
(34) 添加initGame方法:
- (void) initGame
&&& // Initialize the game
&&& // Set player's turn to the x player because X always goes first
&&& self.theGameState.playersTurn=TTxPlayerT
&&& // Set the status label
&&& self.statusLabel.text = @&X to move&;
&&& // Clear the board state
&&& [self.theGameState.boardState removeAllObjects];
&&& for (int i=0;i&=8;i++)
&&&&&&& // Insert a space to indicate a blank in the grid
&&&&&&& [self.theGameState.boardState insertObject:@& & atIndex:i];
&&& [self updateBoard];
(35) 添加updateBoard方法:
- (void) updateBoard
// Given the state, update the board
for (int i=0;i&=8;i++)
&&&&&&& if ([[self.theGameState.boardState objectAtIndex:i] isEqualToString:@&x&])
&&&&&&&&&&& [[spaceButton objectAtIndex:i] setImage:self.xImage forState:UIControlStateNormal];
&&&&&&& elseif ([[self.theGameState.boardState objectAtIndex:i] isEqualToString:@&o&])
&&&&&&&&&&& [[spaceButton objectAtIndex:i] setImage:self.oImage forState:UIControlStateNormal];
&&&&&&& else
&&&&&&&&&&& [[spaceButton objectAtIndex:i] setImage:nil forState:UIControlStateNormal];
(36) 您需要一个方法来判定玩家是否获胜,因此添加didPlayerWin:方法:
- (BOOL) didPlayerWin: (NSString*) player
// This method determines if the given player has won
&&& if (([[self.theGameState.boardState objectAtIndex:0]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:1]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:2]
isEqualToString:player]) ||
&&&&&&& ([[self.theGameState.boardState objectAtIndex:3]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:4]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:5]
isEqualToString:player]) ||
&&&&&&& ([[self.theGameState.boardState objectAtIndex:6]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:7]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:8]
isEqualToString:player]) ||
&&&&&&& ([[self.theGameState.boardState objectAtIndex:0]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:3]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:6] isEqualToString:player]) ||
&&&&&&& ([[self.theGameState.boardState objectAtIndex:1]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:4]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:7]
isEqualToString:player]) ||
&&&&&&& ([[self.theGameState.boardState objectAtIndex:2]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:5]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:8]
isEqualToString:player]) ||
&&&&&&& ([[self.theGameState.boardState objectAtIndex:0]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:4]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:8]
isEqualToString:player]) ||
&&&&&&& ([[self.theGameState.boardState objectAtIndex:2]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:4]
isEqualToString:player] &&
&&&&&&&& [[self.theGameState.boardState objectAtIndex:6]
isEqualToString:player])
&&&&&&& return YES;
&&&&&&& return NO;
(37) 添加checkGameOver方法,用于判定游戏是否结束:
- (TTGameOverStatus) checkGameOver
&&& // This method checks to see if the game is over
&&& // Did x win?
&&& if ([self didPlayerWin:@&x&])
&&&&&&& return TTGameOverxW
&&& // Did o win?
&&& elseif ([self didPlayerWin:@&o&])
&&&&&&& return TTGameOveroWins&&& }
&&& // No winner. Check to see if there are open spaces left on the board
&&& // because if there are open spaces, the game is not over
&&& for (int i=0; i&=8; i++) {
&&&&&&& if ([[self.theGameState.boardState objectAtIndex:i] isEqualToString:@& &])
&&&&&&&&&&& // Cannot be a tie because there is an open space
&&&&&&&&&&& return TTGameNotO
&&& // No open spaces and no winner, so the game is a tie
&&& return TTGameOverT
(38) 添加updateGameStatus方法,基于游戏状态更新状态标签:
- (void) updateGameStatus
&&& // Check for win or tie
&&& TTGameOverStatus gameOverStatus = [self checkGameOver];
&&& switch (gameOverStatus) {
&&&&&&& case TTGameNotOver:
&&&&&&&&&&& // The game is not over
&&&&&&&&&&& // Next player's turn
&&&&&&&&&&& if (self.theGameState.playersTurn == TTxPlayerTurn)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& // Set the status label
&&&&&&&&&&&&&&& self.statusLabel.text = @&X to move&;
&&&&&&&&&&& }
&&&&&&&&&&& else
&&&&&&&&&&& {
&&&&&&&&&&&&&&& // Set the status label
&&&&&&&&&&&&&&& self.statusLabel.text = @&O to move&;
&&&&&&&&&&& }
&&&&&&&&&&&
&&&&&&&&&&& case TTGameOverxWins:
&&&&&&&&&&& case TTGameOveroWins:
&&&&&&&&&&& case TTGameOverTie:
&&&&&&&&&&& // Game is over
&&&&&&&&&&& [self endGameWithResult:gameOverStatus];
(39) 实现spaceButtonTapped操作方法,在玩家点击的位置放置形状棋子:
- (IBAction)spaceButtonTapped:(id)sender {
&&& int spaceIndex = [sender tag];
&&& // If the space is blank let the player place the shape
&&& if ([[self.theGameState.boardState objectAtIndex:spaceIndex] isEqualToString:@& &])
&&&&&&& // Update game state
&&&&&&& if (self.theGameState.playersTurn == TTxPlayerTurn)
&&&&&&&&&&& [self.theGameState.boardState replaceObjectAtIndex:spaceIndex withObject: @&x&];
&&&&&&&&&&& // It is now o's turn
&&&&&&&&&&& self.theGameState.playersTurn = TToPlayerT
&&&&&&& else
&&&&&&&&&&& [self.theGameState.boardState replaceObjectAtIndex:spaceIndex withObject: @&o&];
&&&&&&&&&&& // It is now x's turn
&&&&&&&&&&& self.theGameState.playersTurn = TTxPlayerT
&&&&&&& // Update the board
&&&&&&& [self updateBoard];
&&&&&&& // Update the game status
&&&&&&& [self updateGameStatus];
(40) 添加endGameWithResult:方法结束游戏:
-(void) endGameWithResult:(TTGameOverStatus) result
&&& NSString* gameOverM
&&& switch (result) {
&&&&&&& case TTGameOverxWins:
&&&&&&&&&&& gameOverMessage = [NSString stringWithString:@&X wins&];
&&&&&&&&&&&
&&&&&&& case TTGameOveroWins:
&&&&&&&&&&& gameOverMessage = [NSString stringWithString:@&O wins&];
&&&&&&&&&&&
&&&&&&& case TTGameOverTie:
&&&&&&&&&&& gameOverMessage = [NSString stringWithString:@&The game is a tie&];
&&&&&&&&&&&
&&&&&&& default:
&&&&&&&&&&&
&&& // Show an alert with the results
&&& UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@&Game Over&
&&&&&&& message:gameOverMessage
&&&&&&& delegate:self
&&&&&&& cancelButtonTitle:@&OK&
&&&&&&& otherButtonTitles: nil];
&&& [alert show];
(41) 添加alertView:clickedButtonAtIndex:UIAlert委托方法,这样可以在玩家确认知晓游戏已经结束后,重新启动游戏:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
&&& // Reset the game
&&& [self initGame];
(42) 生成并运行游戏。您应该可以在井字格的空白区域摆放形状棋子。某一玩家获胜或者和棋时,游戏会显示提示信息。确认提示信息后,游戏将重新开始。找个朋友好好下上两盘吧,玩得开心点啊!
首先使用Interface Builder搭建井字棋游戏界面建立视图。完成之后,在项目中添加了GameState模型类。
在GameState类的头文件中创建一个枚举,其值用于记录玩家的回合:
typedef enum {
&&& TTxPlayerTurn = 1,&&&&& // The x player's turn
&&& TToPlayerTurn = 2&&&&&& // The o player's turn
} TTPlayerT
枚举或enum仅仅是一组命名的整数常量。可使用名称代替数字使得代码更加易读。使用typedef语句允许您基于其他类型定义自己的类型。因此,在本示例中,您定义类型TTPlayerTurn是一个枚举类型,其中TTxPlayerTurn等于1,TToPlayerTurn等于2。
接下来,使用新建的枚举类型添加一个成员变量来记录当前回合的玩家:
TTPlayerTurn playersT
还添加了一个NSMutableArray成员变量用来记录棋盘状态:
NSMutableArray* boardS
最后,添加两个属性公开刚刚建立的成员变量:
@property TTPlayerTurn playersT
@property (strong, nonatomic) NSMutableArray* boardS
完成头文件后,切换到GameState实现文件。init方法是唯一实现的方法。
在init方法中做的第一件事情是调用超类的init版本方法:
self = [super init];
确认从超类的init方法返回一个有效的实例后,分配并初始化一个初始容量为9的boardState数组,因为棋盘共有9个棋格:
boardState = [[NSMutableArray alloc] initWithCapacity:9];
由于在井字棋游戏中,通常由执叉叉(X)形状的玩家先行,因此将playersTurn成员变量设置为TTxPlayerTurn枚举值:
playersTurn = TTxPlayerT
接下来,您需要处理视图控制器了。首先,为GameState.h头文件添加一条#import语句,因为会在视图控制器中用到GameState对象:
#import &GameState.h&
接下来,创建另一个枚举表示游戏状态:
typedef enum {
&&& TTGameNotOver = 0,&&&&&&&&&&&&& // The game is not over
&&& TTGameOverxWins = 1,&&&&&&&&&&& // The x player won
&&& TTGameOveroWins = 2,&&&&&&&&&&& // The o player won
&&& TTGameOverTie = 3,&&&&&&&&&&&&& // The game is a tie
} TTGameOverS
然后,添加成员变量来保存将在棋盘上摆放棋子的图像实例:
UIImage* xI
UIImage* oI
还添加了一个GameState类的实例,来保存游戏状态:
GameState* theGameS
接下来,为类的属性添加属性声明:
@property (strong, nonatomic) UIImage* xI
@property (strong, nonatomic) UIImage* oI
@property (strong, nonatomic) GameState* theGameS
@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *spaceB
@property (strong, nonatomic) IBOutlet UILabel *statusL
最后添加方法声明:
- (IBAction)spaceButtonTapped:(id)
- (void) initG
- (void) updateB
- (void) updateGameS
- (TTGameOverStatus) checkGameO
- (BOOL) didPlayerWin: (NSString*)
- (void) endGameWithResult:(TTGameOverStatus)
完成头文件之后,准备开始实现游戏代码。不过,首先需要在viewDidLoad方法中完成一些初始设置工作。在viewDidLoad方法中加载玩家形状的图像:
xImage = [UIImage imageNamed:@&x.png&];
oImage = [UIImage imageNamed:@&o.png&];
然后,创建一个GameState的实例:
theGameState = [[GameState alloc] init];
完成初始化代码后,移动到viewWillAppear方法为其添加代码。在视图控制器加载完XIB文件中的元素之后,但在视图真正显示在屏幕之前,该方法由框架调用。在viewWill- Appear方法中添加一行代码来调用initGame方法:
[self initGame];
接下来,实现initGame方法,此方法用于初始化游戏。此方法的代码可以放在viewWillAppear中,然而由于我知道会在每次游戏结束之后准备下一轮游戏时调用它们,因此我将它们提取到一个独立的方法中。
在initGame方法中做的第一件事情是将叉叉(X)玩家设置为当前玩家:
// Set player's turn to the x player because X always goes first
self.theGameState.playersTurn=TTxPlayerT
接下来,设置状态标签的文本以反映当前玩家的回合:
// Set the status label
self.statusLabel.text = @&X to move&;
然后,通过在数组中的每一项插入空格清除棋盘状态:
// Clear the board state
[self.theGameState.boardState removeAllObjects];
for (int i=0;i&=8;i++)
&&& // Insert a space to indicate a blank in the grid
&&& [self.theGameState.boardState insertObject:@& &atIndex:i];
最后调用updateBoardmethod方法,在稍后可以看到该方法:
[self updateBoard];
接下来,实现updateBoardmethod方法,此方法基于棋盘状态数组的内容更新棋盘的视图。为此,使用索引值0~8依次遍历数组中的每一项:
// Given the state, update the board
for (int i=0;i&=8;i++)
在循环中,检测当前索引的对象是否是字符串&x&,该字符串用于表示在棋盘上的对应位置显示叉叉(X)形状:
if ([[self.theGameState.boardState objectAtIndex:i] isEqualToString:@&x&])
如果数组中保存的是&x&,将相同索引值对应的按钮的图像设置为叉叉(X)图像。
[[spaceButton objectAtIndex:i] setImage:self.xImageforState:UIControlStateNormal];
然后,添加一个else if块,判定是否需要在当前棋格放置圈圈(O)图像:
elseif ([[self.theGameState.boardState objectAtIndex:i] isEqualToString:@&o&])
&&& [[spaceButton objectAtIndex:i]
setImage:self.oImage forState:UIControlStateNormal];
最后,添加一个else语句处理棋格为空的情况。将按钮的图像设置为nil可清空棋格:
&&& [[spaceButton objectAtIndex:i] setImage:nil forState:UIControlStateNormal];
接下来,建立didPlayerWin:方法用于判定玩家是否获胜。此方法仅是一个大型的if复合语句,检查每一种可能获胜的组合。您使用&&运算符检查连续的棋格,使用||运算符将每组检查连接在一起。player参数的值是字符串&x&或者&o&,其内容取决于您正在检查哪一个玩家的获胜情况。如果||运算符分隔的任意一组条件为真,则方法返回YES,表示该玩家获胜。如果没有条件为真,则方法返回NO。
if语句的第一部分是横排检查。首先检查第一行,棋盘索引为0、1、2:
if (([[self.theGameState.boardState objectAtIndex:0] isEqualToString:player] &&
&&&& [[self.theGameState.boardState objectAtIndex:1] isEqualToString:player] &&
&&&& [[self.theGameState.boardState objectAtIndex:2] isEqualToString:player]) ||
如果索引0、1、2都等于player形状,则此部分条件为true,方法返回YES。接下来,检查其他两行,索引为3、4、5和6、7、8:
([[self.theGameState.boardState objectAtIndex:3] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:4] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:5] isEqualToString:player]) ||
([[self.theGameState.boardState objectAtIndex:6] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:7] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:8] isEqualToString:player]) ||
之后开始竖排检查,第一列索引为:0、3、6:
([[self.theGameState.boardState objectAtIndex:0] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:3] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:6] isEqualToString:player]) ||
检查其他两列,索引为:1、4、7和2、5、8:
([[self.theGameState.boardState objectAtIndex:1] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:4] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:7] isEqualToString:player]) ||
([[self.theGameState.boardState objectAtIndex:2] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:5] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:8] isEqualToString:player]) ||
最后,检查两条对角线,索引为:0、4、8和2、4、6:
([[self.theGameState.boardState objectAtIndex:0] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:4] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:8] isEqualToString:player]) ||
([[self.theGameState.boardState objectAtIndex:2] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:4] isEqualToString:player] &&
&[[self.theGameState.boardState objectAtIndex:6] isEqualToString:player])
如果您对表示棋格的索引值感到困惑,请参见图10-2。
如果任一行、列或对角线检测为true,则指定的玩家获胜返回YES,否则返回NO:
&&&&&&& return YES;
&&&&&&& return NO;
接下来建立的方法是checkGameOver。checkGameOver方法检测游戏是否结束,并返回一个TTGameOverStatus枚举成员,该枚举是在头文件中建立的。如果您还记得,该枚举类型共包含四种可能的游戏状态:叉叉(X)获胜、圈圈(O)获胜、和局和游戏未结束。
首先,使用didPlayerWin:方法检查叉叉(X)是否获胜:
// Did x win?
if ([self didPlayerWin:@&x&])
如果叉叉(X)获胜,则返回TTGameOverxWins枚举值:
&&& return TTGameOverxW
然后,检查圈圈(O)是否获胜,如果是,则返回TTGameOveroWins枚举值:
// Did o win?
elseif ([self didPlayerWin:@&o&])
&&& return TTGameOveroW
如果执行到这两个检测之后的代码,表明没有玩家获胜。这时,您需要判定一下游戏是否需要继续,还是已经出现了和局的情况。唯一一种出现和局的可能就是所有棋格均已被填满。因此,可循环遍历boardState数组中的每个元素,检查是否还有棋格是空的:
// No winner. Check to see if there are open spaces left on the board
// because if there are open spaces, the game is not over
for (int i=0; i&=8; i++) {
&&& if ([[self.theGameState.boardState objectAtIndex:i] isEqualToString:@& &])
如果发现了一个空的棋格,则游戏不可能是和局,因此返回TTGameNotOver表明游戏尚未结束:
&&&&&&& // Cannot be a tie because there is an open space
&&&&&&& return TTGameNotO
最后,如果循环遍历完空棋格后仍然没有返回,说明所有棋格均已被填满,因此游戏是和局:
// No open spaces and no winner, so the game is a tie
return TTGameOverT
接下来,建立updateGameStatus方法,基于游戏状态更新状态标签。如果游戏结束,此方法还将显示游戏结束消息。
首先,检查游戏是否结束:
// Check for win or tie
TTGameOverStatus gameOverStatus = [self checkGameOver];
因为需要根据不同的gameOverStatus结果执行不同的操作,因此使用一个switch语句以执行正确的代码块:
switch (gameOverStatus) {
如果游戏尚未结束,根据下一回合的玩家将状态标签更新为相应的文字:
case TTGameNotOver:
&&& // The game is not over
&&& // Next player's turn
&&& if (self.theGameState.playersTurn == TTxPlayerTurn)
&&&&&&& // Set the status label
&&&&&&& self.statusLabel.text = @&X to move&;
&&&&&&& // Set the status label
&&&&&&& self.statusLabel.text = @&O to move&;
如果游戏结束,则调用endGameWithResult:方法结束游戏并显示游戏结果:
&&& case TTGameOverxWins:
&&& case TTGameOveroWins:
&&& case TTGameOverTie:
&&& // Game is over
&&& [selfendGameWithResult:gameOverStatus];
在此之后,准备开始建立游戏的UI交互元素thespaceButtonTapped操作方法。您曾经在Interface Builder中,将所有按钮的点击操作调用连接到此方法上。
首先,保存玩家点击的按钮索引:
int spaceIndex = [sender tag];
您曾在Interface Builder中设置过按钮的tag值,用来区分不同的按钮。
接下来,编写了一个if语句判断玩家点击的棋格是否为空:
// If the space is blank let the player place the shape
if ([[self.theGameState.boardState objectAtIndex:spaceIndex] isEqualToString:@& &])
此if语句强制玩家仅能在空闲的棋格上摆放他的形状棋子。
确认玩家在棋盘上点击的位置为空后,根据当前回合的玩家更新游戏状态。检查当前是否是叉叉(X)玩家:
// Update game state
if (self.theGameState.playersTurn == TTxPlayerTurn)
如果是叉叉(X)玩家,使用字符串&x&更新boardState数组中与玩家点击位置索引对应的内容:
[self.theGameState.boardState replaceObjectAtIndex:spaceIndex withObject: @&x&];
然后,设置游戏状态的playersTurn属性,表明现在轮到圈圈(O)玩家下棋了:
// It is now o's turn
self.theGameState.playersTurn = TToPlayerT
如果是圈圈(O)玩家,在else语句中做同样的处理:
&&&&&&& [self.theGameState.boardState replaceObjectAtIndex:spaceIndex withObject: @&o&];
&&&&&&& // It is now x's turn
&&&&&&& self.theGameState.playersTurn = TTxPlayerT
在此,使用字符串&o&更新boardState数组,并设置playersTurn属性,表明现在轮到叉叉(X)玩家下棋了。
最后,调用updateBoard方法更新棋盘,并调用updateGameStatus方法来更新游戏状态:
// Update the board
[self updateBoard];
// Update the game status
[self updateGameStatus];
要结束这个游戏,您需要编写endGameWithResult方法。由于需要根据不同的结果参数执行不同的操作,因此使用了一个switch语句:
switch (result) {
根据不同的结果,设置合适的游戏结束消息:
case TTGameOverxWins:
&&& gameOverMessage = [NSString stringWithString:@&X wins&];
case TTGameOveroWins:
&&& gameOverMessage = [NSString stringWithString:@&O wins&];
case TTGameOverTie:
&&& gameOverMessage = [NSString stringWithString:@&The game is a tie&];
然后,创建并配置一个UIAlertView显示游戏结束消息:
// Show an alert with the results
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@&Game Over&
&&& message:gameOverMessage
&&& delegate:self
&&& cancelButtonTitle:@&OK&
&&& otherButtonTitles: nil];
将UIAlertView的委托方法设置为self,这样当玩家确认知晓游戏结束后,框架将调用alertView:clickedButtonAtIndex:方法。
最后,显示消息框:
[alert show];
最后实现UIAlertView的委托方法alertView:clickedButtonAtIndex:。在此方法中,调用initGame方法重新开始游戏:
[self initGame];
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。}

我要回帖

更多关于 fm2015 tac 无法导入 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信