box2d concave body - iphone

I am creating a box2d body for my iOS game, that is built from 4 shapes that are convex.
The problem is that it fails when calling the init method.
Here's my code:
#implementation Banan
-(void)createBodyAtLocation:(CGPoint)location
{
int num;
float density = 1.0f;
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = b2Vec2(location.x/PTM_RATIO/RETSIZE, location.y/PTM_RATIO/RETSIZE);
body = world->CreateBody(&bodyDef);
body -> SetUserData(self);
//first shape
b2FixtureDef fixtureDef1;
b2PolygonShape shape1;
num = 4;
b2Vec2 verts1[4];
verts1[0].Set(58.9f / PTM_RATIO, 46.3f / PTM_RATIO);
verts1[1].Set(63.3f / PTM_RATIO, 41.5f / PTM_RATIO);
verts1[2].Set(47.4f / PTM_RATIO, 15.6f / PTM_RATIO);
verts1[3].Set(43.6f / PTM_RATIO, 24.3f / PTM_RATIO);
shape1.Set(verts1, num);
fixtureDef1.shape = &shape1;
fixtureDef1.density = density;
//second
b2FixtureDef fixtureDef2;
b2PolygonShape shape2;
num = 5;
b2Vec2 verts2[5];
verts2[0].Set(42.1f / PTM_RATIO, 21.7f / PTM_RATIO);
verts2[1].Set(46.6f / PTM_RATIO, -0.1f / PTM_RATIO);
verts2[2].Set(29.1f / PTM_RATIO, -32.2f / PTM_RATIO);
verts2[3].Set(2.5f / PTM_RATIO, -45.2f / PTM_RATIO);
verts2[4].Set(6.8f / PTM_RATIO, -10.4f / PTM_RATIO);
shape2.Set(verts2, num);
fixtureDef2.shape = &shape2;
fixtureDef2.density = density;
//third
b2FixtureDef fixtureDef3;
b2PolygonShape shape3;
num = 4;
b2Vec2 verts3[4];
verts3[0].Set(5.6f / PTM_RATIO, -9.7f / PTM_RATIO);
verts3[1].Set(-0.3f / PTM_RATIO, -45.7f / PTM_RATIO);
verts3[2].Set(-32.7f / PTM_RATIO, -41.2f / PTM_RATIO);
verts3[3].Set(-28.2f / PTM_RATIO, -15.7f / PTM_RATIO);
shape3.Set(verts3, num);
fixtureDef3.shape = &shape3;
fixtureDef3.density = density;
//fourth
b2FixtureDef fixtureDef4;
b2PolygonShape shape4;
num = 4;
b2Vec2 verts4[4];
verts4[0].Set(-28.7f / PTM_RATIO, -14.8f / PTM_RATIO);
verts4[1].Set(-40.8f / PTM_RATIO, -36.0f / PTM_RATIO);
verts4[2].Set(-60.5f / PTM_RATIO, -2.4f / PTM_RATIO);
verts4[3].Set(-58.0f / PTM_RATIO, 2.9f / PTM_RATIO);
shape4.Set(verts3, num);
fixtureDef4.shape = &shape4;
fixtureDef4.density = density;
//attach to shape
body->CreateFixture(&fixtureDef1);
body->CreateFixture(&fixtureDef2);
body->CreateFixture(&fixtureDef3);
body->CreateFixture(&fixtureDef4);
}
- (id)initWithWorld:(b2World *)tworld atLocation:(CGPoint)location {
if ((self = [super init])) {
world = tworld;
[self initWithFile:#"Banana.png"];
[self createBodyAtLocation:location];
}
return self;
}
-(void)dealloc
{
[super dealloc];
}
#end
The '-initWithWorld: atLocation:' method is invoked by ccTouchBegan method and then kills my app.
The errors are the following:
Assertion failed: (area > 1.19209290e-7F), function ComputeCentroid, ...
-(void)createBananaAtLocation:(CGPoint)location
{
Banan *ban = [[[Banan alloc] initWithWorld:world atLocation:location] autorelease];//fails at this line when is invoked
[self addChild:ban];
}
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView:[touch view]];
touchLocation = [[CCDirector sharedDirector]
convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
b2Vec2 locationWorld =
b2Vec2(touchLocation.x/PTM_RATIO, touchLocation.y/PTM_RATIO);
[self createBananaAtLocation:touchLocation]; //error at this line
return YES;
}

