Adding nodes through update method - ios

I awant to add SKSpriteNode's after something repositioned therefore I want to add the nodes through the update method in Swift Sprite Kit.
There are Objects ( platforms ) that move downwards the y axis, once they are out of screen they appear on the top side again ( like an infinite loop ). I visualize this in the update method. Note that the platforms dont really move, rather then I centered a camera on my Player node that moves up.
After the reposition of the platform node, I want to add an Enemy again on top of the platforms, however because I try it through the update method, it adds sometimes more than 1 node on top of it.
I cant reposition the Enemy like I do with the platforms, because it should be a random enemy node.
Any way to call the SpawnEnemy method in the update method, and check if it was called only once?
My Code:
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.World = SKNode()
self.World.name = "World"
addChild(World)
self.WorldCamera = SKNode()
self.WorldCamera.name = "Camera"
self.World.addChild(WorldCamera)
....
SpawnPlatforms()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform2.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform3.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform4.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform5.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform6.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform7.position.y + 30))
}
func SpawnPlatforms() {
Platform0 = SKSpriteNode (color: SKColor.greenColor(), size: CGSize(width: self.frame.size.width * 2 , height: 25))
Platform0.position = CGPoint(x: self.frame.size.width / 2, y: -36)
Platform0.zPosition = 1
Platform0.physicsBody = SKPhysicsBody(rectangleOfSize:Platform0.size)
Platform0.physicsBody?.dynamic = false
Platform0.physicsBody?.allowsRotation = false
Platform0.physicsBody?.restitution = 0.0
Platform0.physicsBody?.usesPreciseCollisionDetection = true
Platform0.physicsBody?.velocity = CGVectorMake(0, 0)
Platform0.physicsBody?.categoryBitMask = Platform0Category
Platform0.physicsBody?.collisionBitMask = PlayerCategory | EnemyCategory
Platform0.physicsBody?.contactTestBitMask = PlayerCategory | EnemyCategory
World.addChild(Platform0)
Platform1 = SKSpriteNode (color: SKColor.redColor(), size: CGSize(width: self.frame.size.width * 2 , height: 25))
Platform1.position = CGPoint(x: self.frame.size.width / 2, y: Platform0.position.y + self.frame.size.height / 4.5)
Platform1.zPosition = 1
Platform1.physicsBody = SKPhysicsBody(rectangleOfSize:Platform1.size)
Platform1.physicsBody?.dynamic = false
Platform1.physicsBody?.allowsRotation = false
Platform1.physicsBody?.restitution = 0
Platform1.physicsBody?.usesPreciseCollisionDetection = true
Platform1.physicsBody?.categoryBitMask = Platform1Category
Platform1.physicsBody?.collisionBitMask = PlayerCategory | EnemyCategory
Platform1.physicsBody?.contactTestBitMask = PlayerCategory | EnemyCategory
World.addChild(Platform1)
....a.s.o. for the other 6 platform nodes
}
func SpawnEnemy(position: CGPoint!){
let random = arc4random_uniform(4)
switch (random) {
case 0:
let node1 = SpawnNode1(position)
self.addChild(node1)
break
case 1:
let node2 = SpawnNode2(position)
self.addChild(node2)
break
case 2:
let node3 = SpawnNode3(position)
self.addChild(node3)
break
case 3:
break
default:
break
}
}
override func didSimulatePhysics() {
WorldCamera.position = CGPoint(x: Player.position.x, y: Player.position.y)
self.centerOnNode(WorldCamera!)
}
func centerOnNode(node: SKNode) {
let cameraPositionInScene: CGPoint = WorldCamera.scene!.convertPoint(WorldCamera.position, fromNode: World)
World.runAction(SKAction.moveTo(CGPoint(x:World.position.x , y:World.position.y - cameraPositionInScene.y), duration: 1.0))
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if(Platform1.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform1.position = CGPointMake(self.frame.size.width / 2, Platform7.position.y + self.frame.size.height / 4.5)
Platform1.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform1.position.y + 30))
}
if(Platform2.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform2.position = CGPointMake(self.frame.size.width / 2, Platform1.position.y + self.frame.size.height / 4.5)
Platform2.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform2.position.y + 30))
}
if(Platform3.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform3.position = CGPointMake(self.frame.size.width / 2, Platform2.position.y + self.frame.size.height / 4.5)
Platform3.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform3.position.y + 30))
}
if(Platform4.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform4.position = CGPointMake(self.frame.size.width / 2, Platform3.position.y + self.frame.size.height / 4.5)
Platform4.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform4.position.y + 30))
}
if(Platform5.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform5.position = CGPointMake(self.frame.size.width / 2, Platform4.position.y + self.frame.size.height / 4.5)
Platform5.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform5.position.y + 30))
}
if(Platform6.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform6.position = CGPointMake(self.frame.size.width / 2, Platform5.position.y + self.frame.size.height / 4.5)
Platform6.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform6.position.y + 30))
}
if(Platform7.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform7.position = CGPointMake(self.frame.size.width / 2, Platform6.position.y + self.frame.size.height / 4.5)
Platform7.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform7.position.y + 30))
}
}

