I'm trying to create an application in which the user stacks different geometric shapes. In a .scn file, which is loaded inside the ARSCNView, I insert a static plane and then at each tap of the user, the app inserts a dynamic SCNNode.
The first node is being inserted a few inches above the plane, to replicate a falling object. And then, each other node is being dropped on top of another.
This is the main idea of the application; the problem appears after adding 3 or 4 nodes, they appear to slide of each other, almost jiggle, and the whole structure collapses.
This is my node I'm inserting:
let dimension: CGFloat = 0.075
let cube = SCNBox(width: dimension, height: dimension, length: dimension, chamferRadius: 0.0)
let node = SCNNode(geometry: cube)
node.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.dynamic, shape: nil)
node.physicsBody?.mass = 2.0
node.physicsBody?.friction = 1.0
node.physicsBody?.restitution = 0.01
node.physicsBody?.damping = 0.0
node.physicsBody?.angularDamping = 0.0
node.physicsBody?.rollingFriction = 1.0
node.physicsBody?.allowsResting = true
let insertionYOffset = 0.3
node.position = SCNVector3(hitResult.worldCoordinates.x, hitResult.worldCoordinates.y + Float(insertionYOffset), hitResult.worldCoordinates.z)
I've tried to play with the values and these are the best ones, but they aren't enough to create a stable structure of blocks.
As a requirement, I need to keep the blocks dynamic, they need to be affected by gravity, wind, etc.
The problem most likely has something to do with a factor called Dynamic which will allow it to continuously move or may have to do with the objects hitting each other to fix that problem all you have to do is change the collision mask to two different numbers.
I see two points that may be the flaws of your simulation:
Your objects are stacked. When two objects collides there's a small amount of error added in order to keep the physics in a stable phase and prevent weird behaviors. So when you stack objects you add error amounts and the simulation becomes inaccurate. I suggest making objects static over time or depending of the position, or reduce gravity by a bit.
Most of the physics engines works better with objects sized with the unit size (most of the cases, it's 1 like in Box2D or PhysX). Objects that are very small or very big tend to act less natural. 0.075 sounds a bit small for a simulation. I don't see a obvious solution, maybe look for a way to change the reference size.
Related
My goal is to create a single physics body out of several of SceneKit's SCNBox geometries.
My understanding is that when I pass an SCNPhysicsShape(shapes:transforms:) to an SCNPhysicsBody(type:shape:), it should create a single physics body.
However, I end up with something that seems suspiciously like it's not a single physics body at all, but rather several separate physics bodies -- one for each shape I passed into SCNPhysicsShape(shapes:transforms:).
When I turn on scnView.debugOptions = .showPhysicsShapes, I can clearly see red lines defining the separate bodies in question. On its own, this isn't very convincing evidence (it's conceivable that those lines could be shown for whatever reason while still being a single physics body).
But there's another piece of data, here: The project in which I'm encountering this issue features a small ball that rolls around the scene -- and when that ball rolls over the red lines in question, the ball bounces up into the air a bit. So, it's quite obvious that, whatever is actually going on, there are edges where I would expect to see none.
This behavior is clearly visible in the following GIF. In it, each colored block is a separate SCNBox geometry with its own physics body. Each block has the exact same position.z. The ball bounces considerably as it crosses the point where one geometry meets another.
Here's some code illustrating the issue. parent is an SCNNode that holds the child nodes, and is the node to which I assign the physics body. Please assume that all properties are defined; I'm omitting things that aren't terribly relevant.
let childShape1 = SCNBox(width: 6, height: 2, length: 6, chamferRadius: 0.0)
//Other child shapes defined here...
//Set up the positional translation relative to the child node's parent:
let translateMatrixShape1 = SCNMatrix4MakeTranslation(childShape1.position.x, childShape1.position.y, childShape1.position.z)
//Other child translations defined here...
let parentShape = SCNPhysicsShape(shapes: [childShape1, childShape2, childShape3, childShape4], transforms: [translateMatrixShape1, translateMatrixShape2, translateMatrixShape3, translateMatrixShape4])
parent.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.static, shape: parentShape)
Now, parentShape is four rectangular boxes arranged around a central point, creating a sort of picture-frame-shaped object.
The ball is an SCNNode with an SCNSphere geometry and a dynamic physics body.
Question: Does anyone have any idea what might be going on, here? Have I somehow misunderstood how this whole thing works, or is this a limitation of SceneKit?
Usually you can create one single PhysicsBody from several geometry objects by making a flattenendClone from out of your (i.Ex.) parent node. This new node will have one single geometry then. For the PhysicsBody you can then use the geometry element of the new node. In Addition I recoommend to use a concavePolyhedron for the static body type. (I hope I understood you correctly)
I need to move a SpriteNode across the frame. I am currently having the issue of it having glitches while doing so (losing a couple of frames? not really sure). What I need is for the object to travel horizontally without a fixed direction x destination so that if x is offset by some impact the object does not attempt to return to its initialized x coordinate.
Here is how that part of the code looks like right now:
`spaceRock.physicsBody?.affectedByGravity = false
spaceRock.physicsBody = SKPhysicsBody(texture: (spaceRock?.texture)!,
size:(spaceRock.size))
spaceRock.zPosition = 1
spaceRock.position=CGPoint(x: xSpawnPosition, y:
Double(self.size.height/2+(spaceRock.size.height)))
spaceRock.physicsBody!.isDynamic = true
spaceRock.physicsBody!.allowsRotation = true
spaceRock.physicsBody!.categoryBitMask = 00000001
spaceRock.physicsBody!.contactTestBitMask = 00000010
addChild(spaceRock)
spaceRock.physicsBody?.applyTorque(CGFloat(randTorque))
//spaceRock.run(SKAction.moveTo(y: -self.size.height-
spaceRock.size.height, duration: rockTravelTime))`
The comment is what I have been using up until this point.
Thank you for the help!
You are mixing physics simulation (SKPhysicsBody) and direct movement of the Sprite (SKAction.moveTo) - although you may be able to do that to achieve special effects, in general they will fight each other and cause problems.
If you need your objects to move realistically, then use an SKPhysicsBody and apply forces (Torque, thrust etc.) to move it. This will be quite complex to program and has a significant processing overhead.
If you prefer to move your sprites directly then get rid of the SKPhysicsBody and your moveTo should then work fine.
I am evaluating the iOS SpriteKit physics engine, and for a test I have created a simple scene with Xcode that contains two circle-shaped nodes:
Both nodes have physics bodies of circular shape and 5 kg of mass.
I am connecting both nodes with a SKPhysicsJointSpring. Here is the entire setup (in viewDidLoad()):
let path = NSBundle.mainBundle().pathForResource("MyScene", ofType: "sks")!
let scene = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as! SKScene
let border = SKPhysicsBody(edgeLoopFromRect: scene.frame)
border.restitution = 0;
border.friction = 0;
scene.physicsBody = border
let player = scene.childNodeWithName("player")! // large ball
let tail1 = scene.childNodeWithName("tail1")! // smaller ball
player.physicsBody!.usesPreciseCollisionDetection = true
tail1.physicsBody!.usesPreciseCollisionDetection = true
let spring1 = SKPhysicsJointSpring.jointWithBodyA(player.physicsBody!, bodyB: tail1.physicsBody!, anchorA: player.position, anchorB: tail1.position)
spring1.damping = 1
spring1.frequency = 3
scene.physicsWorld.addJoint(spring1)
spriteKitView.presentScene(scene)
Note: The gravity is set to (0,0) in the scene editor.
When I move the bigger node (say, by capturing touches with touchesBegan() and setting the node's position to the touch position), the other follows according to spring parameters.
However, if I move the node fast enough, so that the spring forces become extreme, both nodes overlap when the spring is contracting. I am expecting them to collide with each other since their collisionBitMask is by default set to -1 (all bits set). I have enabled usesPreciseCollisionDetection but the effect is still visible.
When I add an edge loop around the scene they do collide with that edge as expected. Same with additional nodes that have a physics body (but no joints attached).
I have the impression that the presence of the spring somehow makes the engine ignore collisions between nodes that are connected with joints.
Did anyone else observe this, too?
Did I forget anything? Or is this working as intended?
Attempting to answer my own question.
I found out that other joints (such as SKPhysicsJointLimit) disable collision between the joined nodes as well. My only conclusion is that all SpriteKit joints somehow consider two objects as one, where collisions are deemed undesirable. Though in my app I would prefer to have collisions enabled. Maybe I should file an enhancement at Apple developer feedback.
So far my workarounds include:
Create spring behaviour by adding a SKFieldNode.springField plus a SKFieldNode.dragField as children to the sprite node. Pro: as children these fields automatically follow their parent position; Con: this spends 2 precious bits of fieldBitMask which puts a natural limit to the number of such nodes.
Manually apply some spring + drag force at every frame. Pro: does not waste bits of fieldBitMask, Con: compute spring + drag forces manually.
So I tend to favor option 2.
To summarize: If you want spring behaviour between 2 nodes while maintaining collisions, implement forces manually and do not use a joint.
This function is supposed to add a 3D model to screen in a confined space:
func addCharacter(gridPosition: SCNVector3, levelNode: SCNNode){
let carScene = SCNScene(named: "assets.scnassets/Models/cube.dae")
// Create a material using the model_texture.tga image
let carMaterial = SCNMaterial()
carMaterial.diffuse.contents = UIImage(named: "assets.scnassets/Textures/model_texture.tga")
carMaterial.locksAmbientWithDiffuse = false
// Create a clone of the Car node of the carScene - you need a clone because you need to add many cars
let carNode = carScene!.rootNode.childNodeWithName("Cube", recursively: false)!.clone() as SCNNode
carNode.name = "Cube"
carNode.position = gridPosition
// Set the material
carNode.geometry!.firstMaterial = carMaterial
// Create a physicsbody for collision detection
let carPhysicsBodyShape = SCNPhysicsShape(geometry: SCNBox(width: 0.30, height: 0.20, length: 0.16, chamferRadius: 0.0), options: nil)
carNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Kinematic, shape: carPhysicsBodyShape)
carNode.physicsBody!.categoryBitMask = PhysicsCategory.Car
carNode.physicsBody!.collisionBitMask = PhysicsCategory.Player
levelNode.addChildNode(carNode)
}
The code works if I change the dimensions of the dae file before adding it to Xcode, and by dimensions I mean the Bounding Box Size property of Xcode. I wanted to know if It was possible to change this bounding box size directly from Xcode. It does not seem possible from the UI. Is it possible with code? Or, even better, scale down the object maintaining proportions to a size for the XYZ between the range 0.3 -> 0.7? At the moment my objects show a box size of over 45 for XYZ. Furthermore, If I was to use a .scn file instead of a .dae in the code above, would that still work?
EDIT:
If I change the size via code, would it have an impact on efficiency? I notice that for larger .dae models the fps drops from 60 to 30 and the game slows down.
Changing carNode's scale property will reduce the apparent size of the car.
However, I think you'll be ahead to load your .DAE into the Xcode scene editor. That will allow you to scale it down ahead of time (in the Node Inspector, option-command-3). You can also, if you want, add your texture. Then save it as a .SCN file, which is compressed and should load faster.
Changing the scaling, either in code or in the Xcode scene editor, won't affect efficiency. Reducing the complexity of the car (the number of polygons/vertices) will speed things up, though.
Consider creating SCNLevelOfDetail instances for your car node. That will cause the renderer to use substitute, lower resolution geometry when the node is far from the camera. The WWDC 2014 slide deck demonstrates this, slide 38, AAPLSlideLOD.m.
I have a number of individual nodes (tiles) that make up the background of my game. They're each 32x32p, and are positioned next to each other to form floor/ roof/ obstacles. I load a map from a JSON-file, and position the nodes on a background-layer/node based on the contents of the JSON-file when the scene is initialized.
This is working fine, however, I'm experiencing some bugs when moving the background layer/ the physics engine is doing its thing. Some of the nodes move 1 point/pixel away from each other, creating a gap between them. And since the background is a different color, this looks really bad.
The problem mostly occurs further out on the map (not right away), at the same time as I'm either applying impulses to the player or the physics engine is bouncing the player (or similar).
Here's a picture that illustrate my problem:
(Click to open in separate tab)
As you can see, right between the o and d in nodes, there is a gap (more easily visible in the full sized image in the link). There are many gaps like these occurring now and then while playing the game, and they only appear for a split second or two. Sometimes they stay when the player stops moving.
I move the player in the didSimulatePhysics by increasing/ decreasing its x-value. Then lastly in the didSimulatePhysics I call this method to center the worldNode on the player:
- (void)centerViewOn:(CGPoint)centerOn {
CGFloat x = Clamp(centerOn.x, self.size.width / 2, _bgLayer.layerSize.width - self.size.width / 2);
_worldNode.position = CGPointMake(-x, _worldNode.position.y);
}
The worldNode contains both the bgLayer which contains all the individual background nodes, and the player itself.
What is the best way to solve this? One plausible solution could be to make every tile 33x32p, so they're overlapping. In that case you won't see the gaps occurring now and then. But I think it would be better to "kill" the problem rather than hide it.
Anyone have experience laying out their map like this? Or have anyone experienced the same problems before? And if so, how did you solve it?
Thanks in advance!
This is the casting to Int solution based on #LearnCocos2D comment:
- (void)centerViewOn:(CGPoint)centerOn {
CGFloat x = Clamp(centerOn.x, self.size.width / 2, _bgLayer.layerSize.width - self.size.width / 2);
_worldNode.position = CGPointMake(Int(-x), Int(_worldNode.position.y));
}