I have 4 nodes of enemies. They are appearing on a scene from the right to the left bound. But often they are appearing in groups for some reason. It looks unpleasantly so I want to set the distance between them. I know that I can do it with SKConstraint, but XCode says that I'm doing something wrong. That's how I tried to do it:
mouse = SKSpriteNode(texture: mouseArray[0]);
self.mouse.position = CGPointMake(CGRectGetMaxX(self.frame) + self.mouse.size.width, CGRectGetMidY(self.frame) - 138)
self.mouse.size = CGSizeMake(self.mouse.size.width + self.mouse.size.width / 2, self.mouse.size.height + mouse.size.height / 2)
let constraint = SKConstraint.distance(range: 50...100, toNode: cat)
Xcode tells me that argument for parameter ToPoint is missing. I don't know why and what point it is talking about.
I couldn't find examples of using SKConstraint.distance online. How to use constraints properly for this purpose? Thanks!
SKConstraint's distance class method takes an SKRange as its first argument not a closed range (e.g., 10...50). For an example,
Swift 2
let range = SKRange(lowerLimit:50, upperLimit:100)
let constraint = SKConstraint.distance(range, toNode:cat)
mouse.constraints = [constraint]
Swift 3
let range = SKRange(lowerLimit:50, upperLimit:100)
let constraint = SKConstraint.distance(range, to: cat)
mouse.constraints = [constraint]
Related
I have 10 SKSpriteNodes that are connected via SKPhysicsJointPins. Together, they form a chain (each node is a chain-link image and each node has a physicsBody).
There are also some balls that bounce around and interact with the chain.
Desired behavior: The balls should never penetrate the chain.
Actual behavior: The chain-link joints allow too much leeway, and the balls go through the chain. To be clear, I'm not talking about tunneling -- the balls don't go through the nodes, themselves. Instead, the chain-nodes sort of move aside and the balls go through the opening.
The code to create the chain-links (please assume that all variables are defined and that the positioning of each node is correct. Hopefully you can see what's going on here):
let chains = mainData.getLevelChains()
let bodyA = SKSpriteNode(imageNamed: "chainLink.png")
bodyA.size = CGSize(width: chainLinkWidth, height: chainLinkHeight)
bodyA.position = CGPoint(x:chains[i].leftAnchorPosition.x + CGFloat(halfChainLinkWidth) + (chainWidth * (0.1 * CGFloat(j))), y: chains[i].leftAnchorPosition.y)
bodyA.name = "body"
bodyA.physicsBody = SKPhysicsBody(texture: bodyA.texture!, size: CGSize(width: chainLinkWidth, height: chainLinkHeight))
bodyA.physicsBody?.categoryBitMask = CollisionTypes.chain.rawValue
bodyA.physicsBody?.collisionBitMask = CollisionTypes.ball.rawValue
addChild(bodyA)
let joint = SKPhysicsJointPin.joint(withBodyA: previousChainLink.physicsBody!, bodyB: bodyA.physicsBody!, anchor: CGPoint(x: Double(previousChainLink.position.x)+halfChainLinkWidth, y: Double(previousChainLink.position.y)))
scene?.physicsWorld.add(joint)
Question: What can I do to ensure that the chain maintains its structural integrity so the balls never go through the chain?
In the end, I solved the problem by setting the mass of the chain-links to a value slightly greater than that of the balls.
ball.physicsBody?.mass = 5.0
chainLink.physicsBody?.mass = 6.0
I've been trying to implement an infinite background animation, which should change between 4 images of equal height and then repeat the sequence. However, it does not seem to work properly.
Note anchorPoint = CGPoint(x: 0.5, y: 0)
func updateBackground(currentTime: TimeInterval){
var delta: CGFloat = 0.0
if lastUpdate != nil {
delta = CGFloat(currentTime - lastUpdate)
}
//First increment position
activeBackground1.position.y += delta*backgroundVelocity
activeBackground2.position.y += delta*backgroundVelocity
//Detect bounds surpass
if activeBackground1.position.y > activeBackground1.size.height + screen.height/2 {
lastSky = (lastSky + 1)%4
sky1 = SKTexture(imageNamed: "sky" + String(lastSky))
activeBackground1.texture = sky1
//Reposition: background1 new position is equal to minus the entire height of
//background2 + its y size.
activeBackground1.position.y = -abs(activeBackground2.size.height-activeBackground2.position.y)
}
if activeBackground2.position.y > activeBackground2.size.height + screen.height/2 {
lastSky = (lastSky + 1)%4
sky1 = SKTexture(imageNamed: "sky" + String(lastSky))
activeBackground2.texture = sky1
activeBackground2.position.y = -abs(activeBackground1.size.height-activeBackground1.position.y)
}
}
The update algorithm works fine, but when it is needed to reposition one of the two background, it seems there is an offset of about 10.0 CGFloat from one background to another. What am I doing wrong?
EDIT: It turned out that the error was located in my image, which presented some blank rows and therefore generated visualisation glitches. So my code works perfectly.
I do the test and most likely you should use something like:
activeBackground2.position.y = activeBackground1.size.height + activeBackground1.position.y
instead of
activeBackground2.position.y = -abs(activeBackground1.size.height-activeBackground1.position.y)
I did this example and it works correctly: https://github.com/Maetschl/SpriteKitExamples/tree/master/InfiniteBackground/InfiniteBackground
Feel free to see and use.
Your problem is floating point math causing rounding errors. I am on a phone right now so I can’t wrote code, but what you want to do is have 1 parent SKNode to handle your entire background.
Add your background slivers to the parent node.
You then place the moving action on the parent only.
As each sliver leaves the screen, you take the sliver, and move it to the end of the other slivers.
This jumping should always be done with integer math, leaving no holes.
Basically:
The floating point moving math is done on the parent node.
The integer based tile jumping is done on each of the slivers.
I would like to test which nodes are near another particular node.
In the following example, the green field is used to check which fields are nearby. It should return the yellow, orange (horizontal to green and only one field removed) and blue (diagonal and only one field removed) field.
Each field is a node:
let greenField = SKSpriteNode(imageNamed: "greenField")
greenField.zPosition = 1
let yellowField = SKSpriteNode(imageNamed: "yellowField")
yellowField.zPosition = 1
...
Does anyone have any idea how to test this? I would be very grateful for any answer.
You can check the distance of each node.
CGFloat distance = hypotf(p1.x - p2.x, p1.y - p2.y);
Tip: Make sure the anchor point is in the center to make it easier.
Hello I'm trying to spawn bullets at the bottom of my screen to travel upwards but the current code that I have spawns the bullets at the top of the screen. I've tried making the height negative and nothing happened. Here's the code I'm working with, thanks.
let randomBulletPosition = GKRandomDistribution(lowestValue: -300, highestValue: 300)
let position = CGFloat(randomBulletPosition.nextInt())
bullet.position = CGPoint(x: position, y: self.frame.size.height + bullet.size.height)
Some nice conversions will help you.
Now, do not do this all the time, this should be a one and done type deal, like in a lazy property.
First, we want to get the bottom of our view
let viewBottom = CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY) //In a UIView, 0,0 is the top left corner, so we look to bottom middle
Second, we want to convert the position to the scene
let sceneBottom = scene!.view!.convert(viewBottom, to:scene!)
Finally we want to convert to whatever node you need it to be a part of. (This is optional if you want to place it on the scene)
let nodeBottom = scene!.convert(sceneBottom,to:node)
Code should look like this:
let viewBottom = CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY)
let sceneBottom = scene!.view!.convert(viewBottom!, to:scene!)
let nodeBottom = scene!.convert(sceneBottom,to:node)
Of course, this is a little ugly.
Thankfully we have convertPoint and convert(_from:) to clean things up a little bit
let sceneBottom = scene.convertPoint(from:viewBottom)
Which means we can clean up the code to look like this:
let sceneBottom = scene.convertPoint(from:CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY))
let nodeBottom = node.convert(sceneBottom,from:scene!)
Then we can make it 1 line as:
let nodeBottom = node.convert(scene.convertPoint(from:CGPoint(x:scene!.view!.midX,y:scene!.view!.frame.maxY),from:scene!)
As long as the node is available to the class, we can make it lazy:
lazy var nodeBottom = self.node.convert(self.scene!.convertPoint(CGPoint(x:self.scene!.view!.midX,y:self.scene!.view!.frame.maxY),from:self.scene!)
This means the first time you call nodeBottom, it will do these calculations for you and store it into memory. Everytime after that, the number is preserved.
Now that you know where the bottom of the screen is in the coordinate system you want to use, you can assign the x value to whatever your random is producing, and you can subtract the (node.height * (1 - node.anchorPoint.y)) to fully hide your node from the scene.
Now keep in mind, if your node moves between various parents, this lazy will not update.
Also note, I unwrapped all optionals with !, you may want to be using ? and checking if it exists first.
I am trying to set angle constraints for body parts in Swift 2.0.
I have tried to automatically set them in the scene editor under IK Constraints and this failed.
I subsequently set them in the code:
lowerArmBack.reachConstraints?.lowerAngleLimit = 0
lowerArmBack.reachConstraints?.upperAngleLimit = CGFloat(10)
Neither has worked (with or without CGFloat). I am trying to follow this tutorial: http://www.raywenderlich.com/80917/sprite-kit-inverse-kinematics-swift but have stumbled in to issues since the Swift update.
Essentially I want to limit angles to prevent the arms moving in all 360 degrees but this isn't happening.
You need to set a zRotation constraint with a range for the body part you want:
let range = SKRange(lowerLimit: CGFloat(0).degreesToRadians(),
upperLimit: CGFloat(160).degreesToRadians())
let rotationConstraint = SKConstraint.zRotation(range)
lowerArmFront.constraints = [rotationConstraint]
Regarding the tutorial, I'm actually working on it's update fixing the issue.
You can also add a SKReachConstraint in the following way:
lowerLeg.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(-45).degreesToRadians(), upperAngleLimit: 0)
upperLeg.reachConstraints = SKReachConstraints(lowerAngleLimit: CGFloat(-45).degreesToRadians(), upperAngleLimit: CGFloat(160).degreesToRadians())
Hope it helps!