ブルドーザーをタッチして、砂山を崩す。
崩した砂をそのまま道路に空いた穴に落としましょう。
ただそれだけの簡単、子供向けアプリのサンプルです。

ポイント
砂同士の衝突判定は、簡素に右側が当たっていたら、左に少しずらす
左が当たっていたら、右に、下が当たっていたら停止といった感じに
NSTimerのなかで判定しています。
砂とブルドーザーのシャベル部分が当たったかどうかの判定は、
UIBezierPathのCGPathContainsPointを使って判定してみました。

環境
今回つくったiPhoneアプリサンプルは、
XcodeのiOS6 iPhone Simulatorで動かしています。


サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

@interface ViewController () {

    UIView *bulldozer;

    UIView *blade;

    CAShapeLayer *bladeLayer;

    NSTimer *timer;

    NSMutableArray *sand;

}

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

    

    [self createBulldozer];

    

    [self createGround];

    

    [self createSand];

}

– (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    static BOOL lock;

    if (!lock) {

        lock = YES;

        [self startTimer];

    }

}

– (void)createBulldozer

{

    bulldozer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];

    bulldozer.center = CGPointMake(260, 300);

    bulldozer.backgroundColor = [UIColor clearColor];

    

    UIView *body = [[UIView alloc] initWithFrame:CGRectMake(30, 50, 70, 40)];

    body.backgroundColor = [UIColor yellowColor];

    [bulldozer addSubview:body];

    

    blade = [[UIView alloc] initWithFrame:CGRectMake(0, 50, 30, 50)];

    blade.backgroundColor = [UIColor clearColor];

    [bulldozer addSubview:blade];

    

    UIBezierPath *path = [[UIBezierPath alloc] init];

    [path moveToPoint:CGPointMake(0, 0)];

    [path addArcWithCenter:CGPointMake(0, 25) radius:25 startAngle:-M_PI*0.5 endAngle:M_PI*0.5 clockwise:YES];

    bladeLayer = [[CAShapeLayer alloc] initWithLayer:blade.layer];

    bladeLayer.path = path.CGPath;

    bladeLayer.fillColor = [UIColor clearColor].CGColor;

    bladeLayer.strokeColor = [UIColor yellowColor].CGColor;

    bladeLayer.lineWidth = 5.0;

    [blade.layer addSublayer:bladeLayer];

    

    UIView *tireF = [[UIView alloc] initWithFrame:CGRectMake(30, 80, 30, 30)];

    tireF.backgroundColor = [UIColor whiteColor];

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

    tireF.layer.borderWidth = 10;

    tireF.layer.cornerRadius = 15;

    [bulldozer addSubview:tireF];

    UIView *tireR = [[UIView alloc] initWithFrame:CGRectMake(70, 80, 30, 30)];

    tireR.backgroundColor = [UIColor whiteColor];

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

    tireR.layer.borderWidth = 10;

    tireR.layer.cornerRadius = 15;

    [bulldozer addSubview:tireR];

    

    UIView *cockpit = [[UIView alloc] initWithFrame:CGRectMake(50, 10, 40, 40)];

    cockpit.layer.borderWidth = 5;

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

    [bulldozer addSubview:cockpit];

    

    UIView *driver = [[UIView alloc] initWithFrame:CGRectMake(55, 25, 20, 20)];

    driver.backgroundColor = [UIColor blackColor];

    driver.layer.cornerRadius = 10;

    [bulldozer addSubview:driver];

    [self.view addSubview:bulldozer];

    

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];

    [bulldozer addGestureRecognizer:tap];

}

– (void)tap

{

    bulldozer.tag = 10;

}

– (void)createSand

{

    sand = [[NSMutableArray alloc] init];

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

        float x = arc4random() % 50 + 100;

        float y = arc4random() % 50;

        UIView *s = [[UIView alloc] initWithFrame:CGRectMake(x + 10, y, 5, 5)];

        s.backgroundColor = [UIColor brownColor];

        s.tag = 1;

        [self.view addSubview:s];

        

        [sand addObject:s];

    }

}

– (void)createGround

{

    UIView *ground = [[UIView alloc] initWithFrame:CGRectMake(0, 350, 320, 130)];

    ground.backgroundColor = [UIColor brownColor];

    [self.view addSubview:ground];

    

    UIView *hole = [[UIView alloc] initWithFrame:CGRectMake(50, 350, 50, 50)];

    hole.backgroundColor = [UIColor blackColor];

    [self.view addSubview:hole];

}

– (void)startTimer

{

    timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(tick:) userInfo:nil repeats:YES];

}

– (void)tick:(NSTimer*)sender

{

    float velocity = 1;

    

    for (UIView *v1 in sand) {

        for (UIView *v2 in sand) {

            if (v2 == v1) {

                continue;

            }

            

            CGPoint o = v1.frame.origin;

            CGSize s = v1.frame.size;

            

            CGRect right = CGRectMake(o.x + s.width, o.y, 1, s.height);

            CGRect left = CGRectMake(o.x, o.y, 1, s.height);

            CGRect bottom = CGRectMake(o.x, o.y+s.height, s.width, 1);

            

            if(CGRectIntersectsRect(right, v2.frame)) {

                v1.center = CGPointMake(v1.center.x1, v1.center.y);

            } else if(CGRectIntersectsRect(left, v2.frame)) {

                v1.center = CGPointMake(v1.center.x + 1, v1.center.y);

            }

            if (CGRectIntersectsRect(bottom, v2.frame)) {

                v1.tag = 2;

            }

        }

        

        if (v1.center.x > 50 && v1.center.x < 100 && v1.center.y < 400 && v1.tag != 2) {

            // hole

            v1.center = CGPointMake(v1.center.x, v1.center.y + 1);

        } else if (v1.center.y < 350 && v1.tag != 2) {

            // ground

            v1.center = CGPointMake(v1.center.x, v1.center.y + 1);

        } else {

            v1.tag = 1;

        }

    }

    

    if (bulldozer.tag == 10) {

        bulldozer.center = CGPointMake(bulldozer.center.x-velocity, bulldozer.center.y);

        

        for (UIView *s in sand) {

            CGPoint cp = [self.view convertPoint:s.center toView:blade];

            cp = CGPointMake(cp.x + 1, cp.y1);

            if (CGPathContainsPoint(bladeLayer.path, 0, cp, YES)) {

                s.center = CGPointMake(s.center.x1, s.center.y);

            }

        }

    }

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end