How do I make 'n' number of SKSpriteNode move in all direction continuously Swift? - swift

I want 'n' number of nodes keep moving in all direction continuously.
Below is how I coded
func spriteCollection(count : Int) -> [SKSpriteNode]{
var spriteArray = [SKSpriteNode]()
for _ in 0..<count {
let sprite = SKSpriteNode(imageNamed: "node.png")
//giving random position to sprites,
let x = CGFloat(arc4random_uniform(UInt32(((self.scene?.frame.width)! - sprite.size.width))))
let y = CGFloat(arc4random_uniform(UInt32(((self.scene?.frame.height)! - sprite.size.height))))
sprite.position = CGPoint(x: x, y: y)
spriteArray.append(sprite)
}
return spriteArray
}
override func didMoveToView(view: SKView) {
//here if I pass more than 2, the nodes won't even get displayed sometimes
let spriteCollection = spriteCollection(2)
for sprite in spriteCollection{
let blockOFaction = SKAction.runBlock({
let x = CGFloat(arc4random_uniform(UInt32(((self.scene?.frame.width)! - sprite.size.width))))
let y = CGFloat(arc4random_uniform(UInt32(((self.scene?.frame.height)! - sprite.size.height))))
let action = SKAction.moveTo(CGPoint(x: x , y: y), duration: 2)
print("moving to \(x,y)")
action.speed = 3.0
sprite.runAction(action)
})
let waitAction = SKAction.waitForDuration(1)
sprite.runAction(SKAction.repeatActionForever(SKAction.sequence([blockOFaction, waitAction])))
addChild(sprite)
}
}
The nodes are moving randomly in all direction, but if I add more than 2 sprites, sometimes none of the sprites are visible(just a blank screen) and also I want to increase the number of sprites eventually. Here what I'm doing is, just passing the number of sprites I want(It cannot be increased eventually) So is there any other way to do it. Could anyone please suggest me how can I achieve this..

Related

How to place nodes on the ground?

I am creating a game with SpriteKit. The background is a png image, endlessly moving (parallax scroll):
func parallaxScroll(image: String, y: CGFloat, z: CGFloat, duration: Double, needsPhysics: Bool) {
for i in 0 ... 1 {
// position the first node on the left, and position second on the right
let node = SKSpriteNode(imageNamed: image)
node.position = CGPoint(x: 1023 * CGFloat(i), y: y)
node.zPosition = z
addChild(node)
if needsPhysics {
node.physicsBody = SKPhysicsBody(texture: node.texture!, size: node.texture!.size())
node.physicsBody?.isDynamic = false
node.physicsBody?.contactTestBitMask = 1
node.name = "ground"
}
// make this node move the width of the screen by whatever duration was passed in
let move = SKAction.moveBy(x: -1024, y: 0, duration: duration)
// make it jump back to the right edge
let wrap = SKAction.moveBy(x: 1024, y: 0, duration: 0)
// make these two as a sequence that loops forever
let sequence = SKAction.sequence([move, wrap])
let forever = SKAction.repeatForever(sequence)
// run the animations
node.run(forever)
}
}
The example function below places a box at random y position:
#objc func createObstacle() {
let obstacle = SKSpriteNode(imageNamed: "rectangle")
obstacle.zPosition = -2
obstacle.position.x = 768
addChild(obstacle)
obstacle.physicsBody = SKPhysicsBody(texture: obstacle.texture!, size: obstacle.texture!.size())
obstacle.physicsBody?.isDynamic = false
obstacle.physicsBody?.contactTestBitMask = 1
obstacle.name = "obstacle"
let rand = GKRandomDistribution(lowestValue: -200, highestValue: 350)
obstacle.position.y = CGFloat(rand.nextInt())
// make it move across the screen
let action = SKAction.moveTo(x: -768, duration: 9)
obstacle.run(action)
}
func playerHit(_ node: SKNode) {
if node.name == "obstacle" {
player.removeFromParent()
}
}
func didBegin(_ contact: SKPhysicsContact) {
guard let nodeA = contact.bodyA.node else { return }
guard let nodeB = contact.bodyB.node else { return }
if nodeA == player {
playerHit(nodeB)
} else if nodeB == player {
playerHit(nodeA)
}
}
Instead of placing it at random, I would like to place it on the "ground". The following image illustrates the current placement and the desired placement:
Does anyone know how to place an obstacle node (object) on the ground which is not flat instead of random y position?
You could cast a ray, at the box's intended x position, from the top of the screen to the bottom, and when the ray hits the ground, place the box directly above the hitpoint. See enumerateBodies(alongRayStart:end:using:). In your case you might want to insert into createObstacle something like:
let topOfScreen = size.height / 2
let bottomOfScreen = -size.height / 2
obstacle.position.x = -768
physicsWorld.enumerateBodies(alongRayStart: CGPoint(x: obstacle.position.x, y: topOfScreen), end: CGPoint(x: obstacle.position.x, y: bottomOfScreen)) { body, point, normal, stop in
if body.node?.name == "ground" {
// body is the ground's physics body. point is the point that an object
// falling from the sky would hit the ground.
// Assuming the obstacle's anchor point is at its center, its actual position
// would be slightly above this point
let positionAboveBody = point + (obstacle.size.height / 2)
obstacle.position.y = positionAboveBody
}
}

