May 312014
 

UITextField’s Limitation

UITextField has a property called secureTextEntry that hides the characters as you type them (except for the last one, which is hidden after a brief moment). This is useful for things like a password field.

However, you will notice that the keyboard icon that lets you choose languages is absent. This means that you are limited to using English passwords. It is strange that Apple does not support this (FYI, Android does support it).

Challenges

The only solution I saw was this stackoverflow answer. However, it does not work for all languages. Languages like Korean (Hangul) has a composite character where each letter is composed of multiple symbols. For example, ‘ㅁ’, ‘ㅏ’ and ‘ㄴ’ are all individual characters, but when combined, it becomes ‘만’, which is treated as a single letter.

This means that NSString methods like stringByReplacingCharactersInRange:withString used in the solution in the stackoverflow post does not give the right output.

The Solution

The solution I came up with places a UILabel on top of a UITextField. The UILabel obscures the UITextField, thus hiding the characters being typed in the UITextField. I listen for the UITextFieldTextDidChangeNotification and set UILabel’s text with the appropriate number of “●” (UITextField’s length – 1), except for the last character which is shown. The last character is hidden after a second later if there is no further input.

The cursor is emulated with a ‘|’ character. The blinking is achieved with a NSTimer that toggles the cursor character between a space and a ‘|’.

Demo and Source

You can find the full source along with a sample (the one shown in the video) in my Github repository.

 Posted by at 12:16 am
May 282014
 

Java has a very useful class called the BlockingQueue. With a blocking queue, the dequeue operation will block until there is something to be dequeued. A blocking queue is very handy and can be used to solve the classic producer-consumer problem. I needed a similar class in Objective-C, but I could not find one. The following is a simple implementation.

Queue Implementation

The queue is implemented using NSMutableArray. A queue is a FIFO (First In, First Out) data structure, so enqueue adds an object to the end of the array (simple addObject suffices). Dequeue simply returns the first object in the array, and then removes it from the array (items are effectively shifted up a slot after the deletion).

Locking and Thread Synchronization

NSCondition is used for locking and thread synchronization. If you have two threads sharing a resource (the queue in our case), you will have the following code for the producer and the consumer.

The consumer:

[lock lock];
while (!someValueIsTrue) 
{
    [lock wait];
}
// Do protected work (in our case, dequeue)
[lock unlock]

The producer:

[lock lock];
// Do protected work (in our case, enqueue)
someValueIsTrue = true;
[lock signal];  
[lock unlock];

The Implementation

Here is the implementation of enqueue and dequeue. It is very simple and generally follows the pattern above. Remember, queueing and dequeueing must happen on different threads, so I am using a dispatch queue. If you call NSCondition’s lock in the same thread, you will get a deadlock.

- (void)enqueue:(id)object
{
    [_lock lock];
    [_queue addObject:object];
    [_lock signal];
    [_lock unlock];
}

- (id)dequeue
{
    __block id object;
    dispatch_sync(_dispatchQueue, ^{
        [_lock lock];
        while (_queue.count == 0)
        {
            [_lock wait];
        }
        object = [_queue objectAtIndex:0];
        [_queue removeObjectAtIndex:0];
        [_lock unlock];
    });
    
    return object;
}

Source Code and Sample

I hope this has been helpful to someone.

You can find the complete implementation along with sample code in my Github repository.

 Posted by at 3:49 pm
Sep 042012
 

Here is how to symbolicate iOS crash reports. Make sure the .app or .ipa and the .dSYM files are the same ones created at the time of the compilation.

Step 1

If you have an .app file
Place the .app file and the .dSYM file into a folder.

If you have an .ipa file
Rename it to a .zip file and extract it. It should create a directory called Payload with the the .app and .dSYM files.

Step 2

In terminal, go into the directory with the .app and .dSYM files and run the command.

atos -arch armv6|armv7 -o yourApp.app/yourApp address

Example:
atos -arch armv7 -o testApp.app/testApp 0x0006468e

 Posted by at 10:25 am
Apr 032012
 

Max Texture Size of iOS Devices

