I decided to learn Swift. I’ve always wanted to do some 3D game coding but have never taken the initiative learn a language that’s good for that. I’ve had some time in the evenings free up recently and took advantage.
Now I’m still learning Swift. I’ve only been messing with it for two weeks. My whole goal since starting has been to make a bimini ring toss game. If you’ve never heard of it, it’s popular in craft beer pubs. There’s a metal ring that hangs from the ceiling on a string. You pull the ring back and try to land it onto a hook on the wall.
SCNNode Shapes
There’s a few basic shapes provided in SceneKit that you can use to start building. I used a SCNTorus for the ring.
https://gist.github.com/ChrisFlannagan/29b3773b92b423890432a85032d87cb8
Like I said, I’m still learning, but here’s the gist of my understanding. SCNNodes are the visible objects you can manipulate in the 3D world. You add them to an SCNScene which is the world your 3D app lives in.
You can’t see the SCNNodes you add until they have shape so you need to build an SCNGeometry and add it to your SCNNode. The geometry can be built easily using some pre-built shapes like SCNTorus or SCNBox and a few sizing parameters.
Your nodes aren’t going to do much either until you add a SCNPhysicsBody to them. I’m still trying to wrap my head around the physics stuff but I get the basics which is all you need to build something like this rope. By making a physics body object and using .dynamic for type and nil for shape it just defaults physics boundaries to the shape of the node and interacts with other nodes that have physics bodies.
Faking A String
So googling “Swift 3D string” didn’t help me at all. Seeing as string is a global keyword for nearly every coding language, there were no results about an actual twine string being created in 3D.
I started looking for “rope” and found one codebase in c# that built a rope by chaining a bunch of cylinders together that had pivot joints connecting them. Made sense!
I want to learn Swift though, this meant I needed to build this functionality myself but at least I had a reference now. Let’s start by picking our shape. Since I wanted more of a string than a rope I went with spheres. The code example here has them bigger than they will be when the game is ready but it’s good for getting the functionality built.
https://gist.github.com/ChrisFlannagan/a27c23a9aa73e0975eb12e67bd261d07
I put this into a class. The full repo for this project can be downloaded and run in xcode 8 or newer. I create what I call links in the rope. I start with my base link called rope on init since it will be the main point of the whole object. Then I have a function for creating link nodes.
It’s important to note I’m using a radius of 0.1. I’m still not completely comfortable with these units, I’m trying to figure them out, but relatively they make sense.
Next, in the game’s main view controller I generate my rope and all the links in a loop and store them in an array called links. I do this because I wanted to add all my nodes to the scene in the same block of code. It’s not required to do that but I like the organization.
https://gist.github.com/ChrisFlannagan/59118b9af7abb047c00d228e4ec21ed0
The SCNPhysicsBallSocketJoint class took me the longest to find and learn it’s what I need to connect all these pieces together. Implementing it correctly took me some time but the logic makes sense when you really think about it.
Coordinates in SceneKit
Your SCNScene object has coordinates for specific points in x, y and z format. x = horizontal line, y = vertical line and z = depth (near/far) line. Now your SCNNode’s ALSO have x, y and z coordinates for points on the actual node. This can get confusing so take your time getting positioning of objects correct.
For the objects, we don’t need to set a position. Everything will be spawned at 0, 0, 0 which is right in the middle. You should add a SCNFloor that’s got a -y position so when gravity turns on your objects don’t just drop down into nothingness.
SCNPhysicsBallSocketJoint requires two SCNNode’s passed to it and it needs a x,y,z point on each node to connect them together. So bodyA is our link and bodyB is the previous link created in the loop. Using an SCNVector3 object we build the x,y,z coordinates.
When nodes are created they default to having the coordinates 0,0,0 right in the middle of the object. Remember our spheres are 0.1 radius. That means we want out point of connection to be on the top of one sphere and bottom of the other. So we usr -0.05 on one link and 0.05 on the other link for x, y and z. Now that I think about it a radius is half the width of our sphere so this number should be 0.1 but 0.05 works so maybe I’m wrong about where the default point is. Needless to say, you will have to do some math and toy with these numbers.
When you build your joint you have to add it to the SCNScene as a behavior using the addBehavior() method.
Dive In
Finally, we add all of these items to the SCNScene as children and we are set. They all need to be child nodes of the same parent node, I use on called Holder that I created. It’s an empty node with no geography so you can’t see it, it’s just like a container to keep them all related to each other.
https://gist.github.com/ChrisFlannagan/2557b1423b591dfbd37f6c3b5b17d11b
Check out the full repo and toy with it if you want. You can see that I have a ring object I attach to the end for my bimini game. I toy with mass on some nodes as I think that helps the ring feel heavier in the game world. I have a ceiling for attaching the rope and so on.
Hi Chris, I am from Philly and working on a startup. I followed your 3d chain article and building an app with a small SCNSphere joining to SCNCone with SCNPhysicsBallSocketJoint. But the joint doesn’t come where I expect. I want to join sphere bottom with top of the cone, but the joint is always lot below. I have been stuck with this problem from past few days and have spent so much time. Wondering if you can help. Considering how much time it will save me, I can also pay for your time. Please let me know if you’re willing to do a web conference call.