SetScale unintentionally affecting SKSpriteNode position - swift

I'm making my first shooter game using Swift and SpriteKit and I've recently been running into problems with setScale(). I have a Laser class whose instances are added as children to a Ship class. I now realize that any down scaling of the parent node will scale its child as well. In my update method in GameScene I check the position of each child named 'Laser' to determine if it should be removed from its parent when offscreen. I recently updated some of my sprites- including their relative sizes, and I noticed that the position of each laser is way off, as they are removed far before they reach the end of the screen and upon debugging their x positions are in fact far larger than where they are displayed onscreen. I also noticed that the starting x of each Laser instance is relative to its original position within the Ship instance. So the ship may be halfway across the screen but the laser x position still starts at 0 and if the ship is able to pass the laser, the laser's position becomes negative--is there a way to grab its position on the screen rather than relative to its start within its parent?
This is the code I'm doing the check with:
self.aShip.gun.enumerateChildNodesWithName("laser",
usingBlock: { node, _ in
if let aLaser = node as? Laser { i
if(aLaser.position.x > self.size.width){
aLaser.removeFromParent()
}
}
}
)
It seems like scaling has a ton more baggage than I would normally assume, so any insight into this problem or how to manage the relation between code and sprites would be awesome!!
EDIT:
I found that adding let positionInScene = self.convertPoint(aLaser.position, fromNode: self.aShip.gun) under if let aLaser = node as? Laser {and consequently using positionInScene rather than the aLaser position works.. however, I still don't fully understand the scaling effect going on with aLaser and it's position and don't know if its efficient to have to convert positions like this at the rate of update (60 times a second).

Related

Unity AR Rotate Scene to match reference point

How to Match a reference point in 2 different AR scenes by position and rotation?
Here are some details about my project:
I have 2 scenes: "new scan" and "load scan". In the "new scan" scene I instantiate a 3d cube and make all the other points relative to it. This is my reference point. Then I instantiate some more points and finally save all the data to the device (my phone).
Next, in "load scan" I load the scene again and instantiate the cube in the exact same world position. For now, I managed to set the right position for each point but the axis is rotated because I start the scene from a different real-world location and different phone rotation.
Based on the cubes which are instantiated in the same place, I need to match the rotation and the position of the scene so the points will appear in the same relative position as the first cube.
Note: one can assume that the cube will instantiate with the user standing in the same direction as the desired position. But Do NOT assume that the user starts the "load scan" scene in the same direction as the "new scan" scene (which effect the whole scene rotation).
Here is a visualization of the problem:
Image of New Scan:
Image of Load Scan:
Thanks
If you want to make sure that the cube will appear in the same position/rotation in every AR session you have several options:
Use an Image marker
Use ARWorldMap (iOS exclusive)
Use a Cloud Tracking solution (google cloud anchors / Azure spatial anchors)
Of course you can also try to make the user place the cube correctly themselves, or redesign your app to work without these restrictions.
So I've found a solution but this is not the ultimate solution:
First, make a class with public static parameter so we can pass it through other scripts and scenes. Something like that:
public static class SceneStage
{
public static int ResetScene = 0;
}
Now, every time the camera turns on, check the SceneStage.ResetScene state. If value == 0 don't do anything, otherwise ask the user to stand facing the desire direction and then press a button, which call the function ResetScene:
private void ResetScene(int _scene)
{
var xrManagerSettings = UnityEngine.XR.Management.XRGeneralSettings.Instance.Manager;
xrManagerSettings.DeinitializeLoader();
SceneManager.LoadScene(_scene); // reload current scene
xrManagerSettings.InitializeLoaderSync();
}
Here I send the scene build index to the function with:
ResetScene(SceneManager.GetActiveScene().buildIndex);
So basically, the flow is like that: for the first time we open the scene (when SceneStage.ResetScene = 1) -> change the value to 0, and reset the scene. The second time don't do anything, but when we leave the scene set the value back to 1 so the next scene will reset too (because the ARPose driver still tracking the environment).

SKEmitterNode tied to a moving SKNode

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

Get Orientation of SCNNode Swift [duplicate]

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

setting scnnode presentation position

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.

Make a SKLabelNode in spritekit appear above all other sprites

Hi im pretty new to programming and I have encountered a small issue. I am creating a simple game which has a score label. However, the score label is covered whenever a downward scrolling obstacle passes. I would like for the score label to not be covered by the obstacles.Is there a way i can change the sprite hierarchy in spritekit? Thanks
I would recommend creating a node for the static stuff that should be fixed on screen (like a game HUD). Then you set this node's zPosition to be higher then the game play node (where all game objects are placed and manipulated).
This makes it also easier implementing a camera or moving the game play node if needed without the score label or any other fixed hud element moving as well.
So you will have something like this :
SKNode *hudNode = [SKNode node];
hudNode.zPosition = 1000;
[hudNode addChild:scoreLabel];
SKNode *gamePlayNode = [SKNode node];
gamePlayNode.zPosition = 0; // Not mandatory as it defaults to 0 but I put this here for clarification
[self addChild:hudNode]; // self is your scene instance
[self addChild:gamePlayNode];
You can change the zPosition property of your label node, by making it 100 it will be above all other nodes (default value is 0.0):
labelNode.zPosition = 100;
Nodes are drawn in the order they are added to the scene (or parent node). You can change the order in which nodes are drawn by setting their zPosition property.
From Apple's documentation...
...The z position is the node’s height relative to its parent node, much as a node’s position
property represents its x and y position relative to parent’s
position. So you use the z position to place a node above or below the
parent’s position.
When you take z positions into account, here is how the node tree is
rendered:
Each node’s global z position is calculated.
Nodes are drawn in order from smallest z value to largest z value.
If two nodes share the same z value, ancestors are rendered first, and siblings are rendered in child order.
You can also use zPosition to optimize rendering performance...
...it might be better if Sprite Kit could gather all of the nodes that share the same texture and drawing mode and and draw them with a single drawing pass. To enable these sorts of optimizations, you set the view’s ignoresSiblingOrder property to YES.