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

IOS/Xcode工具

一:主要功能

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

  1. 压缩图片:需要给传递过来的任意图片,根据手机模拟器中View大小,重新设置图片的尺寸,即压缩图片运用UIGraphicsBeginImageContext进行实现。
  2. 切割图片separateImage:将尺寸设置好的图片,进行切割成3*3 或者4*4,获取整个View的尺寸大小和图片的尺寸大小,进行大小比较,当图片的大小超过容器View的时候,将图片进行大小的缩放。然后根据切割的块,来计算出每个图块的宽高。运用两层For循环,将完整的图片按照每块设置的大小进行切割,并将0-0位置设置为空白位置 。将每块图片View的信息放到数组中。存放的信息有:当前拼图的下标,以及拼图起始没有打乱顺序的下标,用来最后判断游戏是否胜利的标记。
  3. 打乱图片顺序:产生两个NSINteger随机数,来作为存放图片View的数组的下标,从而获取图片信息,然后将两张图片进行位置对换,并改变当前拼图的下标。然后用逆序数的奇偶性来判断游戏是否能正常复位(有时候随机打乱的图片,并不能够恢复原来的位置即无解,则就需要运用逆序数的奇偶性进行判断),并判断拼图是否完全的打乱。如果两个条件有一方符合,将再次遍历打乱图片,产生两个随机数…否则游戏开始。
  4. 拼图移动:由于每个图块都有在合适的位置进行移动的实现方法,所以如果我们点击的图片,能与空白位进行移动,那么就交换这两张图块。
  5. 游戏完成:遍历的从存放每个图块视图的数组中取出每块图片View,并判断每块图片的当前下标,是否与最终特定的下标位置相等,如果每个图块都完全对照,则表示图片拼凑完成,则游戏胜利,弹出对话框。

二:效果图

  • 开始:

    开始

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

    2

  • 拼图进阶:

    3

  • 成功:

    4

三:代码

If you are interested, study the code.

  1. 拼图视图PuzzleImageView.m
  2. 拼图视图PuzzleImageView.h
  3. 拼图视图控制器PuzzleViewController.m
  4. 拼图视图控制器PuzzleViewController.h
  5. 菜单视图控制器MenuViewController.m
  6. 菜单视图控制器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

四:主要函数解析

  1. -(void) initData:主要的功能是初始化拼图标题,以及指定图片名称。
  2. ByScalingToSize:(CGSize)targetSize:压缩图片,重新设置图片尺寸。
  3. -(NSMutableArray *) separateImage:根据不同的等级来对图片进行切割。(对切割好的图片设置好 两个下标Tag(nowIndex, resultIndex))用来最后判断拼图是否成功复位。
  4. -(void)puzzleTheImage: 打乱图块顺序
  5. -(BOOL) makePuzzleCanBeSolved:逆序数检查图片是否能复位即有解。
  6. -(BOOL) makePuzzleFinished:判断每个图块是否全部打乱,也是用来判断游戏是否复位成功的判断函数的实现。
  7. -(void)exchangePuzzleFrameWithZero:交换两个图块。并实时判断游戏是否成功结束。
  8. -(void) puzzleImageViewShouldMove:判断图块是否能与空白位进行移动。若可以则移动。

简单的介绍逆序数

  1. 拼图复位判断:循环遍历每个图块,看每个图片的当前下标nowIndex,是否与最初分块的下标值(resultIndex)相等,如果完全一致,表示拼图成功复位。
  2. 逆序数奇偶性判断有无解:对源状态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

代码

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值