实现 iOS 上的井字游戏

jopen 9年前

实现 iOS 上的井字游戏

实现 iOS 上的井字游戏

简介

本文介绍如何用 MVC 模式在 iOS 上实现双人对战的井字游戏. 读者最好有一些 iOS 编程基础, 以便更好的理解本文的代码. 希望这篇文章对提高读者的 iOS 和 MVC 编程水平有所帮助.

背景

井字游戏

关于游戏的规则我就不多说了, 相信大家都知道怎么玩. (如果不清楚游戏规则, 请点 这里). 下面要介绍的是人和人对战的井字游戏, 至于人机对战不属于本文讨论的范围.

MVC 模式

MVC 是 iOS 开发中常用的一种设计模式. MVC 把应用程序中的对象分成三类:

  • Model: 只负责程序中的数据部分, 与用户界面无关.

  • View: 负责程序界面的显示和用户交互部分.

  • Controller: 是连接 Model 和 View 的一条纽带. 比如, 它把用户输入的数据传给 Model, 或者把 Model 的数据传给 View. 

我们的游戏将使用 MVC 模式来实现. 下面的图片是 MVC 各部分之间的关系:

 

实现 iOS 上的井字游戏

MVC 的更多内容请点 这里.

实现

Model

我们已经讲过, Model 对象与用户界面无关.  游戏项目中, 有个类 (TTTicTacToe.m), 跟用户界面没有半毛钱关系, 纯粹是用来实现游戏逻辑, 保存游戏状态的. 比如:  棋盘 - 一个简单的 char 二维数组, 保存玩家每一步走法的函数, 判断有没有玩家胜出的函数等等. 而 View 则通过 UIButton 把游戏的状态显示出来.

数组跟按钮之间的映射

我们已经讲过, 游戏的状态保存在 Model 类的一个二维数组中. 而棋盘则用 UIButton 对象实现. 当 View 要在用户界面显示游戏状态的时候, 需要找个方法, 把二维数组跟 UIButton 对象关联起来. 由于 UIButton 对象通过 tag (也就是 ID) 访问, 我们从 1 到 16, 给它编个号. 然后再想办法跟数组关联起来就可以了.  Model 的二维数组看起来像酱紫:

实现 iOS 上的井字游戏

通过这个公式: buttonID = i*4 + j + 1, 就能把数组元素[i,j] 跟 UIButton 对象的 ID 关联起来. 比如: 数组元素[2,3]对应的是 ID(也叫tag) 为 12 的 UIButton 对象.

View Controller

下面, 我们讲下这个实现游戏逻辑的函数(包括用户界面). 当用户按下某个按钮(UIButton 对象)时, 该函数就会被调用(我们把按钮的坐标当作参数传给它). 代码已经写了详细的注释, 我再简单说下. View 调用 Model 对象(self.game 是 TTTicTacToe.m 类的对象)保存玩家下棋的数据的.   然后调用 redrawTable 函数, 把 Model 中的游戏状态显示出来(这个函数后面再讲). 最后调用 self.game checkForWin 函数判断有没有玩家获胜.

-(void) userMoveX:(int)x andY:(int)y  {      BoardCoord pos;      pos.x = x;      pos.y = y;            //由 x 和 y 坐标推算出按钮的 ID.      int buttonID = 4 * y + x + 1;            // 玩家最多走 16 步棋.      if(self.counter > 15)          return;            // 保存玩家下棋的状态.      [self.game updateBoardAtPos:pos withPlayer:((self.counter) % 2)];            // 更新用户界面      [self redrawTable];            // 判断有没有玩家胜出?      if([self.game checkForWin:pos])      {          if((self.counter % 2) == 0)          {              NSLog(@"Win X");              self.status.text = @"Player X wins!";          }          else          {              self.status.text = @"Player O wins!";              NSLog(@"Win O");          }                    // 如果其中一个玩家获胜, 游戏结束          // 禁用所有按钮(Disabled)          for(int i=1; i<=16; i++)          {              //通过 tag 获取按钮对象              UIButton *b = (UIButton*)[self.view viewWithTag:i];              // 禁用按钮              b.enabled = NO;          }              }            // 玩家当前点到的按钮也要禁用      UIButton *b = (UIButton*)[self.view viewWithTag:buttonID];      b.enabled = NO;        // counter 增 1      self.counter++;  }

注意: (self.counter) % 2 这行代码中, self.counter 表示玩家总共已经下了几步棋. 对 2 求余, 结果是 0 表示玩家 X, 1 表示玩家 O . 

最后就剩下这个用按钮显示棋盘的函数了:

-(void) redrawTable  {      for(int i=0; i < SIZE; i++)          for(int j=0; j < SIZE; j++)          {              // 获取按钮的 handle              UIButton*b = (UIButton*) [self.view viewWithTag:(i*4+j+1)];                            UIImage *btnImage;              if([self.game objectAtX:j andY:i] == 'X')              {                   // 设为 X 图标                   btnImage = [UIImage imageNamed:@"xIcon.png"];              }else              {                  if([self.game objectAtX:j andY:i] == 'O')                  {                      // 设为 O 图标                      btnImage = [UIImage imageNamed:@"oIcon.png"];                  }              }                        // 在按钮上面显示图标              // 如果 btnImage 为 nil, 则不用显示              [b setImage:btnImage forState:UIControlStateNormal];          }   }

历史

  •  Version 1.00 - Initial release