Your problem is you do not create any kind of check that moving the platform has already spawned an enemy. Sure, you move the platform, but until your player moves, it will constantly spawn enemies. I do not see where this is happening, but you should spawn enemy when you move the platform, not on your update
Edit:
yikes, I see it now, this is some ugly code lol, In all those ifs that move the platform, you should spawn the enemy. But this needs to be worked to also check if the previous platform is below the current, not above
Edit++:
Upon further review and added details, try something like this instead:
First, give all Platforms a name "Platform"
Then
override func update(currentTime: CFTimeInterval) {
for node in scene.children
{
if (node.name == "Platform" && !node.intersectsNode(scene) && node.position.y < player.y - (self.frame.size.height / 4.5))
{
let platform = node as! SKSpriteNode
platform.position.y += (7 * (self.frame.size.height / 4.5))
platform.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: platform.position.y + 30))
}
}
}
Question: How does this even work, I would just check if a platform hits a certain point in the scene, recycle it, instead of having to check every single platform and moving it's previous one

so you're spawning an enemy on every frame.. thats going to be way too many enemies.. I'm still not totally following the exact architecture of your game, but this might help you out.
get delta time to see how much time has passed.. and every 10 seconds (or whatever) call your spawn enemy code instead of calling it on every frame
// time values
var delta = NSTimeInterval(0)
var last_update_time = NSTimeInterval(0)
let spawnTimerMax = NSTimeInterval(10)
var spawnTimer = NSTimeInterval(0)
override func update(currentTime: NSTimeInterval) {
if last_update_time == 0.0 {
delta = 0
} else {
delta = currentTime - last_update_time
}
last_update_time = currentTime
// ten seconds elapsed, spawn enemy and restart.
spawnTimer += delta
if spawnTimer >= spawnTimerMax {
// your spawn code goes here
spawnTimer = 0
}
}

Related

How to create SKSprite node one by one in For loop using Swift SpriteKit

I want to create 10 SKSPrite nodes via a For loop. I want the nodes to appear one by one but with my code, all 10 nodes appear at the same time. Increasing the wait duration does not help. Thank you in advance.
for i in 1 ... 10 {
self.stone[i - 1].position = CGPoint(x: 0 , y: -100)
self.stone[i - 1].anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.stone[i - 1].size = CGSize(width: 50, height: 50)
self.stone[i - 1].physicsBody = SKPhysicsBody(circleOfRadius: 20)
self.stone[i - 1].physicsBody!.affectedByGravity = false
self.stone[i - 1].physicsBody!.categoryBitMask = PhysicsCategory.Object1
self.stone[i - 1].zPosition = 2
self.addChild(self.stone[i - 1])
let actionMove = SKAction.move(to: CGPoint(x: 0, y: 0), duration: 0.3)
let actionRolling = SKAction.animate(with: stone[i - 1].arrayTexture, timePerFrame: 0.05)
let actionDelay = SKAction.wait(forDuration: 1.0)
let actionSequence = SKAction.sequence([actionMove,actionRolling,actionDelay])
stone[i - 1].run(actionSequence)
}
all your doing in your code is making a loop that creates the nodes, any duration that you apply to those nodes in that loop will affect them all equally. You need an variable outside of the loop that gets incremented in the loop per instance of node.
var delay: Double = 1.0
for i in 1 ... 10 {
self.run(.wait(forDuration: delay) {
self.stone[i - 1].position = CGPoint(x: 0 , y: -100)
self.stone[i - 1].anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.stone[i - 1].size = CGSize(width: 50, height: 50)
self.stone[i - 1].physicsBody = SKPhysicsBody(circleOfRadius: 20)
self.stone[i - 1].physicsBody!.affectedByGravity = false
self.stone[i - 1].physicsBody!.categoryBitMask = PhysicsCategory.Object1
self.stone[i - 1].zPosition = 2
self.addChild(self.stone[i - 1])
let actionMove = SKAction.move(to: CGPoint(x: 0, y: 0), duration: 0.3)
let actionRolling = SKAction.animate(with: stone[i - 1].arrayTexture, timePerFrame: 0.05)
let actionDelay = SKAction.wait(forDuration: 1.0)
let actionSequence = SKAction.sequence([actionMove,actionRolling,actionDelay])
stone[i - 1].run(actionSequence)
}
delay += 1.0
}

Spritekit ground not touching bottom of screen

I am working on a spritekit game and it all is working correctly, yet my ground is in the middle of the simulators screen. I have tried everything to make it be at the bottom of the screen. one function.
let groundTexture = SKTexture(imageNamed: "Ground")
groundTexture.filteringMode = .nearest
for i in stride(from: 0, to: 2 + self.frame.size.width / groundTexture.size().width, by: 1) {
let ground = SKSpriteNode(texture: groundTexture)
ground.zPosition = 1.0 //-10
//ground.position = CGPoint(x: (groundTexture.size().width / 2.0 + (groundTexture.size().width * CGFloat(i))), y: groundTexture.size().height / 4.0) //original position
ground.position = CGPoint(x: 0, y: groundTexture.size().height / +0) //tried this from a tutorial
ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.size.width * 2.0, height: groundTexture.size().height / 4.0)) //erase * 4 test
ground.setScale(1.2)
ground.physicsBody?.isDynamic = false
ground.physicsBody?.allowsRotation = false
ground.physicsBody?.affectedByGravity = false
ground.physicsBody?.categoryBitMask = groundCategory
//contact and collision bitmask
ground.physicsBody?.contactTestBitMask = playerCategory | enemy1Category | enemy2Category | enemy3Category | enemy4Category | obstacleCategory | coinCatergory
ground.physicsBody?.collisionBitMask = playerCategory | enemy1Category | enemy2Category | enemy3Category | enemy4Category | obstacleCategory | coinCatergory
ground.physicsBody?.restitution = 0.0
self.addChild(ground)
let moveLeft = SKAction.moveBy(x: -groundTexture.size().width, y: 0, duration: 5)
let moveReset = SKAction.moveBy(x: groundTexture.size().width, y: 0, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
ground.run(moveForever)
}
By default a scene's anchorPoint coordinates are 0, 0 unless otherwise specified. anchorPoint(x: 0, y: 0) is the center of the screen. You are not specifying a position for your ground so it gets automatically added to the scenes anchorPoint (which is the middle of the screen).
You need to either change the scenes anchorPoints to the bottom of the screen or adjust the ground position accordingly such as...
ground.position = CGPoint(x: 0 - self.size.width / 2 + ground.size.width / 2, y: 0 - self.size.height / 2 + ground.size.height / 2)
(the above example assumes you are adding the ground in the scene and self = scene)
for your reference...

