昨日作ったランダムに積み木を落とすやつをBox2D(物理エンジン)を使って書いてみる。

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

ポイント

・Box2dは次のアドレスからソースを取得

 svn checkout http://box2d.googlecode.com/svn/trunk/ box2d-read-only

手順

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をおいた場所のパスを入れておく 

参考サイト

http://www.cocoanetics.com/2010/05/physics-101-uikit-app-with-box2d-for-gravity/

サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

#import <Box2D/Box2D.h>

#define PTM_RATIO 16

@interface ViewController () {

    b2World *world;

    NSTimer *timer;

}

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

    

    // Box2Dの世界を作る

[self createPhysicsWorld];

}

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

{

    // タイマーをスタート

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

}

-(void)createPhysicsWorld

{

CGSize screenSize = self.view.bounds.size;

    

// 重力

b2Vec2 gravity;

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

    

// ワールドオブジェクト

world = new b2World(gravity);

world->SetContinuousPhysics(true);

    

b2BodyDef groundBodyDef;

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

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

    

b2EdgeShape groundBox;

    

// bottom

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

groundBody->CreateFixture(&groundBox, 0);

    

// left

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

groundBody->CreateFixture(&groundBox, 0);

    

// right

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

groundBody->CreateFixture(&groundBox, 0);

}

-(void) tick:(NSTimer *)sender

{

    static float lastTime;

    if (lastTime > 0.2) {

        [self addNewBrick];

        lastTime = 0;

    }

    lastTime += sender.timeInterval;

    

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 *oneView = (__bridge UIView *)b->GetUserData();

            

// y Position subtracted because of flipped coordinate system

CGPoint newCenter = CGPointMake(b->GetPosition().x * PTM_RATIO,

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

oneView.center = newCenter;

            

CGAffineTransform transform = CGAffineTransformMakeRotation(- b->GetAngle());

            

oneView.transform = transform;

}

}

}

– (void)addNewBrick

{

    float x = arc4random() % 320;

    

    UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];

    v.center = CGPointMake(x, 0);

    v.backgroundColor = [UIColor brownColor];

    v.layer.borderWidth = 1.0;

    v.layer.borderColor = [UIColor whiteColor].CGColor;

    v.layer.shadowOpacity = 0.01;

    [self.view addSubview:v];

    

    [self addPhysicalBodyForView:v];

}

-(void)addPhysicalBodyForView:(UIView *)physicalView

{

// Define the dynamic body.

b2BodyDef bodyDef;

bodyDef.type = b2_dynamicBody;

    

CGPoint p = physicalView.center;

CGPoint boxDimensions = CGPointMake(physicalView.bounds.size.width/PTM_RATIO/2.0,physicalView.bounds.size.height/PTM_RATIO/2.0);

    

bodyDef.position.Set(p.x/PTM_RATIO, (460.0 – p.y)/PTM_RATIO);

bodyDef.userData =  (__bridge void *)physicalView;

    

// Tell the physics world to create the body

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

    

// Define another box shape for our dynamic body.

b2PolygonShape dynamicBox;

    

dynamicBox.SetAsBox(boxDimensions.x, boxDimensions.y);

    

// Define the dynamic body fixture.

b2FixtureDef fixtureDef;

fixtureDef.shape = &dynamicBox;

fixtureDef.density = 3.0f;

fixtureDef.friction = 0.3f;

fixtureDef.restitution = 0.5f; // 0 is a lead ball, 1 is a super bouncy ball

body->CreateFixture(&fixtureDef);

    

// a dynamic body reacts to forces right away

body->SetType(b2_dynamicBody);

    

// we abuse the tag property as pointer to the physical body

physicalView.tag = (int)body;

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    [timer invalidate], timer = nil;

}

@end