概要
五角形等のポリゴンをBox2dを使って表示してみる。
(XcodeのiOS6 iPhone Simulatorで試しています。)
ポイント
・PanGestureRecognizerをつかって指で移動させる
・PolygonShapeのSet(points, 5) で五角形
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 <Box2D/Box2D.h>
#import “ViewController.h”
//
// PolygonMesh
//
@interface PolygonMesh : UIView
@property (nonatomic, assign) CGPoint p1,p2,p3,p4,p5;
@end
@implementation PolygonMesh
@synthesize p1,p2,p3,p4,p5;
– (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor redColor] set];
CGContextSetLineWidth(ctx, 2.0);
NSArray *arr = [NSArray arrayWithObjects:[NSValue valueWithCGPoint:p1],[NSValue valueWithCGPoint:p2],[NSValue valueWithCGPoint:p3],[NSValue valueWithCGPoint:p4],[NSValue valueWithCGPoint:p5], nil];
for (int i=1; i<[arr count] + 1; i++) {
CGPoint start = [[arr objectAtIndex:i-1] CGPointValue];
CGPoint end = [[arr objectAtIndex:i % ([arr count])] CGPointValue];
if (!CGPointEqualToPoint(end,CGPointZero)) {
CGContextMoveToPoint(ctx, start.x, start.y);
CGContextAddLineToPoint(ctx, end.x, end.y);
CGContextStrokePath(ctx);
CGContextFillEllipseInRect(ctx, CGRectMake(end.x–4, end.y–4, 8, 8));
}
}
}
@end
//
// ViewController
//
#define PTM_RATIO 16.0
@interface ViewController () {
b2World *world;
NSTimer *timer;
int status;
}
@end
@implementation ViewController
– (void)viewDidLoad
{
self.view.backgroundColor = [UIColor blackColor];
[super viewDidLoad];
[self createWorld];
[self createPolygons];
[self start];
}
-(void)createWorld
{
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;
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);
groundBox.Set(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox, 0);
}
// 五角形を作る
– (void)createPolygons
{
// Viewを用意
float r = 45;
float angle = M_PI / 2.5;
PolygonMesh *pentagon = [[PolygonMesh alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
pentagon.p1 = CGPointMake(r*cos(angle)+50, r*sin(angle)+50);
pentagon.p2 = CGPointMake(r*cos(angle*2)+50, r*sin(angle*2)+50);
pentagon.p3 = CGPointMake(r*cos(angle*3)+50, r*sin(angle*3)+50);
pentagon.p4 = CGPointMake(r*cos(angle*4)+50, r*sin(angle*4)+50);
pentagon.p5 = CGPointMake(r*cos(angle*5)+50, r*sin(angle*5)+50);
[self.view addSubview:pentagon];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
[pentagon addGestureRecognizer:pan];
// Box2dとViewを連携
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
CGPoint p = pentagon.center;
bodyDef.position.Set(p.x/PTM_RATIO, (self.view.bounds.size.height – p.y)/PTM_RATIO);
bodyDef.userData = (__bridge void *)pentagon;
b2Body *body = world->CreateBody(&bodyDef);
b2Vec2 points[5];
float x,y;
CGPoint wp;
wp = pentagon.p1;
x = (wp.x – pentagon.center.x) / PTM_RATIO;
y = (pentagon.center.y – wp.y) / PTM_RATIO;
points[0].Set(x, y);
wp = pentagon.p2;
x = (wp.x – pentagon.center.x) / PTM_RATIO;
y = (pentagon.center.y – wp.y) / PTM_RATIO;
points[1].Set(x,y);
wp = pentagon.p3;
x = (wp.x – pentagon.center.x) / PTM_RATIO;
y = (pentagon.center.y – wp.y) / PTM_RATIO;
points[2].Set(x, y);
wp = pentagon.p4;
x = (wp.x – pentagon.center.x) / PTM_RATIO;
y = (pentagon.center.y – wp.y) / PTM_RATIO;
points[3].Set(x, y);
wp = pentagon.p5;
x = (wp.x – pentagon.center.x) / PTM_RATIO;
y = (pentagon.center.y – wp.y) / PTM_RATIO;
points[4].Set(x, y);
b2PolygonShape dynamicBox;
dynamicBox.Set(points, 5);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
fixtureDef.restitution = 0.5f;
body->CreateFixture(&fixtureDef);
body->SetType(b2_dynamicBody);
}
– (void)tap:(UIGestureRecognizer*)gr
{
CGPoint p = [gr locationInView:self.view];
if (gr.state == UIGestureRecognizerStateBegan) {
status = 1;
} else if (gr.state == UIGestureRecognizerStateChanged) {
gr.view.center = p;
} else if (gr.state == UIGestureRecognizerStateEnded){
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) {
if ((__bridge UIView *)b->GetUserData() == gr.view) {
float32 angle = b->GetAngle();
const b2Vec2 position(p.x/PTM_RATIO,(self.view.bounds.size.height – p.y)/PTM_RATIO);
b->SetTransform(position, angle);
b->SetType(b2_dynamicBody);
if (!b->IsAwake()) {
b->SetAwake(true);
}
}
}
status = 0;
}
}
– (void)start
{
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f/60.0f target:self selector:@selector(tick:) userInfo:nil repeats:YES];
}
– (void)tick:(NSTimer*)sender
{
int32 velocityIterations = 8;
int32 positionIterations = 2;
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();
if (status == 0) {
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)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end