Can You Add SKPhysicsBody To Only One Side Of A Rectangular Node? - swift

So let's say I have this rectangular SKNode. Can I add SKPhysicsBody to only one side of the rectangle i.e. only the top side, so that only the top side could detect collision and not the other sides (then the physics indicator blue line would only appear on the top and not anywhere else)?
I wanted to create a game, but I don't know whether something like this could work.
So if I could, how?
If I can't, is there a way around this issue?
I wanted to add a normal rectangle with physicsBody on only one side.
See the rectangle image here, the blue part is the place where I want the physicsBody
So I've tried adding an edgeBased physicsBody, but it doesn't seem to work (either the physicsBody didn't get created or it is in the wrong position).
let rectangle = SKSpriteNode(imageNamed: "Rectangle")
rectangle.size = CGSize(width: 128, height: 128)
rectangle.position = CGPoint(x: frame.midX, y: frame.midY)
rectangle.physicsBody = SKPhysicsBody(edgeFrom: CGPoint(x: rectangle.position.x - rectangle.size.width/2, y: rectangle.position.y + rectangle.size.width/2), to: CGPoint(x: rectangle.position.x + rectangle.size.width/2, y: rectangle.position.y + rectangle.size.width/2))
rectangle.physicsBody!.restitution = 0.0
rectangle.physicsBody!.categoryBitMask = physicsCategories.groundCategory
rectangle.physicsBody!.collisionBitMask = physicsCategories.squareCategory
addChild(rectangle)
Thanks!

You could, but it would also detect collision coming from the 'wrong' side i.e. with an object that has traveled through the node.
Physics bodies can be volume or edge based, but even an edge-based one that was only a pixel thick still has 4 sides.
If you could supply a drawing of exactly what you want, we could help better as there are probably several different approaches.

Related

SKSpriteNode - constant speed, and detect which SKSpriteNode does something

I have two questions:
How can I make an SKSpriteNode move at a constant speed and direction?
I'm currently using item.physicsBody?.velocity, but I want just a constant speed, in the direction to the left.
let item = SKSpriteNode(color: .black, size: CGSize(width: 32, height: 16))
item.position = CGPoint(x: 600, y: self.heights[random])
item.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:32, height: 16))
addChild(item)
item.physicsBody?.velocity = CGVector(dx: -360, dy: 0)
item.physicsBody!.affectedByGravity = false
I have the above code in touchesBegan(). I want to add some code to update(), to detect when one of these created SKSpriteNodes does a certain thing (e.g. reaches a specified x position), and then make a change to that SKSpriteNode (e.g. delete it). How do I do that, so that I get that particular SKSpriteNode?
Just manually update your sprite's position. Define a speed in points per second, use a timer in update() to see how much time has passed since it was last called and with a bi of maths, work out its new position. I've done this and it works.
I'm not sure what the relevance of the touchesBegan() code is to this point (are you creating a sprite and setting it moving when you touch the screen?), but in general you could iterate over the array of child nodes in the scene using enumerateChildNodes(). When you have a node that matches your criteria (specific x position etc.), perform the action on the node.

How to smooth curve in UIBezierPath symmetrically

I am writing a custom component in Swift with UIBezierPath and I am facing difficulties to correctly find the control points to create a perfect path around a circle. Please have a look of the component below:
Please note that the circle is not following the yellow ball perfectly in the blue circle area, I tried to set the control points to the right position but they are not resulting a good result:
The function used to describe the path above is partially this:
//private let MAGIC_NUMBER: CGFloat = 0.552284749831
private let MAGIC_NUMBER: CGFloat = 0.5
//...
let secondPointEnd: CGPoint = CGPoint(x: middleLeft.x + ballRadius/2, y: middleLeft.y + ballRadius/2)
path.addCurve(
to: secondPointEnd,
controlPoint1: CGPoint(x: firstPointEnd.x, y: secondPointEnd.y - (ballRadius * MAGIC_NUMBER)),
controlPoint2: CGPoint(x: firstPointEnd.x, y: secondPointEnd.y))
let thirdPointStart: CGPoint = CGPoint(x: secondPointEnd.x + ballRadius/2, y: secondPointEnd.y - ballRadius/2)
path.addCurve(
to: thirdPointStart,
controlPoint1: CGPoint(x: thirdPointStart.x, y: secondPointEnd.y),
controlPoint2: CGPoint(x: thirdPointStart.x, y: secondPointEnd.y - (ballRadius * MAGIC_NUMBER)))
For the full source code (Playground): https://gist.github.com/ppamorim/feb3318ac30e20a75d9c62e8abdc2efa
I tried to fix it with multiple configurations, I had no success so far, I also tried to apply the magic number 0.552284749831 but it makes it worse. Am I missing some control point configuration? Is there any invalid control point there?
Could you guys please help me to find what is wrong with this bezier path?
Regards,
Pedro
With help from #Bob, I was able to complete the UIBezierPath like he described: https://gist.github.com/ppamorim/9390708c455a950a65621715ce7b6c82
I wouldn’t recommend rendering the circular bit with beziers. They’re extremely close, but not spot on. It’s fine approximation for corner rounding of a rectangle (or any polygon), but for a true circular portion of the path you should use addArc(withCenter:radius:startAngle:endAngle:clockwise:).
You can use beziers leading into and out of the arc, if you want, but for the circular portion, use an arc. E.g. see https://stackoverflow.com/a/60862388/1271826.