When loading OpenGL textures (or sprites, sprite sheets in Cocos2d), the max size for

1024 x 1024 2048 x 2048 4096 x 4096
iPhone 2g
iPhone 3g
iPhone 3gs
iPhone 4
iPad
iPhone 4s
iPad 2
iPad 3
 Posted by at 4:50 pm
Apr 012012
 

Prerequisites

This is the final part to a four part series about simulating a soft body physics in Box2d. Before continuing, you should read and understand the following tutorials.

1. Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan)
2. Soft Body Physics with Box2d and Cocos2d Part 1/4
3. Soft Body Physics with Box2d and Cocos2d Part 2/4
4. Soft Body Physics with Box2d and Cocos2d Part 3/4

In this final part, I will be showing you how to texture map the circular polygon constructed from the coordinates of the Box2d wheel structure.

Texture Mapping a Circle


In the previous article, we texture mapped a square. Now, we will texture map a circle. In the image above, you see that the circle polygon is constructed with a triangle fan, with the center at 1, then going to 2, 3, 4, 5, 6, 7 and 9 (which is the same as 2). We need to map these vertices to the texture image. So we map 1 to A, 2 to B, 3 to C, 4 to D, 5 to E, 6 to F, 7 to G, 8 to H and 9 to I (etc, for however many number of vertices you have).

Just remember that the texture map coordinates must be in the range of 0 to 1. We use the cosine and sine functions to calculate the x and y coordinates of each segment. However, cosine and sine functions give outputs in the rage of -1 to 1. So we must normalize the cosine and sine functions so that the values fall between 0 to 1.

Here is the source code for MyNode. You will also find the full source code for download and a demo video at the bottom of the article.

#import "CCNode.h"
#import "Box2D.h"

typedef struct {
    GLfloat x;
    GLfloat y;
} Vertex2D;

static inline Vertex2D Vertex2DMake(GLfloat inX, GLfloat inY) {
    Vertex2D ret;
    ret.x = inX;
    ret.y = inY;
    return ret;
}

#define NUM_SEGMENTS 20

@interface MyNode : CCNode {
    // Texture
    CCTexture2D *texture;
    
    // Physics bodies
    NSMutableArray *bodies;
    float deltaAngle;
    
    // Center circle
    b2Body *innerCircleBody;
    
    // Polygon vertices
    Vertex2D triangleFanPos[NUM_SEGMENTS+2];
    
    // Texture coordinate array
    Vertex2D textCoords[NUM_SEGMENTS+2];
}

- (void) createPhysicsObject:(b2World*)world;
- (void) bounce;

@end
#import "cocos2d.h"
#import "MyNode.h"

#define PTM_RATIO 32.f

@implementation MyNode

- (id) init {
    self = [super init];

    texture = [[CCTextureCache sharedTextureCache] addImage:@"Ball.png"];        

    return self;
}

