Prevent Location Offset With SpriteKit Camera Node - ios

I have noticed that the centerOnNode: method as shown,
- (void)centerOnNode:(SKNode *)node {
cameraOffset = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - cameraOffset.x, node.parent.position.y - cameraOffset.y);
}
greatly impacts the relative positioning of child nodes. As soon as this method runs, the child nodes do in fact seem to be impacted. The following image show the logic of NO movement then with moving left and slightly down:
I drew a light blue box to estimate the physics body that it seems the paths are referencing instead of the updated frame. The lines and circles that you see represent 2 methods I am using for pathfinding.
The line with the dots are just ray tests that I am doing to see if a ray along the green line intersects the square physics body.
The single point you see at the top corner is from using GameplayKit to construct a path that avoids the black square as an obstacle.
I am struggling to figure out how to avoid the camera repositioning from effecting the positioning of the children in the scene.
FYI: I have tested the pathfinding with moving the character but NOT the camera and it works perfectly (shown below)
Clearly the camera offset is the issue. If anyone can tell me what to do to keep the camera movement and the precision of the pathfinding I would greatly appreciate it.

Related

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.

Making SpriteNode position on path relative to yPos of other SKSpriteNode

I'm developing a SpriteKit game using Swift and Xcode 6.3. I want a sprite node to follow along a defined path. I also want the position of the node following the defined path to be relative to the, in this case y coordinate change, of the other node.
The pink one is the one I move with my finger. Its "path" is from the origin to the horizontal pink line. If I drag it up 20% of its path I want the other two to move 20% in the same time. If I drag it back it will make the others go backwards on their path as well. The black dots represent this.
I'm sorry for not posting any code. I read about following a path in SpriteKit but nothing of what i found would have helped here. Thanks for your help.

Controlling movement of flying a vehicle with SceneKit

I currently have a scene which contains a central node at the root of the scene with earth-like geometry and a node representing a flying vehicle.
However I cannot find the right approach to control the movement of the vehicle. I need the ability to turn left and right while orbiting at a static altitude and speed.
I have tried many combinations of animations and physics body forces all leading to undesirable results.
The closest I've come is:
Setting the pivot property of the vehicle to the centre of the scene
Then setting an Action like below to control moving forward
[_vehicleNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:-1 y:0 z:0 duration:10.0]]];
Then finally applying forces for turning left and right with
[_vehicleNode.physicsBody applyTorque:SCNVector4Make(0, 1, 0, 1) impulse:YES];
However I cannot seem to set the pivot and/or position to the right value to get the desired result.
Edit: It appears as the above method would be the solution I'm looking for, however for some reason when I add geometry to the vehicle node, it's position in the scene graph gets changed dramatically. When I add hardcoded buttons to change it's position to where it belongs it appears correct for only that single frame then straight back to being in the middle of nowhere.
Edit 2: After replacing all geometry with a primitive sphere for testing the node is now rotating as intended but is now unaffected by physics forces appearing to ignore it's declaration as a dynamicBody.
If I understand what you are trying to achieve correctly, you can try this:
add a first node to your scene, positioned at (0,0,0) and make it rotate forever along the Y axis using an SCNAction
add your ship node as a child of the first node. Position it at (X,0,0) so that it orbits around the earth
rotate your ship node along the X axis with an action or an animation

CC3Node rotation - Cocos3d

I am developing an iPhone application that uses Cocos3d. I have drawn a scene in the XZ plane ( y = 0 ). Now, I want to rotate the scene around a specified point in the XZ plane, whenever the user touches the screen with two fingers; the rotation point will be the center of the two touch points.
I started by projecting the two touch points to the 3D scene, by finding the intersecting between the CC3Ray (issued from the camera and passing by the touch point) and the XZ plane.
Now that I have the two points in the XZ plane, I can calculate the rotation point (that will be the middle point between these two points).
In order to rotate the scene around this point, I have added it to a parent node. Now all I have to do is to translate it by the negation of the coordinates of the middle point, rotate its parent by the angle, and translate it back by the coordinates of middle point.
Here is the code that I am using (in the ccTouchesMoved method):
// Assuming that the root is a CC3Node and it is the scene that I need to rotate
// and middle refers to the center of rotation
[root translateBy:cc3v(-middle.x, 0, -middle.z)];
[root.parent rotateByAngle:30 aroundAxis:cc3v(0, 1, 0)];
[root translateBy:cc3v(middle.x, 0, middle.z)];
However, I am not able to rotate the scene around the middle point.
Can anyone help me to resolve this problem?
Thank you!
Edit:
I also tried to add these lines of codes in the ccTouchesBegan method:
[root translateBy:CC3VectorNegate(middle)];
[self.cc3Scene.activeCamera translateBy:CC3VectorNegate(middle)];
And in the ccTouchesMoved:
[root rotateByAngle:angle aroundAxis:cc3v(0, 1, 0)];
It works only for the first time the user touches the screen, and then, whenever he/she touches it again, a unwanted translation is happening!
I think the problem is with the ccTouchesBegan method.
On any one node, rotation, translation, and scale transforms are independent of each other. They are each applied to the rest pose of the node, and are not accummulative with each other. This is so that interaction appears natural and expected by the developer controlling the node. In other words, during gameplay, rotating a character after it has been moved, rotates the character in place, not around a translated location. Similarly, translating a node translates it regardless of how the node has previously been rotated.
This is different control than taking a single matrix and accumulating transforms into it, which is how the matrix-based rotate-around-a-distant-point technique works.
However, you can effectively rotate a node around a location that is not its origin by:
Transforming the local rotation location to the global coordinate space.
Rotating the node as normal.
Transforming the (now rotated) local rotation location to the global coordinate space.
Align the rotation locations found in steps 1 & 3 by translating the node by the difference between the two locations.
You can perform steps 1 & 3 using the node's globalTransformMatrix. Code for the steps above is as follows:
CC3Vector gblRotLocBefore = [aNode.globalTransformMatrix transformLocation: rotationLoc];
[aNode rotateByAngle: angle aroundAxis: kCC3VectorUnitYPositive];
CC3Vector gblRotLocAfter = [aNode.globalTransformMatrix transformLocation: rotationLoc];
[aNode translateBy: CC3VectorDifference(gblRotLocBefore, gblRotLocAfter)];
Using this technique, you do not need to involve the node's parent.

Adjacent SKPhysicsBody not working properly

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.

Resources