I am attempting to move a SKEmitterNode to follow a bullet in my game to give it a trailing effect however, no matter which way I attempt to implement this, it doesn't seem to work how I want it to and I'm at a loss for how to make this.
I have attempted to add the emitter to my main scene and manually moved the node a few times per second but it ends up not leaving a trail and keeping all the particles in one place like this:
Next I attempted to set the target node, however when I do this the trail goes for a bit then stops rather than following the bullet like it's supposed to. It also rotates and distorts from the rotation of the projectile like shown here:
For reference of the type of effect I'm looking for:
You should populate the targetNode property of your emitter with a node that is not moving like the scene.
emitterNode.targetNode = self // where self is the current scene
Related
I am working on a basic racing game using Apple's SceneKit and am running into issues simulating a car. Using the SCNPhysicsVehicle behavior, I am able to properly set up the car and drive it using the documented methods.
However, I am unable to get the car's position. It seems logical that the SCNPhysicsVehicle would move the SCNNodes that contain the chassis and the wheels as the car moves but the SCNNodes remain at their original position and orientation. Strangely enough, the chassis' SCNPhysicsBody's velocity remains accurate throughout the simulation so I can assume that the car's position is based off of the SCNPhysicsBody and not the SCNNode. Unfortunately, there is no documented method that I have found to get an SCNPhysicsBody's position.
Getting a car's position should be trivial and is essential to create a racing game but I can't seem to find any way of getting it. Any thoughts or suggestions would be appreciated.
Scene Kit automatically updates the position of the node that owns an SCNPhysicsBody based on the physics simulation, so SCNNode.position is the right property to look for.
The catch is that there are actually two versions of that node in play. The one you typically access is called the "model" node. It reflects the target values for properties you set, even if you set those properties through an animation. The presentationNode reflects the state of the node currently being rendered — if an animation is in progress, the node's properties have intermediate values, not the target values of the animation.
Actions, physics, constraints, and any scene graph changes you make inside update/render loop methods directly target the "presentation" version of your scene graph. So, to read node properties that have been set by the physics simulation, get the presentationNode for the node you're interested in (the node that owns the vehicle's chassisBody physics body), then read the presentation node's position (or other properties).
I have the same problem with my player node.
I move it with applyForce (to manage collision detection).
But when i check node position after some movement, the node position has not move (presentation node is the actual position as rickster write in his answer)
I manage to update the scnNode.position with renderer loop
You have to set position of your node with the presentationNode position.
node.position = node.presentationNode.position
Set this into renderer(_: updateAtTime) and your node position will sync with any animation you made to the physicsBody
I want to set the transform of both a given scn:SCNNode and its current presentation node, to a new value. However, I'm having trouble setting the presentation node. I've tried four ways:
Set presentation node's transform:
scn.position = newVal
scn.presentation.position = newVal
scn.presentation.transform is read only -- it can't be set. (BTW, setting the presentation node's transform compiles with no errors, perhaps something to clean up)
Use resetTransform():
scn.position = newVal
scn.physicsBody.resetTransform()
does nothing. The docs say it "Updates the position and orientation of a body in the physics simulation to match that of the node to which the body is attached". But it isn't changed. Not clear why!
Remove the physicsBody while setting:
let pb = scn.physicsBody
scn.physicsBody = nil
scn.position = newVal
scn.physicsBody = pb
This sets the presentation transform to newVal ("yea!"), but Physics does not work ("boo!"). Perhaps one cannot reuse a physics body.
Add a new physics body after setting:
scn.position = newVal
scn.physicsBody = SCNPhysicsBody(type:.dynamic, shape:nil)
but alas, scn.presentation.position doesn't have newVal.
Thoughts?
I've been noticing this as well in my SceneKit-using project. The direct answer to your question is: “The presentation node automatically updates its transform to match the scene node's transform. If it doesn't seem to be updating, make sure the scene node's isPaused is set to false.” However, it's likely that your scene nodes are un-paused and/or you're not using animations at all, and yet this issue occurs.
I started noticing this issue happening intermittently on iOS 11 when everything in my project worked great on iOS 10. I haven't touched SCNAnimations or anything like that— I have my own animation system, so I just update my nodes' .position every renderer(_:, updateAtTime:).
So AFAICT, the issue here isn't anything you're doing— the issue is that SceneKit has always been buggy and poorly-written (some parts of the API still haven't been fully updated for Swift), and will likely remain buggy and poorly-written because it seems the SceneKit team has completely moved onto working on ARKit. So the solution to your problem seems to be “just try a bunch of stuff, setting .position more frequently than necessary, or at a different time in the update loop, or really hack-y solution that seems to work-around the issues in SceneKit”.
For me, personally, I'm slowly working towards abandoning SceneKit in my project— I've mostly switched to custom Metal shaders, and then I'll do my own scene render loop, and eventually I'll replace the scene graph and SCNNode usage leaving me with zero reliance on the buggy mess that is SceneKit.
I wish I had better news for you, but I've been dealing with the weirdness of SceneKit for 2 years now. It's unfinished garbage.
Update: I ended up “fixing” my presentation nodes misbehaving by using a dirty hack that adds a tiny amount of “wiggle” to the node's position each frame:
public func updateNode()
{
let wigglePositionValue = SCNVector3(
x: Float.random(in: -0.00001...0.0001),
y: Float.random(in: -0.00001...0.0001),
z: Float.random(in: -0.00001...0.0001)
)
self.scnNode.position = _locationModel.value + wigglePositionValue
}
Side-note: As expected, “jiggling” node positions like this also disallows SceneKit from doing some of its batching/rendering optimizations, and (in my experience) incurs a performance hit. It's a horrible hacky work-around, only intended for worst-case scenarios (“the client says we have to go live with the game this week or we don't get paid, and now this stupid SceneKit bug?!”)
So yeah… the real solution here is “don't use SceneKit”.
I have the same issue using ARKit. Because physicsBody adjust itself according to presentation node instead of the node itself, and because the presentation node doesn't update immediately when you change the node's transform, the physicsBody you get might be weird.
There is one solution I found recently:
To change presentation node's transform, use SCNAction. For example, if you want to set position, use node.runAction(SCNAction.move(by: SCNVector3, duration: 0)) instead of simply node.position = SCNVector3. This way, the presentation node immediately updates and you get the right physics.
In addition, this approach only works on the node itself (not applying to children). So if you run action on the node's child, you still can't get the right presentation node and physics. To change the child node's transform and presentation transform, use the node.position = SCNVector3 approach.
To sum up, when changing node's transform, run an action, and when changing the node's children' transforms, directly set the transform.
I've an explosion in my SKScene and I want this Explosion even effect another objects close to its , Object are in all sides .I want to give them force to run away with this explosion .
It sounds like you need an SKFieldNode implementing a vortexField():
https://developer.apple.com/reference/spritekit/skfieldnode#1654415
SKFieldNode:
A node that applies physics effects to a portion of the scene.
vortextField()
Creates a field node that applies a perpendicular force to physics bodies.
I have two SKSpriteNode-s(let's call them SKSPriteNode A and SKSpriteNode B), A is my hero sprite and B is a scene element, and sometimes, the didBeginContact fires off when they collide and sometimes it doesn't. A has physicsBody set based on texture and B has rectangle based physicsBody set for it. I can see their textures collide and intersect but I don't see the NSLog fire off in the didBeginContact. This is not totally consistent because another time they collide and the contact event fires off just fine. I need this to be consistent, naturally. Anyone has a suggestion on this issue? The scene is the only contact delegate for the physicsWorld, so only the scene can trigger and go in the event handler, so I know it is working - at least some of the time. Code in scene:
-(void)didBeginContact:(SKPhysicsContact *)contact {
NSLog(#"Contact happened!!!!!! :) ");
}
I have my custom class StaticLevelElement set so it extends SKSpriteNode:
#interface StaticLevelElement : SKSpriteNode
The B has been set as StaticLevelElement with a triangular image:
StaticLevelElement * B = [StaticLevelElement spriteNodeWithImageNamed:#"triangle"];
I tried to set the B's physicsBody to have texture based physicsBody:
B.physicsBody = [SKPhysicsBody bodyWithTexture:[SKTexture textureWithImageNamed:#"triangle"] size:B.size];
and I have the same result - intermittent contact, fires off occasionally, and not really consistent. Same happens if I set the B to have rectangle based physicsBody:
B.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:B.size];
It is my opinion and I can say that with a medium level of certainty, that the anchorPoint caused this. I had it set to (0.5, 0) and that seemed to had caused this issue by, apparently, moving the whole physicsBody, which I dont like at all. Once I deleted this manual setting of anchorPoint (and thus returning it to (0.5, 0.5)), the contact looks like happening more or less properly. Still not perfect - not triggering for every time the overlap happens, but I think much better. As I said, I am still not completely clear on when does contact happens and how the anchorPoint influences it, but looks like it is.
Okay I have some physics objects, and they are all nicely categorized, and my didBeginContact is properly fired, and does what it is supposed to do. The problem is this:
I have two categories of objects, say ball and paddle.... When the ball touches a paddle the ball should explode... simple enough.. The problem lies in that the ball could touch 2 paddles at the same time... So, more than one didBeginContact gets called, and as such more than 1 explosion happens (1 per paddle the ball contacted with).. So the problem I am trying to figure out is, how do I remove/ignore all subsequent contacts with paddles from being handled if the code has already handled a collision involving the original ball? Removing the SKSpriteNode from parent before starting the explosion does not nullify the other contacts, they still get handled... so how do I tell it.. HEY PHYSICS CONTACTS STUFF... that body is no longer in the picture... so just throw those contacts away and don't worry about them?
I suppose I could explicitly check that the parent still has the SKSpriteNode available in the contact code before doing the explosion etc, but that seems kludgy at best, though I suppose it would work... Is there another/better way to handle this? I have to believe there is.
The moment you get the contact, set the contactBitMask of the ball node's physicsBody as 0. I am assuming the node needs to be destroyed and does not need to be reused.
This should prevent multiple contacts from appearing for the same node.
If the above doesn't work, you can try the following methods:
Removing the node from parent within the contact delegate, and triggering the explosion animation subsequently.
Subclass SKSpriteNode or SKPhysicsBody for the ball nodes and add a property for eg. alreadyTouched. Then you can check and set the property from within the contact delegate.
If you have three physicsBodies (ball, paddle1 and paddle2) and ball is set to collide with paddles (but paddles do not collide with each other) and the ball collides with 2 paddles during the same update cycle, then the game engine will generate 2 separate SKPhysicsContact objects - ball & paddle1 and ball & paddle2.
didBegincontact (Swift2) or didBegin(contact:) (Swift 3) (dBC for brevity) will then be called twice during the same update() cycle i.e. there will be 2 calls to dBC between calls to update()- firstly for the SKPhysicsContact between ball & paddle1 and then for the contact between ball & paddle2. You can do ball.removeFromParent() during the first contact, and the ball will be removed from the node tree but this will not prevent the second call to dBC for the ball & paddle2 collision and you will likely perform the same actions against ball (exploding, removing the score etc).
The simplest thing to do is to create a subclass of SKSPriteNode for your ball (ballNode?) and add a single property isActive, initialised to true. This is very easy.
In dBC, when the ball hits the paddle, check its isActive property and if false, simply return. If it's true, perform the normal collision actions and then set ball.isActive to false. When dBC is called the second time, the check against isActive will prevent any duplicate actions.
You could also use the ball's userData (part of SKSpriteNode) to store the isActive values to prevent subclassing, if you'd prefer that.
There are other ways to handle this issue, but so far I've found this one to work and to be the simplest.