- (void) createPhysicsObject:(b2World *)world {
    // Center is the position of the circle that is in the center (inner circle)
    b2Vec2 center = b2Vec2(240/PTM_RATIO, 160/PTM_RATIO);
    b2CircleShape circleShape;
    circleShape.m_radius = 0.1f;
    
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &circleShape;
    fixtureDef.density = 0.1;
    fixtureDef.restitution = 0.05;
    fixtureDef.friction = 1.0;
    
    // Delta angle to step by
    deltaAngle = (2.f * M_PI) / NUM_SEGMENTS;
    
    // Radius of the wheel
    float radius = 50;

    // Need to store the bodies so that we can refer back
    // to it when we connect the joints
    bodies = [[NSMutableArray alloc] init];
    
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        // Current angle
        float theta = deltaAngle*i;
        
        // Calculate x and y based on theta
        float x = radius*cosf(theta);
        float y = radius*sinf(theta);
        // Remember to divide by PTM_RATIO to convert to Box2d coordinate
        b2Vec2 circlePosition = b2Vec2(x/PTM_RATIO, y/PTM_RATIO);
        
        b2BodyDef bodyDef;
        bodyDef.type = b2_dynamicBody;
        // Position should be relative to the center
        bodyDef.position = (center + circlePosition);
        
        // Create the body and fixture
        b2Body *body;
        body = world->CreateBody(&bodyDef);
        body->CreateFixture(&fixtureDef);
        
        // Add the body to the array to connect joints to it
        // later. b2Body is a C++ object, so must wrap it
        // in NSValue when inserting into it NSMutableArray
        [bodies addObject:[NSValue valueWithPointer:body]];
    }
    
    // Circle at the center (inner circle)
    b2BodyDef innerCircleBodyDef;
    // Make the inner circle larger
    circleShape.m_radius = 0.8f;
    innerCircleBodyDef.type = b2_dynamicBody;

    // Position is at the center
    innerCircleBodyDef.position = center;
    innerCircleBody = world->CreateBody(&innerCircleBodyDef);
    innerCircleBody->CreateFixture(&fixtureDef);
    
    // Connect the joints
    b2DistanceJointDef jointDef;
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        // The neighbor
        const int neighborIndex = (i + 1) % NUM_SEGMENTS;
        
        // Get current body and neighbor
        b2Body *currentBody = (b2Body*)[[bodies objectAtIndex:i] pointerValue];
        b2Body *neighborBody = (b2Body*)[[bodies objectAtIndex:neighborIndex] pointerValue];
        
        // Connect the outer circles to each other
        jointDef.Initialize(currentBody, neighborBody,
                            currentBody->GetWorldCenter(), 
                            neighborBody->GetWorldCenter() );
        // Specifies whether the two connected bodies should collide with each other
        jointDef.collideConnected = true;
        jointDef.frequencyHz = 0.0f;
        jointDef.dampingRatio = 0.0f;
        
        world->CreateJoint(&jointDef);
        
        // Connect the center circle with other circles        
        jointDef.Initialize(currentBody, innerCircleBody, currentBody->GetWorldCenter(), center);
        jointDef.collideConnected = true;
        jointDef.frequencyHz = 8.0;
        jointDef.dampingRatio = 0.5;
        
        world->CreateJoint(&jointDef);
    }
}



- (void) draw {
    // Using the wheel defined by the box2d objects, we'll be mapping a circle on
    // top of it using a triangle fan. First, we calculate the center. The center
    // needs to be mulitplied by the PTM_RATIO (to get the pixel coordinate from box2d coordinate)
    // and also must be offset by the current position (remember, in HelloWorldLayer, we set
    // the position to the center of the screen (myNode.position = ccp(240, 160).
    triangleFanPos[0] = Vertex2DMake(innerCircleBody->GetPosition().x * PTM_RATIO - self.position.x, 
                                     innerCircleBody->GetPosition().y * PTM_RATIO - self.position.y);
    // Use each box2d body as a vertex and calculate coordinate for the triangle fan
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        b2Body *currentBody = (b2Body*)[[bodies objectAtIndex:i] pointerValue];
        Vertex2D pos = Vertex2DMake(currentBody->GetPosition().x * PTM_RATIO - self.position.x, 
                                    currentBody->GetPosition().y * PTM_RATIO - self.position.y);
        triangleFanPos[i+1] = Vertex2DMake(pos.x, pos.y);
    }
    // Loop back to close off the triangle fan
    triangleFanPos[NUM_SEGMENTS+1] = triangleFanPos[1];

    // The first vertex is the center of the triangle fan.
    // So the first coordinate of the texture map should
    // map to the center of the triangle fan. The texture
    // map coordinates are normalized between 0 and 1, so
    // the center is (0.5, 0.5)
    textCoords[0] = Vertex2DMake(0.5f, 0.5f);
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        
        GLfloat theta = M_PI + (deltaAngle * i);
        
        // Calculate the X and Y coordinates for texture mapping.
        // Need to normalize the cosine and sine functions so that the
        // values are between 0 and 1. Cosine and sine functions have
        // values from -1 to 1, so we divide by 2 and add 0.5 to it to
        // normalize the values between 0 and 1.
        textCoords[i+1] = Vertex2DMake(0.5+cosf(theta)*0.5, 
                                       0.5+sinf(theta)*0.5);

    }
    // Close it off.
    textCoords[NUM_SEGMENTS+1] = textCoords[1];
    
    // Enable texture mapping stuff
    glEnable(GL_TEXTURE_2D);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    
    // Bind the OpenGL texture
    glBindTexture(GL_TEXTURE_2D, [texture name]);

    // Send the texture coordinates to OpenGL
    glTexCoordPointer(2, GL_FLOAT, 0, textCoords);
    // Send the polygon coordinates to OpenGL
    glVertexPointer(2, GL_FLOAT, 0, triangleFanPos);
    // Draw it 
    glDrawArrays(GL_TRIANGLE_FAN, 0, NUM_SEGMENTS+2);
    
    glEnableClientState(GL_COLOR_ARRAY);
}

- (void) bounce {
    b2Vec2 impulse = b2Vec2(innerCircleBody->GetMass() * 0, innerCircleBody->GetMass() * 40);
    b2Vec2 impulsePoint = innerCircleBody->GetPosition();
    innerCircleBody->ApplyLinearImpulse(impulse, impulsePoint);	          
}

@end

Source and Video

You can download the full source code here.

Soft Body Physics with Box2d and Cocos2d Part 1/4
Soft Body Physics with Box2d and Cocos2d Part 2/4
Soft Body Physics with Box2d and Cocos2d Part 3/4

 Posted by at 12:35 pm
Mar 312012
 

Prerequisites

This is the third part to a four part series about simulating a soft body physics in Box2d. Before continuing, you should read and understand the following tutorials.

1. Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan)
2. Soft Body Physics with Box2d and Cocos2d Part 1/4
3. Soft Body Physics with Box2d and Cocos2d Part 2/4

The next step is to texture map the circle polygon. But before texture mapping the circle we have created in part 2 of the tutorial, let’s get some basics out of the way and texture map a simple shape in OpenGL. I will save the actual texture mapping of the soft body physics object for the final installment of the tutorial in part 4.

Texture Mapping Basics

The idea behind texture mapping is fairly straight forward. For each vertex of a polygon, we map that vertex to a coordinate on the texture. In the image above, the polygon is a square constructed with a triangle strip and has 4 vertices. We map each vertex of the square to a coordinate on the texture. We store the texture coordinates into an array called a texture coordinate array. Once we have the vertex array (for the vertices of the polygon) and the texture coordinate array, we send those off to OpenGL.

A Few Things to Remember

1. Texture coordinates must in the range of 0 to 1, with the origin at (0, 0).
2. On iOS (and hence when texture is loaded in Cocos2d), the texture is vertically flipped about the x-axis.
3. The width and height of an OpenGL texture must be in powers of 2.

To solve the #2 above, we’re going to simply flip the mapping like in the image below so that the top maps to the bottom and bottom maps to the top.

Let’s Code!

We will be loading the texture using Cocos2d. We then define the vertices for the square (triangle strip), vertices for the texture coordinates and then send them off to OpenGL. Here is MyNode.h and MyNode.m. Instantiate an instance of MyNode in HelloWorldLayer and add it to the layer. When you run it, you should see an image of Kirby on the screen mapped to the square polygon. The code is commented so you should be able to easily follow it.

#import "CCNode.h"

typedef struct {
    GLfloat x;
    GLfloat y;
} Vertex2D;

static inline Vertex2D Vertex2DMake(GLfloat inX, GLfloat inY) {
    Vertex2D ret;
    ret.x = inX;
    ret.y = inY;
    return ret;
}

@interface MyNode : CCNode {
    Vertex2D vertices[4];   // For the polygon
    Vertex2D textCoords[4]; // For the texture

    CCTexture2D *texture;
}
@end
#import "cocos2d.h"
#import "MyNode.h"

@implementation MyNode

- (id) init {
    self = [super init];

    // Load the texture
    texture = [[CCTextureCache sharedTextureCache] addImage:@"Kirby.png"];        

    GLfloat size = [texture pixelsWide]/2;

    // Vertices for the square (we'll be using a triangle strip)
    vertices[0] = Vertex2DMake(-size, size);
    vertices[1] = Vertex2DMake(size, size);
    vertices[2] = Vertex2DMake(-size, -size);
    vertices[3] = Vertex2DMake(size, -size);
    
    // Because the texture is flipped about its x-axis,
    // we flip the y coordinates to correct this.
    textCoords[0] = Vertex2DMake(0, 0);
    textCoords[1] = Vertex2DMake(1, 0);
    textCoords[2] = Vertex2DMake(0, 1);
    textCoords[3] = Vertex2DMake(1, 1);

    // If you use the mapping below, then the image will
    // be flipped. To correct this, use the coordinates
    // above, or save the image in a flipped state so it
    // will be unflipped when mapped!
    /*
    textCoords[0] = Vertex2DMake(0, 1);
    textCoords[1] = Vertex2DMake(1, 1);
    textCoords[2] = Vertex2DMake(0, 0);
    textCoords[3] = Vertex2DMake(1, 0);
     */
    
    return self;
}

- (void) draw {
    // Following two states must be enabled for texture mapping
    glEnable(GL_TEXTURE_2D);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glDisableClientState(GL_COLOR_ARRAY);
    
    // Bind the OpenGL texture
    glBindTexture(GL_TEXTURE_2D, [texture name]);

    // The vertices for the polygon
    glVertexPointer(2, GL_FLOAT, 0, vertices);
    // The vertices for the texture
    glTexCoordPointer(2, GL_FLOAT, 0, textCoords);
    // Send it off to OpenGL
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // Re-enable states
    glEnableClientState(GL_COLOR_ARRAY);
}
@end

Source and Video

You can download the source code for the tutorial here.

Soft Body Physics with Box2d and Cocos2d Part 1/4
Soft Body Physics with Box2d and Cocos2d Part 2/4
Soft Body Physics with Box2d and Cocos2d Part 4/4

 Posted by at 11:59 am
Mar 312012
 

Prerequisites

This is the second part to a four part series about simulating a soft body physics in Box2d. Before continuing, you should read and understand the following two tutorials.

1. Quick Tip: Mixing OpenGL and Cocos2d (Triangle Fan)
2. Soft Body Physics with Box2d and Cocos2d Part 1/4

We will be using OpenGL to map a circle to the Box2d wheel object, using a triangle fan.

Making the Wheel Bounce

We will also be making the wheel bounce when you touch the screen. In HelloWorldLayer, we will be calling the bounce method in MyNode, which will apply an impulse force to the wheel. Here is the code to do this. If you are unfamiliar with how Box2d works, please refer to the Box2d tutorials on this site.

- (void) bounce {
    b2Vec2 impulse = b2Vec2(innerCircleBody->GetMass() * 0, innerCircleBody->GetMass() * 150);
    b2Vec2 impulsePoint = innerCircleBody->GetPosition();
    innerCircleBody->ApplyLinearImpulse(impulse, impulsePoint);	        
}

Drawing the Circle

Inside the draw method is where we can place custom OpenGL calls in Cocos2d, so this is where we will be drawing the circle. What we are doing is using the Box2d objects we defined as the vertices for the triangle fan. It’s a simple matter of calculating the vertices to send to OpenGL. Here is the draw method of MyNode.

