「粘り強さ!」

これを育むために適したゲームは何。。。?

「パズル」はどうだろう、と思い作ってみました。

(iOS 5でやってます。)

カメラで撮った写真をそのまま、パズルにするアプリです。

やっていることは、

・UIImagePickerController を使ってカメラから画像を取り込む

・画像のサイズを変更する

・画像をパズルのピースに分割する

・パズルのピースにドラッグで動かせるようにジェスチャーを付ける

といったかんじです。

ローテーション関連の実装はしていないです。

端末の向きは、縦方向だとちゃんと動きます。

コードはこちらになります。ぜんぶViewController.m に書いたので、

Single View Application を作って、そのままメインのViewController に書けば

動くと思います。

ViewController.m

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

// ピースの数, 4, 9, 16 で動作は確認。 sqrt使うのでで整数になるような数。

#define PIECE_COUNT 9 

@interface ViewController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate>

@property (nonatomic, strong) UIImagePickerController *imagePickerController;

@property (nonatomic, strong) UIImage *pazzleImage;

@property (nonatomic, strong) NSMutableArray *piesesInfo;

@property (nonatomic, strong) UIButton *pickerOpen;

@property (nonatomic, assign) int correctPieceCnt;

@end

@implementation ViewController

@synthesize imagePickerController, pazzleImage, piesesInfo, pickerOpen, correctPieceCnt;

– (void)viewDidLoad

{

    [super viewDidLoad];

    

    self.view.backgroundColor = [UIColor whiteColor];

    

    imagePickerController = [[UIImagePickerController alloc] init];

    imagePickerController.delegate = self;

    

    

    // カメラ シミュレーターの場合、カメラ使えない、

    // テスト中は sourceType をコメントして、フォルダから画像をえらぶとやりやすい。

    // シミューレータに画像を取り込むには、シミュレータのブラウザで適当な画像を検索して保存すると楽。

//    imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;

    

    // LOGO

    UILabel *logo = [[UILabel alloc] init];

    logo.text = @” Pazzle “;

    logo.font = [UIFont boldSystemFontOfSize:80];

    [logo sizeToFit];

    logo.center = CGPointMake(self.view.center.x, 160);

    logo.backgroundColor = [UIColor greenColor];

    logo.textColor = [UIColor whiteColor];

    [self.view addSubview:logo];

    

    

    [self showPickerButton];

    

}

– (void)showPickerButton

{

    if(!pickerOpen) {

        pickerOpen = [UIButton buttonWithType:UIButtonTypeRoundedRect];

        [pickerOpen setTitle:@” Make Pazzle “ forState:UIControlStateNormal];

        [pickerOpen sizeToFit];

        pickerOpen.center = CGPointMake(self.view.center.x, self.view.frame.size.height * .8);

        [pickerOpen addTarget:self action:@selector(showPicker) forControlEvents:UIControlEventTouchUpInside];

    }

    [self.view addSubview:pickerOpen];

}

– (void)hidePickerButton

{

    [pickerOpen removeFromSuperview];

}

– (void)showPicker

{

    [self presentModalViewController:imagePickerController animated:YES];

}

– (void)showPazzle

{

    

    // 画像を画面の 2/3 位の大きさにする。

    float pazzleWidth = self.view.frame.size.width * 0.7;

    float pazzleHeight = self.view.frame.size.height * 0.7;

    pazzleImage = [self createPazzleImage:pazzleImage scaledToSize:CGSizeMake(pazzleWidth, pazzleHeight)];

    UIImageView *pazzleFrame = [[UIImageView alloc] initWithImage:pazzleImage];

    pazzleFrame.contentMode = UIViewContentModeScaleAspectFit;

    pazzleFrame.center = CGPointMake(self.view.center.x, pazzleHeight / 2.0);

    pazzleFrame.alpha = 0.4;

    [self.view addSubview:pazzleFrame];

    

    

    

    // ピースに分ける

    int divide = sqrt(PIECE_COUNT);

    CGSize size = CGSizeMake(pazzleWidth / divide, pazzleHeight / divide);

    [self createPieces:pazzleImage cropsize:size pazzleFrame:pazzleFrame.frame];

    

    

    // ピースを下の方に散らかす

    NSMutableSet *positions = [[NSMutableSet alloc] init];

    float w = self.view.frame.size.width / PIECE_COUNT;

    float h = self.view.frame.size.height * 0.85;

    for (int i=0; i<PIECE_COUNT; i++) {

        // 配置場所

        [positions addObject:[NSValue valueWithCGPoint:CGPointMake(w * i + w/2, h)]];

    }

    

    for (NSDictionary *info in piesesInfo) {

        // 配置する

        UIView *piece = [info objectForKey:@”piece”];

        NSValue *center = [positions anyObject];

        piece.center = [center CGPointValue];

        [positions removeObject:center];

        [self.view addSubview:piece];

    }

    

    

}

// カメラから取得した画像をリサイズ

– (UIImage *)createPazzleImage:(UIImage *)image scaledToSize:(CGSize)newSize {

    UIGraphicsBeginImageContext(newSize);

    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    

    UIGraphicsEndImageContext();

    return newImage;

}

– (void)createPieces:(UIImage*)image cropsize:(CGSize)size pazzleFrame:(CGRect)frame

