iPhone トロッコ ジャンプ

Box2Dを使って、坂を転がってくるトロッコをジャンプさせる簡単なゲームをiPhoneアプリとして書いてみます。坂の角度を指で変更できるようにして、遠くにジャンプさせたり、ちょっとだけしかジャンプしないようにとあそんでみましょう。


動作イメージ
XcodeからiOS6 iPhone Simulatorで動かすとこんな感じになります。

Box2d導入手順(覚え書き)
1. Box2DのサイトのDownloadからソースを取得する
2 Box2Dをプロジェクトに追加
3. C++ で書く。Box2dを使うソースの拡張子を .mm に
4. Build Settings (All) の Header Search PathsにBox2Dを配置したフォルダを指定

サンプルコード

#import “ViewController.h”

#import <QuartzCore/QuartzCore.h>

#import <Box2D/Box2D.h>

#define PixelToMeter_RATIO 16.0

#define screen CGSizeMake(568, 300)

@interface ViewController () {

    b2World *world;

    b2Body *slopeBody;

    CAShapeLayer *sl;

    

    NSTimer *timer;

    CGPoint slopeTop;

}

@end

@implementation ViewController

– (void)viewDidLoad

{

    [super viewDidLoad];

    self.view.backgroundColor = [UIColor blueColor];

    

    [self createWorld];

    

    [self createSlope:200];

    

    [self createCart];

    

    [self start];

}

– (void)createWorld

{    

    b2Vec2 gravity;

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

    

    world = new b2World(gravity);

    world->SetContinuousPhysics(true);

    

    b2BodyDef groundBodyDef;

    groundBodyDef.position.Set(0, 0);

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

    

    b2EdgeShape groundBox;

    groundBox.Set(b2Vec2(0,0), b2Vec2(screen.width/PixelToMeter_RATIO, 0));

    

    groundBody->CreateFixture(&groundBox, 0);

}

– (void)createSlope:(float)height

{

    slopeTop = CGPointMake(0, height);

    CGPoint bottom = CGPointMake(200, 300);

    CGPoint jump0 = CGPointMake(260, 300);

    CGPoint jump1 = CGPointMake(280, 280);

    

    // view

    [sl removeFromSuperlayer];

    

    UIBezierPath *path = [UIBezierPath bezierPath];

    CGPoint p[] = {slopeTop, bottom, jump0, jump1};

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

        [path moveToPoint:p[i * 2]];

        [path addLineToPoint:p[i * 2 + 1]];

    }

    sl = [[CAShapeLayer alloc] init];

    sl.fillColor = [UIColor blackColor].CGColor;

    sl.strokeColor = [UIColor blackColor].CGColor;

    sl.lineWidth = 5;

    sl.path = path.CGPath;

    [self.view.layer addSublayer:sl];

    

    if (slopeBody) {

        world->DestroyBody(slopeBody);

    }

    

    b2BodyDef groundBodyDef;

    groundBodyDef.position.Set(0, 0);

    slopeBody = world->CreateBody(&groundBodyDef);

    

    b2EdgeShape s1;

    s1.Set(b2Vec2(slopeTop.x, (screen.heightslopeTop.y)/PixelToMeter_RATIO), b2Vec2(bottom.x /PixelToMeter_RATIO, (screen.height – bottom.y)/PixelToMeter_RATIO));

    slopeBody->CreateFixture(&s1, 0);

    

    b2EdgeShape ground;

    ground.Set(b2Vec2(jump0.x /PixelToMeter_RATIO, 0), b2Vec2(jump1.x /PixelToMeter_RATIO, (screen.height – jump1.y)/PixelToMeter_RATIO));

    slopeBody->CreateFixture(&ground, 0);

}

– (void)createCart

{

    // cart body

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

    v.center = CGPointMake(20, 0);

    v.backgroundColor = [UIColor orangeColor];

    [self.view addSubview:v];

    b2FixtureDef fixtureDef;

fixtureDef.density = 1.0f;

fixtureDef.friction = 0.3f;

fixtureDef.restitution = 0.1f;

    b2Body *cartBody = [self addPhysicalBodyForBoxView:v type:fixtureDef];

    cartBody->SetLinearVelocity(b2Vec2(10.0, –80.0));

    

    // tires

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

        UIView *tire = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];

        tire.backgroundColor = [UIColor blackColor];

        tire.layer.cornerRadius = 9.0;

        tire.center = CGPointMake(20 + i *20, 25);

        [self.view addSubview:tire];

        b2Body *tireBody = [self addPhysicalBodyForCircleView:tire type:fixtureDef];

        

        b2RevoluteJointDef revoluteJointDef;

        revoluteJointDef.bodyA = cartBody;

        revoluteJointDef.bodyB = tireBody;

        revoluteJointDef.collideConnected = false;

        

        revoluteJointDef.localAnchorA.Set(-1.0 + i * 2.0, 0);

        revoluteJointDef.localAnchorB.Set(0,0);

        

        world->CreateJoint(&revoluteJointDef);

    }

}

– (b2Body*)addPhysicalBodyForBoxView:(UIView *)physicalView type:(b2FixtureDef)fixtureDef

{    

    // Define the dynamic body.

    b2BodyDef bodyDef;

    bodyDef.type = b2_dynamicBody;

    

    CGPoint p = physicalView.center;

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

    

    bodyDef.position.Set(p.x/PixelToMeter_RATIO, (screen.height – p.y)/PixelToMeter_RATIO);

    bodyDef.userData = (__bridge void *)physicalView;

    

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

    

    b2PolygonShape dynamicBox;

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

    fixtureDef.shape = &dynamicBox;

    body->CreateFixture(&fixtureDef);

    

    body->SetType(b2_dynamicBody);

    

    return body;

}

– (b2Body*)addPhysicalBodyForCircleView:(UIView *)physicalView type:(b2FixtureDef)fixtureDef

{    

    // Define the dynamic body.

    b2BodyDef bodyDef;

    bodyDef.type = b2_dynamicBody;

    

    CGPoint p = physicalView.center;    

    bodyDef.position.Set(p.x/PixelToMeter_RATIO, (screen.height – p.y)/PixelToMeter_RATIO);

    bodyDef.userData = (__bridge void *)physicalView;

    

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

    

    b2CircleShape circle;

    circle.m_radius = physicalView.bounds.size.width/PixelToMeter_RATIO/2.0;

    fixtureDef.shape = &circle;

    body->CreateFixture(&fixtureDef);

    body->SetType(b2_dynamicBody);

    

    return body;

}

– (void)start

{

    // タイマーをスタート

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

}

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

{

    CGPoint p = [[touches anyObject] locationInView:self.view];

    [self createSlope:p.y];

}

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

{

    [self createCart];

}

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

CGPoint newCenter = CGPointMake(b->GetPosition().x * PixelToMeter_RATIO, self.view.bounds.size.height – b->GetPosition().y * PixelToMeter_RATIO);

oneView.center = newCenter;

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

oneView.transform = transform;

}

}

}

– (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

@end