Adjacent SKPhysicsBody not working properly - ios

I am having a problem when trying to put two blocks that have physics bodies on top of one another.
http://s1173.photobucket.com/user/Kyle_Decot/media/example_zps02f027fe.mp4.html
As you can see in the video I am able to place my block on top of the stacked blocks even though they are placed right on top of one another.
Both the player block and the other blocks inherit from a base Block class which looks like this
#import "Block.h"
#implementation Block
+ (void)loadSharedAssets {
}
- (id)initWithColor:(UIColor *)color size:(CGSize)size {
self = [super initWithColor:color size:size];
if (self) {
self.texture = [SKTexture textureWithImageNamed:#"tile"];
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: self.size];
self.physicsBody.usesPreciseCollisionDetection = YES;
self.physicsBody.dynamic = NO;
}
return self;
}
#end
Update
I've added a picture to make the problem a little more clear. Essentially the problem is that even though the blue blocks are right above one another, I am still able to jump up (w/ the red block) and sit on the edge of the bottom blue block (which shouldn't be possible). It seems something is off w/ the physics bodies or something.
You can see that the red block is slightly higher than the adjacent blue block for some reason and "sits" on the edge of the bottom blue block. when I jump up against it.

EDIT: replaced original suggestion with answer
Scrolling to section 4.5 Edge Shapes of the box2d documentation and you will find the cause of this (SpriteKit uses Box2D under the hood for it's physics implementation)
In many cases a game environment is constructed by connect several edge shapes end-to-end. This can give rise to an unexpected artifact when a polygon slides along the chain of edges. In the figure below we see a box colliding with an internal vertex. These ghost collisions are caused when the polygon collides with an internal vertex generating an internal collision normal.
The two edges here (edge1 and edge2) are the left hand side edges of the two blue boxes in your game (so picture it rotated 90 digs counter-clockwise)
Box2D introduced ChainShapes to get around this issue (which you can find referenced in section 5.6 Edge Shapes.
The idea is to replace groups of square physics bodies with a chain of these vertices whenever generating more than one box at the same time.
I believe you can access these within SpriteKit by using bodyWithEdgeChainFromPath and passing in a Core Graphics path consisting of the corner points of the boxes you want to combine to form the collision chain

I could imagine that tiny numerical errors in the simulation run by SpriteKit, in particular regarding collision detection and motion will make the red box indeed sit on the edge of the lower blue box. This position may be stable simply because the blue box accelerates the red box leftwards for the short amount of time it remains on screen.
A solution could be to use specific body types that merge the different configurations of boxes with smooth left vertical surfaces that occur in your game. In the specific case, it would be a box that is twice as high than wide. Before you spend a lot of time on this, you could test first of all, if removing the lower blue box stops the red box from getting stuck.
You don't need to define different Block classes for different box configurations: your initWithColor:(UIColor *)color size:(CGSize)size already appears to accept different sizes, but you might want to provide additional texture tiles and identifiers for those to select from.

EDIT: since you've clarified your question a bit and you seem to want the red one to fall back to the ground, while the blue blocks stay where they were, it seems that the bodies may be too rough, which makes them stick, and the red one doesn't slide off.
There's the friction property - modifying it can turn the body from a rough one (1.0) to one that's as smooth as an ice cube (0). Per the documentation, the default value is 0.2, which may still be too rough for your needs. Try setting it to physicsBody.friction = 0.0 in all involved bodies.
EDIT 2: it seems that it's a deeper bug. You can try the following workarounds to achieve your desired effect, if you want to sacrifice a little accuracy:
set your first blue block physics body to the target rectangle size, then make each subsequent lower block body 1-2 pixels narrower, so that you evade the 'lip'
set your physics bodies to circles rather than rectangles if all else fails - bodyWithCircleOfRadius:self.size.width*0.5. For a fast enough simulation, this might do.

Related

How can I get boxes to snap into position when I drag and drop them into certain regions of the screen?

I am currently building a game on swift, using Storyboards. The game revolves around generating income from fishing lobsters. Users have lobster pots, which they can place into either inshore or outshone regions of the water. With no prior experience. I have minimal knowledge on how to code in swift.
My problem at the moment is understanding collision detection. There are three regions of the screen where the users can drag their pots into. The first screen is the starting position of the lobster pots, from which the player must drag the pots into either inshore or offshore locations. Currently, I have managed to code the action of dragging and dropping the pots, so they can be placed into any point on the screen. What I hope to do is to be able to have the pots to snap into position when the pots are dropped within the regions of either the inshore of offshore boxes. Furthermore, when the pots are dropped into place, I would like them to be organized in a row, equally spaced, and dropping into a row below, filling up the box.
Image -
I think I should also mention that the background is an image view, taken as a screenshot of the view when the game is running. I did this to avoid layering, as some pots would sometimes move behind the boxes when dragging them.
Thanks in advance.
Here some ideas:
You already have the code to move the tiles, that's good. All you now need is same math.
Although your background is an image, you also need some data model to keep, where your stuff is (or where your pots belong to). It is important to know, if a pot is in "My Pots" or "Inshor" or "Outshore". This information has to keep in some objects, like "myPots" and "inshore" and so on.
So dragging doesn't only move the pots on screen, it also changes where a pot belong to.
Hint: A representation of a area (myPots, ...) can be done with invisible areas. Invisible, because you already have the background. But a invisible rectangle gives you the ability to resize the ui without complicated re-calculations.
I would devide the area like this:
The coordinates are just examples for better understanding.
Most game engines work with coordinate (0,0) at top left.
So if you drag and release a pot, you have to calculate the end point of drag and compare it with your areas. No complicated collision detection necessary, because you only test if a point is in an area. But if you want collision detection, search for AABB collision detection (like here https://studiofreya.com/3d-math-and-physics/simple-aabb-vs-aabb-collision-detection/).
In your case it would be enough to have the decision:
if draggedPot.endCoordinate.y > 100 {
// in or out shore
if draggedPot.endCoordinate.x > 300 {
// outshore
}
else {
// inshore
}
}
else {
// still in myPots
}
I hope you get the idea :)
For arrange in a row it's also some math. Loop over the pots in an area, place one by one, always start-x + width-of-pot + some space. If this is greater than width of the area, set y to height of pot + some space and x starts at zero.

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 Render Many SpriteKit Nodes at Once?

I am using SpriteKit to render a large (20 x 20) dot grid that looks like this:
I'd like to highlight rows or columns based on user input. For example, I'd like to change rows 1-10 to a red color, or columns 5-15 to a blue color.
What is the most performant way to do this?
I've tried:
Naming each GridNode based on the column it's in (e.g. #"column-4). Then use enumerateChildNodesWithName: with the string as #"column-n", changing the color of each node (by changing SKShapeNode setFillColor:) in the enumerate block.
Giving all the columns a parent node associated with that column. Then telling the parent node to change its alpha (thus changing the alpha of all its children).
Making arrays for the different columns, then looping through each node and changing its color or alpha.
I've tried making the GridDot class an SKEffectNode with shouldRasterize: set to YES. I've tried both an SKShapeNode and a SKSpriteNode as its child. I've also tried taking away the SKEffectNode parent and just render an SKSpriteNode.
Each of these options makes my whole app lag and makes my framerate drop to ~10 FPS. What is the correct way to change the color/alpha of many nodes (without dropping frames)?
At its heart, the issue is rendering this many nodes, yes?
When I faced similar performance problems while using SKShapeNode I came up with this solution:
Create SKShapeNode with required path and color.
Use SKView's method textureFromNode:crop: to convert SKShapeNode to an SKTexture
Repeat steps 1,2 to create all required textures for a node.
Create SKSpriteNode from a texture
Use created SKSpriteNode in your scene instead of SKShapeNode
Change node's texture when needed using SKSpriteNode's texture property
If you have a limited set of collors for your dots, I think this aproach will fit fine for your task.
In contrast to #amobi's statement, 400 nodes is not a lot. For instance, I have a scene with ~400 nodes and a render time of 9.8ms and 9 draw calls.
If you have 400 draw calls though, you should try to reduce that number. To determine the amount of draw calls needed for each frame rendered, implement (some of) the following code. It is actually taken from my own SpriteKit app's ViewController class which contains the SpriteKit scene.
skView.showsFPS = YES;
skView.showsNodeCount = YES;
skView.showsDrawCount = YES;
Proposed solution
I recommend using SKView's ignoresSiblingOrder. This way, SKSpriteNodes with equal zPosition are drawn in one draw call, which (for as many nodes/draw you appear to have) is horribly efficient. Set this in the -viewDidLoad method of the SKView's ViewController.
skView.ignoresSiblingOrder = YES;
I see no reason to burden the GPU with SKEffectNodes in this scenario. They are usually a great way to tank your frame rate.
Final thoughts
Basic performance issues mean you have a CPU or a GPU bottleneck. It is difficult to guess which you're suffering from with the current information. You could launch the Profiler, but Xcode itself also provides valuable information when you are running your app in an attached device. FPS in the Simulator is not representative for device performance.

How to detect intersecting nodes of children of other SKSpriteNodes

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...

Should I use a sprite(image) or should I draw?

Below is a screenshot from a famous structure building game.
As you can see rods are attached together with ball joints. I'm trying to achieve something similar with Cosos2d on iPhone. I've got two questions here:
Should I draw those ball joints or should I just add them as sprites?
I guess the rods are not drawn bur are rather images. But considering that the length of the rods will change depending on need, how do I make rods with different lengths from just one image?
I am not sure what would be the best approach for one. I would imagine that either way would be fine, but it would be harder to achieve whatever visual affect you want through drawing them. I think the hardest part would be making sure that they are anchored to vertices in a grid system that has the dimension that you want.
As for (2) you can always scale your image.
CCSprite *rod = [CCSprite spriteWithFile:#"rod.png"];
rod.scaleY = 2.0; //or scaleX
Then rotate and change its anchor points as needed.
Again I imagine the most difficult part would be anchoring them and positioning them correctly, so that align properly with the vertices.

Resources