{

    // { @”piece”:表示用、@”answer”:動かした後の比較用 }

    piesesInfo = [[NSMutableArray alloc] init];

    

    // ピースの縦、横の数

    int divide = sqrt(PIECE_COUNT);

    

    for (int i=0; i<divide; i++) {

        for (int j=0; j<divide; j++) {

            // 切り取り

            CGRect crop = CGRectMake(size.width * i, size.height * j, size.width, size.height);

            UIImageView *piece = [self cropRect:crop image:pazzleImage];

            // 小さめにする。

            piece.bounds = CGRectMake(0, 0, piece.bounds.size.width * 0.5, piece.bounds.size.height * 0.5);

            

            // ピースを指で動かすためのジェスチャーを追加

            piece.userInteractionEnabled = YES;

            UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(movePiece:)];

            [piece addGestureRecognizer:pan];

            

            

            

            // 答え

            float ansX = frame.origin.x + i * frame.size.width / divide;

            float ansY = frame.origin.y + j * frame.size.height / divide;

            UIView *ans = [[UIView alloc] initWithFrame:CGRectMake(ansX, ansY, frame.size.width / divide, frame.size.height / divide)];

            

            

            [piesesInfo addObject:[NSDictionary dictionaryWithObjectsAndKeys:piece, @”piece”, ans, @”answer”, nil]];

        }

    }

}

// イメージの切り取り (パズルのピース用)

– (UIImageView*)cropRect:(CGRect)cropRect image:(UIImage*)origin

{

    CGImageRef imageRef = CGImageCreateWithImageInRect([origin CGImage], cropRect);

    // or use the UIImage wherever you like

    UIImageView *iv = [[UIImageView alloc] initWithImage:[UIImage imageWithCGImage:imageRef]]; 

    CGImageRelease(imageRef);

    

    iv.frame = cropRect;

    iv.contentMode = UIViewContentModeScaleToFill;

    iv.layer.borderColor = [UIColor grayColor].CGColor;

    iv.layer.borderWidth = 1;

    return iv;

}

#pragma mark – piese gesture

– (void)movePiece:(UIPanGestureRecognizer*)gr

{

    // 小さいときの場所と大きさを一時保管

    static CGRect startPosition;

    static UIView *answer;

    

    // ジェスチャー開始の時

    if ([gr state] == UIGestureRecognizerStateBegan) {

        startPosition = gr.view.frame;

        for (NSDictionary *info in piesesInfo) {

            if ([[info objectForKey:@”piece”] isEqual:gr.view]) {

                answer = [info objectForKey:@”answer”];

            }

        }

        // 大きく

        gr.view.bounds = answer.bounds;

        // 前に

        gr.view.layer.zPosition = 1;

        // 浮いてるように見せる

        gr.view.layer.shadowOffset = CGSizeMake(5, 5);

        gr.view.layer.shadowRadius = 5;

        gr.view.layer.shadowOpacity = 0.5;

    }

    

    

    // 指の位置に移動させる

    gr.view.center = [gr locationInView:self.view];

    

    

    // 指を離した時

    if ([gr state] == UIGestureRecognizerStateEnded) {

        

        // 正解チェック

        // 誤差を 20×20 rect くらいで

        CGRect pieseRect = CGRectMake(gr.view.center.x, gr.view.center.y, 1, 1);

        CGRect ansRect = CGRectMake(answer.center.x – 10, answer.center.y – 10, 20, 20);

        if (CGRectIntersectsRect(pieseRect, ansRect)) {

            // 正解の場合

            gr.view.layer.position = answer.center;

            // 浮いてないように

            gr.view.layer.shadowOpacity = 0;

            // z-position

            gr.view.layer.zPosition = 0;

            

            // 正解数に +1

            correctPieceCnt++;

            // 全部のピースがハマったか?

            if (correctPieceCnt >= PIECE_COUNT) {

                // クリア

                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@”Good” message:@”Clear” delegate:self cancelButtonTitle:@”OK” otherButtonTitles:nil];

                [alert show];

                

                // データ初期化

                [self resetData];

                

                // カメラ呼び出しボタンを再表示

                [self showPickerButton];

            }

       

            

        } else {

            // 不正解 元に戻す (アニメーション)

            [UIView animateWithDuration:0.5 animations:^{

                gr.view.frame = startPosition;

            } completion:^(BOOL finished) {

                // 浮いてないように

                gr.view.layer.shadowOpacity = 0;

                // z-position

                gr.view.layer.zPosition = 0;

            }];

            

        }

    }

    

}

– (void)resetData

{

    pazzleImage = nil;

    piesesInfo = nil;

    correctPieceCnt = 0;

}

#pragma mark – image picker delegates

– (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo

{

    // 古い View を消しておく。

    for (UIView *v in self.view.subviews) {

        [v removeFromSuperview];

    }

    [self hidePickerButton];

    

    

    pazzleImage = image;

    [picker dismissViewControllerAnimated:YES completion:^{

        [self showPazzle];

    }];

}

– (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker

{

    [picker dismissModalViewControllerAnimated:YES];    

}

#pragma mark – view controller

– (void)viewDidUnload

{

    [super viewDidUnload];

    imagePickerController = nil;

    pazzleImage = nil;

    piesesInfo = nil;

    pickerOpen = nil;

}

– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

{

    return NO;

}

@end