您的位置:首页 > 移动开发 > IOS开发

基于IOS平台的游戏之小拼图

2016-06-13 19:10 603 查看
IOS/Xcode工具

一:主要功能

在拼图小游戏开发过程中,实现的主要的功能。

压缩图片:需要给传递过来的任意图片,根据手机模拟器中View大小,重新设置图片的尺寸,即压缩图片运用UIGraphicsBeginImageContext进行实现。

切割图片separateImage:将尺寸设置好的图片,进行切割成3*3 或者4*4,获取整个View的尺寸大小和图片的尺寸大小,进行大小比较,当图片的大小超过容器View的时候,将图片进行大小的缩放。然后根据切割的块,来计算出每个图块的宽高。运用两层For循环,将完整的图片按照每块设置的大小进行切割,并将0-0位置设置为空白位置 。将每块图片View的信息放到数组中。存放的信息有:当前拼图的下标,以及拼图起始没有打乱顺序的下标,用来最后判断游戏是否胜利的标记。

打乱图片顺序:产生两个NSINteger随机数,来作为存放图片View的数组的下标,从而获取图片信息,然后将两张图片进行位置对换,并改变当前拼图的下标。然后用逆序数的奇偶性来判断游戏是否能正常复位(有时候随机打乱的图片,并不能够恢复原来的位置即无解,则就需要运用逆序数的奇偶性进行判断),并判断拼图是否完全的打乱。如果两个条件有一方符合,将再次遍历打乱图片,产生两个随机数…否则游戏开始。

拼图移动:由于每个图块都有在合适的位置进行移动的实现方法,所以如果我们点击的图片,能与空白位进行移动,那么就交换这两张图块。

游戏完成:遍历的从存放每个图块视图的数组中取出每块图片View,并判断每块图片的当前下标,是否与最终特定的下标位置相等,如果每个图块都完全对照,则表示图片拼凑完成,则游戏胜利,弹出对话框。

二:效果图

开始:



拼图开始(点击菜单可以返回):



拼图进阶:



成功:



三:代码

If you are interested, study the code.

拼图视图PuzzleImageView.m

拼图视图PuzzleImageView.h

拼图视图控制器PuzzleViewController.m

拼图视图控制器PuzzleViewController.h

菜单视图控制器MenuViewController.m

菜单视图控制器MenuViewControlle.h

1:拼图视图PuzzleImageView.m

//PuzzleImageView.m

#import <Foundation/Foundation.h>
#import "PuzzleImageView.h"
@implementation PuzzleImageView

- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}

- (id) initWithImage:(UIImage *)image
{
if (self = [super initWithImage:image]) {
[self setUserInteractionEnabled:YES];//交互设置
[self setMultipleTouchEnabled:YES];//多指触控
self.layer.borderWidth = 1;//注意边框为1,来判断拼图是否能移动
}
return self;
}

-(BOOL)canMoveToPoint:(CGPoint)pos
{
//判断拼图是否能够移动,注意Origin的取值。
CGPoint point = self.frame.origin;
CGSize size = self.frame.size;
//可用Log输出日志对其中取值进行查看
//左右移动的拼图
if (abs(abs(point.x - pos.x) - size.width) <1 && (point.y == pos.y))
{
return YES;
}
//上下移动拼图
else if(abs(abs(point.y - pos.y) - size.height) <1 && (point.x == pos.x))
{
return YES;
}
else
{
return NO;
}
}

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//代理
if(self.delegate && [self.delegate respondsToSelector:@selector(puzzleImageViewShouldMove:)])
{
[self.delegate puzzleImageViewShouldMove:self];
}
}
@end


2: 拼图视图PuzzleImageView.h

//PuzzleImageView.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@class PuzzleImageView;

@protocol PuzzleDelegate <NSObject>

@optional
-(void) puzzleImageViewShouldMove:(PuzzleImageView *)imageview;

@end

@interface PuzzleImageView : UIImageView

@property (assign, nonatomic) id <PuzzleDelegate> delegate;

@property (assign, nonatomic) NSInteger resultIndex;
@property (assign, nonatomic) NSInteger nowIndex;

-(BOOL)canMoveToPoint:(CGPoint)pos;
@end