how to make a node stack on top of another node and follow the object?

I'm trying to create a game in which i have a object 1 rotatiing in a circle and another object appears and places itself ontop of object 1. currently the object just rotates around object 1 without stacking ontop of it. how do i get the object to stack itself on top and then follow it's orbit? here's my code now.
let player = SKSpriteNode(imageNamed: "Light")
override func didMoveToView(view: SKView) {
// 2
backgroundColor = SKColor.whiteColor()
// 3
player.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5)
// 4
player.size = CGSize(width:70, height:60 )
addChild(player)
let dx = player.position.x - self.frame.width / 2
let dy = player.position.y - self.frame.height / 2
let rad = atan2(dy, dx)
circle = UIBezierPath(arcCenter: CGPoint(x: size.width / 2, y: size.height / 2), radius: 120, startAngle: rad, endAngle: rad + CGFloat(M_PI * 4), clockwise: true)
let follow = SKAction.followPath(circle.CGPath, asOffset: false, orientToPath: true, speed: 100)
player.runAction(SKAction.repeatActionForever(follow))
}
func addMonster() {
let monster = SKSpriteNode(imageNamed: "plate")
// Determine where to spawn the monster along the Y axis
let actualY = random(min: monster.size.height/1, max: size.height - monster.size.height/1)
// Position the monster slightly off-screen along the right edge,
// and along a random position along the Y axis as calculated above
monster.position = CGPoint(x: size.width * 0.5 + monster.size.width/2, y: actualY)
// Add the monster to the scene
addChild(monster)
// Determine speed of the monster
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(3.0))
// Create the actions
let actionMove = SKAction.moveTo(CGPoint(x: -monster.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
let follow = SKAction.followPath(circle.CGPath, asOffset: false, orientToPath:true, speed: 100)
monster.runAction(SKAction.repeatActionForever(follow))
}
Your verbal description appears to have something like the following in mind (you can copy and paste this into an iOS playground):
import UIKit
import SpriteKit
import XCPlayground
let scene = SKScene(size: CGSize(width: 400, height: 400))
let view = SKView(frame: CGRect(origin: .zero, size: scene.size))
view.presentScene(scene)
XCPlaygroundPage.currentPage.liveView = view
scene.backgroundColor = .darkGrayColor()
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let player = SKShapeNode(rectOfSize: CGSize(width: 70, height: 60), cornerRadius: 5)
let radius: CGFloat = 120
let circle = CGPathCreateWithEllipseInRect(CGRect(x: -radius, y: -radius, width: radius * 2, height: radius * 2), nil)
let followCircle = SKAction.followPath(circle, asOffset: false, orientToPath: true, speed: 100)
player.runAction(.repeatActionForever(followCircle))
let monsterSize = CGSize(width: 35, height: 30)
let monster = SKShapeNode(rectOfSize: monsterSize, cornerRadius: 4)
monster.fillColor = .redColor()
monster.runAction(.repeatActionForever(followCircle))
scene.addChild(player)
scene.addChild(monster)
Where you make use of a random(min:max:) function that is probably implemented along the lines of:
public func random(min min: CGFloat, max: CGFloat) -> CGFloat {
return min + (CGFloat(arc4random()) / CGFloat(UInt32.max)) * (max - min)
}
But you also have the code that seems to be saying:
let y = random(
min: scene.size.height / -2 + monsterSize.height / 2,
max: scene.size.height / 2 - monsterSize.height / 2
)
let xFrom = scene.size.width / 2
let xTo = scene.size.width / -2
monster.position = CGPoint(x: xFrom, y: y)
monster.runAction(
.moveToX(xTo, duration: NSTimeInterval(random(min: 2, max: 3)))
)
But this is unused. Instead the monster is set to follow the same circle path as the player. And so I am not sure really what exactly you are trying to achieve. (By the way, the simplest way of getting the monster "stack on top" of the player would be to make it the child node of the player...)
Hope some of this guesswork helps you in some way (to refine your question and code sample if nothing else).

Pause in SpriteKit game

func pause() {
pausedSpeed = self.speed
self.speed = 0
backgroundMusicPlayer.pause()
worldNode.alpha = 0.7
resumeButton = SKShapeNode(rectOfSize: CGSize(width: 100, height: 50))
resumeButton.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 - 50)
resumeButton.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 100, height: 50))
resumeButton.name = resumeButtonName
pauseLabel = SKLabelNode(text: "PAUSED")
pauseLabel.fontSize = 100
pauseLabel.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + 100)
pauseLabel.fontColor = UIColor.blackColor()
worldNode.addChild(resumeButton)
worldNode.addChild(pauseLabel)
}
Even thought I set self.speed to 0, a node still keeps moving. Any ideas why this might be happening?
Do not change the speed of the node. Just change node.paused = true
In your case, you should define all your control buttons on one SKNode, and declare all the game elements on another SKNode. In that case, if you want to pause the game, just make the second SKNode pause.

