How to detect intersecting nodes of children of other SKSpriteNodes - ios

I hope you all can help with this. I'm working on app of a board game. I have hex shaped tiles which are called randomly and laid out at the start of the game. Each of these tiles has four sides with a value of 1 and the other two sides have values of 2 and 3.
Each tile is a SKSpriteNode with transparent rectangle Nodes on the edges. There are 5 different types of tiles and they need to be separate Sprites with child nodes because in addition to being randomly laid out they area also randomly rotated. So I need to know programmatically which tile edges are touching which edges of other tiles.
Like this:
https://app.box.com/s/nnym97st3xmrsx979zchowdq1qwsmpoo
(I tried to post an image of what I'm trying to accomplish, but apparently I don't have a high enough of a rating.) ;-)
For example: If a "2" is touching a "3", etc.
I first tried Collision detection, but of course that only works with dynamic, moving objects.
I tried an IF statement to compare if the other nodes were touching and then remembered that the coordinates where specific to the Parent Node, so that didn't work.
I then tried intersectsNode, but that seems to only work with nodes under the same parent.
I am currently working with convertPoint in order to get the coordinates to match the scene and thus be comparable. But I can't seem to get it work the way I need.
There must be something simple that I am not seeing. Any ideas?

Certainly not simple.
One solution would be to start all your shapes slightly spaced out from each other. Add invisible child nodes with physics bodies to all six sides and give each physics body an appropriate category based on their rating (1, 2 or 3).
When you start the game, move all the outer nodes into their proper position (sides touching) by using whatever movement method your prefer. This will give you contact messages as each hex side touches another. The contact messages will tell you what side number is touching its neighbor.
The exact coding of this idea depends on your current code, game play, etc...

Related

Detecting collision on only one side of a rectangular physics body - Swift3

I am currently working on a 2D endless runner, written in Swift3 and using Spritekit.
Short question:
Is there a way to only check for collisions on the right side of my character's rectangular physics body?
More info:
The platform on which the character runs is made of puzzle pieces and the user builds upon it as the game progresses. The character progresses left to right, in respect to the background (which goes right to left).
I want the character to automatically jump when he collides with a piece on his right side. However, any pieces that the player puts to the right of him (same Y value as the character) is of the same class as the pieces underneath him.
So the same code that checks for collision between the character and pieces to his right, and make him jump, will also make him jump as long as the game detects collision between the character and the pieces under him.
I have not been able to find another problem like mine, since usually others' characters are colliding with objects of different classes from their ground class.
Thanks!
P.S. I have tried to make my character a SKSpriteNode with two physics bodies, but I could not find any helpful documentation. If it helps any, my character also performs a looping running animation--though I can't imagine that would harm anything.
You could achieve that by detecting collisions with your rectangle and then deciding whether the collision was with the side of your interest or not. Here is a discussion about how to do that. Good luck!
Have you tried adding a non visible sub node (e.g. feetNode) to your character's sprite node and giving that sub node the physics body (class) for floor contact ?
Depending on the rest of your logic, it may allow you to use a different physics class for your character have more flexibility in collision detection.
In fact, you could probably use that approach with several sub nodes in your character's sprite node and have multiple collision behaviours for the character depending on what hits it.
Once you obtain the contact point of the 2 bodies that are colliding, determine which body is the one that is colliding by checking the categorymasks and then check its CGPoints x position. This x position can be compared to the other body's x position to know exactly which side it is colliding from.
if Body A's x position > Body B's x position, Body A is on the right and if not, its on the left.
As simple as that.
Hope this helps!

Prevent Node From Leaving Boundary

I am using GameplayKit to create paths around obstacles in a given map boundary (lets say a rectangle 1000x1000).
I know that you can make certain nodes avoid "obstacles" when pathfinding, which I am using quite nicely. What I am curious about however is such:
Is there a way to use this same logic and count anything not in the map boundary as an "obstacle"?
A work around would be to create 2 SKNodes and fit them together to create an inner "hole" which becomes 1000x1000, but I am trying to avoid unnecessary addition of nodes if there is a better way. Below I am showing what I could do.
Ideally I want to make the red and black area treated as an obstacle so that all paths remain inside the main square.
UPDATE:
I am using GameplayKit as I have already said, and the pathfinding algorithm can not count regions that are NOT included in a given physics body as an obstacle. It can only count obstacles to be closed polygons that lie within a region. Creating the 2 nodes as shown above works because the pathfinding will now not be able to create any points that lie outside the green rect.
Just have your game layered like this
Scene
SKNode topshape
SKNode bottomshape
SKNode innerbox <-- This is the size of your inner square
SKNode gamenodes <-- Place all inner nodes here
...
Then attach a SKPhysicsBody using an edge loop rectangle the size of innerbox, to the innerbox SKNode and make it a wall category, this will keep all your nodes inside, providing your nodes do not move at insane speeds breaking the engine.
You would be adding 1 additional node instead of 2 (Technically you could make bottomshape and topshape 1 node, making it 0 nodes added), but all processing would get done within the inner node, so not much overhead gets added.

