Last active
April 16, 2020 18:22
-
-
Save pontusarmini/bd72a48b0932086df63a to your computer and use it in GitHub Desktop.
Soft body CCSprite originally created by Sébastien Dabet (Gist: https://gist.github.com/sdabet/08a6f41415c5e3b8be0f and article: http://2sa-studio.blogspot.se/2014/05/soft-bodies-with-cocos2d-v3.html) updated for the new rendering engine (Cocos2d-SpriteBuilder >3.1). This is the slightly optimized Swift version set up for use with SpriteBuilder.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import CoreGraphics | |
class SwiftBubble:CCSprite { | |
static let π = CGFloat(M_PI) | |
//Number of segments (the more the smoother circle) and segment radius | |
static let numSegments = 60 | |
let physicsBodyRadius:CGFloat = 2.0 | |
//Control stiffness and damping | |
let innerStiffness:CGFloat = 1500 | |
let innerDamping:CGFloat = 50 | |
let outerStiffness:CGFloat = 1000 | |
let outerDamping:CGFloat = 50 | |
//This is set in didLoadFromCCB | |
var bubbleRadius:CGFloat! | |
//Vertices and texCoords | |
var vertices = [ccVertex2F]() | |
var texCoords = [ccTex2F]() | |
//Colors can be set up once in this implementation | |
let texColor = ccColor4F(r:1.0,g:1.0,b:1.0,a:1.0) | |
var verticesHasBeenSetUp = false | |
func didLoadFromCCB() | |
{ | |
bubbleRadius = contentSize.width/2 | |
let center = CGPointMake(bubbleRadius,bubbleRadius) | |
physicsBody = CCPhysicsBody(circleOfRadius: physicsBodyRadius, andCenter: center) | |
physicsBody.allowsRotation = false | |
let childDist = bubbleRadius - physicsBodyRadius | |
for i in 0...SwiftBubble.numSegments-1 | |
{ | |
let childAngle = CGFloat(i) * 2.0 * SwiftBubble.π / CGFloat(SwiftBubble.numSegments) | |
let child = CCNode() | |
child.physicsBody = CCPhysicsBody(circleOfRadius:physicsBodyRadius, andCenter:CGPointZero) | |
child.physicsBody.allowsRotation = false | |
child.position = CGPointMake(bubbleRadius + childDist * cos(childAngle), bubbleRadius + childDist * sin(childAngle)) | |
addChild(child) | |
CCPhysicsJoint(springJointWithBodyA: physicsBody, | |
bodyB: child.physicsBody, | |
anchorA: center, | |
anchorB: CGPointZero, | |
restLength: childDist, | |
stiffness: innerStiffness, | |
damping: innerDamping) | |
} | |
for (index, child) in enumerate(children as! [CCNode]) | |
{ | |
let previous = index == 0 ? children[SwiftBubble.numSegments-1] as! CCNode : children[index-1] as! CCNode | |
CCPhysicsJoint( | |
springJointWithBodyA: child.physicsBody, | |
bodyB: previous.physicsBody, | |
anchorA: CGPointZero, | |
anchorB: CGPointZero, | |
restLength: childDist * 2.0 * SwiftBubble.π/CGFloat(SwiftBubble.numSegments), | |
stiffness: outerStiffness, | |
damping: outerDamping | |
) | |
} | |
//In this implementation, the texture coordinates only has to be set up once | |
setUpTexCoords() | |
//Set up vertices for the first draw call | |
setUpVertices() | |
} | |
func setUpVertices() | |
{ | |
if verticesHasBeenSetUp {return} | |
//First, the center vertex | |
vertices.append(ccVertex2F(x: GLfloat(bubbleRadius), y: GLfloat(bubbleRadius))) | |
//Then the vertices creating the circle | |
for child in children as! [CCNode] { | |
vertices.append(ccVertex2F(x: GLfloat(child.position.x + physicsBodyRadius * (child.position.x-bubbleRadius)/bubbleRadius), y: GLfloat(child.position.y + physicsBodyRadius * (child.position.y-bubbleRadius)/bubbleRadius))) | |
} | |
//Lastly, the first vertex again (it'll be connected with the last) | |
vertices.append(vertices[1]) | |
verticesHasBeenSetUp = true | |
} | |
func updateVertices() | |
{ | |
if(!verticesHasBeenSetUp) { | |
setUpVertices() | |
} | |
for(index, child) in enumerate(children as! [CCNode]) | |
{ | |
vertices[index+1] = ccVertex2F(x: GLfloat(child.position.x + physicsBodyRadius * (child.position.x-bubbleRadius)/bubbleRadius), y: GLfloat(child.position.y + physicsBodyRadius * (child.position.y-bubbleRadius)/bubbleRadius)) | |
} | |
vertices[vertices.count-1] = vertices[1] | |
} | |
func setUpTexCoords() | |
{ | |
let deltaAngle:CGFloat = (2.0 * SwiftBubble.π) / CGFloat(SwiftBubble.numSegments) | |
texCoords.append(ccTex2F(u: 0.5, v:0.5)) | |
for i in 0...SwiftBubble.numSegments-1 { | |
let coordAngle = SwiftBubble.π + (deltaAngle * CGFloat(i)) | |
texCoords.append(ccTex2F(u: GLfloat(0.5 + cos(coordAngle)*0.5), v: GLfloat(0.5 + sin(coordAngle)*0.5))) | |
} | |
texCoords.append(texCoords[1]) | |
} | |
//Helper function that creates a CCVertex. Tagged @inline to make it a little faster | |
@inline(__always) func makeVertex(v:ccVertex2F, texCoord:ccTex2F, color:ccColor4F, transform: GLKMatrix4) -> CCVertex | |
{ | |
return CCVertex( | |
position: GLKMatrix4MultiplyVector4(transform, GLKVector4Make(v.x,v.y,0.0,1.0)), | |
texCoord1: GLKVector2Make(texCoord.u, texCoord.v), | |
texCoord2: GLKVector2Make(0.0, 0.0), | |
color: GLKVector4Make(color.r, color.g, color.b, color.a) | |
) | |
} | |
override func draw(renderer: CCRenderer!, transform: UnsafePointer<GLKMatrix4>) { | |
//Update vertices positions | |
updateVertices() | |
//Buffer | |
let buffer = renderer.enqueueTriangles(UInt(SwiftBubble.numSegments), andVertexes: UInt(vertices.count), withState: renderState, globalSortOrder: 0) | |
//Feed the buffer the vertices | |
for i in 0...SwiftBubble.numSegments+1 { | |
CCRenderBufferSetVertex(buffer,Int32(i),makeVertex(vertices[i], texCoord: texCoords[i], color: texColor, transform: transform.memory)) | |
} | |
//Feed the buffer the triangles | |
for i in 0...SwiftBubble.numSegments-1 { | |
CCRenderBufferSetTriangle(buffer, Int32(i), GLushort(0), GLushort(i+1), GLushort(i+2)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment