Skip to content

Instantly share code, notes, and snippets.

@pontusarmini
Last active April 16, 2020 18:22
Show Gist options
  • Save pontusarmini/bd72a48b0932086df63a to your computer and use it in GitHub Desktop.
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.
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