- (void) draw {
    // Using the wheel defined by the box2d objects, we'll be mapping a circle on
    // top of it using a triangle fan. First, we calculate the center. The center
    // needs to be mulitplied by the PTM_RATIO (to get the pixel coordinate from box2d coordinate)
    // and also must be offset by the current position (remember, in HelloWorldLayer, we set
    // the position to the center of the screen (myNode.position = ccp(240, 160).
    triangleFanPos[0] = Vertex2DMake(innerCircleBody->GetPosition().x * PTM_RATIO - self.position.x, 
                                     innerCircleBody->GetPosition().y * PTM_RATIO - self.position.y);
    

    // Use each box2d body as a vertex and calculate coordinate for the triangle fan
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        b2Body *currentBody = (b2Body*)[[bodies objectAtIndex:i] pointerValue];
        Vertex2D pos = Vertex2DMake(currentBody->GetPosition().x * PTM_RATIO - self.position.x, 
                                    currentBody->GetPosition().y * PTM_RATIO - self.position.y);
        triangleFanPos[i+1] = Vertex2DMake(pos.x, pos.y);
    }
    
    // Loop back to close off the triangle fan
    triangleFanPos[NUM_SEGMENTS+1] = triangleFanPos[1];

    // Disable the states. We're not texturing mapping just yet.
    glDisable(GL_TEXTURE_2D);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);

    // Set the color to red
    glColor4f(1.f, 0.f, 0.f, 1.f);

    glVertexPointer(2, GL_FLOAT, 0, triangleFanPos);
    // Number of vertices is NUM_SEGMENTS+2 because we have the origin
    // plus the loop back to close off the triangle fan
    glDrawArrays(GL_TRIANGLE_FAN, 0, NUM_SEGMENTS+2);

    // Re-enable states
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnable(GL_TEXTURE_2D);
}

Source and Video

You can download the full source for this tutorial here.

Soft Body Physics with Box2d and Cocos2d Part 1/4
Soft Body Physics with Box2d and Cocos2d Part 3/4
Soft Body Physics with Box2d and Cocos2d Part 4/4

 Posted by at 7:50 am
Mar 292012
 

Rigid Body and Soft Body Physics

This is the first of a 4 part series which will show you how to simulate a simple soft body physics object in Box2d. We will be attempting to draw a fully texture mapped, squishy object. In part 1, I will be constructing the physics object.

Box2d is a rigid body physics engine. This means that the physics bodies are non-deformable. But imagine a scenario where you do want a deformable physics body. A good example is a water balloon. The water balloon should be squishy and should deform when it hits the ground. A soft body physics engine can handle something like this. Unfortunately, Box2d is not a soft body physics engine. But there is a way to simulate this in Box2d using distance joints.

Distance Joint

A distance joint keeps the distance between two points a constant. The key feature of distance joint that will help us achieve the soft body physics simulation is that a distance joint can be made soft to simulate a springy connection. We will be creating a wheel like structure in Box2d and then connecting all of the bodies with a springy distance joint in order to simulate the squishiness.

You can read more about distance joints here.

Wheel Construction

The shape to the left is what we will be constructing in Box2d. The greater the number of circles in the outer ring, the better the circle will look. In a later part of the tutorial, I will go through constructing a polygon in OpenGL to map to this Box2d wheel construction.

The wheel is constructed of circles surrounding an inner circle. All of the circles are connected to each other by distance joints. The distance joints are configured to be springy. You may find it helpful to read through this post, which explains drawing a circle in Cocos2d using OpenGL. The math part will be relevant to our discussion here.

Let’s Start

Create a Box2d Cocos2d project. You will want to turn on the b2DebugDraw::e_jointBit so that you can see the joints in the debug drawing. I will not go through how to set up the physics world here. You can find articles on this site that can explain it to you if you do not know how to do this. Also, sample source code will be posted so you can refer to that as well. In HelloWorldLayer.mm, you will see the code below. MyNode is the our custom node which will hold our squishy physics object. We add this to the layer and move it to the center of the screen.

// MyNode is the squishy physics
MyNode *node = [MyNode node];
node.position = ccp(240, 160);
[node createPhysicsObject:world];
[self addChild:node];

Here is the source code for MyNode. We create the outer circles and lay them out in a circle pattern around the center circle. Then we connect them all together with distance joints. The code is well commented so you should be able to figure out what’s going on from reading it.

#import "CCNode.h"
#import "Box2D.h"

typedef struct {
    GLfloat x;
    GLfloat y;
} Vertex2D;

static inline Vertex2D Vertex2DMake(GLfloat inX, GLfloat inY) {
    Vertex2D ret;
    ret.x = inX;
    ret.y = inY;
    return ret;
}

@interface MyNode : CCNode {
}

- (void) createPhysicsObject:(b2World*)world;
@end
#import "cocos2d.h"
#import "MyNode.h"

#define PTM_RATIO 32.f

@implementation MyNode

- (id) init {
    self = [super init];   

    return self;
}

// The number of outer circles (more, the smoother the circle)
#define NUM_SEGMENTS 12

- (void) createPhysicsObject:(b2World *)world {
    // Center is the position of circle that is in the center
    b2Vec2 center = b2Vec2(240/PTM_RATIO, 260/PTM_RATIO);
    
    b2CircleShape circleShape;
    circleShape.m_radius = 0.25f;
    
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &circleShape;
    fixtureDef.density = 0.1;
    fixtureDef.restitution = 0.05;
    fixtureDef.friction = 1.0;
    
    // The greater the number, the more springy
    float springiness = 4.0;
    
    // Delta angle to step by
    float deltaAngle = (2.f * M_PI) / NUM_SEGMENTS;
    
    // Radius of the wheel
    float radius = 50;

    // Need to store the bodies so that we can refer back
    // to it when we connect the joints
    NSMutableArray *bodies = [NSMutableArray array];
    
    // For each segment...
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        // Current angle
        float theta = deltaAngle*i;
        
        // Calcualte the x and y based on theta 
        float x = radius*cosf(theta);
        float y = radius*sinf(theta);
        
        // Remember to divide by PTM_RATIO to convert to Box2d coordinates
        b2Vec2 circlePosition = b2Vec2(x/PTM_RATIO, y/PTM_RATIO);
        
        b2BodyDef bodyDef;
        bodyDef.type = b2_dynamicBody;
        // Position should be relative to the center
        bodyDef.position = (center + circlePosition);
        
        // Create the body and fixture
        b2Body *body;
        body = world->CreateBody(&bodyDef);
        body->CreateFixture(&fixtureDef);
        
        // Add the body to the array to connect joints to it
        // later. b2Body is a C++ object, so must wrap it
        // in NSValue when inserting into it NSMutableArray
        [bodies addObject:[NSValue valueWithPointer:body]];
    }
    
    // Circle at the center (inner circle)
    b2BodyDef innerCircleBodyDef;
    innerCircleBodyDef.type = b2_dynamicBody;    
    // Position is at the center
    innerCircleBodyDef.position = center;    
    b2Body *innerCircleBody = world->CreateBody(&innerCircleBodyDef);
    innerCircleBody->CreateFixture(&fixtureDef);
    
    // Connect the joints    
    b2DistanceJointDef jointDef;
    for (int i = 0; i < NUM_SEGMENTS; i++) {
        // The neighbor.
        int neighborIndex = (i + 1) % NUM_SEGMENTS;
        
        // Get the current body and the neighbor
        b2Body *currentBody = (b2Body*)[[bodies objectAtIndex:i] pointerValue];
        b2Body *neighborBody = (b2Body*)[[bodies objectAtIndex:neighborIndex] pointerValue];

        // Connect the outer circles to each other
        jointDef.Initialize(currentBody, neighborBody,
                            currentBody->GetWorldCenter(), 
                            neighborBody->GetWorldCenter() );
        jointDef.collideConnected = true;
        jointDef.frequencyHz = springiness;
        jointDef.dampingRatio = 0.5f;
        
        world->CreateJoint(&jointDef);
        
        // Connect the center circle with other circles        
        jointDef.Initialize(currentBody, innerCircleBody, currentBody->GetWorldCenter(), center);
        jointDef.collideConnected = true;
        jointDef.frequencyHz = springiness;
        jointDef.dampingRatio = 0.5;
        
        world->CreateJoint(&jointDef);
    }

}

@end

Video and Code

You can download the sample source for this article here.

Soft Body Physics with Box2d and Cocos2d Part 2/4
Soft Body Physics with Box2d and Cocos2d Part 3/4
Soft Body Physics with Box2d and Cocos2d Part 4/4

 Posted by at 9:07 pm
Mar 232012
 

Cocos2d and OpenGL

