Quantcast
Channel: Hub.ae » Jelly Physics
Viewing all articles
Browse latest Browse all 2

Soft Body Physics (Jelly) Using SpriteKit

$
0
0

You will need SKUtils by  Ray Wenderlich. The library just makes life easier when working with SpriteKit

Creating soft body physics or jelly physics is nothing new. I based my approach on the awesome demo posted at Chipmunk2d. Chipmunk2d in case you don’t know is an excellent 2d physics engine, but since we are in SpriteKit land we will go with the default physics engine (Box2d).

The process of creating the illusion of soft body simulation is pretty simple. All you need is a few rigid bodies and some joints to connect them.

Soft Body Physics

 

Lets go over the steps in a little more detail:

  1. Create the main circle (a physics body)
  2. Create the outer circle (also a physics body)
  3. Create a Spring Joint to connect the outer circle to the main circle
  4. Repeat Step 2 and 3 a few times to get the general shape of the soft body (you need a minimum of 4 and probably no more than 20)
  5. Create a Spring Joint between each outer circle

Once you have the underlying physics body created then all that is needed is to draw a circle on top of it and have it conform to the position of each outer circle. Since SpriteKit doesn’t allow us low level access to the rendering pipeline, we fake it by using  SKShapeNode and creating a path that passes through each outer circle.

Sounds complicated but it isn’t. This is the code for it all:

The header file first:

#import <SpriteKit/SpriteKit.h>
 
@interface MyScene : SKScene
 
@end

and the implemention

#import "MyScene.h"
float m_SphereRadius = 0.25f;
float m_Radius = 30.0f;
 
// The mass of the entire sprite (each of the n reference point is 1/n times this value)
float m_Mass = 2.0f;
float m_Mass_Center = 4.0f;
 
// Circle-configuration only - how many rigid bodies are placed around the circle radius
int m_RadiusPoints = 22;
 
// how fast does the body recover from a bounce
float m_DampingRatio = 2.0f;
 
// How stiff the soft body physics springs are
float m_Stiffness = 9.0F;
 
@implementation MyScene {
 
    SKSpriteNode *mainCircle;
    NSMutableArray *bodies;
    SKShapeNode *ballLine;
    CGMutablePathRef pathToDraw;
    SKColor *ballOutlineColor;
    SKColor *ballFillColor;
    float ballGlowWidth;
}
 
-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
 
        self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
 
    }
    return self;
}
 
- (void)didMoveToView:(SKView *)view
{
 
    self.physicsWorld.gravity = CGVectorMake(0, -10);
    self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
 
    ballOutlineColor = [SKColor lightGrayColor];
    ballFillColor = [SKColor magentaColor];
    ballGlowWidth = 3.0f;
 
    [self createBall];
}
 
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
 
    for (UITouch *touch in touches) {
 
       [mainCircle.physicsBody applyImpulse:CGVectorMake(0, 1000)];
    }
 
}
 
-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    [self drawBall];
 
}
 
-(void) drawBall
{
 
    //remove the old ball from the scene
    [ballLine removeFromParent];
 
    // draw the new ball
    ballLine = [SKShapeNode node];
    SKSpriteNode *point = [bodies objectAtIndex:0];
 
    // create a path between the outer circles
    pathToDraw = CGPathCreateMutable();
    CGPathMoveToPoint(pathToDraw, NULL, point.position.x, point.position.y);
 
    for(int loop = 1; loop < [bodies count]; loop++)
    {
     point = [bodies objectAtIndex:loop];
     CGPathAddLineToPoint(pathToDraw, NULL, point.position.x, point.position.y);
 
    }
 
    // close the final gap between the last point and first point
    point = [bodies objectAtIndex:0];
    CGPathAddLineToPoint(pathToDraw, NULL, point.position.x, point.position.y);
 
    //draw the line
    ballLine.path = pathToDraw;
    [ballLine setStrokeColor:ballOutlineColor];
    [ballLine setGlowWidth:ballGlowWidth];
    [ballLine setFillColor:ballFillColor];
 
    [self addChild:ballLine];
    CGPathRelease(pathToDraw);
}
 
-(void) createBall
{
 
    int numPoints = m_RadiusPoints;
    float radius = m_Radius;
    float bigCircle_radius = m_Radius/2;
    float smallCircle_radius = m_Radius/6;
 
    //to hold the outer circles
    bodies = [[NSMutableArray alloc] initWithCapacity:numPoints];
 
    // create the main circle
    mainCircle = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(10, 10)];
    mainCircle.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bigCircle_radius];
    mainCircle.physicsBody.dynamic = YES;
    mainCircle.physicsBody.affectedByGravity = NO;
    mainCircle.physicsBody.mass = m_Mass_Center;
    mainCircle.position = CGPointMake(200   , 280);
    mainCircle.name = @"player";
    [self addChild:mainCircle];
 
    // create the outer circles
    for(int loop = 0; loop < numPoints; loop++)
          {
                // Work out the correct offset to place each outer circle
                float angle = ((M_PI * 2)/numPoints) * loop;
                float x_offset = cosf(angle);
                float y_offset = sinf(angle);
 
                x_offset *= radius;
                y_offset *= radius;
 
              SKSpriteNode *point = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(10, 10)];
              point.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:smallCircle_radius];
              point.physicsBody.dynamic = YES;
              point.physicsBody.affectedByGravity = YES;
              point.physicsBody.mass = m_Mass/numPoints;
              point.position = CGPointAdd(mainCircle.position,CGPointMake(x_offset, y_offset));
              point.name = @"smallcircle";
              [self addChild:point];
 
              // attach each out circle to the main circle by using a joint
              [self AttachPoint:mainCircle secondPoint:point];
              [bodies addObject:point];
            }
 
    // create a joint between each outer circile
    for(int loop = 1; loop < [bodies count]; loop++)
        {
                [self AttachPoint:[bodies objectAtIndex:loop]  secondPoint:[bodies objectAtIndex:(loop-1)]] ;
        }
 
    // close the outer circle by creating a joint between the last outer circle and the first out circle
    [self AttachPoint:[bodies objectAtIndex:([bodies count]-1)] secondPoint:[bodies objectAtIndex:1]];
 
    [self drawBall];
 
}
 
-(void)AttachPoint:(SKSpriteNode *)point1 secondPoint:(SKSpriteNode *)point2 {
 
    // create a joint between two bodies
    SKPhysicsJointSpring *joint = [SKPhysicsJointSpring jointWithBodyA:point1.physicsBody bodyB:point2.physicsBody anchorA:point1.position anchorB:point2.position  ];
 
    joint.damping = m_DampingRatio;
    joint.frequency = m_Stiffness;
 
    [self.physicsWorld addJoint:joint];
}
 
@end

Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images