I have a sphere that is a dynamic body. I would like to animate the scale of this sphere so that it grows in size:
let sphere = SCNNode(geometry: SCNSphere(radius: 1))
scene.rootNode.addChildNode(sphere)
sphere.physicsBody = SCNPhysicsBody.dynamicBody()
sphere.runAction(SCNAction.scaleTo(10, duration: 1))
However, this does not seem to work. The sphere falls down due to gravity, but stays the same size. If I comment out the line that gives the sphere its physics body, then the scaling animation plays out like it should.
This strange phenomenon is even observed without an animation. Simply changing the scale of the sphere directly doesn't work:
let sphere = SCNNode(geometry: SCNSphere(radius: 1))
scene.rootNode.addChildNode(sphere)
sphere.physicsBody = SCNPhysicsBody.dynamicBody()
sphere.scale = SCNVector3(x: 10, y: 10, z: 10)
The sphere drops down but remains with a radius of 1. Unless the sphere is scaled before its physics body is added. In which case, the sphere starts out scaled to 10 and drops down, keeping its radius of 10.
Interestingly, upon further inspection by printing out the sphere's scale after the animation has supposedly run, it is observed that the scale has indeed changed, it's just not visible unless the physics body is removed:
let sphere = SCNNode(geometry: SCNSphere(radius: 1))
scene.rootNode.addChildNode(sphere)
sphere.physicsBody = SCNPhysicsBody.dynamicBody()
sphere.runAction(SCNAction.scaleTo(10, duration: 1), completionHandler: {() in
println(sphere.scale.x)
sphere.physicsBody = nil
})
Why is it not possible to change the scale of dynamic body? (Note that static and kinematic bodies will scale just fine.) Is it possible to achieve this in some way?
You wouldn't just need a "scaling velocity" to interact with other objects as one changes size, you'd need a "scaling force". How strongly should it push on things that it collides with while growing? And what happens if it's in a situation that depends on its mass, like balancing on a seesaw?
Game engines are already loose approximations of real-world physics, so asking them to figure out Ant-Man physics on their own is a bit of a stretch. If they could do that, they could probably also start building killer robots, and that'd kinda ruin your day. :)
Anyway, depending on how you want your expanding sphere to affect things, you have a few options. One is to delete and re-create the physics body at intervals:
let duration = 5.0
node.runAction(.customActionWithDuration(duration, actionBlock: { node, progress in
let scale = 1.0 + progress / CGFloat(duration)
node.physicsBody = nil
node.scale = SCNVector3(x: scale, y: scale, z: scale)
node.physicsBody = .dynamicBody()
}))
This does it every frame, which could be costly — you might want to throw some gating on progress in there so it happens less often. (And depending on what other effects need to happen as a result of the sphere changing size, you could set things like mass when you re-create the physics body.)
Another option might be to look at SCNPhysicsField. Use a radial gravity field to make a region that shoves everything out of its area of influence, then animate its parameters to change size and strength over time.
I ran into this when trying to scale a model I was importing from a .scn file. Just like rickster did, I ended up removing the physics body, scaled the shape & then re-attached the physics body. This was only done during startup, so not a big performance impact.
Physics simulation is a kind of animation in addition of implicit/explicit animations or actions. So when change the animatable property to a node physics affected, take special consideration of animation state of node.
on the other hand,sceneKit apply result of physics simulation to presentationNode, which represents node's current render state. you can check information of presentationNode to confirm why your animation sucks.
Related
I'm writing a 3D space game using SceneKit and I'm pretty happy with it so far, but I'm starting to hit some limitations and would like some advice.
I have a sphere which represents a star that sits on a point light, and I'd like to add some effects to this to make it look more realistic. I think I should use the sphere's shaderModifiers to do this, but I'm not sure which modifiers I should be looking at i.e. to achieve a lens flare effect. Infact if anyone can give me a clear explanation of the differences between: ShaderModifiers, SCNProgram and SCNTechnique that would be great!
I'd like to draw a 1px circle to represent an orbit. I've tried using a cylinder that is really thin, but this results in some visual artefacts (the ring seems to have gaps at larger distances and break up). Any ideas how I can do this and maintain a nice smooth circle?
The shortest way to have a flare effect for SceneKit models is to use a CoreImage framework's capabilities.
For making a visible orbit use a regular PNG-image technique with premultiplied alpha channel (RGBxA). Look at this link to find out how a png image with orbits looks like (save a file). And if you wanna automatically orient a plane toward a camera use a constraint.
And here's a code:
let orbitsOnPlane = SCNPlane(width: 10, height: 10)
let material = SCNMaterial()
material.diffuse.contents = UIImage(named:"overlayOrbits.png")
orbitsOnPlane.materials = [material]
let orbitNode = SCNNode()
orbitNode.geometry = orbitsOnPlane
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
let constraint = SCNLookAtConstraint(target: orbitNode)
cameraNode.constraints = [constraint]
I wanted to do a simple asteroids games where the asteroids go from far to bypass you (z>0) in SceneKit.
let moveAction = SCNAction.move(to: SCNVector3(0, -10, 10), duration: 2)
rockNode.runAction(moveAction)
Says I have a spaceship in Z axis 0, even though I can visually see the asteroids pass through the spaceship, the collision detection does not occurred. The collision only happened if the end point of the moveAction ends in Z axis 0 and in the same location as the spaceship.
Does detection only occurred after the moveAction ended (thus will not detect collision)? If yes, what solution do I have in detecting the collision during the asteroids movement?
did you set the isDynamic to true on both of your objects? and they both need to have different categoryBitmasks otherwise SceneKit will treat them as the same objects (can't comment don't have 50 rep yet) if not then do so collision should happen even when moving an object programmatically
Make the physics body as: kinematic. So it can detect collision even during SCNAction.
eg:
rockNode?.type = .kinematic
Or you can set it from Physics Inspector
Keep coding....... :)
I have a few images that can detect when they get touched right now I am using
plane.physicsBody = SKPhysicsBody(circleOfRadius: plane.frame.height / 2)
but I need it so it is the boarder of the image not a circle or rectangle.
We have a plane and you want a physical body.
Your first approach is circleOfRadius (the physical representation is a circle with a given radius). You can use another shape, such a rectangle:
plane.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(100, height: 100))
As explained in the comments you can also use the shape of a sprite (texture) to create the shape of the physics body:
plane.physicsBody = SKPhysicsBody(texure: plane.texture!, size: plane.size)
But a good way to choose it's this: if you want your game to run smootly on many different devices, don't make sure the physics calculations become too complicated. In other words if you have many object around your plane with physical behavior moving around and colliding with each other, you may choose to use simplified shapes to deal with the physics more efficiently, especially if a shape already looks a lot like a rectangle or a circle.
I managed to create a tree and the program is supposed to only allow users to rotate this tree horizontally.
After looking at the doc, I use the following code
treeNode.physicsBody?.angularVelocityFactor = SCNVector3Make(0, 0, 1.0)
But this didn't do anything, I was still able to rotate the tree in every direction.
What is the correct way to limit node rotation in only horizontal direction?
Are you really trying to restrict the node's rotation? Or do you want to restrict the camera's rotation?
If the former, you'll have to provide much more detail on your body's physics and structure. An approach using SCNPhysicsHingeJoint seems like it would work.
let joint = SCNPhysicsHingeJoint.init(body: treeNode,
axis: SCNVector3Make(0, 0, 1.0),
anchor: SCNVector3Make(xpos, ypos, zpos))
If you're just trying to control the camera, though, you should turn off allowsCameraControl for the SCNView. That's only useful for quick and dirty testing. Then you can implement the technique described here (Rotate SCNCamera node looking at an object around an imaginary sphere) and modified here (SCNCamera limit arcball rotation).
I am trying to make a tube and its physics body like so in SceneKit.
let BoxGeometry = SCNTube(innerRadius: 5, outerRadius: 12.5, height: 4)
Box = SCNNode(geometry: BoxGeometry)
Box.pivot = SCNMatrix4MakeRotation(Float(M_PI_2/8), 0, 1, 0)
Box.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Static, shape: nil)
Box.physicsBody?.mass = 5
Box.categoryBitMask = colorCategory
scene.rootNode.addChildNode(Box)
However, when another object falls onto this object it does not pass through the center. Instead it sits appearing to be levitating in air. It is acting like the physics body is a complete cylinder, not like the tube with the hole in the middle. How can I fix this so that objects can pass through the center? The tubes appearance looks as expected.
Thanks!
Dynamic bodies in SceneKit must be convex. (If you look into the general theory behind collision detection in games, i.e. not just SceneKit, you'll find that there are massive differences in speed and efficiency between collision detection on convex versus concave shapes.) A tube is a concave shape — it has a hole in it.
Luckily, your tube is being used as a static body. For static bodies only, there's an option to make the physics shape (an approximation of) a concave geometry:
let shape = SCNPhysicsShape(geometry: tube,
options: [SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeConcavePolyhedron])
box.physicsBody = SCNPhysicsBody(type: .Static, shape: shape)
There is a performance cost to this. If you find your framerate limited by CPU usage after this change, or if you want to have a dynamic body be (effectively) concave, you might get better performance by making a physics shape that's a compound of several other shapes — e.g. build a dummy node hierarchy (not one that's actually in your scene) containing a bunch of cylinders or boxes arranged into a ring, then make a physics shape from them with SCNPhycsicsShape(node:options:).
Change the shape of your physicsBody to match the geometry of your tube.