Shooting balls from a rotating cannon

I'm making a SpriteKit game. I have six cannons that I've made rotate back and fourth. I want to shoot cannonballs from each cannon that sync with the rotation of each cannon. I want a duration of one second between each cannonball.
So basically: A cannon that is in constant rotation is shooting cannonballs in the same direction as the rotation.
For the cannons i'm using an extension:
extension CGFloat {
func degreesToRadians() -> CGFloat {
return self * CGFloat.pi / 180
}
}
I'm gonna put the code for just one cannon, since I should be able to figure out how to adjust one of the cannonball movements to the others. Here is one:
func createCannons() {
let cannonLeftBottom = SKSpriteNode(imageNamed: "Cannon")
cannonLeftBottom.anchorPoint = CGPoint(x: 0.5, y: 0.5)
cannonLeftBottom.position = CGPoint(x: -325, y: -420)
cannonLeftBottom.zPosition = 4
cannonLeftBottom.setScale(0.4)
cannonLeftBottom.zRotation = CGFloat(65).degreesToRadians()
let rotateLB = SKAction.rotate(byAngle:
CGFloat(-65).degreesToRadians(), duration: 2)
let rotateBackLB = SKAction.rotate(byAngle:
CGFloat(65).degreesToRadians(), duration: 2)
let repeatRotationLB =
SKAction.repeatForever(SKAction.sequence([rotateLB,rotateBackLB]))
cannonLeftBottom.run(repeatRotationLB)
self.addChild(cannonLeftBottom)
}
Here is my function for the cannonball:
func createBalls() {
let cannonBallLB = SKSpriteNode(imageNamed: "Ball")
cannonBallLB.name = "CannonBall"
cannonBallLB.position = CGPoint(x: -325, y: -420)
cannonBallLB.physicsBody = SKPhysicsBody(circleOfRadius:
cannonBallLB.size.height / 2)
cannonBallLB.physicsBody?.affectedByGravity = false
cannonBallLB.zPosition = 3
cannonBallLB.setScale(0.1)
self.addChild(cannonBallLB)
}
THX!
You need to convert from Polar Coordinates to Rectangular Coordinates.
You do this by using sin and cos
E.G.
let speed = 100 //This would mean move 100 points per second
let force = CGVector(dx:cos(cannon.zRotation) * speed,dy:sin(cannon.zRotation) * speed)
cannonBall.applyForce(force)
Note: Now unless they changed this, force used to be in units of points, if they fixed it to units of meters, then you need to divide your speed by 150, since 150 points = 1 meter in Spritekit

How do you create random nodes to appear at random times but they do not overlap current nodes in scene?