Positioning images to centre

I am trying to make this picture appear at the bottom center but it keeps moving towards the right. I have looked at scales and none of them have worked.
I have tried many different numerals for x and y such as x=7 and y=7 or x=20 and y=5 and they all end up exactly in the middle or to the right. I also considered using negative numbers but didn't know how
player = SKSpriteNode(imageNamed: "shuttle")
player.position = CGPoint(x: self.frame.size.width/7, y: player.frame.size.height/7)
self.addChild(player)

spritekit how to selectively scale nodes

as background lets assume I have a map- literally a road map being rendered inside my SKScene. Roads are represented by SKShapenodes with path set to an array of CGPoints. I want the user to be able to zoom in/out so I created a camera node:
var cam: SKCameraNode = SKCameraNode()
and as the user wants to zoom in/out by scrolling on the trackpad:
let zoomInAction = SKAction.scale(to: CGFloat(scale), duration: 0.0)
camera?.run(zoomInAction)
This works great however I have an additional complexity which I'm not sure how to handle. I want some nodes (for examples road name labels, icons, map legend) to be exempt from scaling- such that as a user zooms in/out the road name label remains the same size while the road shape scales proportionally.
Not sure how to handle this? Can I have a hierarchy of scenes so one layer scales and the other doesnt scale? Can that be achieved by attaching the camera node to the "scalable" layer? Any help appreciated!
Here is the case. If you want the node scale won't change with camera, just add the node to the tree of camera. Don't forget add cameraNode to scene, otherwise, those nodes connected to camera won't be rendered.
In the following, label is rendered via camera and won't change scale.
let label = SKLabelNode.init(text: "GFFFGGG")
label.fontSize = 30
label.fontColor = UIColor.black
label.name = "cool"
label.zPosition = 100
let camera = SKCameraNode()
camera.addChild(label)
scene.addChild(camera)
scene.camera = camera
camera.position = CGPoint.init(x: 0, y: 0)
camera.xScale = 2.0
If you have nodes connecting to scene before,
you may remove the node from parent and then add to camera.
If using a function to batch handling them should not be as hard as thought.
Maybe not necessary:
You may transfer them to cameraNode tree via camera.convert(point: , from:) etc.

ARKit - create world boundaries/get position at edge of visible plane?

Ok, what I am trying to do is create physics body/colliding boundaries for my character, my SCNNode, in my SceneKit game Im building with ARKit. This is so my node cannot move out of the user's vision/go so far away that it isn't visible as it is currently doing. My SCNNode is moved by user input, so I need to make "world boundaries" while ARKit still doesn't have vertical wall detection
I know you can place an object some set distance ahead of you in the real world as stated here Understand coordinate spaces in ARKit
and I have done that with this, just making a box with physics body here -
let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0) //change to be VERY TALL - need to make it a giant room
node.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.static, shape: nil)
box.firstMaterial?.diffuse.contents = UIColor.red
box.firstMaterial?.isDoubleSided = true
node = SCNNode(geometry: box)
node.position = SCNVector3(view.pointOfView.simdWorldFront + float3(0, 0, -0.5)) //random distance ahead
And this works, and I could add it as a child node of camera so it moves as user moves, but I don't think Im doing this correctly.
Essentially I need 4 walls to box the user/SCNNode in (corral the character) that are infinitely high, and at the VERY edge of the horizontal plane that the user can see. Really I don't know what this distance should be in the x plane:
+ float3(0, 0, -0.5)
How would you create AR boundaries like this?