Cocos2d hides most of the complexities of OpenGL, but every now and then you will be forced to write some custom OpenGL code. You can override the draw method of CCNode to perform your custom OpenGL drawing. You don’t have to write the code for scaling, rotating or translating inside the draw method. Cocos2d will handle that for you. For example, if you manually draw a circle inside the draw method, you can move it with openGL’s glTranslate() call. However, a simpler way is to just reposition it by modifying the position property of the node:

mNode.position = ccp(100, 100)

If you need a primer on OpenGL, a good resource is Jeff Lamarche’s tutorials here.

Triangle Fan


For this quick tip, I will be drawing a circle using a triangle fan. Above is an image of a triangle fan. I’m going to be using a triangle fan to draw a circle on the screen.

It is pretty simple. First you define the origin, which is represented by 1 on the image. And then you define the rest of the vertices (2, 3, 4, 5, 6, 7) and then finally close it off by going back to 2. After you have defined all the vertices, you send it off to OpenGL to render it onto the screen.

Easy Math

I want to draw a circle, and I do not want to manually enter in all the vertices. So I will be generating the vertices in code. If you remember back to high school math, the x and y of a circle can be calculated with the use of the sine and cosine functions.

x = radius * cosine(theta)
y = radius * sine(theta)

Simple, right?

Code

First, create a Cocos2d project. Then create a class called MyNode, which inherits from CCNode. Inside HelloWorldLayer, create an instance of MyNode and add it to the scene like below. Notice that after the node has been added, I move it to position 200, 200 (I can do with OpenGL inside the draw method, but it’s faster and easier this way).

   
MyNode *n = [MyNode node];
n.position = ccp(200, 200);
[self addChild:n];

Here is the MyNode.h.

#import "CCNode.h"

typedef struct {
    GLfloat x;
    GLfloat y;
} Vertex2D;

static inline Vertex2D Vertex2DMake(GLfloat inX, CGFloat inY) {
    Vertex2D ret;
    ret.x = inX;
    ret.y = inY;
    return ret;
}
@interface MyNode : CCNode {
    Vertex2D *vertices;
    float radius;
    int numSegments;
}

And here is MyNode.m. I’ve commented the code, so it should be pretty easy to follow.

#import "MyNode.h"
#import "cocos2d.h"

@implementation MyNode

- (id) init {
    self = [super init];
    if (self) {
        // Radius of circle
        radius = 100;
        
        // Number segments of circle (bigger number, smoother circle)
        numSegments = 10;
        
        // Angle to step by
        float deltaAngle = 2 * M_PI / numSegments;
        
        // Allocate memory for vertices. We need 1 extra for the origin
        // and another for the end.
        vertices = (Vertex2D*) malloc(sizeof(Vertex2D) * (numSegments+1+2));
        
        // The origin vertex
        vertices[0] = Vertex2DMake(0, 0);
        
        for (int i = 1; i <= numSegments; i++) {
            GLfloat theta = deltaAngle*(i-1);
            
            // Calculate x and y based on theta
            GLfloat x = radius * cosf(theta);
            GLfloat y = radius * sinf(theta);
            
            vertices[i] = Vertex2DMake(x, y);
        }
        
        // Close it off
        vertices[numSegments+1] = vertices[1];
        
    }
    return self;

}



- (void) draw {
    [super draw];
    
    // Disable these states before drawing else you won't see anything
    glDisable(GL_TEXTURE_2D);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    
    // Set the color
    glColor4f(1.0, 0.0, 0.0, 1.0);
    
    // First argument - 2 numbers per Vertex2D (x and y)
    // Second argument - data type
    // Third argument - stride (ignore for now)
    // Fourth argument - pass the vertices
    glVertexPointer(2, GL_FLOAT, 0, vertices);
    
    // Last argument - total number of vertices
    glDrawArrays(GL_TRIANGLE_FAN, 0, (numSegments+2));
    
    // Renable disabled states
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnable(GL_TEXTURE_2D);
}
@end

Source Code

You can find the source code for download here.

It is based on Cocos2d 1.0.1 on Mac OS X Lion and XCode 4.3.

 Posted by at 3:27 pm