Find opposite transformation of SKSpriteNode - ios

I want to add a SKShapeNode drawn from my SKScene to a SKSpriteNode. The SKSpriteNode may already have some transformations that automatically translate to the SKShapeNode, which I don't want to happen. I've added a screenshot of the way the shapenode jumps after adding. I have also added my code where I add the shape node as a child and apply some opposite transformations to the node (works fine in some cases, but for example when I scale, move and rotate the robot, the drawing jumps to another position).
func addMaskPath(_ path: CGPath) {
let drawing = MaskLayer(withPath: path)
drawing.zRotation = -zRotation
drawing.xScale = drawing.xScale / xScale
drawing.yScale = drawing.yScale / yScale
drawing.position.x = (drawing.position.x - position.x) / xScale
drawing.position.y = (drawing.position.y - position.y) / yScale
drawing.position.y = drawing.position.y*cos(zRotation) - drawing.position.x*sin(zRotation)
drawing.position.x = -drawing.position.y*sin(zRotation) + drawing.position.x*cos(zRotation)
cropMask?.eraserMask?.addChild(drawing)
}
MaskLayer is a SKShapeNode. How do I easily find the transformations of my SKSpriteNode and apply them the opposite way to my SKShapeNode so my drawing will visually stay exactly the same?
Screenshot

You shouldn't add your SKShapeNode to a SKSpriteNode if you don't want it to inherit its transformation.
I suggest having a parent SKNode that'll hold your SKSpriteNode and add your SKShapeNode to the parent SKNode instead of the SKSpriteNode

Related

Constantly offsetting Position of an SKNode - Swift Sprite Kit

I created a Sprite SKNode Class which works like a StackView. In Sprite Kit objects origin is always centered therefore I would like to offset the position by a certain amount (which is calculateAccumulatedFrame().width/2) so it can be positioned comfortable. What would be the best way to do so?
First I thought I could do something like this
override var position: CGPoint {
didSet {
self.position = CGPoint(x: position.x - self.calculateAccumulatedFrame().width/2, y: position.y)
}
}
But of course this is not possible since this would end in an endless loop
What would be other options?
In Sprite Kit objects origin is always centered
That is not true. There is this useful property called anchorPoint that you can set to specify where the "origin" is.
Judging from your code, you seem to want the anchor point to be on the midpoint of the right edge of the sprite, assuming the sprite to be a rectangle. This means that you want the anchor point to be (1, 0.5):
yourSprite.anchorPoint = CGPoint(x: 1, y: 0.5)

Detecting when SKSpriteNode is completely below another SKSpriteNode

For a game I'm creating, an SKSpriteNode gradually makes its way down the user's screen. There's another SKSpriteNode (position is static) near the bottom of the screen, leaving only a little bit of space for the original SKSpriteNode to fit in. I need to detect when the first SKSpriteNode is COMPLETELY below the second SKSpriteNode, which I'm having a bit of trouble with. Here's the code I'm currently using:
if (pos.y > barPosY) //pos.y = 1st SKSpriteNode, barPosY = 2nd SKSpriteNode
{
touchedTooEarly = true
}
For some reason, when the first SKSpriteNode goes just a little bit over the 2nd SKSpriteNode (not all the way), it still detects it as being completely over. Is there a coordinate space issue I'm missing?
The logic
A sprite a covers a sprite b if
b.frame is inside a.frame
b.zPosition is below a.zPosition
The extension
Now let's build an extension
extension SKSpriteNode {
func isCoveredBy(otherSprite: SKSpriteNode) -> Bool {
let otherFrame = CGRect(
origin: convertPoint(otherSprite.position, fromNode: otherSprite),
size: otherSprite.frame.size
)
return zPosition < otherSprite.zPosition && CGRectContainsRect(frame, otherFrame)
}
}
Problem #1: transparency
This mechanism totally ignores transparency.
Problem #2: same sprite
If you compare 2 sprites of the same type the function CGRectContainsRect will return true only when they are exactly aligned. Althoug you can solve this problem creating a smaller rectangle when you compare the sprites.