I am fairly new to iOS Swift and sprite kit, but I am currently developing a game where I would like items (SKSpriteNodes) to appear at random times and when they do appear, they don't overlap with existing nodes in the game scene.
Below is my code so far for producing a random node and a specified with range time interval to appear across the scene width/height. However, I haven't incorporated the code that allows them to not overlap or be on top of other present nodes in the scene.
override func didMove(to view: SKView) {
let wait = SKAction.wait(forDuration: 10, withRange: 5)
let spawn = SKAction.run {
self.randomFood()
}
let spawning = SKAction.sequence([wait,spawn])
self.run(SKAction.repeat((spawning), count: 5))
}
func randomFood() {
//supposed to pick random point within the screen width
let xPos = randomBetweenNumbers(firstNum: 0, secondNum: frame.width )
let foodNode = SKSpriteNode(imageNamed: "burgerFood") //create a new food item each time
foodNode.position = CGPoint(x: xPos, y: self.frame.size.height/2)
foodNode.setScale(0.10)
foodNode.physicsBody = SKPhysicsBody(circleOfRadius: foodNode.size.width/2)
foodNode.zPosition = 10
foodNode.alpha = 1
foodNode.physicsBody?.affectedByGravity = false
foodNode.physicsBody?.categoryBitMask = 0
foodNode.physicsBody?.contactTestBitMask = 0
addChild(foodNode)
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
}

Scaling a node based on position

I was wondering if there is a way to scale a sprite node based on its position?
So for example when a characters y position increases, you want him to appear smaller like he is walking further away from you. And when his y position decreases you want him to appear larger as he is closer to you.
let swipeUp = UISwipeGestureRecognizer()
override func didMoveToView(view: SKView) {
swipeUp.addTarget(self, action: "swipedUp")
swipeUp.direction = .Up
self.view!.addGestureRecognizer(swipeUp)
swipeDown.addTarget(self, action: "swipedDown")
swipeUp.direction = .Down
self.view!.addGestureRecognizer(swipeDown)
addSprite()
}
func swipedUp(){
let amountToMove: CGFloat = 100
let move: SKAction = SKAction.moveByX(0, y: amountToMove, duration: 0.1)
character.runAction(move)
}
func swipedDown(){
let amountToMove: CGFloat = 100
let move: SKAction = SKAction.moveByX(0, y: -amountToMove, duration: 0.1)
character.runAction(move)
}
func addSprite(){
let sprite = SKSpriteNode(imageNamed: "testImage")
sprite.position = CGPoint(x: xPos, y: yPos)
}
override the sprite position property and set the scale factor in it. Here is a very basic example, you will need to determine what your scale factor actually is based on y.
override var position{
didSet{
self.setScale((screen.height - y) / (screen.height / 2))
}
}

Randomize image in array in sprite kit swift.

I was wondering if someone could show me how to make it so that I can spawn random image missiles. Right now I am using one image called "meteor", I have a few more images I would like to show and randomize. I know I need to put them in an array and create an arc for random. I have done it for sound but I'm not sure how to do it for images. This is my code so far.
var lastMissileAdded : NSTimeInterval = 0.0
let missileVelocity : CGFloat = 4.0
func addMissile() {
// Initializing missile node
var missile = SKSpriteNode(imageNamed: "meteor")
missile.setScale(0.44)
// Adding SpriteKit physics body for collision detection
missile.physicsBody = SKPhysicsBody(rectangleOfSize: missile.size)
missile.physicsBody?.categoryBitMask = UInt32(obstacleCategory)
missile.physicsBody?.dynamic = true
missile.physicsBody?.contactTestBitMask = UInt32(shipCategory)
missile.physicsBody?.collisionBitMask = 0
missile.physicsBody?.usesPreciseCollisionDetection = true
missile.name = "missile"
// Selecting random y position for missile
var random : CGFloat = CGFloat(arc4random_uniform(300))
missile.position = CGPointMake(self.frame.size.width + 20, random - 20)
self.addChild(missile)
}
func moveObstacle() {
self.enumerateChildNodesWithName("missile", usingBlock: { (node, stop) -> Void in
if let obstacle = node as? SKSpriteNode {
obstacle.position = CGPoint(x: obstacle.position.x - self.missileVelocity, y: obstacle.position.y)
if obstacle.position.x < 0 {
obstacle.removeFromParent()
}
}
})
}
All you need to do is name them meteor0, meteor1 and meteor2 and use String Interpolation to create your node with your random image:
var missile = SKSpriteNode(imageNamed: "meteor\(arc4random_uniform(3))")