In your case vertices are oriented clockwise, while b2Polygon requirement is counterclockwise.
It leads to calculation of negative area of the polygon. You have to change order of your vertices.

Related

How to shoot arrow in Box2d?

I am trying to shoot arrow with force in left to right direction on Touches Ended method.I applied linear impulse to B2Body, but it is not shooting in correct direction.
Here is my code
(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"ended");
//Add a new body/atlas sprite at the touched location
CGPoint location;
for( UITouch *touch in touches )
{
location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
}
if (fabs(ccpDistance(startTouchPosition, location)) < 3) {
return;
}
CGPoint diff = ccpSub(startTouchPosition, location);
diff = CGPointMake(-diff.x, -diff.y);
//CGFloat distanceX = bird.position.x - 50.0;
// CGFloat distanceY = bird.position.y - 160.0;
CGFloat velocityX = diff.x*-1/5;
CGFloat velocityY = diff.y*-1/5;
b2Vec2 birdVelocity = b2Vec2(velocityX,velocityY);
//make b2Body
CGFloat sphereX = arrow.position.x/PTM_RATIO;
CGFloat sphereY = arrow.position.y/PTM_RATIO;
b2BodyDef bodyDef;
bodyDef.position.Set(sphereX,sphereY);
bodyDef.type = b2_dynamicBody;
int num = 6;
b2Vec2 verts[] = {
b2Vec2(-56.5f / PTM_RATIO, 0.8f / PTM_RATIO),
b2Vec2(-51.9f / PTM_RATIO, -2.6f / PTM_RATIO),
b2Vec2(53.7f / PTM_RATIO, -2.2f / PTM_RATIO),
b2Vec2(58.7f / PTM_RATIO, 0.7f / PTM_RATIO),
b2Vec2(52.5f / PTM_RATIO, 3.0f / PTM_RATIO),
b2Vec2(-55.0f / PTM_RATIO, 1.2f / PTM_RATIO)
};
b2PolygonShape polyShape;
polyShape.Set(verts, num);
b2FixtureDef fixtureDef;
fixtureDef.shape= &polyShape;
fixtureDef.density=4;
fixtureDef.restitution=0.1;
fixtureDef.friction=0.5;
b2Body * physicsBird = world->CreateBody(&bodyDef);
physicsBird->CreateFixture(&fixtureDef);
physicsBird->GetUserData();
arrow.rotation=atan2f(arrow.position.x-startTouchPosition.x,arrow.position.y-startTouchPosition.y)*180/M_PI +90;
direction = arrow.rotation;
b2Vec2 force = b2Vec2(direction, 24.8);
physicsBird->ApplyLinearImpulse(force, physicsBird->GetWorldCenter());
physicsBird->SetLinearDamping(0.4);
}
but it is not working.Please help!

Check Rotation in iphone SDk

