How to add to a variable in SpriteKit - ios

Relatively new to swift so apologies if I'm being stupid.
I'm using Xcode 13.3 beta 3
I have some code which spawns in a sprite named Monster and moves it from the right of the screen to the left at random speeds and at a random y location.
When it moves off screen it removes from parent and transitions to a loose screen.
what I want it to do when it leaves the screen is for it to add to a variable I've defined at the top of the class:
var pass = 0
here's the code for the monsters:
func addMonster() {
let monster = SKSpriteNode(imageNamed: "monster")
monster.physicsBody = SKPhysicsBody(rectangleOf: monster.size)
monster.physicsBody?.isDynamic = true
monster.physicsBody?.categoryBitMask = PhysicsCatagory.monster
monster.physicsBody?.contactTestBitMask = PhysicsCatagory.projectile
monster.physicsBody?.collisionBitMask = PhysicsCatagory.none
let actualY = random(min: monster.size.height/2, max: size.height - monster.size.height/2)
monster.position = CGPoint(x: size.width + monster.size.width/2, y: actualY)
addChild(monster)
let actualDuration = random(min: CGFloat(1.65), max: CGFloat(5.5))
let actionMove = SKAction.move(to: CGPoint(x: -monster.size.width/2, y: actualY), duration: TimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
let loseAction = SKAction.run() { [weak self] in
guard let `self` = self else {return}
let end = SKTransition.crossFade(withDuration: 0.5)
let gameOverScene = GameOverScene(size: self.size, won: false)
self.view?.presentScene(gameOverScene, transition: end)
}
monster.run(SKAction.sequence([actionMove,loseAction, actionMoveDone]))
}
what I want is the loose action to just add +1 to the pass variable then have an if statement for when pass is greater than 3 to run the code that goes to the loose screen.
Hope that all makes sense
any help would be much appreciated!

Related

double spawning (& overlapping) enemies on Spritekit

I'm having trouble with an endless run game, mainly with the spawning on the two kind of the enemies that I created, sometimes happens that two enemies spawns when we call the function "startDifficultyTimer".
Basically when i call that function the game spawn 2 nodes in the same row so the player is forced to lose, I want to avoid that but I don't know how, I tried almost everything,I've tried to remove createsequentialenemy from didmove but the problem still persist, I think (as newbie) that the problem is in startDifficultTimer function because when the value reach the limit the problem vanish
I've posted the code below, sorry for the probably huge mistake but we're new in swift development game, an huge thank you all!
func createEnemy() {
let enemy: Enemy
let duration: CGFloat
switch Int(arc4random() % 100) {
case 0...70:
enemy = Enemy.createEnemy()
duration = CGFloat(Float(arc4random()%1)) + durationV
enemy.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 55, height: 37))
let enemyf = enemy.frame
let fixedx = frame.width + enemy.frame.width/2.0
let positions = [ CGPoint(x: fixedx, y: 383), CGPoint(x: fixedx, y: 447), CGPoint(x: fixedx, y: 511)]
let position = positions[Int(arc4random_uniform(UInt32(positions.count)))]
enemy.position = position
case 71...100:
enemy = Enemy.createEnemyMedium()
duration = CGFloat(Float(arc4random()%1)) + durationV
enemy.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 40, height: 70))
let enemyf = enemy.frame
let fixedx = frame.width + enemy.frame.width/2.0
let positions = [ CGPoint(x: fixedx, y: 415), CGPoint(x: fixedx, y: 479)]
let position = positions[Int(arc4random_uniform(UInt32(positions.count)))]
enemy.position = position
default:
enemy = Enemy.createEnemy()
//type = .small
duration = CGFloat(Float(arc4random()%1)) + durationV
let enemyf = enemy.frame
let fixedx = frame.width + enemy.frame.width/2.0
let positions = [ CGPoint(x: fixedx, y: 383), CGPoint(x: fixedx, y: 447), CGPoint(x: fixedx, y: 511)]
let position = positions[Int(arc4random_uniform(UInt32(positions.count)))]
let texture = SKTexture(imageNamed: "dronea1")
enemy.position = position
}
enemy.physicsBody!.isDynamic = false
enemy.physicsBody!.categoryBitMask = PhysicsCategory.Enemy
addChild(enemy)
let moveTo = SKAction.moveTo(x: 0.0, duration: TimeInterval(duration))
enemy.run(.repeatForever(.sequence([moveTo, .removeFromParent()])))
}
func createSequentialEnemies() {
// remove previous action if running. This way you can adjust the spawn duration property and call this method again and it will cancel previous action.
removeAction(forKey: spawnKey)
let spawnAction = SKAction.run(createEnemy)
let spawnDelay = SKAction.wait(forDuration: spawnDuration)
let spawnSequence = SKAction.sequence([spawnAction, spawnDelay])
run(SKAction.repeatForever(spawnSequence), withKey: spawnKey)later
}
func startDifficultyTimer() {
let difficultyTimerKey = "DifficultyTimerKey"
let action1 = SKAction.wait(forDuration: 1)
let action2 = SKAction.run { [unowned self] in
guard self.spawnDuration > 0.5 else { // set a min limit
self.removeAction(forKey: difficultyTimerKey) // if min duration has been reached than you might as well stop running this timer.
return
}
self.spawnDuration -= 0.5 // reduce by half a second
self.createSequentialEnemies() // spawn enemies again
}
let sequence = SKAction.sequence([action1, action2])
run(SKAction.repeatForever(sequence), withKey: difficultyTimerKey)
}
I think that the issue is that you are saying "stop this action" when the timer changes. But that statement doesn't know if an enemy has just been created or is just about to be created. Wherever it is in its loop you are stopping it and starting the loop over. So if it had just created an enemy and you stop the loop and start it over by generating a new enemy faster you will get two enemies in a row.
A way around this could be to run your sequence in reverse. Pause and then generate your enemy.
let spawnSequence = SKAction.sequence([spawnDelay, spawnAction])
you might get a slightly longer gap between enemies but you won't get two that span on top of each other.
Otherwise you could track the spawn time each time the last enemy is spawned and minus it from the next spawn time to put a custom wait action in between.

