Moving objects to the center of the screen - ios

I'm having a coding issue right now. I have a two walls/blocks moving to the center of the screen, but when they reach the center they don't stop once they reached the center. How do I make them stop after they reach/touch each other/the center.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
if (playButton.containsPoint(location))
{
playButton.removeFromParent()
title.removeFromParent()
//Wall Timer
wallTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: ("walls"), userInfo: nil, repeats: false)
//Physics World
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
}
else
{}
}
}
func didBeginContact(contact: SKPhysicsContact)
{
if contact.bodyA.node != nil && contact.bodyB.node != nil {
let firstBody = contact.bodyA.node as! SKSpriteNode
let secondBody = contact.bodyB.node as! SKSpriteNode
if ((firstBody.name == "leftWall") && (secondBody.name == "rightWall")) {
collisionWalls(firstBody, rightWall: secondBody)
}
else if ((firstBody.name == "rightWall") && (secondBody.name == "leftWall")) {
collisionWalls(secondBody, rightWall: firstBody)
}
}
}
func collisionWalls(leftWall : SKSpriteNode, rightWall : SKSpriteNode)
{
leftWall.removeAllActions()
rightWall.removeAllActions()
}
func walls() {
let leftWall = SKSpriteNode(imageNamed: "blue background1")
let rightWall = SKSpriteNode(imageNamed: "blue background1")
//Left Wall Code
leftWall.size = CGSizeMake(300, 90)
leftWall.position = CGPoint(x: scene!.frame.width / 6, y: scene!.frame.height / 6)
leftWall.zPosition = 1.0
leftWall.physicsBody = SKPhysicsBody(rectangleOfSize: leftWall.size)
leftWall.physicsBody?.affectedByGravity = false
leftWall.physicsBody?.dynamic = false
leftWall.name = "leftWall"
leftWall.physicsBody?.categoryBitMask = PhysicsCatagory.leftWall
leftWall.physicsBody?.collisionBitMask = PhysicsCatagory.rightWall
leftWall.physicsBody?.contactTestBitMask = PhysicsCatagory.rightWall
leftWall.removeFromParent()
self.addChild(leftWall)
//Right Wall Code
rightWall.size = CGSizeMake(300, 90)
rightWall.position = CGPointMake(self.size.width * 0.87, scene!.frame.height / 6)
rightWall.zPosition = 1.0
rightWall.physicsBody = SKPhysicsBody(rectangleOfSize: rightWall.size)
rightWall.physicsBody?.affectedByGravity = false
rightWall.physicsBody?.dynamic = false
rightWall.name = "rightWall"
rightWall.physicsBody?.categoryBitMask = PhysicsCatagory.rightWall
rightWall.physicsBody?.collisionBitMask = PhysicsCatagory.leftWall
rightWall.physicsBody?.contactTestBitMask = PhysicsCatagory.leftWall
rightWall.removeFromParent()
self.addChild(rightWall)
//Right and Left Wall actions
let moveLeft = SKAction.moveToX(scene!.frame.width * 1.35, duration: 5.0)
let moveRight = SKAction.moveToX(self.size.width * -0.59, duration: 5.0)
leftWall.runAction(SKAction.sequence([moveLeft]))
rightWall.runAction(SKAction.sequence([moveRight]))
}

The SKActions are not stopping at the middle of the screen. Try this:
let moveLeft = SKAction.moveToX(scene!.frame.width / 2, duration: 5.0)
let moveRight = SKAction.moveToX(scene!.frame.width / 2, duration: 5.0)

Have you tested to see if func didBeginContact(contact: SKPhysicsContact) gets fired?If it does not, than this is a problem I once had, Until I realized that this class (the one that contains func didBeginContact(contact: SKPhysicsContact)) must be set as the SKScene's contactdelegateafter that it should work.(if not please say so)
Or in your case: you should make sure self.contactdelegate = self.

Related

SKSpriteNode disappear in GameScene

