Detecting touch inside bounding box (alpha mask) of Sprite using SpriteKit - ios

I am starting to learn Swift2 to develop on iOS using SpriteKit and I cannot seem to detect if I touched a visible part of my Sprite.
I can find which node was touched like this:
let touchedNode = self.nodeAtPoint(location)
But this detect the touch on the sprite. I would like to detect the touch on the "not transparent" part of the sprite only.
I tried creating a PhysicsBody with an alpha mask bounding box and testing if the bounding box of the node I selected contains the location of the touch like this:
sprite.physicsBody = SKPhysicsBody.init(texture: sprite.texture!, size: sprite.size)
sprite.physicsBody?.affectedByGravity = false
if (touchedNode.containsPoint(location)){
But it didn't help. If I click in a transparent part of my sprite, the event still triggers.
The documentation says "A new physics body is created that includes all of the texels in the texture that have a nonzero alpha value.", so shouldn't it work?
Thanks for your time.
PS: I also tried to be more generous on my alpha threshold, but this also did not work (in case my transparency wasn't perfect).
UPDATE:
To add a little more details, I am building a level editor. This means that I will create many nodes, depending on what the user chooses, and I need to be able to select/move/rotate/etc those nodes. I am actually using SKSpriteNodes as my PNG pictures are automatically added in xcassette that way. I decided to use the PhysicsBody's Alpha Mask bounding box, as this value is shown in the Scene Editor (Xcode) when you select a node, and when selected, the Alpha Mask highlights exactly the part of my Sprite that I want to be able to detect a touch inside.
If I am using the wrong ideas/techniques, please tell me. This is possible as I am only starting to use Swift and SpriteKit.
UPDATE 2
I queried the physicsWord (as recommended) to get the right physicsBody and got the name of the attached node like this:
let body = self.physicsWorld.bodyAtPoint(location)
print(body?.node?.name)
But this is still printing the name of the node even if I touch outside the bounding box, which makes no sense to me.
Thank you anyway for your help.

The SKPhysicsBody doesn't define the bounds of the SKNode. You can do the hit check by querying the SKPhysicsWorld of your scene by calling bodyAtPoint on it. That will return the SKPhysicsBody you are interested in.

I was having the same problem. You actually can't perform the touch on the alpha mask of a Sprite Node and it says it right in the Apple documentation. You have to use a Composite Box. I used a shape Node and changed the "line width" to 0 so essentially the composite box is invisible. Make sure that the Z axis is higher for the composite box and then run the touch event through the composite box shape node instead of the original node.

Related

RealityKit: Render an object under a grounding shadow

I have a placement marker (a simple plane with green corners) to visualize detected planes (via ARRaycastResult) in an ARView. This placement marker uses UnlitMaterial with a texture. Everything works fine as long as there is no other object added.
When I add another object, RealityKit also adds grounding shadow (invisible plane) right under the object. It works as a shadow plane and occludes everything behind it - including my placement marker.
Here is a picture of the placement marker (part of which is hidden under the shadow plane):
Is there any way to prevent this clipping? I was lookig for something like rendering order (as ARKit does have one), but have not fond anything in RealityKit yet.
I would like to keep the shadow plane if possible.
Edit: Added official name of the invisible plane (grounding shadow)
Reality kit automatically adds "grounding shadows" if the AnchoringComponent.Target is of type "plane". According to an Apple Engineer this can be aso simulated using a DirectionalLight with a Shadow and placing a plane (with an OcclusionMaterial) under the model. So the grounding shadow is probably also made out of OcclusionMaterial. This also explains why this grounding shadow occludes other objects.
There is an option to disable rendering of these grounding shadows. It can be done by inserting of disableGroundingShadows option into ARView's renderOptions property.
arView.renderOptions.insert(.disableGroundingShadows)
I have not yet found a way to override OcclusionMaterial (if any).

How to add directional light for shadows properly in sceneView of SceneKit?

I am learning ARKit. I'm placing Virtual Objects in Augmented Reality scene and struggling in these problems!
I am using this demo project from github
1- How to add only one directional light for all (separate) nodes in SceneView of SceneKit & Move directional light with camera position? So that my added shadow can also move with light direction.
If I translate the object shadows are working as it should be. But If I rotate object now shadow should not move on the plan. They are moving because of light is at fixed position.
2- Shadow is looking fine only in case If I add only one object on the plane. But If I add two or more objects more directional lights are adding in SceneView. Now every object has more than one shadows. I want it to restrict only one shadow.
I have added light and shadow plane in sceneKit editor. (not programmatically). Here are my scenekit editor's screenshots.
3- I have read and confirmed that shadow are adding only If I set directional light property to deffered. But in this case app is crashing If I call remove all nodes from sceneView's root node. My removing nodes code is.
self.sceneView.scene.rootNode.enumerateChildNodes { (node, stop) -> Void in
node.removeFromParentNode()
print("removed ", node.name as Any)
}
You can watch my apps video for more clearance. Apps video, How it is working now
My requirement is to add only one shadow for every object. When I Rotate and translate objects shadows should look real.
I also have tried it as removing light from scn file of vase, and add a separate light.scn file having only light in it. added These two (vase and light) nodes in sceneView. But No shadow is appearing.
Directional lights are interpreted from the shader only with their direction (that one light-red vector coming out of the node in Xcode). It does not matter where the position of the light is. Directional light are often used for imitating sun light.
I implemented a similar project so here is my attempt.
I added one directional light from a separate SCN-File to the scene when I initialize the SCNScene.
My settings for it:
castsShadow: true
mode: deferred (my App is not crashing, if I remove my objects from the scene :/ )
And actually thats it to make it work in my project.
One thing about your planes: I think you have not disabled castsShadow on the planes. Therefore a user can see the planes.
Edit:
I could reproduce the crash. It does occur when removing the directional light. Thats why the app is not crashing in my project. So you could do it like me and add the directional light in viewDidLoad() for example.

Spritekit silhouette effect when passing in front of object

I'm working on a sprite kit game and I'm trying to create a particular silhouette lighting effect whenever objects pass in front of a particular object (in this case a red sun). I want anything in front of it to appear black, and everything else to appear as normal (so if an object is partially in front, only that part should be black).
But I'm not sure how to do it in an efficient way. I've tried playing around with different SKBlendModes but so far haven't had much success. The major challenge seems to be achieving the effect whilst preserving the colour of the sun behind.
I'm not sure if it's possible to achieve this with the existing SKBlendModes, or if something else will be required.
I've attached a diagram to illustrate what I'm going for and an image of the current best fit.
You can create the silhouette effect with an SKCropNode, a mask node, and a background node. An SKCropNode selectively mask pixels of the sprite nodes in its node tree. Here, it will provide the silhouette effect by hiding/revealing portions of a black circle.
First, create the following objects:
A shape node (yellow circle) with its zPosition set to -1
A sprite node (black circle) with the same size as the yellow one
A crop node (SKCropNode) and a mask node (SKNode)
Next,
Add the yellow circle at the center of the scene
Add the black sprite as a child of the crop node
Assign the mask node to the maskNode property of the crop node
Add the crop node at the center of the scene
Since the mask node currently has no children, the black circle will be completely hidden and the entire yellow circle beneath it will be visible. As we add sprites to the mask node, the pixels of the black circle that intersect with the sprite will be unmasked (shown in black), producing the silhouette effect.
All that's left is to add a "presentation" node that will be displayed when the mask node's sprite is outside of the black circle. The presentation node will mirror the mask node's sprite's position and rotation. This should be done in didSimulatePhysics to ensure that the presentation and mask-node sprites are synchronized after the physics and actions have been updated.
But wait...if the presentation node moves over the yellow circle, won't it eclipse the Sun? Not if we set its presentation node's zPosition to -2.
The following shows an implementation of the above:
This problem was interesting to me, so I came up with a simple solution. It's not perfect nor does it scale very well, but the basics fulfill your need I believe and it may point others in the right direction.
I don't use any special SKBlendMode. Instead, I am using a duplicate object for the object which moves in front of the sun. This duplicate object is cropped using an SKCropNode with a duplicate of the sun as the maskNode. So, my node tree is as follows:
SKSpriteNode (sun) (the white sun)
SKSpriteNode (object1) (the rotating purple square with alpha 0.25)
SKCropNode
maskNode: SKSpriteNode (sunDuplicate = [sun copy])
SKSpriteNode (object1duplicate, same size as object1 but with [SKColor blackColor])
Inside the update method, align the duplicate with the original to ensure animation is properly forwarded.
- (void)update:(CFTimeInterval)currentTime {
self.object1duplicate.position = self.object1.position;
self.object1duplicate.zRotation = self.object1.zRotation;
}
As I said, this doesn't scale very well since you'll manually have to add duplicate objects and keep track of them for each object moving in front of the sun object. Perhaps SKShader can be of more elegant use.

More precisse shape or mask for a SKSpriteNode object

I'm bulding a simple application using SpriteKit for iOS 7 / 8 but I'm having some issues with my sprites when I try to touch and launch some events from one of two nodes that are too close to each other.
When I try to touch one of them, I end up touching the upper layer one square mask, and if I change the [self addChaild:] order it happens the same but with the other one.
I want to know if I can create a more precisse mask for my nodes so this won happen when I try to touch them. I know I can create a physics world and a physics body for each of them to control Collision Detection but I don't know if this is the right approach.
BTW... I'm using the methods touchesBegan:withEvent: and touchesEnded:withEvent: for touching events detection.
PD: if you need any more info about my implementation please let me know.

Sprite kit skview.showPhysics bug?

Here is the code for setting the characters physics body.
character.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:character.size];
It works fine, I mean it stays where I want it to but as you can see it displays the character's physics body in the bottom left of the screenshot (which is the grey box).
Is this a bug or can this be fixed?
I had a similar problem which may or may not solve yours.
It looks like you are using some sort of viewport system. Perhaps with a node called world and one called camera? If this is the case then you must be moving your world node every frame in order to center the content correctly? When drawing physics, you need to move the world node at -didEvaluateActions instead of -didSimulatePhysics

Resources