how would I keep spawning an SKSpriteNode in random locations on the right side of the screen to translate it from the this random location the left?

I am not sure how to go about doing this. The code below is supposed to move the ball from the right side of the screen to the left, remove the ball and then spawn another. However it is not working as the ball does nothing (By the way I am using swift 4)
import UIKit
import SpriteKit
class ViewController: UIViewController {
let Ball = SKSpriteNode(imageNamed: "ball")
override func viewDidLoad() {
super.viewDidLoad()
moveBall()
}
func moveBall() {
let startPoint = CGPoint(x: 500, y: 0)
let endPoint = CGPoint(x: -300, y: 0)
let moveToEndAction = SKAction.move(to: endPoint, duration: 2)
let atEndAction = SKAction.removeFromParent()
let resetTostartAction = SKAction.move(to: startPoint, duration: 0)
let moveToEndThenStartAgain = SKAction.repeatForever(SKAction.sequence([moveToEndAction, atEndAction, resetTostartAction]))
Ball.run(SKAction.repeatForever(moveToEndThenStartAgain))
}
}
Based off how I have interpreted the tough wording and lack of code for your question, this is what I have come up with.
You want to create the object that you are moving. I'm assuming you have created a class for this (then again, I don't know because you didn't give any information). So if you do have a class, follow these guidelines:
let object = Object()
//wherever you decide to start your object
let randomStartX = randomCGFloat(min: randomVal, max: randomVal)
let randomStartY = randomCGFloat(min: randomVal, max: randomVal)
object.position = CGPoint(x: randomStartX, y: randomStartY)
self.addChild(object)
The random start and end positions will be specific to the range of where you want the object to start and end. That is for you to figure out and play with.
Now to move the object, you will want to create a new function. Let's say spawnNewObject(). In this function you will want to use SKActions in order to move the object to the end location. The speed will be whatever you want it to be.
func spawnNewObject(){
let randomEndX = randomCGFloat(min: randomVal, max: randomVal)
let randomEndY = randomCGFloat(min: randomVal, max: randomVal)
let move = SKAction.move(to: CGPoint(x: randomEndX, y: randomEndY), duration: speed)
object.run(sequence)
}
Now, if you want to remove the object whenever it reaches its end point, update the function to this:
func spawnNewObject(){
let randomEndX = randomCGFloat(min: randomVal, max: randomVal)
let randomEndY = randomCGFloat(min: randomVal, max: randomVal)
let move = SKAction.move(to: CGPoint(x: randomEndX, y: randomEndY), duration: speed)
let removeFromParent = SKAction.removeFromParent()
let sequence = SKAction.sequence([move,removeFromParent])
object.run(sequence)
}

Swift: Detect when SKSpriteNode has even touched

