I learn how to create games on iOS. And now I create a Flappy bird clone.
Flappy Bird Clone
And I'm stack on that - when bird touch some pipe it begin rotate. And game of course is over. But when I start a new game Bird still rotates.
If I put this line
bird.physicsBody?.allowsRotation = false
in the GameScene.swift then bird stop rotate at all. That is not what I want. I want to allow rotation. But when start a new game Bird should be in default position and do not rotate.
What should I do to make it work? Thanks for help.
//
// GameScene.swift
// Flappy Bird
//
// Created by Admin on 10.10.15.
// Copyright (c) 2015 Alex. All rights reserved.
//
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var score = 0
var gameOver = false
var gameOverLabel = SKLabelNode()
var scoreLabel = SKLabelNode ()
var bird = SKSpriteNode()
var bg = SKSpriteNode()
var movingObjects = SKSpriteNode()
var labelContainer = SKSpriteNode()
var pipe1 = SKSpriteNode()
var pipe2 = SKSpriteNode()
enum ColliderType: UInt32 {
case Bird = 1
case Object = 2
case Gap = 4
}
func makeBg () {
let bgTexture = SKTexture(imageNamed: "bg.png")
let movebg = SKAction.moveByX(-bgTexture.size().width, y: 0, duration: 10)
let replacebg = SKAction.moveByX(bgTexture.size().width, y: 0, duration: 0)
let movebgForever = SKAction.repeatActionForever(SKAction.sequence([movebg,replacebg]))
for var i: CGFloat = 0; i<2; i++ {
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: bgTexture.size().width / 2 + bgTexture.size().width * i, y: CGRectGetMidY(self.frame))
bg.zPosition = -5
bg.size.height = self.frame.height
bg.runAction(movebgForever)
movingObjects.addChild(bg)
}
}
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
self.addChild(movingObjects)
self.addChild(labelContainer)
makeBg()
scoreLabel.fontName = "Helvetica"
scoreLabel.fontSize = 60
scoreLabel.text = "0"
scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height - 70)
addChild(scoreLabel)
let birdTexture = SKTexture(imageNamed: "flappy1.png")
let birdTexture2 = SKTexture(imageNamed: "flappy2.png")
let animation = SKAction.animateWithTextures([birdTexture,birdTexture2], timePerFrame: 0.1)
let makeBirdFlap = SKAction.repeatActionForever(animation)
bird = SKSpriteNode(texture: birdTexture)
bird.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
bird.runAction(makeBirdFlap)
bird.physicsBody = SKPhysicsBody(circleOfRadius: birdTexture.size().height/2)
bird.physicsBody!.dynamic = true
bird.physicsBody!.categoryBitMask = ColliderType.Bird.rawValue
bird.physicsBody?.contactTestBitMask = ColliderType.Object.rawValue
bird.physicsBody?.collisionBitMask = ColliderType.Object.rawValue
self.addChild(bird)
var ground = SKNode()
ground.position = CGPointMake(0, 0)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.size.width, 1))
ground.physicsBody!.dynamic = false
ground.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
ground.physicsBody?.contactTestBitMask = ColliderType.Object.rawValue
ground.physicsBody?.collisionBitMask = ColliderType.Object.rawValue
self.addChild(ground)
_ = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("makePipes"), userInfo: nil, repeats: true)
}
func makePipes () {
let gapHeight = bird.size.height * 4
let movementAmount = arc4random() % UInt32(self.frame.size.height / 2)
let pipeOffset = CGFloat(movementAmount) - self.frame.size.height / 4
let movePipes = SKAction.moveByX(-self.frame.size.width * 2, y: 0, duration: NSTimeInterval (self.frame.size.width / 100))
let removePipes = SKAction.removeFromParent()
let moveAndRemovePipes = SKAction.sequence([movePipes,removePipes])
let pipeTexture1 = SKTexture(imageNamed: "pipe1.png")
let pipe1 = SKSpriteNode(texture: pipeTexture1)
pipe1.position = CGPoint(x: CGRectGetMidX(self.frame) + self.frame.size.width, y: CGRectGetMidY(self.frame) + pipeTexture1.size().height/2 + gapHeight/2 + pipeOffset)
pipe1.runAction(moveAndRemovePipes)
pipe1.physicsBody = SKPhysicsBody(rectangleOfSize: pipeTexture1.size())
pipe1.physicsBody?.dynamic = false
pipe1.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe1.physicsBody?.contactTestBitMask = ColliderType.Object.rawValue
pipe1.physicsBody?.collisionBitMask = ColliderType.Object.rawValue
movingObjects.addChild(pipe1)
let pipeTexture2 = SKTexture(imageNamed: "pipe2.png")
let pipe2 = SKSpriteNode(texture: pipeTexture2)
pipe2.position = CGPoint(x: CGRectGetMidX(self.frame) + self.frame.size.width, y: CGRectGetMidY(self.frame) - pipeTexture2.size().height/2 - gapHeight/2 + pipeOffset)
pipe2.runAction(moveAndRemovePipes)
pipe2.physicsBody = SKPhysicsBody(rectangleOfSize: pipeTexture2.size())
pipe2.physicsBody!.dynamic = false
pipe2.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
movingObjects.addChild(pipe2)
var gap = SKNode()
gap.position = CGPoint(x: CGRectGetMidX(self.frame) + self.frame.size.width, y: CGRectGetMidY(self.frame) + pipeOffset)
gap.runAction(moveAndRemovePipes)
gap.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(pipe1.size.width, gapHeight))
gap.physicsBody?.dynamic = false
gap.physicsBody!.categoryBitMask = ColliderType.Gap.rawValue
gap.physicsBody!.contactTestBitMask = ColliderType.Bird.rawValue
gap.physicsBody!.collisionBitMask = ColliderType.Gap.rawValue
movingObjects.addChild(gap)
}
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == ColliderType.Gap.rawValue || contact.bodyB.categoryBitMask == ColliderType.Gap.rawValue {
score++
scoreLabel.text = String(score)
} else {
if gameOver == false {
gameOver = true
self.speed = 0
gameOverLabel.fontName = "Helvetica"
gameOverLabel.fontSize = 30
gameOverLabel.text = "Game is over. Tap to play again."
gameOverLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
labelContainer.addChild(gameOverLabel)
}
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if gameOver == false {
bird.physicsBody!.velocity = CGVectorMake(0, 0)
bird.physicsBody!.applyImpulse(CGVectorMake(0, 50))
} else {
score = 0
scoreLabel.text = "0"
bird.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
bird.physicsBody?.velocity = CGVectorMake(0, 0)
bird.physicsBody!.allowsRotation = false
movingObjects.removeAllChildren()
makeBg()
self.speed = 1
gameOver = false
labelContainer.removeAllChildren()
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
An easy solution is to reset the bird's angular speed and rotation angle. Modify the code in touchesBegan which is invoked when game is over:
bird.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
bird.physicsBody?.velocity = CGVectorMake(0, 0)
bird.physicsBody?.angularVelocity = 0
bird.zRotation = 0
Related
I'm working on a 2-d Platformer style game. I'm very new to IOS and Swift development. I'm attempting to use a button (a different node) to move my character across the screen from left to right. It works fine until I reach the halfway point then it speeds up dramatically and letting go of the touch of the button doesn't always stop it. Sometimes it requires another touch. Also the background doesn't appear to keep up with the player. Once the player hits mid screen the background should shift as the player continues moving.
I've pieced what I've done together from multiple tutorials SO examples but I'm stuck on this point.
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE PLAYER ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
override func didMove(to view: SKView) {
/* Setup your scene here */
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
leftMoveButton.position.x = 64
leftMoveButton.position.y = 64
leftMoveButton.name = "moveLeft"
addChild(leftMoveButton)
rightMoveButton.position.x = 124
rightMoveButton.position.y = 64
rightMoveButton.name = "moveRight"
addChild(rightMoveButton)
jumpButton.position.x = 771
jumpButton.position.y = 64
jumpButton.name = "jumpButton"
addChild(jumpButton)
fireButton.position.x = 836
fireButton.position.y = 64
fireButton.name = "fireButton"
addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
//let moveAction = SKAction.moveBy(x: 3, y: 0, duration: 1)
//let repeatForEver = SKAction.repeatForever(moveAction)
//let seq = SKAction.sequence([moveAction, repeatForEver])
//run the action on your ship
//player.run(seq)
player.position.x = player.position.x + 3
setViewpointCenter(player.position)
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
if rightMoveButton.contains(touch.location(in: self)) {
forwardMarch = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
if jumpButton.contains(touch.location(in: self)) {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: (centerOfView.x - actualPosition.x) * 3, y: centerOfView.y - actualPosition.y)
tileMap!.position = viewPoint
}
func setupScene() {
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
let point = tileMap!.calculateAccumulatedFrame()
print (point)
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
//print (wallGIDs)
//let node = walls
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
addChild(player)
}
}
Holding the rightMoveButton should move at a consistent pace to the right. As the player gets to mid screen the view point of the background should shift until it reaches the end of the background at which point the player can move off screen and complete the level. Releasing the button should allow the player to stop.
You can move the scene as the character moves by creating an SKCameraNode. You then can choose when to move the camera to create the right affect. Make sure to set the camera as your game’s camera. You can learn about cameras here. As for the speeding up, I assume it has something to do with your physicsBody. If you’re interested about player movement, you could look here or here. There are many other great videos and sites online, which you could find by searching “SpriteKit player movement.”
Eli Front's answer pointed me in the right direction. I still have some issues with this code but the answer to my question was using SKCamera node as Eli pointed out. I just wanted to post the code that works so if anyone has a similar question there's a code example as far as camera moving with the player. The speed issue was essentially an optical illusion with the background moving too fast.
Note that the controls are children of the camera. That is so they move with the camera as well otherwise as your node moves the controls would move off screen.
import UIKit
import SpriteKit
import GameKit
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE Player ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
let cam = SKCameraNode()
var previousUpdateTime: TimeInterval = 0
override func didMove(to view: SKView) {
// SETUP CAMERA
self.camera = cam
scene?.addChild(cam)
cam.position.x = 448
cam.position.y = 212
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
// SETUP CONTROLS
leftMoveButton.position.x = -338
leftMoveButton.position.y = -112
leftMoveButton.name = "moveLeft"
leftMoveButton.zPosition = 5
cam.addChild(leftMoveButton)
rightMoveButton.position.x = -278
rightMoveButton.position.y = -112
rightMoveButton.name = "moveRight"
cam.addChild(rightMoveButton)
jumpButton.position.x = 278
jumpButton.position.y = -112
jumpButton.name = "jumpButton"
jumpButton.zPosition = 5
cam.addChild(jumpButton)
fireButton.position.x = 338
fireButton.position.y = -112
fireButton.name = "fireButton"
cam.addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
if player.position.x > 448 && player.position.x < 1800 {
cam.position.x = player.position.x
} else if player.position.x >= 1800 {
cam.position.x = 1800
}
setViewpointCenter(player.position)
player.position.x = player.position.x + 3
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
player.physicsBody!.velocity.dy = 0.0
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
}
if name == "moveRight" {
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
forwardMarch = false
}
}
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: centerOfView.x - actualPosition.x, y: centerOfView.y - actualPosition.y)
if (actualPosition.x > 1800) {
tileMap!.position = tileMap!.position
} else {
tileMap!.position = viewPoint
}
}
func setupScene() {
// ADD PLAYER SPRITES #TODO MAKE THIS DYNAMIC
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
// let point = tileMap!.calculateAccumulatedFrame()
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
// let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
player.physicsBody!.mass = 1.0
addChild(player)
}
}
As I mentioned I still have some issues like the player node floating/flying when I jump/run at the same time. The 1st jump is the correct height and all subsequent jumps are smaller etc but I'm working on figuring those out.
I am trying to make my 'Enemy' bounce off all the boundaries. For example, when the Enemy collides with the left, right, top and bottom of the screen, it should change direction.
This is my current code:
import SpriteKit
import GameplayKit
struct Physics {
static let Enemy: UInt32 = 0x1 << 1
let BorderCategory : UInt32 = 0x1 << 2
let BottomCategory : UInt32 = 0x1 << 3
let BallCategory : UInt32 = 0x1 << 4
}
class GameScene: SKScene {
var Enemy = SKSpriteNode()
var gameStarted = Bool()
var gameState = "running"
var destX : CGFloat = 0.0
var destY : CGFloat = 0.0
var score = 0
override func didMove(to view: SKView) {
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
let screenSize: CGRect = UIScreen.main().bounds // get the screen size
let screenWidth = screenSize.width //get the width
let screenHeight = screenSize.height //get the height
Enemy = SKSpriteNode(imageNamed: "red2")
Enemy.size = CGSize(width: 60, height: 70)
Enemy.position = (CGPoint(x: self.frame.width / 6 - Enemy.frame.width, y: self.frame.height / 10))
Enemy.physicsBody = SKPhysicsBody(circleOfRadius: Enemy.frame.height / 2)
Enemy.physicsBody?.categoryBitMask = Physics.Enemy
//Enemy.physicsBody?.categoryBitMask = Physics.Ground | Physics.wall
//Enemy.physicsBody?.contactTestBitMask = Physics.Ground | Physics.wall
Enemy.physicsBody?.affectedByGravity = false
Enemy.physicsBody?.isDynamic = true
self.addChild(Enemy)
Enemy.physicsBody?.velocity = CGVector(dx: 50, dy: 50)
if (Enemy.position.x == screenWidth) {
Enemy.physicsBody?.velocity = CGVector(dx: -50, dy: 0) // change direction at edge DOESN'T WORK
}
if(Enemy.position.y == screenHeight){
Enemy.physicsBody?.velocity = CGVector(dx: 0, dy: -50) //change direction at edge DOESN'T WORK
}
}
OK, based on your code I've written a working bouncing sprite. It uses the physics-engine to achieve this, rather than manually changing the direction (which I guess is how you should go about doing this).
override func didMove(to view: SKView) {
let borderBody = SKPhysicsBody(edgeLoopFrom: frame)
borderBody.friction = 0
borderBody.categoryBitMask = Physics.wall
physicsBody = borderBody
let enemy = enemySprite(size: CGSize(width: 60, height: 70), position: CGPoint(x: frame.size.width/2, y: frame.size.height/2))
addChild(enemy)
let force = SKAction.applyForce(CGVector(dx: 300, dy: 300) , duration: 0.1)
enemy.run(force)
}
func enemySprite(size: CGSize, position: CGPoint) -> SKSpriteNode {
let enemy = SKSpriteNode(color: SKColor.red(), size: CGSize(width: 60, height: 80))
enemy.position = CGPoint(x: frame.width/2, y: frame.height/2)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.frame.height / 2) // A bit silly with circle, but you seem to have an image for this use
enemy.physicsBody?.categoryBitMask = Physics.enemy
enemy.physicsBody?.restitution = 1
enemy.physicsBody?.friction = 0
enemy.physicsBody?.collisionBitMask = Physics.wall
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.angularDamping = 0
enemy.physicsBody?.linearDamping = 0
return enemy
}
A question to consider is also how you set up the scene? Is the scene-size the same as the view-size? Otherwise the scene's physicsBody will not make the ball bounce where you expect it to...
It won't (in most cases) because of your conditions - using equal to operators instead of greater/lower than.
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
// Create enemy with physics body attached and add it to the scene
let enemy = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 20, height: 20))
enemy.name = "enemy"
enemy.position = CGPoint(x: size.width / 2, y: size.height / 2)
enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size)
enemy.physicsBody!.affectedByGravity = false
enemy.physicsBody!.linearDamping = 0
enemy.physicsBody!.mass = 0.1
enemy.physicsBody!.velocity = CGVector(dx: 150, dy: 0)
addChild(enemy)
}
override func didSimulatePhysics() {
let enemy = childNodeWithName("enemy")!
if (enemy.position.x > size.width && enemy.physicsBody!.velocity.dx > 0)
|| (enemy.position.x < 0 && enemy.physicsBody!.velocity.dx < 0) {
enemy.physicsBody!.velocity.dx *= -1
}
}
}
I am trying to delete an object (an orb node) when the character comes into contact but it just acts like a circle and the character falls off when it gets on top. I deleted the code for the collision detection because I have no idea if it is right.
Here is my code for GameScene.swift:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
override init(size:CGSize){
super.init(size:size)
let CollisionCategoryPlayer
: UInt32 = 0x1 << 1
let CollisionCategoryPowerUpOrbs
: UInt32 = 0x1 << 2
character.physicsBody?.categoryBitMask = CollisionCategoryPlayer
character.physicsBody?.contactTestBitMask = CollisionCategoryPowerUpOrbs
character.physicsBody?.collisionBitMask = 0
orbNode.physicsBody?.categoryBitMask = CollisionCategoryPowerUpOrbs
orbNode.physicsBody?.collisionBitMask = 0
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
//variables
let character = SKSpriteNode(imageNamed:"square_red.png")
let floor = SKSpriteNode(imageNamed: "platform.jpg")
let platform1 = SKSpriteNode(imageNamed: "platform2.png")
let platform2 = SKSpriteNode(imageNamed: "platform2.png")
let orbNode = SKSpriteNode(imageNamed: "PowerUp.png")
var characterSize:CGFloat = 0.2
var foodCount = 0
override func didMoveToView(view: SKView) {
//World Physics
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
self.physicsWorld.contactDelegate = self
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
//Character
character.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
character.setScale(characterSize)
character.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
character.physicsBody?.allowsRotation = false
self.addChild(character)
//floor
floor.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)*0.2)
floor.setScale(2.0)
floor.physicsBody = SKPhysicsBody(rectangleOfSize: floor.size)
floor.physicsBody?.dynamic = false
self.addChild(floor)
//platform one
platform1.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)*0.7)
platform1.setScale(0.4)
platform1.physicsBody = SKPhysicsBody(rectangleOfSize: platform1.size)
platform1.physicsBody?.dynamic = false
self.addChild(platform1)
//platform two
platform2.position = CGPoint(x: CGRectGetMidX(self.frame)*1.4, y: CGRectGetMidY(self.frame)*1)
platform2.setScale(0.4)
platform2.physicsBody = SKPhysicsBody(rectangleOfSize: platform2.size)
platform2.physicsBody?.dynamic = false
self.addChild(platform2)
//orbNode
orbNode.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
orbNode.setScale(0.2)
self.addChild(orbNode)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
character.removeActionForKey("moveAction")
character.removeActionForKey("shrink")
character.removeActionForKey("rotate")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch:AnyObject in touches {
let location = touch.locationInNode(self)
if location.x < CGRectGetMidX(self.frame) && location.y < CGRectGetMidY(self.frame)*0.7{
//shrinks with each movement
characterSize-=0.005
let moveAction = SKAction.repeatActionForever(SKAction.moveByX(-30, y: 0, duration: 0.1))
let shrink = SKAction.repeatActionForever(SKAction.scaleTo(characterSize, duration: 0.1))
character.runAction(moveAction, withKey: "moveAction")
character.runAction(shrink, withKey: "shrink")
} else if location.x > CGRectGetMidX(self.frame) && location.y < CGRectGetMidY(self.frame)*0.7{
//shrinks with each movement
characterSize-=0.005
let moveAction = SKAction.repeatActionForever(SKAction.moveByX(30, y: 0, duration: 0.1))
let shrink = SKAction.repeatActionForever(SKAction.scaleTo(characterSize, duration: 0.1))
character.runAction(moveAction, withKey: "moveAction")
character.runAction(shrink, withKey: "shrink")
} else if location.y > character.position.y + 15 {
//shrinks with each movement
characterSize-=0.005
character.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 50))
let shrink = SKAction.repeatActionForever(SKAction.scaleTo(characterSize, duration: 0.1))
character.runAction(shrink, withKey: "shrink")
}
}
func didBeginContact(contact: SKPhysicsContact){
}
func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
}
You should group your physics body, you code seems to be all over the place. Also the reason you get the error is because you are settings the physics body before setting the player and missing some for the Orbs.
Here is your same code rewritten and I think a bit more readable and understandable.
import SpriteKit
/// Physics category
struct PhysicsCategory {
static let player: UInt32 = 0x1 << 0
static let orb: UInt32 = 0x1 << 1
static let floor: UInt32 = 0x1 << 2
static let platform: UInt32 = 0x1 << 3
}
/// Image names
struct ImageName {
static let platform1 = "platform2.png"
static let platform2 = "platform2.png"
// can do this for other images as well, but because you create loads of platforms its nice to have a refernce like this
}
/// Always put your keys for removing actions etc into structs to avoid typos
struct Key {
static let moveAction = "moveAction"
static let shrink = "shrink"
static let rotate = "rotate"
}
/// GameScene
class GameScene: SKScene, SKPhysicsContactDelegate {
//variables
let character = SKSpriteNode(imageNamed:"square_red.png")
let floor = SKSpriteNode(imageNamed: "platform.jpg")
var platform1 = SKSpriteNode()
var platform2 = SKSpriteNode()
let orbNode = SKSpriteNode(imageNamed: "PowerUp.png")
var characterSize:CGFloat = 0.2
var foodCount = 0
//didMoveToView
override func didMoveToView(view: SKView) {
//World Physics
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
self.physicsWorld.contactDelegate = self
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
//Character
character.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
character.setScale(characterSize)
character.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
character.physicsBody?.allowsRotation = false
character.physicsBody?.dynamic = true // must be true for collision to fire
character.physicsBody?.affectedByGravity = false
character.physicsBody?.categoryBitMask = PhysicsCategory.player
character.physicsBody?.contactTestBitMask = PhysicsCategory.orb
character.physicsBody?.collisionBitMask = PhysicsCategory.floor | PhysicsCategory.platform
self.addChild(character)
//floor
floor.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)*0.2)
floor.setScale(2.0)
floor.physicsBody = SKPhysicsBody(rectangleOfSize: floor.size)
floor.physicsBody?.dynamic = false
floor.physicsBody?.affectedByGravity = false
floor.physicsBody?.categoryBitMask = PhysicsCategory.floor
///floor.physicsBody?.contactTestBitMask = not needed for now as character is set
///floor.physicsBody?.collisionBitMask = not needed for now as character is set
self.addChild(floor)
//platform one
platform1 = createPlatform(ImageName.platform1)
platform1.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)*0.7)
platform1.setScale(0.4)
self.addChild(platform1)
//platform two
platform2 = createPlatform(ImageName.platform2)
platform2.position = CGPoint(x: CGRectGetMidX(self.frame)*1.4, y: CGRectGetMidY(self.frame)*1)
platform2.setScale(0.4)
self.addChild(platform2)
//orbNode
orbNode.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
orbNode.setScale(0.2)
orbNode.physicsBody = SKPhysicsBody(rectangleOfSize: character.size)
orbNode.physicsBody?.allowsRotation = false
orbNode.physicsBody?.dynamic = false
orbNode.physicsBody?.affectedByGravity = false
orbNode.physicsBody?.categoryBitMask = PhysicsCategory.orb
//orbNode.physicsBody?.contactTestBitMask = // not needed for now because pla
orbNode.physicsBody?.collisionBitMask = PhysicsCategory.platform //
self.addChild(orbNode)
}
/// Create platform (this way you can crate multiple platforms and save yourself tones of code)
func createPlatform(imageNamed: String) -> SKSpriteNode {
let platform = SKSpriteNode(imageNamed: imageNamed)
platform.physicsBody = SKPhysicsBody(rectangleOfSize: platform.size)
platform.physicsBody?.dynamic = false
platform.physicsBody?.affectedByGravity = false
platform.physicsBody?.categoryBitMask = PhysicsCategory.platform
//platform.physicsBody?.contactTestBitMask = /// not needed unless you want didBeginContact
platform.physicsBody?.collisionBitMask = 0 /// Not needed if you tell character to collide with it
return platform
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
character.removeActionForKey(Key.moveAction)
character.removeActionForKey(Key.shrink)
character.removeActionForKey(Key.rotate)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if location.x < CGRectGetMidX(self.frame) && location.y < CGRectGetMidY(self.frame)*0.7{
//shrinks with each movement
characterSize-=0.005
let moveAction = SKAction.repeatActionForever(SKAction.moveByX(-30, y: 0, duration: 0.1))
let shrink = SKAction.repeatActionForever(SKAction.scaleTo(characterSize, duration: 0.1))
character.runAction(moveAction, withKey: Key.moveAction)
character.runAction(shrink, withKey: Key.shrink)
} else if location.x > CGRectGetMidX(self.frame) && location.y < CGRectGetMidY(self.frame)*0.7{
//shrinks with each movement
characterSize-=0.005
let moveAction = SKAction.repeatActionForever(SKAction.moveByX(30, y: 0, duration: 0.1))
let shrink = SKAction.repeatActionForever(SKAction.scaleTo(characterSize, duration: 0.1))
character.runAction(moveAction, withKey: Key.moveAction)
character.runAction(shrink, withKey: Key.shrink)
} else if location.y > character.position.y + 15 {
//shrinks with each movement
characterSize-=0.005
character.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 50))
let shrink = SKAction.repeatActionForever(SKAction.scaleTo(characterSize, duration: 0.1))
character.runAction(shrink, withKey: Key.shrink)
}
}
}
func didBeginContact(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
}
/// Player with Orb
if (firstBody.categoryBitMask == PhysicsCategory.player) && (secondBody.categoryBitMask == PhysicsCategory.orb) {
///Player hit orb, remove Orb
secondBody.node?.removeFromParent()
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
I am creating a game where random objects fall in random places and there is a ball with a fixed y-axis and keeps moving along the x-axis , and it has to move throw the objects , and i am trying to count the scores and the way i do it is i created a physics object and it is places in the same y-axis as the ball , and when the objects collide the score function should be called and increment the score by 1, but for some reason the app keeps crashing and it gives me this error.
2015-07-13 01:23:17.242 WalkRun[26792:3773366] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: <SKSpriteNode> name:'(null)' texture:[<SKTexture> 'shortwall' (60 x 60)] position:{30, 768} size:{60, 60} rotation:0.00'
*** First throw call stack:
(
0 CoreFoundation 0x00000001073c33f5 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001092eabb7 objc_exception_throw + 45
2 CoreFoundation 0x00000001073c332d +[NSException raise:format:] + 205
3 SpriteKit 0x0000000107ebfaf6 -[SKNode addChild:] + 111
4 WalkRun 0x00000001071c8c44 _TFC7WalkRun9PlayScene10leftObjectfS0_FT_T_ + 2868
5 WalkRun 0x00000001071c7ddc _TFC7WalkRun9PlayScene10randObjectfS0_FT_T_ + 236
6 WalkRun 0x00000001071c8102 _TToFC7WalkRun9PlayScene10randObjectfS0_FT_T_ + 34
7 Foundation 0x0000000107a39fd4 __NSFireTimer + 83
8 CoreFoundation 0x000000010732b4e4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
9 CoreFoundation 0x000000010732b0a5 __CFRunLoopDoTimer + 1045
10 CoreFoundation 0x00000001072ee3dd __CFRunLoopRun + 1901
11 CoreFoundation 0x00000001072eda06 CFRunLoopRunSpecific + 470
12 GraphicsServices 0x000000010e7569f0 GSEventRunModal + 161
13 UIKit 0x0000000108046550 UIApplicationMain + 1282
14 WalkRun 0x00000001071d54ce top_level_code + 78
15 WalkRun 0x00000001071d550a main + 42
16 libdyld.dylib 0x0000000109ad9145 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
and this is my code:
//
// PlayScene.swift
// WalkRun
//
// Created by naeim on 7/10/15.
// Copyright (c) 2015 naeim. All rights reserved.
//
import Foundation
import SpriteKit
class PlayScene: SKScene, SKPhysicsContactDelegate{
var ball = SKSpriteNode(imageNamed: "ball")
var wall = SKNode()
var wallRight = SKNode()
var wallMiddle = SKNode()
var ballSpeed = CGFloat()
var bigWall = SKSpriteNode(imageNamed: "shortwall")
var tallWall = SKSpriteNode(imageNamed: "tallwall")
var ballGroup:UInt32 = 1
var objectGroup:UInt32 = 2
var gapGroup:UInt32 = 3
var gameOver = 0
var movingObjects = SKNode()
var score = 0
var scoreLabel = SKLabelNode()
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
backgroundColor = UIColor(hex: 0x80d9ff)
self.physicsWorld.gravity = CGVectorMake(-9,0)
self.addChild(movingObjects)
//creating the ball
ball.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame) + self.ball.size.height * 2)
ball.physicsBody = SKPhysicsBody(circleOfRadius: self.ball.size.width / 2)
ball.zPosition = 10
//let the ball rotate forever
ballSpeed = 3
let rotateAction = SKAction.rotateByAngle(ballSpeed, duration: 1)
let repeatAction = SKAction.repeatActionForever(rotateAction)
ball.runAction(repeatAction)
ball.physicsBody?.categoryBitMask = ballGroup
ball.physicsBody?.collisionBitMask = objectGroup
ball.physicsBody?.contactTestBitMask = objectGroup
self.addChild(ball)
//creating the wall of the left
wall.position = CGPointMake(CGRectGetMinX(self.frame),CGRectGetMinY(self.frame))
wall.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(2, self.frame.size.height * 2.0))
wall.physicsBody?.dynamic = false
wall.physicsBody?.categoryBitMask = objectGroup
self.addChild(wall)
//creating the wall of the right
wallRight.position = CGPointMake(CGRectGetMaxX(self.frame), CGRectGetMinY(self.frame))
wallRight.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(2, self.frame.size.height * 2.0))
wallRight.physicsBody?.dynamic = false
wallRight.physicsBody?.categoryBitMask = objectGroup
self.addChild(wallRight)
//creating the middle wall that objects pass by
wallMiddle.position = CGPointMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame))
wallMiddle.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, 1))
wallMiddle.physicsBody?.dynamic = false
wallMiddle.physicsBody?.categoryBitMask = gapGroup
wallMiddle.physicsBody?.collisionBitMask = gapGroup
wallMiddle.physicsBody?.contactTestBitMask = objectGroup
self.addChild(wallMiddle)
//creating the label
scoreLabel.fontName = "Helvetica"
scoreLabel.fontSize = 60
scoreLabel.text = "0"
scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) + 70 )
self.addChild(scoreLabel)
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("randObject"), userInfo: nil, repeats: true)
var timerObjects = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("checkObjectPlace"), userInfo: nil, repeats: true)
}
func checkObjectPlace(){
}
//function to randomly choose which object
func randObject(){
if gameOver == 0{
var rand = arc4random_uniform(6)+1
switch(rand){
case 1:
leftObject()
case 2:
middleObject()
case 3:
rightObject()
case 4:
LeftAndMiddleObject()
case 5:
rightAndLeftObject()
case 6:
rightAndMiddleObject()
default:
println("error !! non a number other than 0, 1, 2 has been choosen .")
}
}
}
//function to create the left objects
func leftObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
bigWall.position = CGPointMake(CGRectGetMinX(self.frame) + bigWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
bigWall.runAction(moveAndRemoveObjects)
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(bigWall)
}
else
{
tallWall.position = CGPointMake(CGRectGetMinX(self.frame) + tallWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
}
}
//function to create the middle objects
func middleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
bigWall.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
bigWall.runAction(moveAndRemoveObjects)
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(bigWall)
}
else
{
var tallWall = SKSpriteNode(imageNamed: "tallwall")
tallWall.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
}
}
//function to create the right objects
func rightObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
bigWall.position = CGPointMake(CGRectGetMaxX(self.frame) - bigWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
bigWall.runAction(moveAndRemoveObjects)
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(bigWall)
}
else
{
var tallWall = SKSpriteNode(imageNamed: "tallwall")
tallWall.position = CGPointMake(CGRectGetMaxX(self.frame) - tallWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
if ball.position.x == tallWall.position.x{
scoreIncrement()
}
}
}
//function to create a right and left object
func rightAndLeftObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
var tallWall = SKSpriteNode(imageNamed: "tallwall")
tallWall.position = CGPointMake(CGRectGetMinX(self.frame) + tallWall.size.width / 2, CGRectGetMaxY(self.frame))
bigWall.position = CGPointMake(CGRectGetMaxX(self.frame) - bigWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
bigWall.runAction(moveAndRemoveObjects)
tallWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
movingObjects.addChild(bigWall)
bigWall.position = CGPointMake(CGRectGetMaxX(self.frame) - bigWall.size.width / 2, CGRectGetMaxY(self.frame))
}
else
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
var tallWall = SKSpriteNode(imageNamed: "tallwall")
bigWall.position = CGPointMake(CGRectGetMaxX(self.frame) - bigWall.size.width / 2, CGRectGetMaxY(self.frame))
tallWall.position = CGPointMake(CGRectGetMaxX(self.frame) - tallWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
tallWall.runAction(moveAndRemoveObjects)
bigWall.runAction(moveAndRemoveObjects)
bigWall.physicsBody?.categoryBitMask = objectGroup
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
movingObjects.addChild(bigWall)
}
}
func rightAndMiddleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
var tallWall = SKSpriteNode(imageNamed: "tallwall")
tallWall.position = CGPointMake(CGRectGetMaxX(self.frame) - tallWall.size.width / 2, CGRectGetMaxY(self.frame))
bigWall.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
bigWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
movingObjects.addChild(bigWall)
}
else
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
var tallWall = SKSpriteNode(imageNamed: "tallwall")
bigWall.position = CGPointMake(CGRectGetMidX(self.frame) , CGRectGetMaxY(self.frame))
tallWall.position = CGPointMake(CGRectGetMaxX(self.frame) - tallWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
bigWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
movingObjects.addChild(bigWall)
}
}
func LeftAndMiddleObject(){
var rand = arc4random_uniform(2) + 1
if rand == 1
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
var tallWall = SKSpriteNode(imageNamed: "tallwall")
tallWall.position = CGPointMake(CGRectGetMinX(self.frame) + tallWall.size.width / 2, CGRectGetMaxY(self.frame))
bigWall.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
bigWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
movingObjects.addChild(bigWall)
}
else
{
var bigWall = SKSpriteNode(imageNamed: "shortwall")
var tallWall = SKSpriteNode(imageNamed: "tallwall")
bigWall.position = CGPointMake(CGRectGetMidX(self.frame) , CGRectGetMaxY(self.frame))
tallWall.position = CGPointMake(CGRectGetMinX(self.frame) + tallWall.size.width / 2, CGRectGetMaxY(self.frame))
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
tallWall.runAction(moveAndRemoveObjects)
bigWall.runAction(moveAndRemoveObjects)
tallWall.physicsBody = SKPhysicsBody(rectangleOfSize: tallWall.size)
tallWall.physicsBody?.dynamic = false
bigWall.physicsBody = SKPhysicsBody(rectangleOfSize: bigWall.size)
bigWall.physicsBody?.dynamic = false
bigWall.physicsBody?.categoryBitMask = objectGroup
tallWall.physicsBody?.categoryBitMask = objectGroup
movingObjects.addChild(tallWall)
movingObjects.addChild(bigWall)
}
}
func scoreIncrement(){
score = score + 1
scoreLabel.text = "\(score)"
}
func didBeginContact(contact: SKPhysicsContact) {
println("contact")
if contact.bodyA.categoryBitMask == gapGroup || contact.bodyB.categoryBitMask == gapGroup {
println("gap contact")
} else {
gameOver = 1
movingObjects.speed = 0
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
if gameOver == 0 {
ball.physicsBody?.velocity = CGVectorMake(0, 0)
ball.physicsBody?.applyImpulse(CGVectorMake(70,0))
}
}
override func update(currentTime: NSTimeInterval) {
}
}
The error suggests you are trying to add a child to a node while it already has a parent. The stack trace points you to leftObject method. In here you have an SKAction setup to move over a time interval and then remove from parent.
var moveObjects = SKAction.moveByX(0, y: -self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
var removeObjects = SKAction.removeFromParent()
var moveAndRemoveObjects = SKAction.sequence([moveObjects,removeObjects])
bigWall.runAction(moveAndRemoveObjects)
This adds the action to the node and it runs but your code path will continue down to
movingObjects.addChild(bigWall)
almost instantly. Looks like a race condition where you think the code will wait until the action has finished running before continuing on.
You have a instance property called bigWall and in most of your methods you create a local object called bigWall and add it to the scene. Since it is a new object each time the methods are call that works fine. However in the method leftObject you do not have a local object bigWall so it is referencing the instance property. The first time you call leftObject it will work fine adding your instance property bigWall to the scene. The second time you call the method it will try to add the bigWall instance property to the scene when it is already in the scene and that is where you will get the error.
I am trying to reload the GameScene from my EndScene. The idea is the player will hit the "Restart" Button, reloading the GameScene allowing them to try again. Here is my code for the two scenes. Does anyone have a solution? Please give me the exact code I would need, this is my first time using Xcode, and I am new to using code. It would help me understand better how to deal with a problem like this in the future.
Here is the code for the GameScene:
var Highscore = Int()
var Score = Int()
var Player = SKSpriteNode (imageNamed: "good.png")
var ScoreLbl = UILabel()
override func didMoveToView(view: SKView) {
var HighscoreDefault = NSUserDefaults.standardUserDefaults()
if (HighscoreDefault.valueForKey("Highscore") != nil){
Highscore = HighscoreDefault.valueForKey("Highscore") as! NSInteger
}
else {
Highscore = 0
}
physicsWorld.contactDelegate = self
self.scene?.backgroundColor = UIColor.blackColor()
self.addChild(SKEmitterNode(fileNamed: "Rain"))
Player.position = CGPointMake(self.size.width / 2, self.size.width / 6)
Player.physicsBody = SKPhysicsBody(rectangleOfSize: Player.size)
Player.physicsBody?.affectedByGravity = false
Player.physicsBody?.categoryBitMask = PhysicsCategory.Player
Player.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
Player.physicsBody?.dynamic = false
var Timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("SpawnBullets"), userInfo: nil, repeats: true)
var EnemyTimer = NSTimer.scheduledTimerWithTimeInterval(0.8, target: self, selector: Selector("SpawnEnemies"), userInfo: nil, repeats: true)
self.addChild(Player)
ScoreLbl.text = "\(Score)"
ScoreLbl = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
ScoreLbl.backgroundColor = UIColor(red: 0.1, green: 0.1, blue: 1.0, alpha: 0.3)
ScoreLbl.textColor = UIColor.whiteColor()
self.view?.addSubview(ScoreLbl)
}
func didBeginContact(contact: SKPhysicsContact) {
var firstBody : SKPhysicsBody = contact.bodyA
var secondBody : SKPhysicsBody = contact.bodyB
if ((firstBody.categoryBitMask == PhysicsCategory.Enemy) && (secondBody.categoryBitMask == PhysicsCategory.Bullet) || (firstBody.categoryBitMask == PhysicsCategory.Bullet) && (secondBody.categoryBitMask == PhysicsCategory.Enemy)){
CollisionWithBullet(firstBody.node as! SKSpriteNode, Bullet: secondBody.node as! SKSpriteNode)
}
else if ((firstBody.categoryBitMask == PhysicsCategory.Enemy) && (secondBody.categoryBitMask == PhysicsCategory.Player) || (firstBody.categoryBitMask == PhysicsCategory.Player) && (secondBody.categoryBitMask == PhysicsCategory.Enemy)){
CollisionWithPerson(firstBody.node as! SKSpriteNode, Person: secondBody.node as! SKSpriteNode)
}
}
func CollisionWithBullet(Enemy: SKSpriteNode, Bullet: SKSpriteNode){
Enemy.removeFromParent()
Bullet.removeFromParent()
Score++
ScoreLbl.text = "\(Score)"
}
func CollisionWithPerson(Enemy: SKSpriteNode,Person: SKSpriteNode){
var ScoreDefault = NSUserDefaults.standardUserDefaults()
ScoreDefault.setValue(Score, forKey: "Score")
ScoreDefault.synchronize()
if (Score > Highscore){
var HighscoreDefault = NSUserDefaults.standardUserDefaults()
HighscoreDefault.setValue(Score, forKey: "Highscore")
}
Enemy.removeFromParent()
Person.removeFromParent()
self.view?.presentScene(EndScene())
ScoreLbl.removeFromSuperview()
self.removeAllActions()
}
func SpawnBullets() {
var Bullet = SKSpriteNode (imageNamed: "bullet.png")
Bullet.zPosition = -5
Bullet.position = CGPointMake(Player.position.x, Player.position.y)
let action = SKAction.moveToY(self.size.height + 30, duration: 1.0)
let actionDone = SKAction.removeFromParent()
Bullet.runAction(SKAction.sequence([action, actionDone]))
Bullet.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet.size)
Bullet.physicsBody?.categoryBitMask = PhysicsCategory.Bullet
Bullet.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
Bullet.physicsBody?.affectedByGravity = false
Bullet.physicsBody?.dynamic = false
self.addChild(Bullet)
}
func SpawnEnemies () {
var Enemy = SKSpriteNode (imageNamed: "bad.png")
var MinValue = self.size.width / 8
var MaxValue = self.size.width - 15
var Spawnpoint = UInt32(MaxValue - MinValue)
Enemy.position = CGPointMake(CGFloat(arc4random_uniform(Spawnpoint)), self.size.height)
Enemy.physicsBody = SKPhysicsBody(rectangleOfSize:Enemy.size)
Enemy.physicsBody?.categoryBitMask = PhysicsCategory.Enemy
Enemy.physicsBody?.contactTestBitMask = PhysicsCategory.Bullet
Enemy.physicsBody?.affectedByGravity = false
Enemy.physicsBody?.dynamic = true
let action = SKAction.moveToY(-70, duration: 2.5)
let ActionDone = SKAction.removeFromParent()
Enemy.runAction(SKAction.sequence([action, ActionDone]))
Enemy.runAction(SKAction.repeatActionForever(action))
self.addChild(Enemy)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
Player.position.x = location.x
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
Player.position.x = location.x
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
And here is the code for the EndScene:
var RestartBtn : UIButton!
var Highscore : Int!
var ScoreLbl : UILabel!
var HighscoreLbl : UILabel!
override func didMoveToView(view: SKView) {
scene?.backgroundColor = UIColor.blackColor()
RestartBtn = UIButton(frame: CGRect(x: 0, y: 0, width: view.frame.size.width / 3, height: 30))
RestartBtn.center = CGPoint(x: view.frame.size.width / 2, y: view.frame.size.width / 7)
RestartBtn.setTitle("Restart", forState: UIControlState.Normal)
RestartBtn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
RestartBtn.addTarget(self, action: Selector("Restart"), forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(RestartBtn)
var ScoreDefault = NSUserDefaults.standardUserDefaults()
var Score = ScoreDefault.valueForKey("Score") as! NSInteger
var HighscoreDefault = NSUserDefaults.standardUserDefaults()
Highscore = HighscoreDefault.valueForKey("Highscore") as! NSInteger
ScoreLbl = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.size.width / 3, height: 30))
ScoreLbl.center = CGPoint(x: view.frame.size.width / 2, y: view.frame.size.width / 4)
ScoreLbl.text = "\(Score)"
self.view?.addSubview(ScoreLbl)
ScoreLbl.textColor = UIColor.whiteColor()
HighscoreLbl = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.size.width / 3, height: 30))
HighscoreLbl.center = CGPoint(x: view.frame.size.width / 2, y: view.frame.size.width / 2)
HighscoreLbl.text = "\(Highscore)"
self.view?.addSubview(HighscoreLbl)
HighscoreLbl.textColor = UIColor.whiteColor()
}
func Restart(){
self.view?.presentScene(GameScene(), transition: SKTransition.crossFadeWithDuration(0.3))
RestartBtn.removeFromSuperview()
ScoreLbl.removeFromSuperview()
HighscoreLbl.removeFromSuperview()
}
}
Thank you so much to anyone who can help! It means a lot! :)