Scenekit - SCNLookupConstraint causes ConvertPosition of childNode not to update its position - scenekit

I have a main node, with a sequence of childNodes. The childNodes are firePoints, so as the target comes into view, the main node rotates to target and the firepoint is an offset where I need to shoot from. It works fine if I target via some vector classes I built, but it is smoother if I use SCNConstraint on the main node. The main node (and firepoints) rotate to target, but the fire points vector values do not ever change when convertPosition is called. I can see that the fireNodes are rotating along with the base node properly. Thanks
func shoot()
{
isShooting = true
// Convert position so that projectile fires from FirePoint
let fireNode = gNodes.getNode(vName: attr.name + "FirePoint" + "\(attr.firePointsSequence)", vRequired: true, vError: "FP0-Sproj")
let fireNodeStart = gNodes.gameNodes.convertPosition(fireNode.presentation.position, from: attr.node)
print("FireNodePosition: \(fireNodeStart)
}
func setTarget()
{
attr.node.constraints = []
let vConstraint = SCNLookAtConstraint(target: targetNode)
vConstraint.isGimbalLockEnabled = true
attr.node.constraints = [vConstraint]
}

I had this problem. What solved it for me was using SCN's presentation scn. It has up to date positions.

Related

opengl assimp skeletal node transformation

if i have a setup where in order to transform a bone, I have to:
node.transform = scale(node.transform, 2, 2, 2)
then
bone_transform = inverse:offset_matrix *
(node.parent.transform * node.transform) * (inverse:node.parent.transform) * offset_matrix
However this only works properly for bones that doesn't have any children (or the bottom-most in the node hierarchy)..
therefore having trouble when traversing through the bones part..
how would I do this correctly to get the correct bone_transform for all bones..
So if I want to transform any bone anywhere in the hierarchy (using node.transform), it would also adjust all its children bone_transforms correctly..
maybe something like:
mat4 transform = node.transform
while (node.parent != 0) {
if (node.is_bone) {
transform = node.parent.transform * node.transform
}
node = node.parent
}
or something with node.children recursively?
The offset-matrix is already the global transformation for your bone. It contains already all the transformation data from the root down to your current bone.
That is the reason that your code only works for bones without any parent nodes.

Is there a way to prevent node glitching shaking during collisions?

The function (code below) simply moves a character node (contact.nodeA) back after it has penetrated a collision node (contact.nodeB) that it's not allowed to pass through.
In a scenario where a user is trying to move the character node (contact.nodeA) while it's stuck in a corner, etc. the scene/character repeatedly shakes/glitches while its position is repeatedly being moved back away from contact.nodeB at 60hz.
Is it possible with SCNPhysicsContact in SceneKit to prevent contact.nodeA from moving forward into contact.nodeB in the first place? (ie., why does "contact.penetrationDistance" occur at all? Why can't we just be alerted when a physics contact has begun w/o contact.nodeA actually moving forward into contact.nodeB? We are, after all, catching the contact on "didBegin" yet there's already penetration that we have to readjust.)
(When a user is trying to move the character node while it's "stuck" in a corner, or between two rocks, etc.)
Or, is there another solution to prevent the repeated shaking of contact.nodeA as its position is repeatedly being readjusted to compensate for its penetration into contact.nodeB? Transform instead of position change?
I've already tried setting momentum to 0.0 and removing all animations, etc. However, since contact is still occurring with contact.nodeB, contact.nodeA cannot be moved away since its momentum is still being stopped during contact with nodeB.
Any help would be greatly appreciated.
private func characterNode(_ characterNode: SCNNode, hitWall wall: SCNNode, withContact contact: SCNPhysicsContact) {
if characterNode.name != "collider" { return }
if maxPenetrationDistance > contact.penetrationDistance { return }
var characterPosition = float3(characterNode.parent!.position)
var positionOffset = float3(contact.contactNormal) * Float(contact.penetrationDistance)
maxPenetrationDistance = contact.penetrationDistance
characterPosition.y += positionOffset.y
replacementPositions[characterNode.parent!] = SCNVector3(characterPosition)
}
extension GameViewController : SCNPhysicsContactDelegate {
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
if gameState != .playing { return }
contact.match(BitmaskWall) {
(matching, other) in
if other.name == "collider" {
self.characterNode(other, hitWall: matching, withContact: contact)
}
}
}

Are accessors of class properties useful?

Consider the following accessor methods of the property global_position of the class Node2D:
Vector2 global_position
Setter set_global_position(value)
Getter get_global_position()
But the property is not encapsulated as shown by this example:
tool
extends EditorScript
func _run() -> void:
var n = Node2D.new()
n.global_position = Vector2(100, 100)
print(n.global_position)
which produces:
* scene/2d/canvas_item.cpp:467 - Condition "!is_inside_tree()" is true. Returned: get_transform()
(100, 100)
Are those accessors useless?
They are NOT useless. They are useful… If the node is in the scene tree.
You can add the node to the scene tree with add_child or add_child_below_node.
I'm not sure what you mean by "not encapsulated". Just in case, I'll point out that you are NOT bypassing them.
When you use the property, you are using the getter and setter methdods. The property is a convenience for language binding. Thus, we could also say that you don't need the property, only the getter and setter.
You can see in the source for Node2D, that there is a _bind_methods function that sets all the properties and methods that are exposed to use. This is how global_position looks like:
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
That message you are getting is because you are using global_position on a node that is not on the scene tree. And, yes in that case, it is not useful. We can figure that out with a simple script:
extends Node2D
func _ready() -> void:
global_position = Vector2(200, 300)
var n = Node2D.new()
n.global_position = Vector2(100, 100)
print(n.global_position)
add_child(n)
print(n.global_position)
This outputs:
(100, 100)
(300, 400)
Thus, as you can see, it will have a different global position from what was set. And the difference depends on the position of the parent. Setting position would have the same effect. Thus setting global_position not useful here.
If you want to go deeper on what global_position does, we can have a look at the source of the getter and setter (part of the linked source for Node2D):
Point2 Node2D::get_global_position() const {
return get_global_transform().get_origin();
}
void Node2D::set_global_position(const Point2 &p_pos) {
Transform2D inv;
CanvasItem *pi = get_parent_item();
if (pi) {
inv = pi->get_global_transform().affine_inverse();
set_position(inv.xform(p_pos));
} else {
set_position(p_pos);
}
}
This is set_position, by the way (notice it writes pos):
void Node2D::set_position(const Point2 &p_pos) {
if (_xform_dirty)
((Node2D *)this)->_update_xform_values();
pos = p_pos;
_update_transform();
_change_notify("position");
}
And _update_transform (with conspicuous !is_inside_tree() check):
void Node2D::_update_transform() {
_mat.set_rotation_and_scale(angle, _scale);
_mat.elements[2] = pos;
VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), _mat);
if (!is_inside_tree())
return;
_notify_transform();
}
Notice that _update_transform updates _mat based on pos.
What about get_global_transform? Not in that file. We find it in the source for CanvasItem:
Transform2D CanvasItem::get_global_transform() const {
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!is_inside_tree(), get_transform());
#endif
if (global_invalid) {
const CanvasItem *pi = get_parent_item();
if (pi) {
global_transform = pi->get_global_transform() * get_transform();
} else {
global_transform = get_transform();
}
global_invalid = false;
}
return global_transform;
}
And there is the failed assert you saw: !is_inside_tree().
Oh, about that global_invalid. If you search for it on the source, you will find it is set to true when the node exits the scene tree or the transform is modified (that is in _notify_transform, which I'm not including here, but you can see it is called by _update_transform when it is in the scene tree).
What can we make out of all this?
The property global_position is simply syntactic sugar for the methods get_global_position and set_global_position.
The methods get_global_position and set_global_position work on the global transform, which is inherited from CanvasItem.
To figure out the global position, we need to work out the transforms on the parent nodes. Which means, we could also figure it out by going over the same process, and thus these methods technically not necessary.
It is done lazily. The global position gets invalidated, and it is recomputed on demand.
If the node is not in the scene tree… What parent nodes? In this case using global_position would be doing the same if we were using position. Thus, we could say global_position is not useful when the node is not in the scene tree.
There is an assert that tells you when you are using it and it is not in the scene tree. And that assert gave you the message you posted.

How to determine which childNode is closest to screen center in SceneKit?

I have a bunch of markers placed in my scene as childNodes at fixed node positions in 3D world. When I move the phone around, I need to determine which marker node is the closest to the 2D screen center, so I can get the text description corresponding to that node and display it.
Right now, in a renderloop, I just determined the distance of each node from the screen center in a forEach loop, and decide if that distance is <150, if so get title and copy of that node. However, this doesn't solve my problem because there could be multiple nodes that satisfy that condition. I need to compare the distances from the center across all the nodes and get that one node that's is closest
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval){
scene.rootNode.childNodes.filter{ $0.name != nil }.forEach{ node in
guard let pointOfView = sceneView.pointOfView else { return }
let isVisible = sceneView.isNode(node, insideFrustumOf: pointOfView)
if isVisible {
let nodePos = sceneView.projectPoint(node.position)
let nodeScreenPos = CGPoint(x: CGFloat(nodePos.x), y: CGFloat(nodePos.y))
let distance = CGPointDistance(from: nodeScreenPos, to: view.center)
if distance < 150.0 {
print("display description of: \(node.name!)")
guard let title = ar360Experience?.threeSixtyHotspot?[Int(node.name!)!].title else { return }
guard let copy = ar360Experience?.threeSixtyHotspot?[Int(node.name!)!].copy else { return }
titleLabel.text = title
copyLabel.text = copy
cardView.isHidden = false
}else {
cardView.isHidden = true
}
}
}
}
There are various ways to do this. Like iterating all the nodes to find their distance. But like you said this becomes inefficient.
What you can do is to store your node data in a different format using something like GKQuadTree... https://developer.apple.com/documentation/gameplaykit/gkquadtree
This is a GameplayKit That will allow you to much more quickly iterate the data set so that you can find the node closest to the centre.
It works by breaking down the area into four (hence quad) sections and storing nodes into one of those sections storing the rect of the section too. It then breaks down each of those four sections into four more and so on.
So when you ask for the nodes closest to a given point it can quickly eliminate the majority of the nodes.

remove node from array spritekit swift

I'm working a little game for iOS (spritekit/swift).
I've an array of SKSpriteNode in my scene defined like this:
var spritesTree = [SKSpriteNode]()
then I fill the array with a function and add them to the scene like this:
spritesTree = spritesCollectionTree(count: numTrees)
for sprite in spritesTree {
addChild(sprite)
}
After that, depending the situation the process adds some more trees into the array. I Know how to remove elements from the scene and the array if I Know the index
spritesTree[i].removeFromParent()
spritesTree.removeAtIndex(i)
but my problem is to remove an especific node in this array when I don't Know the index. For exemple when one of the the sprite was touched
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let positionInScene = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
In this case, ¿How can I remove the touchedNode from my array spritesTree of SKSpriteNode if I don't know the index? I read something about indexOf to find the index previoustly but it doesn't work with my SKSpriteNode array. Could you help me please?
Bests,
You can create a name for each SKSpriteNode :
spritesTree[index].name = String(index)
After when you check node touched you convert name to Int
let location = touch.locationInNode(self)
let positionInScene = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
var index = Int(touchedNode.name)
touchedNode.removeFromParent()
spritesTree.removeAtIndex(index)

Resources