I have a game where an object falls from the top of the screen to the bottom randomly. I want to have it to where if the object is tapped, I can tell it to do something, like add points to the score.
Can you help me try to figure this out? I'll be on to answer any questions if you happen to have any. Here is the code I'm using:
class GameSceneTest: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.clearColor()
physicsWorld.gravity = CGVectorMake(0, 0)
physicsWorld.contactDelegate = self
//Change duration
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addObject), SKAction.waitForDuration(1)])
))
//AddMusicHere
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func add Object() {
let Object = SKSpriteNode(imageNamed: "Object\(arc4random_uniform(6) + 1).png")
Object.userInteractionEnabled = true
Object.size = CGSize(width: 50, height: 50)
Object.physicsBody = SKPhysicsBody(rectangleOfSize: Object.size)
Object.physicsBody?.dynamic = true
//Determine where to spawn gem across y axis
let actually = random(min: Object.size.width/2, max: size.width - Object.size.width/2)
//Position Object slightly off screen along right edge
//and along random y axis point
//Object.position = CGPoint(x: size.width + Object.size.width/2 , y: actually)
Object.position = CGPoint(x: actually, y: size.height + Object.size.height/2)
//Add the Object to the scene
addChild(Object)
//Determines speed of Object (edit later to where speed depends on type of Object)
let actualDuration = random(min: CGFloat(4), max: CGFloat(5))
//Create the Actions
let actionMove = SKAction.moveTo(CGPoint(x: actually, y: gem.size.height/2), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
let loseAction = SKAction.runBlock() {
let reveal = SKTransition.flipHorizontalWithDuration(0.5) //Change to flipe vertical
let gameOverScene = GameOverScene(size: self.size, won: false)
self.view?.presentScene(gameOverScene, transition: reveal)
}
Object.runAction(SKAction.sequence([actionMove, loseAction, actionMoveDone]))
}
Take a look at "Moving The Cards Around The Board" in the following tutorial: Ray Wenderlicht SpriteKit Tutorial.
Then read a bit more around handling touch events in iOS (e.g. Handling Touches...).
Then try something in code and if you get stuck post a more specific question - because this one is a little too general.

SKAction.moveToX repeat with value change

i want to be able move the ball with pause every 20 pixels moved i tried this one but didn't do nothing, the ball stays at the point where it started it doesn't move to the end of the screen,i know i didn't put the waitForDuration , because i wanted to check if it will move or not
func spwan()
{
let ball:SKSpriteNode = SKScene(fileNamed: "Ball")?.childNodeWithName("ball") as! SKSpriteNode
ball.removeFromParent()
self.addChild(ball)
ball.name = "spriteToTrack"
ball.zPosition = 0
ball.position = CGPointMake(1950, 1000)
var num:CGFloat = ball.position.x
let a1 = SKAction.moveToX(num - 20, duration: 10)
// i want to get to -50 let a1 = SKAction.moveToX(-50 , duration: 10)
let minus = SKAction.runBlock{
num -= 20
}
let sq1 = SKAction.sequence([a1,minus])
ball.runAction(SKAction.repeatAction(sq1, count: 10)
}
The ball should move 20 pixels at least in the above code, but 20 pixels in 10 seconds might seen like a standstill. Anyways I think you've overcomplicated things quite a bit by using moveToX rather than moveBy:, so (with a bit of rejigging) you're probably better of with something like this:
func spawn() {
let x: CGFloat = 1950
let xDelta: CGFloat = -20
let xDestination: CGFloat = -50
let repeats = Int((x - xDestination)/fabs(xDelta))
let move = SKAction.moveBy(CGVectorMake(xDelta, 0), duration: 2) // made it a lot quicker to show that stuff is happening
move.timingMode = .EaseInEaseOut
let ball:SKSpriteNode = SKScene(fileNamed: "Ball")?.childNodeWithName("ball") as! SKSpriteNode
ball.removeFromParent()
ball.name = "spriteToTrack"
ball.position = CGPointMake(x, 1000)
addChild(ball)
ball.runAction(SKAction.repeatAction(move, count: repeats))
}

How to attach particles to moving object

I've created an PowerUpTrail.sks (particle thingy)
And I'm trying to attach it to a moving object
func SpawnPowerUp(){
let PowerUp = SKSpriteNode(imageNamed: "PowerUp.png")
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 20
let SpawnPoint = UInt32(MaxValue - MinValue)
PowerUp.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.size.height - 100)
//Physics
PowerUp.physicsBody = SKPhysicsBody(rectangleOfSize: PowerUp.size)
PowerUp.physicsBody?.categoryBitMask = PhysicsCategory.PowerUp //Sätter unikt ID för Enemy, hämtat från PhysicsCategory som vi gjorde där uppe
PowerUp.physicsBody?.contactTestBitMask = PhysicsCategory.PowerUp //Kolla om Enemy nuddar Bullet
PowerUp.physicsBody?.affectedByGravity = false
PowerUp.physicsBody?.dynamic = true
let action = SKAction.moveToY(-128, duration: 5)
let actionDone = SKAction.removeFromParent()
PowerUp.runAction(SKAction.sequence([action, actionDone]))
PowerUp.runAction(SKAction.rotateByAngle(5, duration: 5))
/* this does not work!
let PowerUpTrail = SKEmitterNode(fileNamed: "PowerUpTrail.sks")
PowerUpTrail!.targetNode = self
PowerUpTrail!.position = PowerUp.position
self.addChild(PowerUpTrail!)
*/
self.addChild(PowerUp)
}
The particlethingy spawns but it doesnt follow the object. How do I solve this?
The simplest way to make one node's position track another is to use the node hierarchy: make the particle emitter a child node of the powerup sprite.
let powerUp = SKSpriteNode(imageNamed: "PowerUp.png")
// ...
let powerUpTrail = SKEmitterNode(fileNamed: "PowerUpTrail.sks")!
powerUpTrail.targetNode = self
powerUp.addChild(powerUpTrail)
(Also note some Swift style tips: initial-cap is conventionally for type names only; use initial-lowercase for variable names. And unwrap your optional from SKEmitterNode(fileNamed:) once when you create it instead of every time you use it thereafter.)

Resources