疯狂ios讲义之游戏的状态数据模型

博文视点 发布于 2014/02/13 09:27
阅读 264
收藏 1
iOS

对游戏玩家而言,在游戏界面上看到的“元素”千差万别、变化多端,但对游戏开发者而言,游戏界面上的元素在底层都是一些数据,不同的数据所绘制的图片有所不同。因此,建立游戏的状态数据模型是实现游戏逻辑的重要步骤。


†† 13.3.1  定义数据模型


《疯狂连连看》的游戏界面是一个n×m的“网格”,每个网格上显示一张图片,对游戏开发者来说,这个网格只需要用一个二维数据来定义,而每个网格上所显示的图片对底层的数据模型来说,不同的图片对应于不同的数值即可。图13.6显示了数据模型的示意。



13.6  《疯狂连连看》的数据模型

对于图13.6所示的数据模型,只要让数值为0的网格上不绘制图片,其他数值的网格则绘制相应的图片,就可显示出《疯狂连连看》的游戏界面。


本程序实际上并不是直接使用int[][]数组来保存游戏的状态数据,而是采用NSArray来保存游戏的状态模型,而NSArray的元素又是NSArray,相当于创建二维数组,数组元素是FKPiece。因为FKPiece对象封装的信息更多,不仅包含了该方块左上角的XY坐标,还包含了该FKPiece所显示的图片、图片ID(这个图片ID可作为该FKPiece的数据)。


†† 13.3.2  初始化游戏状态数据


为了初始化游戏状态,程序需要创建一个NSArray数组,为此,程序定义一个FKBaseBoard基类,该基类的代码如下。


程序清单:codes/13/Link/Link/sources/board/FKBaseBoard.h


@interface FKBaseBoard : NSObject
- (NSArray*) createPieces:(NSArray*)pieces;
- (NSArray*) create;
@end


程序清单:codes/13/Link/Link/sources/board/FKBaseBoard.m

@implementation FKBaseBoard
// 定义一个方法, 让子类去实现
- (NSArray*) createPieces:(NSArray*)pieces
{
	return nil;
}
- (NSArray*) create
{
	// 创建FKPiece的二维数组
	NSMutableArray* pieces = [[NSMutableArray alloc] init];
	for(int i = 0 ; i < xSize ; i++)
	{
		NSMutableArray* arr = [[NSMutableArray alloc] init];
		for (int j = 0 ; j < ySize ; j++)
		{
			[arr addObject:[NSObject new]];
		}
		[pieces addObject:arr];
	}
	// 返回非空的FKPiece集合,该集合由子类实现的方法负责创建
	NSArray* notNullPieces = [self createPieces:pieces];  //①
	// 根据非空FKPiece对象的集合的大小来取图片
	NSArray* playImages = getPlayImages(notNullPieces.count);
	// 所有图片的宽、高都是相同的,随便取出一个方块的宽、高即可
	int imageWidth = [[playImages objectAtIndex:0] image].size.width;
	int imageHeight = [[playImages objectAtIndex:0] image].size.height;
	for (int i = 0; i < notNullPieces.count; i++)  // 遍历非空的FKPiece集合
	{
		// 依次获取每个FKPiece对象
		FKPiece* piece = [notNullPieces objectAtIndex:i];
		piece.image = [playImages objectAtIndex:i];
		// 计算每个方块左上角的X、Y坐标
		piece.beginX = piece.indexX * imageWidth + beginImageX;
		piece.beginY = piece.indexY * imageHeight + beginImageY;
		// 将该方块对象放入方块数组的相应位置处
		[[pieces objectAtIndex:piece.indexX]
			setObject:piece atIndex:piece.indexY]; 
	}
	return pieces;
}
@end

上面的粗体字代码块用于初始化NSArray代表的二维数组,为有方块的FKPiece元素的beginXbeginYimage属性赋值,beginXbeginY根据该方块在二维数组中的位置动态计算得到。


 提示:

上面程序中用到了 xSize ySize 等常量,这两个常量用于代表该游戏在横向和纵向上各包含多少个方块。程序专门定义了一个 Constants.h 文件来记录这些常量的值,后面有些类也用到了该头文件中的常量,请读者参考本书配套光盘中的 Constants.h 文件。

程序中的①号代码调用了createPieces:方法来创建一个NSArray集合,该方法将会交给其子类去实现,这里是典型的“模板模式”的应用。FKBaseBoard基类完全可以根据FKPiece对象在二维数组中的位置动态地计算它的beginXbeginY,但FKBaseBoard不确定NSArray代表二维数组的哪些元素是有方块的。

由于《疯狂连连看》游戏的初始状态可能有很多种,比如,横向分布的方块、竖向分布的方块、矩阵排列的方块、随机分布的方块等,该程序为了考虑以后的扩展性,此处只采用了模板模式:FKBaseBoard基类负责完成通用的代码,对于暂时无法确定的需要子类实现的部分可定义成createPieces:方法。

