「粘り強さ!」
これを育むために適したゲームは何。。。?
「パズル」はどうだろう、と思い作ってみました。
(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