3:拼图视图控制器PuzzleViewController.m

//PuzzleViewController.m

#import "PuzzleViewController.h"
#import <Foundation/Foundation.h>
@interface PuzzleViewController ()

@end

@implementation PuzzleViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];

//初始化数据
[self initData];
//初始化图片
[self initPuzzleImage];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}

// 初始化必要数据
-(void) initData
{
self.title = @"拼图";

self.string_ImageName = @"IMG.JPG";
}

// 初始化图片
-(void) initPuzzleImage
{
[self resetPuzzleImageWithImage:[UIImage imageNamed:self.string_ImageName]];
}
// 重置图片
-(void) resetPuzzleImageWithImage:(UIImage *)image
{
CGFloat scale = image.size.height / image.size.width;

//重新设置图片尺寸(压缩图片)
UIImage *resultImage = [self image:image ByScalingToSize:CGSizeMake(380, 380*scale)];
self.isStart = NO;
//切割图片
self.array_ImageView = [self separateImage:resultImage ByX:self.lvl andY:self.lvl];
//打乱图片顺序
[self puzzleTheImage];

if(self.view_PuzzleBoard)
{
[self.view_PuzzleBoard removeFromSuperview];
}
//加载图片视图(tip:可以通过修改坐标值,以及更改背景颜色,来观察,绘制位置)
self.view_PuzzleBoard = [self createPuzzleBoardViewWithFrame:CGRectMake(0, 0, 380, [UIScreen mainScreen].bounds.size.height)];
//将拼图视图添加到View中
[self.view addSubview:self.view_PuzzleBoard];
}

//重新设置图片尺寸
- (UIImage *)image:(UIImage *)sourceImage ByScalingToSize:(CGSize)targetSize
{
UIImage *newImage = nil;
CGRect rect = CGRectMake(0.0, 0.0, targetSize.width, targetSize.height);
//压缩图片过程
UIGraphicsBeginImageContext(rect.size);
[sourceImage drawInRect:rect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

if(newImage == nil)
NSLog(@"could not scale image");
return newImage ;
}

// 分解图片
-(NSMutableArray *) separateImage:(UIImage *)image ByX:(int)x andY:(int)y
{
// 数据监测
if (x < 1 || y < 1 || ![image isKindOfClass:[UIImage class]])
{
return Nil;
}

CGFloat sWidth = self.view.frame.size.width;
CGFloat sHeight = self.view.frame.size.height;
CGFloat iWidth = image.size.width;
CGFloat iHeight = image.size.height;
// 图片大小适配(防止图片超过屏幕尺寸)
if (iHeight > sHeight || iWidth > sWidth)
{
CGFloat scala = MIN(sHeight/iHeight, sWidth/iWidth);

iWidth = iWidth * scala;
iHeight = iHeight * scala;
}

NSMutableArray *array = [NSMutableArray array];

float resultX = iWidth * 1.0 / y;
float resultY = iHeight * 1.0 / x;

for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{

CGRect rect = CGRectMake(resultX*j, resultY*i, resultX, resultY);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage],rect);
UIImage* elementImage = [UIImage imageWithCGImage:imageRef];

// 空白位
if (i == 0 && j == 0)
{
elementImage = nil;
}
PuzzleImageView *puzzleImageView=[[PuzzleImageView alloc] initWithImage:elementImage];
puzzleImageView.resultIndex = i * x + j;
puzzleImageView.nowIndex = i * x + j;
puzzleImageView.delegate = self;
puzzleImageView.frame = CGRectMake( 10+resultX * j,  150+resultY * i, resultX, resultY);

[array addObject:puzzleImageView];
}
}

return array;
}
//创建拼图所需的背景图,可将注释放开结合坐标更改,观察具体使用
-(UIView *) createPuzzleBoardViewWithFrame:(CGRect)rect
{
UIView *view = [[UIView alloc]initWithFrame:rect];
//view.backgroundColor = [UIColor grayColor];
//遍历
for (PuzzleImageView *pzView in self.array_ImageView)
{
[view addSubview: pzView];
}
return view;
}

