ボール投げエフェクトをUIViewで表示する方法のメモ

(XcodeのiOS6 Simulatorで試してます。)

弾性、空気抵抗は無し。

重力加速度のみ考慮。

ボールの式

 微小な時間dtとvx, vyで計算する

 速さx方向:vx = vx

 速さy方向:vy = vy – gdt

操作

 タッチでボールの初期位置を決定

 初期位置から指を滑らせると投げる方向を表示

 指を離すとボールを指示マークの方向に投げる

サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

@interface ViewController () {

    CADisplayLink *timer;

    float vx;

    float vy;

    float g;

}

@property (nonatomic, strong) UIView *ball;

@end

@implementation ViewController

@synthesize ball;

– (void)viewDidLoad

{

    [super viewDidLoad];

}

– (void)viewDidAppear:(BOOL)animated

{

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

    [self.view addGestureRecognizer:pan];

}

– (void)pan:(UIPanGestureRecognizer*)pgr

{

    CGPoint touch = [pgr locationInView:self.view];

    

    if (UIGestureRecognizerStateBegan == pgr.state) {

        UIView *marker = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];

        marker.center = touch;

        marker.layer.borderWidth = 2.0;

        marker.tag = 101;

        [self.view addSubview:marker];

    }

    

    if (UIGestureRecognizerStateChanged == pgr.state) {

        // clean

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

            if (v.tag == 102) {

                [v removeFromSuperview];

            }

        }

        

        // create

        UIView *marker = [self.view viewWithTag:101];

        float dx = touch.x – marker.center.x;

        float dy = touch.y – marker.center.y;

        

        if (dx == 0) {

            dx = 0.001;

        }

        if (dy == 0) {

            dy = 0.001;

        }

        

        

        float len = hypot(dx, dy);

        

        for (int i=0; i<len; i += 10) {

            UIView *chain = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 5)];

            chain.layer.cornerRadius = 2.0;

            chain.backgroundColor = [UIColor grayColor];

            chain.tag = 102;

            float x = marker.center.x + (i * dx / len);

            float y = marker.center.y + (i * dy / len);

            chain.center = CGPointMake(x, y);

            

            [self.view addSubview:chain];

        }

        

    }

    

    if (UIGestureRecognizerStateEnded == pgr.state) {

        UIView *marker = [self.view viewWithTag:101];

        [self createBall: marker.center];

        [self.view addSubview:self.ball];

        

        

        float x = touch.x – marker.center.x;

        float y = – (touch.y – marker.center.y);

        

        float adjust = 0.05; // アニメーションの見た目的な調整

        [self throwVX:x*adjust VY:y*adjust];

        

        // clean up

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

            if (v.tag == 101 || v.tag == 102) {

                [v removeFromSuperview];

            }

        }

    }

}

– (void)throwVX:(float)dx VY:(float)dy

{

    float rate = 100; // 100point = 1m 換算にする 重力加速度とのかねあい

    vx = dx * rate;  // dx m/s

    vy = dy * rate;  // dy m/s

    g = 9.8 * rate;  // 9.8m/s^2 固定

    

    [self startTimer];

}

– (void)createBall:(CGPoint)point

{

    self.ball = [[UIView alloc] initWithFrame:CGRectMake(0, 300, 30, 30)];

    ball.center = point;

    ball.layer.cornerRadius = 15;

    ball.backgroundColor = [UIColor lightGrayColor];

    ball.layer.borderColor = [UIColor blackColor].CGColor;

    ball.layer.borderWidth = 1.5;

}

– (void)startTimer

{

    timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateDisplay:)];

    [timer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

}

– (void)updateDisplay:(CADisplayLink*)sender

{

    static float lastTime = 0.0;

    float timeDelta = 0.0;

    if (lastTime == 0.0) {

        lastTime = sender.timestamp;

    } else {

        timeDelta = sender.timestamp – lastTime;

        lastTime = sender.timestamp;

    }

    vy = vy  g * timeDelta;

    float x = ball.center.x + vx * timeDelta;

    float y = ball.center.yvy * timeDelta;

    

    ball.center = CGPointMake(x, y);

    

    // stop ball on the floor

    if (ball.center.y > 400) {

        ball = nil; // ボールのViewをアニメ処理から切り離す。

    }

    

}

– (void)viewDidDisappear:(BOOL)animated

{

    self.ball = nil;

    [timer invalidate];

    timer = nil;

}

@end