I'm facing some problem with spritekit in swift.
I was following closely to online tutorials (combining different tutorials into 1 project), trying out the code when I realised my SKSpriteNodes (my "player" and "enemy") sometimes go missing when I try it out on simulator or my iphone.
My situation is kinda similar to this user's problem here, but I don't think my problem lies with the size.
Can anyone enlighten me? Thank you!
Here's my code.
var player : SKSpriteNode!
var backdrop : SKSpriteNode!
var gameTimer : Timer!
var possibleEnemies = ["enemy01", "enemy02", "enemy03"]
let bulletsCategory : UInt32 = 0x1 << 0
let enemyCategory : UInt32 = 0x1 << 1
override func didMove(to view: SKView) {
player = SKSpriteNode(imageNamed: "bird.png")
player.position = CGPoint(x: 0, y: (player.size.height / 2) )
self.addChild(player)
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
self.anchorPoint = CGPoint (x: 0.5 , y: 0)
createBackdrop()
scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 260, y: self.frame.size.height - 90)
scoreLabel.fontName = "Avenir Book"
scoreLabel.fontSize = 35
scoreLabel.fontColor = UIColor.gray
score = 0
self.addChild(scoreLabel)
gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addEnemies), userInfo: nil, repeats: true)
}
#objc func addEnemies() {
possibleEnemies = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possibleEnemies) as! [String]
let enemy = SKSpriteNode(imageNamed: possibleEnemies[0])
let randomEnemyPosition = GKRandomDistribution(lowestValue: -360, highestValue: 360)
let position = CGFloat(randomEnemyPosition.nextInt())
enemy.position = CGPoint(x: position, y: self.frame.size.height + enemy.size.height)
enemy.physicsBody = SKPhysicsBody(rectangleOf: enemy.size)
enemy.physicsBody?.isDynamic = true
enemy.physicsBody?.categoryBitMask = enemyCategory
enemy.physicsBody?.contactTestBitMask = bulletsCategory
enemy.physicsBody?.collisionBitMask = 0
self.addChild(enemy)
let animationDuration : TimeInterval = 6
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -enemy.size.height), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
enemy.run(SKAction.sequence(actionArray))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
fireBullets()
}
func fireBullets() {
self.run(SKAction.playSoundFileNamed("shoot.wav", waitForCompletion: false))
let bullets = SKSpriteNode(imageNamed: "bullet.png")
bullets.position = player.position
bullets.position.y += 5
bullets.physicsBody = SKPhysicsBody(rectangleOf: bullets.size)
bullets.physicsBody?.isDynamic = true
bullets.physicsBody?.categoryBitMask = bulletsCategory
bullets.physicsBody?.contactTestBitMask = enemyCategory
bullets.physicsBody?.collisionBitMask = 0
bullets.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(bullets)
let animationDuration : TimeInterval = 0.3
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
bullets.run(SKAction.sequence(actionArray))
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody : SKPhysicsBody
var secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask & bulletsCategory) != 0 && (secondBody.categoryBitMask & enemyCategory) != 0 {
hitByBullets(bulletNode: firstBody.node as! SKSpriteNode, enemyNode: secondBody.node as! SKSpriteNode)
}
}
func hitByBullets (bulletNode: SKSpriteNode, enemyNode: SKSpriteNode) {
let shot = SKEmitterNode(fileNamed: "Magic01")!
shot.position = enemyNode.position
self.addChild(shot)
self.run(SKAction.playSoundFileNamed("shot.mp3", waitForCompletion: false))
bulletNode.removeFromParent()
enemyNode.removeFromParent()
self.run(SKAction.wait(forDuration: 2)) {
shot.removeFromParent()
}
score += 1
}
func touchDown(atPoint pos : CGPoint) {
player.position = pos
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
moveBackdrop()
}
the issue for you sprites not showing up is that none of your objects have a zPosition set on them. You need to layer the objects as you expect them to show in the scene.
for example...
background.zPosition = 1
player.zPosition = 1
enemy.zPosition = 1
bullet.zPosition = 2
scoreLabel.zPosition = 100
In my opinion you shouldn't be using timers to generate your enemies. Spritekit has it's own timing functionality built into the update function. Which you are already using to control the timing of the backgrounds.
You had waaaaay to much code in your question, you need to look at how I've tailored the code down to only relevant code to your question. Including all of your code in your question actually makes it more unlikely that you will get the help or answers you need because it is harder to go through all the code to figure out what is happening. Also don't include so many spaces in your code in your question scrolling through hundreds of lines even if a lot of them are spaces is very tedious.
Didn't realised the importance of zPosition since my items show up perfectly on screen some of the times. Added the following in their respective place and they stop disappearing intermittently.
player.zPosition = 3
scoreLabel.zPosition = 100
enemy.zPosition = 3
bullets.zPosition = 2
backdrop.zPosition = 1
shot.zPosition = 3

Asteroid Battle Game Keep Crashing

