書いた線の上にボールを転がすアプリのサンプル

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

ポイント

・物理エンジンにBox2dを使う (手順はページ末尾)

・2点から、UIViewで直線を作成する。

サンプルコード

#import “ViewController.h”

#import <Box2D/Box2D.h>

#import <QuartzCore/QuartzCore.h>

#define PixelToMeterRatio 16

typedef enum{

    FingerTypeCreateBall = 1,

    FingerTypeCreateLine,

} FingerType;

@interface ViewController () {

    b2World *world;

    NSTimer *timer;

    FingerType type;

    CGPoint start;

    CGPoint end;

}

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

    [self createFingerWorld];

    

    [self start];

    

    [self createTypeSelector];

}

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

{

    UITouch *t = [touches anyObject];

    CGPoint p = [t locationInView:self.view];

    

    // ボタンがある場所をクリックした場合はスキップ

    if (CGRectContainsPoint(CGRectMake(270, 0, 50, 150), p)) {

        [self touchesCancelled:touches withEvent:event];

        return;

    }

    

    // ボールのときはタッチした場所にボールを生成

    if (type == FingerTypeCreateBall) {

        [self addBall:p];

    }

    

    // ラインのときは、タッチしたところを開始点として保持

    if (type == FingerTypeCreateLine) {

        start = p;

    }

    

}

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

{

    UITouch *t = [touches anyObject];

    CGPoint p = [t locationInView:self.view];

    

    // ボタンがある場所をクリックした場合はスキップ

    if (CGRectContainsPoint(CGRectMake(270, 0, 50, 150), p)) {

        [self touchesCancelled:touches withEvent:event];

        return;

    }

    

    // ラインのとき、離したところまでViewを生成

    if (type == FingerTypeCreateLine) {

        end = p;

        [self addLine];

    }

}

– (void)start

{

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

}

– (void)createFingerWorld

{

    CGSize screenSize = self.view.bounds.size;

    b2Vec2 gravity;

    gravity.Set(0.0f, –9.8f);

    

    world = new b2World(gravity);

    world->SetContinuousPhysics(true);

    

    b2BodyDef groundBodyDef;

    groundBodyDef.position.Set(0, 0);

    b2Body* groundBody = world->CreateBody(&groundBodyDef);

    

    

b2EdgeShape groundBox;

    

// bottom

groundBox.Set(b2Vec2(0,0), b2Vec2(screenSize.width/PixelToMeterRatio,0));

groundBody->CreateFixture(&groundBox, 0);

    

// left

groundBox.Set(b2Vec2(0,screenSize.height/PixelToMeterRatio), b2Vec2(0,0));

groundBody->CreateFixture(&groundBox, 0);

    

// right

groundBox.Set(b2Vec2(screenSize.width/PixelToMeterRatio,screenSize.height/PixelToMeterRatio), b2Vec2(screenSize.width/PixelToMeterRatio,0));

groundBody->CreateFixture(&groundBox, 0);

}

– (void)createTypeSelector

{

    // ball type button

    UILabel *ballBtn = [[UILabel alloc] initWithFrame:CGRectMake(280, 50, 30, 30)];

    ballBtn.text = @”○”;

    ballBtn.textAlignment = NSTextAlignmentCenter;

    ballBtn.layer.cornerRadius = 5.0;

    ballBtn.layer.borderWidth = 2.0;

    ballBtn.tag = FingerTypeCreateBall;

    ballBtn.userInteractionEnabled = YES;

    ballBtn.backgroundColor = [UIColor grayColor];

    [self.view addSubview:ballBtn];

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

    [ballBtn addGestureRecognizer:tap];

    

    // line type button

    UILabel *lineBtn = [[UILabel alloc] initWithFrame:CGRectMake(280, 100, 30, 30)];

    lineBtn.text = @”—“;

    lineBtn.textAlignment = NSTextAlignmentCenter;

    lineBtn.layer.cornerRadius = 5.0;

    lineBtn.layer.borderWidth = 2.0;

    lineBtn.tag = FingerTypeCreateLine;

    lineBtn.userInteractionEnabled = YES;

    lineBtn.backgroundColor = [UIColor lightGrayColor];

    [self.view addSubview:lineBtn];

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

    [lineBtn addGestureRecognizer:tapLine];

}