Swift SpriteKit Gravity Point

I need to make something like a Gravity Point in Swift.
The SpriteNode is in the middle of the screen, and by Left or Right Gestures it can move along the x-axis.
But after for e.g. a 'RightSwipe', it should come back to his starting point. You could say it should look like a jump along the x-axis.
The World Gravity cant be a specific point, so how can I 'Force' it to come back to his original point?
I tried it with SKFieldNode's RadialGravityField, but after the Nodes impulse is made, it goes back to his original point and disappears.
How can I avoid that?
My Code:
{
.......
Node = SKSpriteNode(texture: Node1)
Node.size = CGSize(width: 100, height: 140)
Node.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 4)
Node.zPosition = 3
Node.runAction(RunAnimation)
Node.physicsBody = SKPhysicsBody(rectangleOfSize: Node.size)
Node.physicsBody?.dynamic = true
Node.physicsBody?.allowsRotation = false
Node.physicsBody?.usesPreciseCollisionDetection = true
Node.physicsBody?.restitution = 0
Node.physicsBody?.velocity = CGVectorMake(0, 0)
self.addChild(Node)
let swipeRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("SwipeRight:"))
swipeRight.direction = .Right
view.addGestureRecognizer(swipeRight)
let swipeLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("SwipeLeft:"))
swipeLeft.direction = .Left
view.addGestureRecognizer(swipeLeft)
}
func SwipeRight(recognizer:UISwipeGestureRecognizer) {
Node.physicsBody?.applyImpulse(CGVectorMake(10,0))
}
func SwipeLeft(recognizer:UISwipeGestureRecognizer) {
Node.physicsBody?.applyImpulse(CGVectorMake(-10,0))
}
override func update(currentTime: CFTimeInterval) {
if(Node.position.x > self.frame.size.width / 1.6)
{
fieldNode = SKFieldNode.radialGravityField()
fieldNode.enabled = true
fieldNode.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 4)
fieldNode.strength = 0.4
addChild(fieldNode)
}
if(Node.position.x < self.frame.size.width / 2.6)
{
fieldNode = SKFieldNode.radialGravityField()
fieldNode.enabled = true
fieldNode.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 4)
fieldNode.strength = 0.4
addChild(fieldNode)
}
Sounds like a job for a radial gravity SKFieldNode. link

Resources