飛ばす角度をうまく調整して、ピンポン球をコップの中に入れてみよう。というLongPressGestureを使ったシンプルなiPhoneアプリを作ってみます。
動作イメージ
XcodeからiOS6 iPhone Simulatorで動かすとこんな感じになります。
ポイント
LongPressGestureを使ってタップ開始と同時にピンポン球を飛ばす方向のガイドを表示します。ガイドはタイマーで90°を行ったり来たりさせて、ゆびを離したときのガイドの方向にピンポンを飛ばしています。タイマーの中でボールとコップの重なりチェックをして、コップに当たるとピンポンが跳ね返るようにしました。
サンプルコード
#import “ViewController.h”
#import <QuartzCore/QuartzCore.h>
#define UIColorHex(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
@interface ViewController () {
UIView *cup;
UIView *ball;
CGPoint ballPosition;
CGPoint ballDirection;
NSTimer *timer;
UILabel *cupin;
UIView *guideLine;
float angle;
BOOL up;
NSTimer *rotateTimer;
}
@end
@implementation ViewController
– (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [self color:4];
[self createCup];
[self createBall];
}
– (void)createCup
{
float w = 70;
float h = w * 1.618;
cup = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, h)];
cup.center = CGPointMake(300, 200);
cup.backgroundColor = [UIColor clearColor];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(0, h)];
[path addLineToPoint:CGPointMake(w, h)];
[path addLineToPoint:CGPointMake(w, 0)];
CAShapeLayer *sl = [[CAShapeLayer alloc] init];
sl.fillColor = [UIColor clearColor].CGColor;
sl.strokeColor = [self color:1].CGColor;
sl.lineWidth = 4;
sl.path = path.CGPath;
[cup.layer addSublayer:sl];
[self.view addSubview:cup];
}
– (void)createBall
{
ballDirection = CGPointMake(0, 0);
ballPosition = CGPointMake(50, 200);
[self showBall];
}
– (void)showBall
{
if (!ball) {
ball = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
ball.backgroundColor = [self color:2];
ball.layer.cornerRadius = 15;
[self.view addSubview:ball];
UILongPressGestureRecognizer *lp = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(shot:)];
lp.minimumPressDuration = 0.1;
[ball addGestureRecognizer:lp];
}
ballPosition = CGPointMake(ballPosition.x + ballDirection.x, ballPosition.y + ballDirection.y);
ball.center = ballPosition;
}
– (void)updateBallDirection
{
// top or bottom
// if (ballPosition.y < 10 || ballPosition.y > 310) {
// ballDirection = CGPointMake(ballDirection.x, -ballDirection.y);
// }
// gravity
ballDirection = CGPointMake(ballDirection.x, ballDirection.y + 0.1);
// cup
float cupx = cup.frame.origin.x;
float cupy = cup.frame.origin.y;
float lineWidth = 5;
float width = cup.bounds.size.width;
float height = cup.bounds.size.height;
CGRect cupLeft = CGRectMake(cupx, cupy, lineWidth, height);
CGRect cupRight = CGRectMake(cupx + width – lineWidth, cupy, lineWidth, height);
CGRect cupBottom = CGRectMake(cupx + 5, cupy + height, width – 10, lineWidth);
if (CGRectIntersectsRect(cupLeft, ball.frame)) {
CGRect hit = CGRectIntersection(cupLeft, ball.frame);
CGPoint hitP = CGPointMake(hit.origin.x + hit.size.width/2.0, hit.origin.y + hit.size.height/2.0);
if (hitP.x > ball.center.x) {
ballDirection = CGPointMake(-1, ballDirection.y);
} else if (hitP.x > ball.center.x) {
ballDirection = CGPointMake(1, ballDirection.y);
}
}
else if (CGRectIntersectsRect(cupRight, ball.frame)) {
CGRect hit = CGRectIntersection(cupRight, ball.frame);
CGPoint hitP = CGPointMake(hit.origin.x + hit.size.width/2.0, hit.origin.y + hit.size.height/2.0);
if (hitP.x > ball.center.x) {
ballDirection = CGPointMake(-1, ballDirection.y);
} else if (hitP.x > ball.center.x) {
ballDirection = CGPointMake(1, ballDirection.y);
}
}
else if (CGRectIntersectsRect(cupBottom, ball.frame)) {
// clear
[timer invalidate];
ballDirection = CGPointMake(0, 0);
cupin = [[UILabel alloc] init];
cupin.text = @”cup in”;
cupin.textColor = [self color:0];
cupin.font = [UIFont boldSystemFontOfSize:20];
[cupin sizeToFit];
cupin.center = cup.center;
cupin.backgroundColor = [UIColor clearColor];
cupin.transform = CGAffineTransformMakeTranslation(0, –500);
[self.view addSubview:cupin];
[UIView animateWithDuration:0.5 animations:^{
cupin.transform = CGAffineTransformIdentity;
}];
cupin.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(restart)];
[cupin addGestureRecognizer:tap];
}
// game over check
if (!CGRectIntersectsRect(ball.frame, self.view.bounds)) {
[self restart];
}
}
– (void)shot:(UILongPressGestureRecognizer*)gr
{
if (gr.state == UIGestureRecognizerStateBegan) {
// show guide
float guideSize = ball.bounds.size.width / 4.0;
guideLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 5)];
guideLine.layer.anchorPoint = CGPointMake(0, 0);
guideLine.layer.position = ball.center;
[self.view addSubview:guideLine];
for (int i=0; i<6; i++) {
UIView *mark = [[UIView alloc] initWithFrame:CGRectMake(i * guideSize, -guideSize / 2.0, guideSize, guideSize)];
mark.layer.cornerRadius = 5;
mark.backgroundColor = [self color:1];
[guideLine addSubview:mark];
}
up = NO;
angle = 0;
rotateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(rotate:) userInfo:nil repeats:YES];
}
if (gr.state == UIGestureRecognizerStateEnded) {
[guideLine removeFromSuperview];
[rotateTimer invalidate];
ballDirection = CGPointMake(6 * cos(angle), 6 * sin(angle));
// throw
timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
}
}
– (void)rotate:(NSTimer*)sender
{
if (angle < –M_PI / 2.0) {
up = YES;
} else if (angle > 0) {
up = NO;
}
if (up) {
angle = angle + M_PI / 60.0;
} else {
angle = angle – M_PI / 60.0;
}
guideLine.transform = CGAffineTransformMakeRotation(angle);
}
– (void)tick:(NSTimer*)sender
{
[self updateBallDirection];
[self showBall];
}
– (void)restart
{
[timer invalidate];
[cupin removeFromSuperview];
[self createCup];
[self createBall];
}
– (UIColor*)color:(int)i
{
switch (i) {
case 0:
return UIColorHex(0xA3D980);
case 1:
return UIColorHex(0x93BF9E);
case 2:
return UIColorHex(0xF2F0D5);
case 3:
return UIColorHex(0x8C8474);
case 4:
return UIColorHex(0x40362E);
default:
break;
}
return nil;
}
– (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end