So I keep getting this error at random times during the play of the game, "fatal error: unexpectedly found nil while unwrapping an Optional value" in my asteroid battle game. I'll post the code here and if anyone could help figure it out I would appreciate it. If you don't understand any part you can ask me
import SpriteKit
import Darwin
struct PhysicsCatagory {
static let Asteroids : UInt32 = 1 //00000000000000000000000000000001
static let Bullet : UInt32 = 2 //0000000000000000000000000000010
static let Fighter : UInt32 = 3 //0000000000000000000000000000100
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var Score = Int()
var scoreLabel = UILabel()
var Fighter = SKSpriteNode(imageNamed: "Fighter")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
physicsWorld.contactDelegate = self
Fighter.position = CGPointMake(self.size.width / 2, self.size.height / 7)
Fighter.physicsBody = SKPhysicsBody(rectangleOfSize: Fighter.size)
Fighter.physicsBody?.affectedByGravity = false
Fighter.physicsBody?.categoryBitMask = PhysicsCatagory.Fighter
Fighter.physicsBody?.contactTestBitMask = PhysicsCatagory.Asteroids
Fighter.physicsBody?.dynamic = false
var Timer = NSTimer.scheduledTimerWithTimeInterval(0.25, target: self, selector: Selector("spawnBullets"), userInfo: nil, repeats: true)
var asteriodsTimer = NSTimer.scheduledTimerWithTimeInterval(0.35 , target: self, selector: Selector("spawnAsteroids"), userInfo: nil, repeats: true)
var furiousAsteriodsTimer = NSTimer.scheduledTimerWithTimeInterval(0.9 , target: self, selector: Selector("spawnFuriousAsteroids"), userInfo: nil, repeats: true)
self.addChild(Fighter)
scoreLabel.text = "\(Score)"
scoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
scoreLabel.backgroundColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.3)
scoreLabel.textColor = UIColor.whiteColor()
self.view?.addSubview(scoreLabel)
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = contact.bodyA
let secondBody : SKPhysicsBody = contact.bodyB
if ((firstBody.categoryBitMask == PhysicsCatagory.Asteroids) && (secondBody.categoryBitMask == PhysicsCatagory.Bullet) || (firstBody.categoryBitMask == PhysicsCatagory.Bullet) && (secondBody.categoryBitMask == PhysicsCatagory.Asteroids)){
bulletHitAsteroids(firstBody.node as! SKSpriteNode, Bullet: secondBody.node as! SKSpriteNode)
}
}
func bulletHitAsteroids(Asteroids: SKSpriteNode, Bullet: SKSpriteNode){
Asteroids.removeFromParent()
Bullet.removeFromParent()
Score++
scoreLabel.text = "\(Score)"
}
func bulletHitFuriousAsteroids(FuriousAsteroids: SKSpriteNode, Bullet: SKSpriteNode){
FuriousAsteroids.removeFromParent()
Bullet.removeFromParent()
Score = Score + 2
scoreLabel.text = "\(Score)"
}
func spawnBullets(){
let Bullet = SKSpriteNode(imageNamed: "Bullet")
Bullet.zPosition = -5
Bullet.position = CGPointMake(Fighter.position.x, Fighter.position.y)
let action = SKAction.moveToY(self.size.height + 30, duration: 0.9)
let actionDone = SKAction.removeFromParent()
Bullet.runAction(SKAction.sequence([action, actionDone]))
Bullet.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet.size)
Bullet.physicsBody?.categoryBitMask = PhysicsCatagory.Bullet
Bullet.physicsBody?.contactTestBitMask = PhysicsCatagory.Asteroids
Bullet.physicsBody?.affectedByGravity = false
Bullet.physicsBody?.dynamic = false
self.addChild(Bullet)
}
func spawnAsteroids(){
let Asteroids = SKSpriteNode(imageNamed: "Asteroid")
let minValue = self.size.width / 8
let maxValue = self.size.width - 20
let spawnPoint = UInt32(maxValue - minValue)
Asteroids.position = CGPoint(x: CGFloat(arc4random_uniform(spawnPoint)) , y: self.size.height)
Asteroids.physicsBody = SKPhysicsBody(rectangleOfSize: Asteroids.size)
Asteroids.physicsBody?.categoryBitMask = PhysicsCatagory.Asteroids
Asteroids.physicsBody?.contactTestBitMask = PhysicsCatagory.Bullet
Asteroids.physicsBody?.affectedByGravity = false
Asteroids.physicsBody?.dynamic = true
let action = SKAction.moveToY(-50, duration: 2.5)
let actionDone = SKAction.removeFromParent()
Asteroids.runAction(SKAction.sequence([action, actionDone]))
self.addChild(Asteroids)
}
func spawnFuriousAsteroids(){
let FuriousAsteroids = SKSpriteNode(imageNamed: "FuriousAsteroid")
let minValue = self.size.width / 8
let maxValue = self.size.width - 20
let spawnPoint = UInt32(maxValue - minValue)
FuriousAsteroids.position = CGPoint(x: CGFloat(arc4random_uniform(spawnPoint)) , y: self.size.height)
FuriousAsteroids.physicsBody = SKPhysicsBody(rectangleOfSize: FuriousAsteroids.size)
FuriousAsteroids.physicsBody?.categoryBitMask = PhysicsCatagory.Asteroids
FuriousAsteroids.physicsBody?.contactTestBitMask = PhysicsCatagory.Bullet
FuriousAsteroids.physicsBody?.affectedByGravity = false
FuriousAsteroids.physicsBody?.dynamic = true
let action = SKAction.moveToY(-50, duration: 1.5)
let actionDone = SKAction.removeFromParent()
FuriousAsteroids.runAction(SKAction.sequence([action, actionDone]))
self.addChild(FuriousAsteroids)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
Fighter.position.x = location.x
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
Fighter.position.x = location.x
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
It is crashing because one of the bodies doesn't have its node. When crash happen, try to type something like this in debugger console :
po firstBody.node, or po secondBody.node. One of these two will be nil.
This is happening when more than two bodies make contact as in your example, where you can have a situation where bullet and a two asteroids might be in a contact at the same time. Because SpriteKit can check for a contact only between two bodies at the time, something like this will happen:
didBeginContact is invoked with two physicsBodies passed in (bullet and first asteroide)
now, you are force unwrapping nodes associated with those bodies and removing the asteroid and the bullet from its parent
then, because there are more contacts occurred
didBeginContact is invoked again, with another two physics bodies, but one of those physicsBodies is bullet's physics body again. Another body is , let say furious asteroid.
now, again you are force unwrapping nodes associated with those two physics bodies, but one of them (bullet) is nil.
Solution would be something like this in your didBeginContact method:
if let firstNode = firstBody.node as? SKSpriteNode, let secondNode = secondBody.node as? SKSpriteNode {
bulletHitAsteroids(firstNode, Bullet: secondNode)
}
This way, one bullet will destroy only one asteroid. Which I guess is what you want and what make sense after all.

Game stops working when restart button clicked

I have just started programming this past week and have been trying to make a simple game app for IOS. Everything had worked properly regarding what I want the game to be doing when playing it. However, after I lose and get taken to my end scene, when I click the restart button, the app appears to take me back to the game scene but instead opens up a dark brown screen and just stays there for a few seconds before going back to the end scene. Then, if I click the restart button again after being taken back to the end scene, it repeats.
It also appears that how long the brown screen lasts is dependent on the sharkTimer with the fastest time interval. So if the fastest on is generating a shark every half a second, the brown screen lasts for half a second before transferring to the end scene.
The game is really simple as the character is a fish that swims up and down and the goal of the game to eat little things called food and to avoid other things called sharks that swim toward you.
Here is a link to my project:
https://www.dropbox.com/sh/z8o3iunz9rng6na/AACDGGOTR-QhqbQc402y4sgRa?dl=0
This is what I've got so far in my GameScene.swift file:
import SpriteKit
import UIKit
struct physicsCategory
{
static let shark: UInt32 = 1
static let food: UInt32 = 2
static let fish: UInt32 = 3
}
class GameScene: SKScene, SKPhysicsContactDelegate
{
var score = Int()
var scoreLabel = UILabel()
var fish = SKSpriteNode(imageNamed: "fish1 copy.png")
var shark = SKSpriteNode(imageNamed: "shark1 copy.png")
var food = SKSpriteNode(imageNamed: "fish game point copy.png")
override func didMoveToView(view: SKView)
{
physicsWorld.contactDelegate = self
self.scene?.backgroundColor = UIColor(red: 117/255.0, green: 208/255.0, blue: 224/255.0, alpha: 1.0)
fish.setScale(0.15)
fish.position = CGPointMake(self.frame.size.width * 0.1, self.frame.size.height * 0.5)
fish.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(35, 40))
fish.physicsBody?.affectedByGravity = false
fish.physicsBody?.categoryBitMask = physicsCategory.fish
fish.physicsBody?.contactTestBitMask = physicsCategory.shark
fish.physicsBody?.contactTestBitMask = physicsCategory.food
fish.physicsBody?.dynamic = false
self.addChild(fish)
var sharkTimer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("SpawnShark"), userInfo: nil, repeats: true)
var sharkTimer2 = NSTimer.scheduledTimerWithTimeInterval(2.33, target: self, selector: Selector("SpawnShark"), userInfo: nil, repeats: true)
var sharkTimer3 = NSTimer.scheduledTimerWithTimeInterval(2.79, target: self, selector: Selector("SpawnShark"), userInfo: nil, repeats: true)
var foodTimer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("SpawnFood"), userInfo: nil, repeats: true)
scoreLabel.text = "\(score)"
scoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
scoreLabel.backgroundColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 0.3)
scoreLabel.textColor = UIColor.whiteColor()
self.view?.addSubview(scoreLabel)
}
func SpawnShark()
{
var shark = SKSpriteNode(imageNamed: "shark1 copy.png")
shark.setScale(0.33)
var MinValue = self.size.height - self.size.height
var MaxValue = self.size.height
var SpawnPoint = UInt32(MaxValue - MinValue)
shark.position = CGPoint(x: self.size.width , y: CGFloat(arc4random_uniform(SpawnPoint)))
let action = SKAction.moveToX(-70, duration: 3.0)
shark.runAction(SKAction.repeatActionForever(action))
let actionDone = SKAction.removeFromParent()
shark.runAction(SKAction.sequence([action, actionDone]))
shark.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(100, 20))
shark.physicsBody?.affectedByGravity = false
shark.physicsBody?.categoryBitMask = physicsCategory.shark
shark.physicsBody?.contactTestBitMask = physicsCategory.fish
shark.physicsBody?.dynamic = true
self.addChild(shark)
}
func SpawnFood()
{
var food = SKSpriteNode(imageNamed: "fish game point copy.png")
food.setScale(0.1)
var MinValue = self.size.height - self.size.height + 10
var MaxValue = self.size.height - 10
var SpawnPoint = UInt32(MaxValue - MinValue)
food.position = CGPoint(x: self.size.width , y: CGFloat(arc4random_uniform(SpawnPoint)))
let action = SKAction.moveToX(-70, duration: 5.0)
food.runAction(SKAction.repeatActionForever(action))
let actionDone = SKAction.removeFromParent()
food.runAction(SKAction.sequence([action, actionDone]))
food.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(10, 10))
food.physicsBody?.affectedByGravity = false
food.physicsBody?.categoryBitMask = physicsCategory.food
food.physicsBody?.contactTestBitMask = physicsCategory.fish
food.physicsBody?.dynamic = true
self.addChild(food)
}
func didBeginContact(contact: SKPhysicsContact)
{
var firstBody: SKPhysicsBody = contact.bodyA
var secondBody: SKPhysicsBody = contact.bodyB
if (firstBody.categoryBitMask == physicsCategory.fish) && (secondBody.categoryBitMask == physicsCategory.food) ||
(firstBody.categoryBitMask == physicsCategory.food) && (secondBody.categoryBitMask == physicsCategory.fish)
{
CollisionWithFood(firstBody.node as! SKSpriteNode, food: secondBody.node as! SKSpriteNode)
}
else if (firstBody.categoryBitMask == physicsCategory.shark) && (secondBody.categoryBitMask == physicsCategory.fish) ||
(firstBody.categoryBitMask == physicsCategory.fish) && (secondBody.categoryBitMask == physicsCategory.shark)
{
CollisionWithFish(firstBody.node as! SKSpriteNode, fish: secondBody.node as! SKSpriteNode)
}
}
func CollisionWithFood(fish: SKSpriteNode, food: SKSpriteNode)
{
food.removeFromParent()
score++
scoreLabel.text = "\(score)"
}
func CollisionWithFish(shark: SKSpriteNode, fish: SKSpriteNode)
{
fish.removeFromParent()
scoreLabel.removeFromSuperview()
self.view?.presentScene(EndScene())
self.view?.presentScene(EndScene(), transition: SKTransition.crossFadeWithDuration(1.0))
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
{
for touch: AnyObject in touches
{
let location = touch.locationInNode(self)
fish.position.y = location.y
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches
{
let location = touch.locationInNode(self)
fish.position.y = location.y
}
}
override func update(currentTime: CFTimeInterval)
{
/* Called before each frame is rendered */
}
}
My second file called EndScene.swift is where the program goes to if the shark gets you and the game ends. Its a white screen with a little restart button in the upper middle of the screen that when you press it, should take you back to the game scene, but instead takes you to a dark brown one.
Here is my EndScene.swift file:
import Foundation
import SpriteKit
class EndScene: SKScene
{
var restartButton: UIButton!
override func didMoveToView(view: SKView)
{
scene?.backgroundColor = UIColor.whiteColor()
restartButton = UIButton(frame: CGRect(x: 0, y: 0, width: view.frame.size.width / 3, height: 30))
restartButton.center = CGPoint(x: view.frame.size.width / 2, y: view.frame.size.width / 7)
restartButton.setTitle("Restart", forState: UIControlState.Normal)
restartButton.setTitleColor(UIColor.darkGrayColor(), forState: UIControlState.Normal)
restartButton.addTarget(self, action: Selector("Restart"), forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(restartButton)
}
func Restart()
{
self.view?.presentScene(GameScene(), transition: SKTransition.crossFadeWithDuration(0.3))
restartButton.removeFromSuperview()
}
}
You should add self.view to GameScene init when presenting the scene
func Restart()
{
self.view?.presentScene(GameScene(size: view.frame.size), transition: SKTransition.crossFadeWithDuration(0.3))
restartButton.removeFromSuperview()
}

Sprite Kit set Min. and Max. for Jump

I want to move a SKSpriteNode on the Y-Axis. The SKSpriteNode called Player has no Velocity.The Player can only jump if a Platform is in contact.
Everytime the Screen is touched, I want to give the Player an impulse with a minimum impulse or a maximum impulse
If the Screen is tapped shortly, the Minimum impulse should be e.g. y = 50.
If the Screen is hold, that means the finger is on the Screen long, the Maximum should be e.g. y = 100.
But the Player should be also able to jump between the Minimum and Maximum height, if for e.g. the Screen is not long but also not shortly pressed, the Player should only get an impulse of y = 70.
If the Screen is hold, the Player should jump to his max height, fall down, and if it is in contact with the Platform again, it should jump, because you still hold the Screen.
I have already tried this with the suggested answer in this Thread:StackOverFlow
But this does not give the Minimum jump, also no Press jump.
For clarity: The impulse should not be after the tap is done, but while it is tapped. The longer you hold, the longer the jump is.
import SpriteKit
import GameKit
struct Constants {
static let minimumJumpForce:CGFloat = 40.0
static let maximumJumpForce:CGFloat = 60.0
static let characterSideSpeed:CGFloat = 18.0
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var Player: SKSpriteNode!
var Platform0: SKSpriteNode!
var World: SKNode!
var Camera: SKNode!
var force: CGFloat = 40.0
var pressed = false
var isCharacterOnGround = false
.....
func SpawnPlatforms() {
Platform0 = SKSpriteNode (color: SKColor.greenColor(), size: CGSize(width: self.frame.size.width , 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
Platform0.physicsBody?.usesPreciseCollisionDetection = true
Platform0.physicsBody?.categoryBitMask = Platform0Category
Platform0.physicsBody?.collisionBitMask = PlayerCategory
Platform0.physicsBody?.contactTestBitMask = PlayerCategory
World.addChild(Platform0)
}
func SpawnPlayer(){
Player = SKSpriteNode (imageNamed: "Image.png")
Player.size = CGSize(width: 64, height: 64)
Player.position = CGPoint(x: self.frame.size.width / 2, y: 0)
Player.zPosition = 2
Player.physicsBody = SKPhysicsBody(rectangleOfSize:CGSize(width: 35, height: 50))
Player.physicsBody?.dynamic = true
Player.physicsBody?.allowsRotation = false
Player.physicsBody?.restitution = 0.1
Player.physicsBody?.usesPreciseCollisionDetection = true
Player.physicsBody?.categoryBitMask = PlayerCategory
Player.physicsBody?.collisionBitMask = Platform0Category
Player.physicsBody?.contactTestBitMask = Platform0Category | Platform1Category | Platform2Category | Platform3Category | Platform4Category | Platform5Category
World.addChild(Player)
}
func jump(force : CGFloat){
if(self.isCharacterOnGround){
self.Player.physicsBody?.applyImpulse(CGVectorMake(0, force))
self.isCharacterOnGround = false
}
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
self.pressed = true
let timerAction = SKAction.waitForDuration(0.0)
let update = SKAction.runBlock({
if(self.force < Constants.maximumJumpForce){
self.force += 2.0
}else{
self.jump(Constants.maximumJumpForce)
self.force = Constants.maximumJumpForce
}
})
let sequence = SKAction.sequence([timerAction, update])
let repeat = SKAction.repeatActionForever(sequence)
self.runAction(repeat, withKey:"repeatAction")
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
self.removeActionForKey("repeatAction")
self.jump(self.force)
self.force = Constants.minimumJumpForce
self.pressed = false
}
}
func didBeginContact(contact: SKPhysicsContact) {
//this gets called automatically when two objects begin contact with each other
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch(contactMask) {
case PlayerCategory | Platform0Category:
//either the contactMask was the bro type or the ground type
println("Contact Made0")
Green = true
self.isCharacterOnGround = true
default:
return
}
}
Here is an working example on how to make something like:
long pressed jump based on duration of press
short (one tap jump)
restrict character to jump while in the air
keep character jumping while finger is on screen
Code (Swift 4.x)
import SpriteKit
struct Constants {
static let minimumJumpForce:CGFloat = 15.0
static let maximumJumpForce:CGFloat = 30.0
static let characterSideSpeed:CGFloat = 18.0
}
class GameScene: SKScene,SKPhysicsContactDelegate
{
let CharacterCategory : UInt32 = 0x1 << 1
let PlatformCategory : UInt32 = 0x1 << 2
let WallCategory : UInt32 = 0x1 << 3
var force: CGFloat = 16.0 //Initial force
var pressed = false
var isCharacterOnGround = false // Use this to prevent jumping while in the air
let character = SKSpriteNode(color: .green, size: CGSize(width: 30, height:30))
let debugLabel = SKLabelNode(fontNamed: "Geneva")
override func didMove(to view: SKView)
{
//Setup contact delegate so we can use didBeginContact and didEndContact methods
physicsWorld.contactDelegate = self
physicsWorld.speed = 0.5
//Setup borders so character can't escape from us :-)
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
self.physicsBody?.categoryBitMask = WallCategory
self.physicsBody?.collisionBitMask = CharacterCategory
//Setup character
character.position = CGPoint(x: 150, y: 150)
character.physicsBody = SKPhysicsBody(rectangleOf: character.size)
character.physicsBody?.categoryBitMask = CharacterCategory
character.physicsBody?.contactTestBitMask = PlatformCategory
character.physicsBody?.collisionBitMask = PlatformCategory | WallCategory
character.physicsBody?.allowsRotation = false
character.physicsBody?.isDynamic = true
character.physicsBody?.restitution = 0.1
self.addChild(character)
generatePlatforms()
debugLabel.text = " DEBUG: "
debugLabel.fontColor = .white
debugLabel.fontSize = 12.0
debugLabel.position = CGPoint(x: frame.midX, y: frame.midY+100)
self.addChild(debugLabel)
}
func generatePlatforms(){
for i in 1...4
{
let position = CGPoint(x: frame.midX, y: CGFloat(i)*140.0 - 100)
let platform = createPlatformAtPosition(position: position)
self.addChild(platform)
}
}
func createPlatformAtPosition(position : CGPoint)->SKSpriteNode{
let platform = SKSpriteNode(color: .green, size: CGSize(width: frame.size.width, height:20))
platform.position = position
platform.physicsBody = SKPhysicsBody(
edgeFrom: CGPoint(x: -platform.size.width/2.0, y:platform.size.height/2.0),
to:CGPoint(x: platform.size.width/2.0, y: platform.size.height/2.0))
platform.physicsBody?.categoryBitMask = PlatformCategory
platform.physicsBody?.contactTestBitMask = CharacterCategory
platform.physicsBody?.collisionBitMask = CharacterCategory
platform.physicsBody?.allowsRotation = false
platform.name = "platform"
platform.physicsBody?.isDynamic = false
platform.physicsBody?.restitution = 0.0
return platform
}
func jump(force : CGFloat){
if(self.isCharacterOnGround){
self.character.physicsBody?.applyImpulse(CGVector(dx: 0, dy: force))
self.character.physicsBody?.collisionBitMask = WallCategory
self.isCharacterOnGround = false
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.pressed = true
let timerAction = SKAction.wait(forDuration: 0.05)
let update = SKAction.run({
if(self.force < Constants.maximumJumpForce){
self.force += 2.0
}else{
self.jump(force: Constants.maximumJumpForce)
self.force = Constants.maximumJumpForce
}
})
let sequence = SKAction.sequence([timerAction, update])
let repeat_seq = SKAction.repeatForever(sequence)
self.run(repeat_seq, withKey:"repeatAction")
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.removeAction(forKey: "repeatAction")
self.jump(force: self.force)
self.force = Constants.minimumJumpForce
self.pressed = false
}
override func update(_ currentTime: TimeInterval) {
debugLabel.text = "DEBUG: onTheGround : \(isCharacterOnGround), force \(force)"
if(character.position.x <= character.size.width/2.0 + 5.0 && character.physicsBody!.velocity.dx < 0.0 ){
character.physicsBody?.applyForce(CGVector(dx: Constants.characterSideSpeed, dy: 0.0))
}else if((character.position.x >= self.frame.size.width - character.size.width/2.0 - 5.0) && character.physicsBody!.velocity.dx >= 0.0){
character.physicsBody?.applyForce(CGVector(dx: -Constants.characterSideSpeed, dy: 0.0))
}else if(character.physicsBody!.velocity.dx > 0.0){
character.physicsBody!.applyForce(CGVector(dx: Constants.characterSideSpeed, dy: 0.0))
}else{
character.physicsBody!.applyForce(CGVector(dx: -Constants.characterSideSpeed, dy: 0.0))
}
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody, secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & CharacterCategory) != 0 &&
(secondBody.categoryBitMask & PlatformCategory != 0)) {
let platform = secondBody.node! as! SKSpriteNode
// platform.color = UIColor.redColor()
let platformSurfaceYPos = platform.position.y + platform.size.height/2.0
let player = contact.bodyB.node! as! SKSpriteNode
let playerLegsYPos = player.position.y - player.size.height/2.0
if((platformSurfaceYPos <= playerLegsYPos)){
character.physicsBody?.collisionBitMask = PlatformCategory | WallCategory
self.isCharacterOnGround = true
if(self.pressed){
let characterDx = character.physicsBody?.velocity.dx
character.physicsBody?.velocity = CGVector(dx: characterDx!, dy: 0.0)
self.jump(force: Constants.maximumJumpForce)
}
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
var firstBody, secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & CharacterCategory) != 0 &&
(secondBody.categoryBitMask & PlatformCategory != 0)) {
let platform = secondBody.node as! SKSpriteNode
let platformSurfaceYPos = platform.position.y + platform.size.height/2.0
let player = contact.bodyB.node as! SKSpriteNode
let playerLegsYPos = player.position.y - player.size.height/2.0
if((platformSurfaceYPos <= playerLegsYPos) && ((character.physicsBody?.velocity.dy)! > CGFloat(0.0))){
character.physicsBody?.collisionBitMask = WallCategory
self.isCharacterOnGround = false
}
}
}
}
Note that this is simple example, and in real application you will probably have to handle states like isOnTheGround in a different way. Right now, to determine if character is on the ground you just set isOnTheGround = true when character make a contact with platform, and set it to false in didEndContact...But there are situations when character can be in contact with platform while in the air (eg. side contact)...
EDIT:
I changed the code to let the player jump while pressed. Here is the result:
Important:
Actual platform implementation and contact handling is up to you and this is not tested. The only purpose of this example is to show you how to jump while pressed. Currently, physicsWorld.speed is set to 0.5 to make animation slower because its easier to debug like that, but you can change this to default value (1.0).
So, as you can see from the image, while player is on the first platform some small jumps are presented (by simple tapping, or short pressing). Then (player is still on first platform) long press has been made, and player has jumped on second platform. After that, another long press is done, but this time without releasing, and player starts jumping from one platform to another using maximum force.
This needs a lot of tweaking and proper platform & contact detection, but it can give you an idea about how to implement jumping you asked about.

Implementing collision detections

Basically the game consists of a basket that the player moves across the screen, the aim of the game is for the player to catch balls falling from the top of the screen. I am currently trying to add collision detection between the balls and the basket, but am facing difficulties namely, implementing this collision detection. I am new to swift, sprite kit and app development, so please help. Any help would be appreciated. Another problem I am facing is that all the balls are falling in the centre of the screen. A line of code is supposed to execute when, the ball hits the basket and following that the ball should disappear, please help as I am new to Spritekit.
import SpriteKit
class GameScene: SKScene {
var basket = SKSpriteNode()
let actionMoveRight = SKAction.moveByX(50, y: 0, duration: 0.2)
let actionMoveLeft = SKAction.moveByX(-50, y: 0, duration: 0.2)
//let physicsBody = SKPhysicsBody(texture: , size: 3500)
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.physicsWorld.gravity = CGVectorMake(0.0, -0.5)
self.backgroundColor = SKColor.whiteColor()
basket = SKSpriteNode(imageNamed: "basket")
basket.setScale(0.5)
basket.position = CGPointMake(self.size.width/2, self.size.height/8)
basket.size.height = 50
basket.size.width = 75
self.addChild(basket)
let updateAction = SKAction.runBlock {
var choice = arc4random_uniform(3)
switch choice {
case 1 :
var ball1 = SKSpriteNode(imageNamed: "redBall")
ball1.position = CGPointMake(self.size.width/3, self.size.height)
ball1.setScale(0.5)
ball1.size.height = 20
ball1.size.width = 30
ball1.physicsBody = SKPhysicsBody(circleOfRadius: ball1.size.height / 2.75)
ball1.physicsBody!.dynamic = true
self.addChild(ball1)
println("0")
case 0 :
var ball2 = SKSpriteNode(imageNamed: "redBall")
ball2.position = CGPointMake(self.size.width/5, self.size.height)
ball2.setScale(0.5)
ball2.size.height = 20
ball2.size.width = 30
ball2.physicsBody = SKPhysicsBody(circleOfRadius: ball2.size.height / 2.75)
ball2.physicsBody!.dynamic = true
self.addChild(ball2)
println("1")
case 2 :
var ball3 = SKSpriteNode(imageNamed: "redBall")
ball3.position = CGPointMake(self.size.width*4/5, self.size.height)
ball3.setScale(0.5)
ball3.size.height = 20
ball3.size.width = 30
ball3.physicsBody = SKPhysicsBody(circleOfRadius: ball3.size.height / 2.75)
ball3.physicsBody!.dynamic = true
self.addChild(ball3)
println("2")
default :
println("Problem")
}
}
let waitDuration : NSTimeInterval = 1.0
let updateAndWaitAction = SKAction.sequence([updateAction,SKAction.waitForDuration(waitDuration)])
let repeatForeverAction = SKAction.repeatActionForever(updateAndWaitAction)
self.runAction(repeatForeverAction)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if location.x > basket.position.x {
if basket.position.x < self.frame.maxX {
basket.runAction(actionMoveRight)
}
}
else {
if basket.position.x > self.frame.minX {
basket.runAction(actionMoveLeft)
}
}
}
}
override func update(currentTime: CFTimeInterval) {
}
}
For now you have a code that typically used in situations where user is taping something. You need to use BodyA & BodyB and assign a bitmasks to your nodes.
self.basket.physicsBody?.categoryBitMask = ColliderType.basket.rawValue
self.basket.physicsBody?.contactTestBitMask = ColliderType.ball1.rawValue
self.basket.physicsBody?.collisionBitMask = ColliderType.ball1.rawValue
self.basket.physicsBody?.contactTestBitMask = ColliderType.ball2.rawValue
self.basket.physicsBody?.collisionBitMask = ColliderType.ball2.rawValue
self.basket.physicsBody?.contactTestBitMask = ColliderType.ball3.rawValue
self.basket.physicsBody?.collisionBitMask = ColliderType.ball3.rawValue
And do that for every ball too. And then in func didBeginContact you should say to Xcode what to do, if you have an animation or something:
if (contact.bodyA.categoryBitMask == ColliderType.ball1.rawValue || contact.bodyB.categoryBitMask == ColliderType.ball1.rawValue) {
yourGameOverFunc()
}
if (contact.bodyA.categoryBitMask == ColliderType.ball2.rawValue || contact.bodyB.categoryBitMask == ColliderType.ball2.rawValue) {
yourGameOverFunc()
}
if (contact.bodyA.categoryBitMask == ColliderType.ball3.rawValue || contact.bodyB.categoryBitMask == ColliderType.ball3.rawValue) {
yourGameOverFunc()
}

Resources