– (void)change:(UIGestureRecognizer*)gr

{

    // ボタンの色を初期化

    [self.view viewWithTag:FingerTypeCreateBall].backgroundColor = [UIColor lightGrayColor];

    [self.view viewWithTag:FingerTypeCreateLine].backgroundColor = [UIColor lightGrayColor];

    

    // 選んだボタンを濃くする

    type = (FingerType)gr.view.tag;

    gr.view.backgroundColor = [UIColor grayColor];

}

// タイマーで時間を進める

– (void)tick:(NSTimer*)sender

{

    int32 velocityIterations = 8;

    int32 positionIterations = 1;

    

    world->Step(1.0f/60.0f, velocityIterations, positionIterations);

    

    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())

    {

        if (b->GetUserData() != NULL)

        {

            UIView *v = (__bridge UIView *) b->GetUserData();

            float x = b->GetPosition().x * PixelToMeterRatio;

            float y = self.view.bounds.size.height – b->GetPosition().y * PixelToMeterRatio;

            CGPoint newCenter = CGPointMake(x,y);

            v.center = newCenter;

            v.transform = CGAffineTransformMakeRotation(-b->GetAngle());

        }

    }

}

// ぼーる

– (void)addBall:(CGPoint)point

{

    UIView *ball = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];

    ball.center = point;

    ball.backgroundColor = [UIColor orangeColor];

    ball.layer.cornerRadius = 5.0;

    [self.view addSubview:ball];

    

    b2BodyDef bodyDef;

    bodyDef.type = b2_dynamicBody;

    

    CGPoint dimensions = CGPointMake(ball.bounds.size.width/PixelToMeterRatio/2.0, ball.bounds.size.height/PixelToMeterRatio/2.0);

    bodyDef.position.Set(ball.center.x/PixelToMeterRatio, (460-ball.center.y)/PixelToMeterRatio);

    bodyDef.userData = (__bridge void *)ball;

    

    b2Body *body = world->CreateBody(&bodyDef);

    b2PolygonShape box;

    box.SetAsBox(dimensions.x, dimensions.y);

    

    b2FixtureDef fixtureDef;

    fixtureDef.shape = &box;

    fixtureDef.density = 10.0f; // 密度

    fixtureDef.friction = 0.1f; // 摩擦

    fixtureDef.restitution = 0.3f; // 反発

    body->CreateFixture(&fixtureDef);

    body->SetType(b2_dynamicBody);

}

// 2点を結ぶ線をUIViewで作成する

– (void)addLine

{

    float w = hypotf(end.xstart.x, end.ystart.y);

    

    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, 3)];

    line.backgroundColor = [UIColor blackColor];

    line.center = CGPointMake(start.x + (end.xstart.x) * 0.5, start.y + (end.ystart.y) * 0.5);

    [self.view addSubview:line];

    

    float th = atan2f(end.ystart.y, end.xstart.x);

    line.transform = CGAffineTransformMakeRotation(th);

    

    b2BodyDef groundBodyDef;

groundBodyDef.position.Set(0, 0); // bottom-left corner

b2Body* groundBody = world->CreateBody(&groundBodyDef);

    

b2EdgeShape groundBox;

    

    CGSize screenSize = self.view.bounds.size;

groundBox.Set(b2Vec2(0,0), b2Vec2(screenSize.width/PixelToMeterRatio,0));

groundBody->CreateFixture(&groundBox, 0);

    

    groundBox.Set(b2Vec2(start.x/PixelToMeterRatio, (460start.y)/PixelToMeterRatio), b2Vec2(end.x/PixelToMeterRatio, (460end.y)/PixelToMeterRatio));

groundBody->CreateFixture(&groundBox, 0);

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end

Box2d導入メモ

1. Box2Dをプロジェクトに組み込む

  Xcodeメニューの File -> Add File

  svnでチェックアウトしたなかから、Box2D.hが入ってるフォルダをAdd

2. C++ で書く。Fileの拡張子を変更

 Box2dを使うソースの拡張子を mm に

 今回は、ViewControler.mm に変更

3.  Box2Dを import できるように Search Pathを追加

 Build Settings (All) の Search Pathsのなかの Header Search Paths をクリック

 Box2Dをおいた場所のパスを入れておく