提示:

由于Objective-C没有提供抽象类和抽象方法的概念,因此,此处将createPieces:方法定义成返回nil的空方法。


上面的程序中还用到了一个ImageUtil工具类,它的作用是自动搜寻项目包的图片,并根据需要随机读取该目录下的图片。后面会详细介绍该工具类的用法。


下面为该FKBaseBoard实现三个子类。

1.矩阵排列的方块


矩阵排列的方块会填满二维数组的每个数组元素,只是把四周留空即可,该子类的代码实现如下(接口部分几乎没有代码,因此此处不再给出)。

程序清单:/Users/yeeku/publish/codes/13/Link/Link/sources/board/FKFullBoard.m


@implementation FKFullBoard
- (NSArray*) createPieces: (NSArray*) pieces
{
	// 创建一个NSMutableArray,该集合中存放初始化游戏时所需的FKPiece对象
	NSMutableArray* notNullPieces = [[NSMutableArray alloc] init];
	// i从1开始,小于pieces.count - 1,用于控制最上、最下一行不放方块
	for (int i = 1; i < pieces.count - 1; i++)
	{
		// i从1开始,小于pieces.count - 1,用于控制最左、最右一列不放方块
		for (int j = 1; j < [[pieces objectAtIndex:i] count] - 1; j++)
		{
			// 先构造一个FKPiece对象,只设置它在FKPiece二维数组中的索引值,
			// 所需要的FKPieceImage由其父类负责设置
			FKPiece* piece = [[FKPiece alloc] initWithIndexX:i indexY:j];
			[notNullPieces addObject:piece];  // 添加到FKPiece集合中
		}
	}
	return notNullPieces;
}
@end

该子类初始化的游戏界面如图13.7所示。

13.7  矩阵排列的方块

2.竖向排列的方块

竖向排列的方块以垂直的空列分隔开,该子类的代码实现如下(接口部分几乎没有代码,因此此处不再给出)。

程序清单:codes/13/Link/Link/sources/board/FKVerticalBoard.m


@implementation FKVerticalBoard
- (NSArray*) createPieces:(NSArray*) pieces
{
	// 创建一个NSMutableArray集合, 该集合中存放初始化游戏时所需的FKPiece对象
	NSMutableArray* notNullPieces = [[NSMutableArray alloc] init];
	for (int i = 0; i < pieces.count; i++)
	{
		for (int j = 0; j < [[pieces objectAtIndex:i] count]; j++)
		{
			// 加入判断,符合一定条件才构造FKPiece对象,并加到集合中
			// 如果i能被2整除,即单数列不会创建方块
			if (i % 2 == 0)
			{
				// 先构造一个FKPiece对象,只设置它在FKPiece二维数组中的索引值,
				// 所需要的FKPieceImage由其父类负责设置
				FKPiece* piece = [[FKPiece alloc] initWithIndexX:i indexY:j];
				// 添加到FKPiece集合中
				[notNullPieces addObject:piece];
			}
		}
	}
	return notNullPieces;
}
@end

程序中的粗体字代码控制了只设置i % 2 == 0的列,也就是只设置索引为偶数的列,该子类初始化的游戏界面如图13.8所示。

13.8  竖向排列的方块

3.横向排列的方块

横向排列的方块以水平的空行分隔开,该子类的代码如下。

程序清单:codes/13/Link/Link/sources/board/FKHorizontalBoard.m


@implementation FKHorizontalBoard
- (NSArray*) createPieces:(NSArray*) pieces
{
	// 创建一个NSMutableArray集合, 该集合中存放初始化游戏时所需的FKPiece对象
	NSMutableArray* notNullPieces = [[NSMutableArray alloc] init];
	for (int i = 0; i < pieces.count; i++)
	{
		for (int j = 0; j < [[pieces objectAtIndex:i] count]; j++)
		{
			// 加入判断,符合一定条件才构造FKPiece对象,并加到集合中
			// 如果j能被2整除,即单数行不会创建方块
			if (j % 2 == 0)
			{
				// 先构造一个FKPiece对象,只设置它在FKPiece二维数组中的索引值,
				// 所需要的FKPieceImage由其父类负责设置
				FKPiece* piece = [[FKPiece alloc] initWithIndexX:i indexY:j];
				// 添加到FKPiece集合中
				[notNullPieces addObject:piece];
			}
		}
	}
	return notNullPieces;
}
@end

程序中的粗体字代码控制了只设置j % 2 == 0的行,也就是只设置索引为偶数的行,该子类初始化的游戏界面如图13.9所示。


13.9  横向排列的方块


——————本文节选自《疯狂ios讲义(上)》




加载中
返回顶部
顶部