In my app, i used SMRotatory wheel for rotation. It is working fine as rotation regarding.
But i want to detect that rotation is clockwise or anti-clockwise??
//My code is as follow:
- (BOOL)continueTrackingWithTouch:(UITouch*)touch withEvent:(UIEvent*)event
{
CGFloat radians = atan2f(container.transform.b, container.transform.a);
// NSLog(#"rad is %f", radians);
CGPoint pt = [touch locationInView:self];
float dx = pt.x - container.center.x;
float dy = pt.y - container.center.y;
float ang = atan2(dy,dx);
float angleDifference = deltaAngle - ang;
// NSLog(#"%f",angleDifference);
if (angleDifference>=0) {
//NSLog(#"positive");
[[NSUserDefaults standardUserDefaults]setValue:#"positive" forKey:#"CheckValue"];
}
else
{
// NSLog(#"negative");
[[NSUserDefaults standardUserDefaults]setValue:#"negative" forKey:#"CheckValue"];
}
container.transform = CGAffineTransformRotate(startTransform, -angleDifference);
bg.transform=CGAffineTransformRotate(startTransform, -angleDifference);
return YES;
}
Try something like this:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentTouch1=[touch locationInView:self.superview];
CGPoint currentTouch2=[touch previousLocationInView:self.superview];
CGFloat x = currentTouch2.x - currentTouch1.x;
CGFloat y = currentTouch2.y - currentTouch1.y;
CGFloat dist = sqrtf(x*x + y*y);
CGFloat cx = currentTouch1.x - self.superview.bounds.size.width / 2;
CGFloat cy = currentTouch2.y - self.superview.bounds.size.height / 2;
CGFloat cdist = sqrtf(cx*cx + cy*cy);
CGFloat angle = atan2(dist, cdist);
//NSLog(#"%f",angle);
CGFloat radians = atan2f(super.transform.b, super.transform.a);
mDegrees = radians * (180 / M_PI);
if (mDegrees < 0.0)
{
mDegrees+=360;
}
NSLog(#"Angle is = %f",angle); //positive is clockwise, negative is counterclockwise
CGAffineTransform transform = CGAffineTransformRotate(self.transform, angle);
self.transform = transform;
touch1 = currentTouch1;
touch2 = currentTouch2;
}

cocos2d + Accelerometer issue

I finished my game (cocos2d) but when I make an archive (from xcode product->Archive) then click on share (to get game.api) then put it in the itunes and sync to my iphone the accelerometer it seem that it didn't work I tested the game in 5 devices and it didn't work, the ship always in fix place and it didn't move, But if I click build and go from the xcode it work properly on all the developer devices I use the code below :
inside the init
- (id)init
{
if ((self = [super init]))
{
self.isAccelerometerEnabled = YES;
[self scheduleUpdate];
}
accelerometer handeler:
- (void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration {
#define kFilteringFactor 0.1
UIAccelerationValue rollingX, rollingY, rollingZ;
rollingX = (acceleration.x * kFilteringFactor) +
(rollingX * (1.0 - kFilteringFactor));
rollingY = (acceleration.y * kFilteringFactor) +
(rollingY * (1.0 - kFilteringFactor));
rollingZ = (acceleration.z * kFilteringFactor) +
(rollingZ * (1.0 - kFilteringFactor));
float accelX = acceleration.x - rollingX;
CGSize winSize = [CCDirector sharedDirector].winSize;
#define kRestAccelX 0.6
#define kShipMaxPointsPerSec (winSize.height*0.5)
#define kMaxDiffX 0.2
float accelDiffX = kRestAccelX - ABS(accelX);
float accelFractionX = accelDiffX / kMaxDiffX;
float pointsPerSecX = kShipMaxPointsPerSec * accelFractionX;
_shipPointsPerSecY = pointsPerSecX;
}
ship position:
- (void)updateShipPos:(ccTime)dt {
CGSize winSize = [CCDirector sharedDirector].winSize;
float maxY = winSize.height - _ship.contentSize.height/2;
float minY = _ship.contentSize.height/2;
float newY ;
(isYAxisInverted) ? newY = _ship.position.y + (-_shipPointsPerSecY * dt) : newY = _ship.position.y + (_shipPointsPerSecY * dt);
newY = MIN(MAX(newY, minY), maxY);
_ship.position = ccp(_ship.position.x, newY);
}
and in the update :
- (void)update:(ccTime)dt {
...
[self updateShipPos:dt];
...}
the solution is to replace:
UIAccelerationValue rollingX;
by
UIAccelerationValue rollingX = 0, rollingY = 0, rollingZ = 0;

iPhone, cocos2d and accelerometer

I'm reading 'Learn iPhone and iPad Cocos2D Game Development' and in chapter 4, there's a simple sample using accelerometer. It works well if I use x-axis only as the book but with y-axis, sprite's movement is not smooth if it on the edge of the screen.
+ (id)scene
{
CCScene* scene = [CCScene node];
CCLayer* layer = [GameScene node];
[scene addChild:layer];
return scene;
}
- (id)init
{
if ((self = [super init]))
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
self.isAccelerometerEnabled = YES;
player = [CCSprite spriteWithFile:#"alien.png"];
[self addChild:player z:0 tag:1];
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float imageHeight = [player texture].contentSize.height;
player.position = CGPointMake(screenSize.width / 2, imageHeight / 2);
[self scheduleUpdate];
}
return self;
}
- (void)dealloc
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
[super dealloc];
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
float deceleration = 0.4f;
float sensitivity = 6.0f;
float maxVelocity = 100;
playerVelocity.x = playerVelocity.x * deceleration + acceleration.x * sensitivity;
playerVelocity.y = playerVelocity.y * deceleration + acceleration.y * sensitivity;
if (playerVelocity.x > maxVelocity)
{
playerVelocity.x = maxVelocity;
}
else if (playerVelocity.x < -maxVelocity)
{
playerVelocity.x = -maxVelocity;
}
if (playerVelocity.y > maxVelocity)
{
playerVelocity.y = maxVelocity;
}
else if (playerVelocity.y < -maxVelocity)
{
playerVelocity.y = -maxVelocity;
}
}
- (void)update:(ccTime)delta
{
CGPoint pos = player.position;
pos.x += playerVelocity.x;
pos.y += playerVelocity.y;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float imageWidthHalved = [player texture].contentSize.width * 0.5f;
float leftBorderLimit = imageWidthHalved;
float rightBorderLimit = screenSize.width - imageWidthHalved;
if (pos.x < leftBorderLimit)
{
pos.x = leftBorderLimit;
playerVelocity = CGPointZero;
}
else if (pos.x > rightBorderLimit)
{
pos.x = rightBorderLimit;
playerVelocity = CGPointZero;
}
float imageHeightHalved = [player texture].contentSize.height * 0.5f;
float topBorderLimit = screenSize.height - imageHeightHalved;
float bottomBorderLimit = imageHeightHalved;
if (pos.y < bottomBorderLimit)
{
pos.y = bottomBorderLimit;
playerVelocity = CGPointZero;
}
else if (pos.y > topBorderLimit)
{
pos.y = topBorderLimit;
playerVelocity = CGPointZero;
}
player.position = pos;
}
What's the problem?
If you want you can try what I did in my game:
#define SIGN(x) ((x < 0.0f) ? -1.0f : 1.0f)
- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration {
float kFilteringFactor = 0.01;
accels[0] = acceleration.x * kFilteringFactor + accels[0] * (1.0f - kFilteringFactor);
accels[1] = acceleration.y * kFilteringFactor + accels[1] * (1.0f - kFilteringFactor);
accels[2] = acceleration.z * kFilteringFactor + accels[2] * (1.0f - kFilteringFactor);
float xx;
float yy;
// extract the acceleration components
xx = -[acceleration x];
yy = [acceleration y];
// Has the direction changed?
float accelDirX = SIGN(xvelocity) * -1.0f;
float newDirX = SIGN(xx);
float accelDirY = SIGN(yvelocity) * -1.0f;
float newDirY = SIGN(yy);
// Accelerate. To increase viscosity, lower the values below 1.0f
if (accelDirX == newDirX)
xaccel = (abs(xaccel) + 0.99f) * SIGN(xaccel);
if (accelDirY == newDirY)
yaccel = (abs(yaccel) + 0.99f) * SIGN(yaccel);
// Apply acceleration changes to the current velocity
xvelocity = -xaccel * xx;
yvelocity = -yaccel * yy;
[sprite moveByAccelerometerX:yvelocity Y:xvelocity];
}
First of all, thanks for reading my book! :)
You can find an improved filtering method (ease exponential) that I'm using in this article, it also contains basic info on how the accelerometer works.
You can learn more about accelerometer input from the Tilt to Live developers and their approach, comes with a sample project.
I have bought the book and I found that the edge of screen collision detection doesn't work properly unless you have the same size sprite as in the example. Either make your sprite the same size or make the sprite wrap around the screen instead of stopping, which is what I did.

How to move a label with the wave of the iPhone?

The code below is from the book Beginning iPhone 3 Development, chapter 15, using the accelerometer to control a ball.
I want to use a label instead of the ball, and only move in the x direction.
However, I donno how to revise the code to achieve this goal.
Can anyone please help me?
THX so much!!!!
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
self.image = [UIImage imageNamed:#"ball.png"];
self.currentPoint = CGPointMake((self.bounds.size.width / 2.0f) +
(image.size.width / 2.0f),
(self.bounds.size.height / 2.0f) + (image.size.height / 2.0f));
ballXVelocity = 0.0f;
ballYVelocity = 0.0f;
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
[image drawAtPoint:currentPoint];
}
- (void)dealloc {
[image release];
[acceleration release];
[super dealloc];
}
//#pragma mark -
- (CGPoint)currentPoint {
return currentPoint;
}
- (void)setCurrentPoint:(CGPoint)newPoint {
previousPoint = currentPoint;
currentPoint = newPoint;
if (currentPoint.x < 0) {
currentPoint.x = 0;
//ballXVelocity = 0;
ballXVelocity = - (ballXVelocity / 2.0);
}
if (currentPoint.y < 0){
currentPoint.y = 0;
//ballYVelocity = 0;
ballYVelocity = - (ballYVelocity / 2.0);
}
if (currentPoint.x > self.bounds.size.width - image.size.width) {
currentPoint.x = self.bounds.size.width - image.size.width;
//ballXVelocity = 0;
ballXVelocity = - (ballXVelocity / 2.0);
}
if (currentPoint.y > self.bounds.size.height - image.size.height) {
currentPoint.y = self.bounds.size.height - image.size.height;
//ballYVelocity = 0;
ballYVelocity = - (ballYVelocity / 2.0);
}
CGRect currentImageRect = CGRectMake(currentPoint.x, currentPoint.y,
currentPoint.x + image.size.width,
currentPoint.y + image.size.height);
CGRect previousImageRect = CGRectMake(previousPoint.x, previousPoint.y,
previousPoint.x + image.size.width,
currentPoint.y + image.size.width);
[self setNeedsDisplayInRect:CGRectUnion(currentImageRect,
previousImageRect)];
}
- (void)draw {
static NSDate *lastDrawTime;
if (lastDrawTime != nil) {
NSTimeInterval secondsSinceLastDraw =
-([lastDrawTime timeIntervalSinceNow]);
ballYVelocity = ballYVelocity + -(acceleration.y *
secondsSinceLastDraw);
ballXVelocity = ballXVelocity + acceleration.x *
secondsSinceLastDraw;
CGFloat xAcceleration = secondsSinceLastDraw * ballXVelocity * 500;
CGFloat yAcceleration = secondsSinceLastDraw * ballYVelocity * 500;
self.currentPoint = CGPointMake(self.currentPoint.x + xAcceleration,
self.currentPoint.y +yAcceleration);
}
// Update last time with current time
[lastDrawTime release];
lastDrawTime = [[NSDate alloc] init];
}
To start, you create the label as a property of the controller. If you use IB then make it an IBOutlet and hook it up. Then in the code above, where it uses the ball image, substitute the label. Of course, you don't have to draw the label it will handle that itself.
The movement logic is exactly the same except you move the center or origin of the label instead of drawing the ball in a rect.
Edit
thank u! But I am not quite understand
how to move the center of the label?
Like this:
self.myLabel.center=CGPointMake(newX,newY);
You can also animate the change so that the label appears to move smoothly. See the UIView class docs for the methods.