// 打乱顺序
-(void)puzzleTheImage
{
// 保持0位不动,否则奇偶性检查无效(需要提前对逆序数有所学习认识)
//随机产生两个1~8之间的数字
NSInteger aIndex = arc4random()%(self.array_ImageView.count - 1) + 1;

PuzzleImageView *aView = [self.array_ImageView objectAtIndex:aIndex];

NSInteger bIndex = arc4random()%(self.array_ImageView.count - 1) + 1;
PuzzleImageView *bView = [self.array_ImageView objectAtIndex:bIndex];
//交换随机产生的两个图片的 NowIndex
[self exchangePuzzleFrameWithZero:aView And:bView withAnimation:NO];

// 检查打乱是否完成,否则递归
if (![self makePuzzleCanBeSolved] || ![self makePuzzleFinished])
{
[self puzzleTheImage];
}
else
{
// 游戏正式开始
self.isStart = YES;
}
}
// 检查无解
-(BOOL) makePuzzleCanBeSolved
{
// 奇偶性总值
NSInteger sum = 0;

// 循环遍历(两层for循环计算的是逆序数值,知道逆序数的怎么计算的,就可以相应理解)
for (NSInteger i = 0; i < self.array_ImageView.count; i++)
{
PuzzleImageView *aView = [self.array_ImageView objectAtIndex:i];

printf("--%d",aView.nowIndex);

for (NSInteger j = i + 1; j < self.array_ImageView.count; j++)
{
PuzzleImageView *bView = [self.array_ImageView objectAtIndex:j];

// printf("b-- %d ",bView.nowIndex);

// 逆序数检查
if (aView.nowIndex > bView.nowIndex)
{
sum ++;
}
}
}

// 根据逆序数奇偶性判断是否有解
if ((sum % 2) == 0)
{
return YES;
}
else
{
printf("无解\n");
return NO;
}
}
// 全部无序
-(BOOL) makePuzzleFinished
{
BOOL flag = YES;
//每个拼图进行一一比对
for (PuzzleImageView *pzView in self.array_ImageView)
{
if (pzView.resultIndex != 0 && pzView.resultIndex == pzView.nowIndex)
{
printf("未全打乱\n");
flag = NO;
break;
}
}
return flag;
}

//拼图移动
-(void) puzzleImageViewShouldMove:(PuzzleImageView *)imageview
{
PuzzleImageView *zeroPZ = [self.array_ImageView objectAtIndex:0];

// 是否允许移动
if ([imageview canMoveToPoint:zeroPZ.frame.origin]) {
//交换
[self exchangePuzzleFrameWithZero:zeroPZ And:imageview withAnimation:YES];
}
}

// 交换两个拼图视图
-(void)exchangePuzzleFrameWithZero:(PuzzleImageView *)zeroView And:(PuzzleImageView *)bView withAnimation:(BOOL)animation
{
CGRect aRect = zeroView.frame;
NSInteger aIndex = zeroView.nowIndex;

[zeroView setFrame:bView.frame];
[zeroView setNowIndex:bView.nowIndex];

[bView setNowIndex:aIndex];
if (animation) {
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
[bView setFrame:aRect];
} completion:Nil];
}else
{
[bView setFrame:aRect];
}

if (self.isStart && [self gameComplete])
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"恭喜" message:@"游戏完成!" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:Nil, nil];
[alert show];
}
}
// 完成退出
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[self.navigationController popViewControllerAnimated:YES];
}
// 判断游戏完成
-(BOOL) gameComplete
{
BOOL flag = YES;
for (PuzzleImageView *pzView in self.array_ImageView)
{
if (pzView.resultIndex != pzView.nowIndex)
{
flag = NO;
break;
}
}
return flag;
}
@end


4:拼图视图控制器PuzzleViewController.h

//PuzzleViewController.h

#import <UIKit/UIKit.h>
#import "PuzzleImageView.h"
#import <Foundation/Foundation.h>

@interface PuzzleViewController : UIViewController <PuzzleDelegate, UIActionSheetDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate>

@property (strong, nonatomic) NSMutableArray *array_ImageView;

@property (strong, nonatomic) NSString *string_ImageName;

@property (assign, nonatomic) BOOL isStart;

@property (assign, nonatomic) NSInteger lvl;

@property (strong, nonatomic) UIView *view_PuzzleBoard;

@end


5: 菜单视图控制器MenuViewController.m

