ハンマー投げみたいな感じで、くるくる回したViewを投げてみる
(XcodeのiOS6 iPhone Simulatorで試しています。)
ポイント
Box2dのjointを使いView二つをくっつける。
PanGestureで指を離したらViewが飛んでいくようにする。
( jointの削除で、ハンマーから手を離したような動きになる )
Box2d導入手順
次のアドレスからソースを取得
svn checkout http://box2d.googlecode.com/svn/trunk/ box2d-read-only
1. Box2Dをプロジェクトに組み込む
Xcodeメニューの File -> Add File
2. C++ で書く。Fileの拡張子を変更
Box2dを使うソースの拡張子を mm に
3. Box2Dを import できるように Search Pathを追加
Build Settings (All) の Search Pathsのなかの Header Search Paths
サンプルコード
#import “ViewController.h”
#import <Box2D/Box2D.h>
#import <QuartzCore/QuartzCore.h>
#define PTM_RATIO 16
@interface ViewController () {
b2World *world;
b2Body *box;
b2Joint *joint;
NSTimer *timer;
UIView *ball;
UIView *backScene;
int ballThrow;
UILabel *meter;
}
@end
@implementation ViewController
– (void)viewDidLoad
{
[super viewDidLoad];
self.view.bounds = CGRectMake(0, 0, 8000, self.view.bounds.size.height);
[self ready];
}
– (void)clear
{
// 初期化
[timer invalidate];
timer = nil;
for (UIView *v in self.view.subviews) {
[v removeFromSuperview];
}
for (UIView *v in self.view.window.subviews) {
if (v != self.view) {
[v removeFromSuperview];
}
}
ball = nil;
backScene = nil;
meter = nil;
world->DestroyBody(box);
world = NULL;
ballThrow = 0;
self.view.center = self.view.window.center;
}
– (void)ready
{
[self createBackGround];
[self createPhysicsWorld];
[self createBox];
[self start];
}
– (void)reset
{
[self clear];
[self ready];
}
– (void) createBackGround
{
CGRect rect = CGRectMake(0, 0, 8000, 400);
backScene = [[UIView alloc] initWithFrame:rect];
backScene.backgroundColor = [UIColor whiteColor];
for (int i=0; i<80; i++) {
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(i*100, 0, 1, self.view.bounds.size.height)];
v.backgroundColor = [UIColor lightGrayColor];
[backScene addSubview:v];
}
[self.view addSubview:backScene];
}
– (void)createBox
{
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 40, 40)];
v.backgroundColor = [UIColor yellowColor];
[self.view addSubview:v];
[self addPhysicalBodyForView:v];
UIView *v2 = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 20, 20)];
v2.backgroundColor = [UIColor redColor];
v2.center = CGPointMake(170, 170);
[self.view addSubview:v2];
[self jointView:v2];
ball = v2;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[v addGestureRecognizer:pan];
}
– (void)createPhysicsWorld
{
CGSize screenSize = CGSizeMake(8000, 310);
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,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO, screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox, 0);
groundBox.Set(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO, 0));
groundBody->CreateFixture(&groundBox, 0);
groundBox.Set(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox, 0);
}
– (void)start
{
timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
}
– (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 * PTM_RATIO,
self.view.bounds.size.height – b->GetPosition().y * PTM_RATIO);
oneView.center = newCenter;
CGAffineTransform transform = CGAffineTransformMakeRotation(- b->GetAngle());
oneView.transform = transform;
}
}
if (ballThrow) {
{ // counter for ghost
static int counter;
counter++;
if (counter > 5) {
counter = 0;
[self showGhost];
}
}
self.view.center = CGPointMake(self.view.window.center.x, ball.center.x);
if (!meter) {
meter = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 200, 50)];
meter.transform = self.view.transform;
meter.font = [UIFont boldSystemFontOfSize:40];
[self.view.window addSubview:meter];
// reset button
UILabel *reset = [[UILabel alloc] initWithFrame:CGRectMake(250, 50, 50, 50)];
reset.transform = self.view.transform;
reset.text = @”reset”;
reset.userInteractionEnabled = YES;
[self.view.window addSubview:reset];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reset)];
[reset addGestureRecognizer:tap];
}
meter.text = [NSString stringWithFormat:@”%.2f”, ball.center.x];
}
}
– (void)showGhost
{
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
v.center = ball.center;
v.transform = ball.transform;
v.backgroundColor = ball.backgroundColor;
v.layer.borderColor =[UIColor whiteColor].CGColor;
v.layer.borderWidth = 1.0;
v.alpha = 0.8;
[self.view insertSubview:v belowSubview:ball];
[UIView animateWithDuration:1.0 animations:^{
v.alpha = 0.1;
} completion:^(BOOL finished) {
[v removeFromSuperview];
}];
}
– (void)addPhysicalBodyForView:(UIView *)physicalView
{
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;
b2Body *body = world->CreateBody(&bodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(boxDimensions.x, boxDimensions.y);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 5.0f;
fixtureDef.friction = 1.1f;
fixtureDef.restitution = 0.1f;
body->CreateFixture(&fixtureDef);
body->SetType(b2_dynamicBody);
}
– (void)jointView:(UIView*)v
{
b2DistanceJointDef jointDef;
box = world->GetBodyList();
b2BodyDef newBodyDef;
newBodyDef.type = b2_dynamicBody;
CGPoint boxDimensions = CGPointMake(v.bounds.size.width/PTM_RATIO/2.0, v.bounds.size.height/PTM_RATIO/2.0);
CGPoint p = v.center;
newBodyDef.position.Set(p.x/PTM_RATIO, (460.0 – p.y)/PTM_RATIO);
newBodyDef.userData = (__bridge void *)v;
b2Body* newBody = world->CreateBody(&newBodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(boxDimensions.x, boxDimensions.y);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 3.0f;
fixtureDef.friction = 0.3f;
fixtureDef.restitution = 0.5f;
newBody->CreateFixture(&fixtureDef);
jointDef.Initialize(box, newBody, box->GetPosition(), newBody->GetPosition());
jointDef.collideConnected = true;
jointDef.frequencyHz = 2.0f;
jointDef.dampingRatio = 2.1;
joint = world->CreateJoint(&jointDef);
}
– (void)pan:(UIGestureRecognizer*)gr
{
if (gr.state == UIGestureRecognizerStateBegan) {
box->SetType(b2_staticBody);
}
if (gr.state == UIGestureRecognizerStateEnded) {
box->SetType(b2_dynamicBody);
world->DestroyJoint(joint);
ballThrow = 1;
[gr.view removeGestureRecognizer:gr];
}
CGPoint p = [gr locationInView:self.view];
gr.view.center = p;
float32 angle = box->GetAngle();
const b2Vec2 position(p.x/PTM_RATIO,(self.view.bounds.size.height – p.y)/PTM_RATIO);
box->SetTransform(position, angle);
if (!box->IsAwake()) {
// box->SetAwake(true);
for (b2Body* b = world->GetBodyList(); b; b=b->GetNext()) {
b->SetAwake(true);
}
}
}
– (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end