Interact with complex figure in iOS

I need to be able to interact with a representation of a cilinder that has many different parts in it. When the users taps over on of the small rectangles, I need to display a popover related to the specific piece (form).
The next image demonstrates a realistic 3d approach. But, I repeat, I need to solve the problem, the 3d is NOT required (would be really cool though). A representation that complies the functional needs will suffice.
The info about the parts to make the drawing comes from an API (size, position, etc)
I dont need it to be realistic really. The simplest aproximation would be to show a cilinder in a 2d representation, like a rectangle made out of interactable small rectangles.
So, as I mentioned, I think there are (as I see it) two opposite approaches: Realistic or Simplified
Is there a way to achieve a nice solution in the middle? What libraries, components, frameworks that I should look into?
My research has led me to SceneKit, but I still dont know if I will be able to interact with it. Interaction is a very important part as I need to display a popover when the user taps on any small rectangle over the cylinder.
Thanks
You don't need any special frameworks to achieve an interaction like this. This effect can be achieved with standard UIKit and UIView and a little trigonometry. You can actually draw exactly your example image using 2D math and drawing. My answer is not an exact formula but involves thinking about how the shapes are defined and break the problem down into manageable steps.
A cylinder can be defined by two offset circles representing the end pieces, connected at their radii. I will use an orthographic projection meaning the cylinder doesn't appear smaller as the depth extends into the background (but you could adapt to perspective if needed). You could draw this with CoreGraphics in a UIView drawRect.
A square slice represents an angle piece of the circle, offset by an amount smaller than the length of the cylinder, but in the same direction, as in the following diagram (sorry for imprecise drawing).
This square slice you are interested in is the area outlined in solid red, outside the radius of the first circle, and inside the radius of the imaginary second circle (which is just offset from the first circle by whatever length you want the slice).
To draw this area you simply need to draw a path of the outline of each arc and connect the endpoints.
To check if a touch is inside one of these square slices:
Check if the touch point is between angle a from the origin at a.
Check if the touch point is outside the radius of the inside circle.
Check if the touch point is inside the radius of the outside circle. (Note what this means if the circles are more than a radius apart.)
To find a point to display the popover you could average the end points on the slice or find the middle angle between the two edges and offset by half the distance.
Theoretically, doing this in Scene Kit with either SpriteKit or UIKit Popovers is ideal.
However Scene Kit (and Sprite Kit) seem to be in a state of flux wherein nobody from Apple is communicating with users about the raft of issues folks are currently having with both. From relatively stable and performant Sprite Kit in iOS 8.4 to a lot of lost performance in iOS 9 seems common. Scene Kit simply doesn't seem finished, and the documentation and community are both nearly non-existent as a result.
That being said... the theory is this:
Material IDs are what's used in traditional 3D apps to define areas of an object that have different materials. Somehow these Material IDs are called "elements" in SceneKit. I haven't been able to find much more about this.
It should be possible to detect the "element" that's underneath a touch on an object, and respond accordingly. You should even be able to change the state/nature of the material on that element to indicate it's the currently selected.
When wanting a smooth, well rounded cylinder as per your example, start with a cylinder that's made of only enough segments to describe/define the material IDs you need for your "rectangular" sections to be touched.
Later you can add a smoothing operation to the cylinder to make it round, and all the extra smoothing geometry in each quadrant of unique material ID should be responsive, regardless of how you add this extra detail to smooth the presentation of the cylinder.
Idea for the "Simplified" version:
if this representation is okey, you can use a UICollectionView.
Each cell can have a defined size thanks to
collectionView:layout:sizeForItemAtIndexPath:
Then each cell of the collection could be a small rectangle representing a
touchable part of the cylinder.
and using
collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath
To get the touch.
This will help you to display the popover at the right place:
CGRect rect = [collectionView layoutAttributesForItemAtIndexPath:indexPath].frame;
Finally, you can choose the appropriate popover (if the app has to work on iPhone) here:
https://www.cocoacontrols.com/search?q=popover
Not perfect, but i think this is efficient!
Yes, SceneKit.
When user perform a touch event, that mean you knew the 2D coordinate on screen, so your only decision is to popover a view or not, even a 3D model is not exist.
First, we can logically split the requirement into two pieces, determine the touching segment, showing right "color" in each segment.
I think the use of 3D model is to determine which piece of data to show in your case if I don't get you wrong. In that case, the SCNView's hit test method will do most of work for you. What you should do is to perform a hit test, take out the hit node and the hit's local 3D coordinate of this node, you can then calculate which segment is hit by this touch and do the decision.
Now how to draw the surface of the cylinder would be the only left question, right? There are various ways to do, for example simply paint each image you need and programmatically and attach it to the cylinder's material or have your image files on disk and use as material for the cylinder ...
I think the problem would be basically solved.

How to correctly use SKConstraints

I'd like to build a menu of 'tiles' using SpriteKit. For simplicity, the tiles are SKSpriteNodes of all the same size, and are housed in a larger 'container' SKSpriteNode. The container node is a mask node, so only a subset of the menu tiles are shown at a given time. Dragging up or down on a tile should scroll the list of tiles up or down, respectively.
Right now, when a drag is detected over a tile, I just reposition all tiles up or down--easy. There are two other constraints on the problem, though. The top tile should never scroll below the top of the mask/container node. And, the bottom tile should never scroll above the bottom of the mask/container node. Taken together, this keeps the list of tiles from ever being completely dragged to a place where they are hidden/unreachable.
This problem seemed like it would be elegantly solved with either SKConstraints or SKPhysicsJoints. I've tried a lot of combinations, but nothing seems to give me the desired effect.
For instance, I've used an SKPhysicsJointFixed to pin each pair of neighboring tiles together. This has two problems--first, the tiles are 'sluggish' when dragged, so the finger drags more quickly than the tile to the point that it is no longer over the tile, and the drag stops being recognized as on the node. Second, this pin allows the tiles to rotate freely about the anchor point. I added an SKConstraint to restrict the z-rotation of every tile, but now the tiles can barely be dragged at all.
I tried implementing everything with just SKConstraints, so I wouldn't have to fuss with setting masses correctly, etc., in order to make the physics approach feel more usable. I added a constraint on the x position of every tile so that they could only be dragged vertically. Then, I added another with SKConstraint.distance(_:toNode:) on every pair of tiles to keep them separated by the correct vertical distance. The problem with this is, given two tiles, if I add this distance constraint on each, only the last tile to be given the constraint is allowed to move. It moves and the other tile follows this tile correctly. That other tile can't be moved at all, though.
Then there comes the problem of keeping at least some part of the set of tiles 'in bounds', so that they never are dragged completely outside of the mask node, and thus not visible/unreachable. A constraint/joint seemed like it might work here--add some constraint/joint to the bottom tile keeping it above the bottom of the mask node, and similarly for the top tile. But now, how do I use a constraint/joint to keep the bottom tile above the bottom of the bottom of the mask node but let it move below this point, and vice versa for the top tile?
Am I going about this all wrong? Is using constraints/joints not the correct approach? Obviously, I can hand code all of these restrictions, but it seems like constraints/joints would solve this problem so much more elegantly, while also letting me rely on the physics world for some springiness, flicking, etc. If there's a good way to do what I'm trying to do, would someone please provide a suggestion?
Many thanks!

How to drag and drop multiple SpriteKit Nodes with the same parent?

I have multiple SKSpriteNodes(some rectangles) which are draggable (I followed the tutorial on Sprite Kit Tutorial: How To Drag and Drop Sprites). When a collision happens between them, I group them (by making the one rectangle a parent and the other a child). No matter how many rectangles I will combine, I manage always to have one parent and multiple rectangles that belong to it. I am doing this cause I want to move cubes belonging in a group together and I observed that if I move the parent, I move all of its children. What I am doing to achieve this is to transform the group at touchBegin and to make the touchedNode a parent and all the other nodes of the group children of this new parent. I believe that the following image may make things a little bit more clear.
The problem I am facing is that I can drag the group even if I touch at the white space (shown with red circle) included between the horizontal and vertical rectangles. As all rectangles shown in the image have the same parent, I guess that there is a bounding box that include them all and this is why the white space in the middle can trigger a drag event.
Does anyone have any idea how I can deal with this issue?
Is it possible to have a bounding box as shown in the following image?
Thanks in advance.
You need to write custom hit testing to perform this kind of trick.
For every click -> For every box (within certain range of touch) -> For every other box (within certain range of touch) -> Combine the two box frames into one (CGRectUnion(<#CGRect r1#>, <#CGRect r2#>)) and see if your finger is within the rect.
This might give results for a lot of dispersed rectangles, so limit your initial search of boxes to a given range from the touch itself.
Apart from that it's just simple code.

Resources