Put a ball inside a rectangle trapped inside, with some velocity and restitution

In this scene:
let rectangle = SKShapeNode(rectangleOfSize: CGSize(300, 400))
rectangle.fillColor = UIColor.clearColor()
rectangle.strokeColor = UIColor.grayColor()
rectangle.strokeWidth = 10
self.addChild(rectangle)
I want to put a ball inside this rectangle, trapped inside, with some velocity and restitution, that will be bouncing and changing the vector direction when collides with one of the walls.
I am not 100% sure if you can do that with SKShapeNodes and physics bodies because the physics body will apply to the whole shape node (with center).
Since you want to make the rectangle visible you can could either make a SKSpriteNode subclass with 4 SKSpriteNodes sides and than shape them as a rectangle.
Alternatively you could just draw a rectangle border in your program of choice, just make sure the centre is nothing basically, and export as PNG. Than just create a SKSpriteNode with the image.
let rectangle = SKSpriteNode(imageNamed: "Rectangle")
let texture = SKTexture(imageNamed: "Rectangle")
rectangle.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
rectangle.physicsBody?.collisionBitMask = 1
Now if you have drawn the rectangle correctly it will only have a physics body on the 4 sides since you did not draw a centre.
Than you simply create your ball and give it a physics body like above, with the same collision bitMask. Position it in the centre of the rectangle and give it an impulse or something. It should bounce around as you wish without leaving the rectangle.
If you are unsure about physics bodies than you need to read a bit about it, e.g
http://www.techotopia.com/index.php/A_Swift_iOS_8_Sprite_Kit_Collision_Handling_Tutorial

Get absolute coordinates of point from scene with anchorPoint of (0.5, 0.5)?

What's the right way to convert a point into absolute coordinates if the containing scene has an anchorPoint of (0.5, 0.5)? Conceptually, we set the anchorPoint to (0.5, 0.5) and then add a child with position (0, 0). Assuming the device is a 5S, the conversion should yield (160, 240).
We're using Swift.
Add a SKNode on your Scene. Then set its position rely on the scene's frame. Sample Code here:
var node = SKNode()
node.anchorPoint = CGPointMake(0,0)
node.position = CGPointMake(-scene.frame.width/2, -scene.frame.height/2)
scene.addChild(node)
Add your sprite on this node, you now can get the absolute position~
You can make use of the following methods provided with SKNode to get relative points in the node tree.
convertPoint:toNode:
convertPoint:fromNode:
Using Carrl's method above to add an SKNode, you can get absolute points as follows:
var relativeNode = SKNode()
relativeNode.anchorPoint = CGPointMake(0,0)
relativeNode.position = CGPointMake(-scene.frame.width/2, -scene.frame.height/2)
scene.addChild(relativeNode)
var absolutePoint = scene.convertPoint(someNode.position, toNode: relativeNode)
//This will result in {160, 240} on a 5S.
These methods take into consideration the various anchorPoints of the nodes.

Swift Spritekit dissipating Gravity field

I have a game in swift using spritekit. If you tap the screen it will create a radial gravity field and pull all the other objects in. I create the gravity field like so
var fieldNode = SKFieldNode.radialGravityField();
fieldNode.falloff = 0.5;
fieldNode.strength = 1;
fieldNode.animationSpeed = 0.5;
It works but my problem is that I only want a sprite to be affected only when it is when it within a certain distance to the centre of the radial gravity, and i will have more than 1 sprite. The way I see it is that there are 2 ways to do it, 1. When a sprite is too far turn off the radial gravity for that sprite or 2. Make the radial gravity dissipate after a certain radius. There is also an overall gravity for the scene.
So the main question is:
How can I either turn off 1 gravity for a sprite OR make a radial gravity dissipate ?
A field node's region property determines its area of effect. The associated SKRegion object lets you define a circular region by its radius.
You can also use the fieldBitMask on a physics body and the categoryBitMask on a field to selectively control which fields affect which bodies.
Try using:
let radius: CGFloat = 1000.0
gravityField.strength = Float(pow(radius, 2)) * pow(10, -3)

Resources