//  ViewController.m

#import <Foundation/Foundation.h>

#import "MenuViewController.h"
#import "PuzzleViewController.h"

@interface MenuViewController ()

@end

@implementation MenuViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {

}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];

[self initData];
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//视图控制器更改,不同视图的跳转(点击Button时候跳转拼图页面)传入的是拼图阶。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *vc = segue.destinationViewController;
if ([vc isKindOfClass:[PuzzleViewController class]])
{
((PuzzleViewController *)vc).lvl = self.lvl;
}
}

// 初始化必要数据
-(void)initData
{
self.title = @"菜单";
self.lvl = 3;
}
//滑块值的更改处理
- (IBAction)sliderValueChange:(id)sender
{
UISlider *slider = sender;

if (slider.value < 4)
{
self.lvl = 3;
[slider setValue:3.0];
self.label_lvl.text = @"简单";
}else if (slider.value < 5)
{
self.lvl = 4;
[slider setValue:4.0];
self.label_lvl.text = @"中等";
}else if (slider.value > 4)
{
self.lvl = 5;
[slider setValue:5.0];
self.label_lvl.text = @"困难";
}
}
@end


6:菜单视图控制器MenuViewControlle.h

//MenuViewControlle.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface MenuViewController : UIViewController

@property (assign, nonatomic) NSInteger lvl;

@property (strong, nonatomic) IBOutlet UILabel *label_lvl;

- (IBAction)sliderValueChange:(id)sender;

@end


四:主要函数解析

-(void) initData:主要的功能是初始化拼图标题,以及指定图片名称。

ByScalingToSize:(CGSize)targetSize:压缩图片,重新设置图片尺寸。

-(NSMutableArray *) separateImage:根据不同的等级来对图片进行切割。(对切割好的图片设置好 两个下标Tag(nowIndex, resultIndex))用来最后判断拼图是否成功复位。

-(void)puzzleTheImage: 打乱图块顺序

-(BOOL) makePuzzleCanBeSolved:逆序数检查图片是否能复位即有解。

-(BOOL) makePuzzleFinished:判断每个图块是否全部打乱,也是用来判断游戏是否复位成功的判断函数的实现。

-(void)exchangePuzzleFrameWithZero:交换两个图块。并实时判断游戏是否成功结束。

-(void) puzzleImageViewShouldMove:判断图块是否能与空白位进行移动。若可以则移动。

简单的介绍逆序数

拼图复位判断:循环遍历每个图块,看每个图片的当前下标nowIndex,是否与最初分块的下标值(resultIndex)相等,如果完全一致,表示拼图成功复位。

逆序数奇偶性判断有无解:对源状态A与目标状态B进行规范化,使得两矩阵的元素0(important)的位置相同;记为新的源状态A’与目标状态B’;

1. 若A'与B'的逆序对的奇偶性相同(即A'与B1的逆序对的奇偶性相同),则A'必定可能转化为B',即A可以转化到B(从这一条性质知道,乱序的拼图是否能够有解);

2. 若A'与B'的逆序对的奇偶性不同(即A'与B2的逆序对的奇偶性相同),则A'必定不可能转化为B',即A不可以转化到B;


根据逆序数想关推论,以3*3为例,矩阵为

0 1 2 逆序数为0 是偶数

3 4 5 所以最后随机乱序的拼图 也应该是偶矩阵排列

6 7 8 逆序数的计算有线性代数知识点可知

逆序数计算的例子:

0 4 5

7 8 3

2 1 6

以 0 4 5 7 8 3 2 1 6 一一看起

4:比4小的值有 3 2 1 (三个)

5:比5小的值有 3 2 1 (三个)

7:比7小的值有 3 2 1 6 (四个)

8:比8小的值有 3 2 1 6 (四个)

2:比2小的值有 1(一个)

1:比1小的值有 (0个)

6:比6小的值显然..

三个+三个+四个+四个+一个= 15 判断一下 奇数。 与最出的偶数矩阵奇偶性不同。故乱序的拼图如果是这个矩阵,则无解。

代码仍需要在不断的学习中完善,带着怀疑的态度来思考(⊙_⊙)?。

360云盘代码(点击前先复制访问密码) 访问密码 1394

代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: