How to reference SKSpriteNode without full instantiation in Swift3? - swift

Previously, I had been using var progressBar = ProgressBar?() right above init within one of my SKSpriteNode subclasses, to allow various methods to reference progressBar throughout the class like so:
class Ship:SKSpriteNode{
var progressBar = ProgressBar?()
static var shipState = "norm"
var powerupOn = false
var moveSpeed:CGFloat
var lives:Int
var canShoot = false
let las = SKSpriteNode(texture:laserTexture)
var gun:GenericGun = GenericGun()
let scaleFactor:CGFloat = 0.07
init(startPosition startPos:CGPoint, controllerVector:CGVector){
self.lives = 3
self.moveSpeed = 160
super.init(texture: ship0, color: UIColor.clear, size: ship0.size())
//Set position
self.position = startPos
attachGun(gun)
self.setScale(scaleFactor)
self.physicsBody = SKPhysicsBody(texture: self.texture!, size: self.size)
self.physicsBody?.isDynamic = true
self.physicsBody?.categoryBitMask = PhysicsCategory.Ship
self.physicsBody?.collisionBitMask = 0
self.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
self.physicsBody?.allowsRotation = false
self.physicsBody?.angularVelocity = CGFloat(0)
self.physicsBody?.usesPreciseCollisionDetection = true
self.physicsBody?.affectedByGravity = false //TBD
self.physicsBody?.velocity.dx = controllerVector.dx * moveSpeed
self.physicsBody?.velocity.dy = controllerVector.dy * moveSpeed
self.name = "ship"
//Call animations
self.animateShip1()
}
func updateVelocity(_ v:CGVector){
self.physicsBody?.velocity.dx = v.dx * moveSpeed
self.physicsBody?.velocity.dy = v.dy * moveSpeed
}
func attachGun(_ gun:GenericGun){
gun.position = CGPoint(x: 0, y: 0)
self.addChild(gun)
}
func animateShip1() {
let animate = SKAction.animate(with: shipFrames, timePerFrame: 0.1)
let forever = SKAction.repeatForever(animate)
self.run(forever)
}
func updateShipProperties(shipVelocity v:CGVector,laserStartPos laserStart:CGPoint){
updateVelocity(v)
progressBar?.position = CGPoint(x: self.position.x - (self.size.width/2 + self.size.width/7.0), y: self.position.y)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In Swift3 I'm getting an error that I need to provide full arguments, is there a way to achieve this same functionality in Swift3?

Related

How to determine the most accurate collision of two circles?

I am creating a new game application in code!
I created some bubbles using spriteKit. I used the texture for this.
They randomly fly around the playing field. When they intersect with each other, they must collide and change their path.
The first problem: of course, the image of a bubble in the shape of a circle. But in a collision, the bubbles touch each other’s faces not as a circle, but as a square
The second problem: they cling to each other, I think this is the problem of the first problem!
If you known how to fix this is problem please help my, because I can't find solve that problem.
I think this problem can be solved with a Mask, but I don’t know how. Thanks a lot)
this is my bubbleClass:
class SKButton: SKSpriteNode{
private var buttonTexture:SKTexture
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
self.buttonTexture = texture!
super.init(texture:buttonTexture , color: .black, size: buttonTexture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}}
This is ViewController:
class GameViewController: UIViewController {
#IBOutlet weak var mySKView: SKView!
override func viewDidLoad() {
super.viewDidLoad()
if let view = mySKView {
let scene = GameScene(size: CGSize(width: 2048, height: 1536))
scene.scaleMode = .aspectFill
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}}
this is GameScene:
class GameScene: SKScene {
private var arrayBubbles = [SKButton?]()
private var arrayVelosity = [CGPoint]()
private var bubbleMovePointsPerSecX = [CGFloat]()
private var bubbleMovePointsPerSecY = [CGFloat]()
private var currentVelosity:CGPoint!
private var lastUpdateTime: TimeInterval = 0
private var dt: TimeInterval = 0
private let min = 0
private let max = 3
private let bubbleTexture = SKTexture(imageNamed: "bubble")
private let playableRect: CGRect!
lazy var background: SKSpriteNode = {
let back = SKSpriteNode(imageNamed: "back")
back.position = CGPoint(x: self.size.width/2, y: size.height/2)
back.anchorPoint = CGPoint(x: 0.5, y: 0.5)
back.zPosition = -1
return back
}()
lazy var bubble1: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/2, y: size.height - (size.height/3))
button.setScale(0)
button.zPosition = 1
button.name = "bubble1"
return button
}()
lazy var bubble2: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/3, y: size.height/2)
button.setScale(0)
button.zPosition = 1
button.name = "bubble2"
return button
}()
lazy var bubble3: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width - (size.width/3), y: size.height/2)
button.setScale(0)
button.zPosition = 1
button.name = "bubble3"
return button
}()
override init(size: CGSize) {
let playableHeight = size.width/(16.0/9.0)
let playableMargin = (size.height - playableHeight)/2
playableRect = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight)
super.init(size:size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(to view: SKView) {
addChilds()
appendBubblesToArray()
setRandomBubbleSpead()
appendVelosityToArray()
showApearBubble()
}
private func addChilds(){
addChild(background)
addChild(bubble1)
addChild(bubble2)
addChild(bubble3)
}
private func appendBubblesToArray(){
arrayBubbles.append(bubble1)
arrayBubbles.append(bubble2)
arrayBubbles.append(bubble3)
}
private func setRandomBubbleSpead(){
for _ in min..<arrayBubbles.count{
let randomSpeadX = CGFloat.random(in: -200..<200)/CGFloat(Double.pi)
bubbleMovePointsPerSecX.append(randomSpeadX)
let randomSpeadY = CGFloat.random(in: -200..<200)/CGFloat(Double.pi)
bubbleMovePointsPerSecY.append(randomSpeadY)
}
}
private func appendVelosityToArray(){
for index in min..<arrayBubbles.count{
currentVelosity = CGPoint(x: bubbleMovePointsPerSecX[index], y: bubbleMovePointsPerSecY[index])
arrayVelosity.append(currentVelosity)
}
}
private func showApearBubble(){
let apearBubble = SKAction.scale(to: 1, duration: 0.5)
let action = [apearBubble]
bubble1.run(SKAction.sequence(action))
bubble2.run(SKAction.sequence(action))
bubble3.run(SKAction.sequence(action))
}
override func didEvaluateActions() {
checkColisions()
}
private func checkColisions(){
enumerateChildNodes(withName: "//*") { [weak self] node, _ in
guard let self = self else { return }
switch node.name{
case "bubble1":
self.checkBubbleIntersection(self.bubble1)
break
case "bubble2":
self.checkBubbleIntersection(self.bubble2)
break
case "bubble3":
self.checkBubbleIntersection(self.bubble3)
break
default:
return
}
}
}
private func hitBubble(_ index: Int){
arrayVelosity[index].y = -arrayVelosity[index].y
arrayVelosity[index].x = -arrayVelosity[index].x
}
private func checkBubbleIntersection(_ firstBubble: SKButton){
for bubble in arrayBubbles {
if firstBubble.name != bubble!.name{
if firstBubble.frame.insetBy(dx: 80,dy: 80).intersects(bubble!.frame){
guard let indexBubble = self.arrayBubbles.firstIndex(of: bubble) else { return }
self.hitBubble(indexBubble)
}
}
}
}
override func update(_ currentTime: TimeInterval) {
if lastUpdateTime > 0{
dt = currentTime - lastUpdateTime
} else{
dt = 0
}
lastUpdateTime = currentTime
for bubble in arrayBubbles{
if bubble != nil {
guard let index = arrayBubbles.firstIndex(of: bubble) else { return }
moveSprite(sprite: arrayBubbles[index]!, velosity: arrayVelosity[index])
boundsCheckBubbles(sprite: arrayBubbles[index]!, index: index)
}
}
}
private func moveSprite(sprite: SKButton, velosity: CGPoint){
let amountToMove = CGPoint(x: velosity.x * CGFloat(dt), y: velosity.y * CGFloat(dt))
sprite.position = CGPoint(x: sprite.position.x + amountToMove.x,
y: sprite.position.y + amountToMove.y)
}
private func boundsCheckBubbles(sprite: SKButton, index: Int){
let bottomLeft = CGPoint(x: 80, y: playableRect.minY)
let topRight = CGPoint(x: size.width-80, y: playableRect.maxY)
if sprite.position.x <= bottomLeft.x{
sprite.position.x = bottomLeft.x
arrayVelosity[index].x = -arrayVelosity[index].x
}
if sprite.position.x >= topRight.x{
sprite.position.x = topRight.x
arrayVelosity[index].x = -arrayVelosity[index].x
}
if sprite.position.y <= bottomLeft.y{
sprite.position.y = bottomLeft.y
arrayVelosity[index].y = -arrayVelosity[index].y
}
if sprite.position.y >= topRight.y{
sprite.position.y = topRight.y
arrayVelosity[index].y = -arrayVelosity[index].y
}
}}
I myself was able to solve this problem.
But I found another way to solve this problem. I use a bitmask and collision!
class GameScene: SKScene, SKPhysicsContactDelegate {
private var arrayBubbles = [SKButton?]()
private var bubbleTexture = SKTexture(image: #imageLiteral(resourceName: "Oval"))
private let playableRect: CGRect!
let bubble1Category:UInt32 = 0x1 << 0;
let bubble2Category:UInt32 = 0x1 << 1;
let bubble3Category:UInt32 = 0x1 << 2;
let borderCategory:UInt32 = 0x1 << 10
var borderBody: SKPhysicsBody!
lazy var bubble1: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/2, y: size.height - (size.height/3))
button.zPosition = 1
button.name = "bubble1"
button.physicsBody = SKPhysicsBody(circleOfRadius: bubbleTexture.size().width/2)
button.physicsBody?.categoryBitMask = bubble1Category
button.physicsBody?.contactTestBitMask = bubble2Category | bubble3Category | borderCategory
button.physicsBody!.collisionBitMask = bubble2Category | bubble3Category | borderCategory
button.physicsBody?.affectedByGravity = false
button.physicsBody?.linearDamping = 0.0
button.physicsBody?.restitution = 0.0
return button
}()
lazy var bubble2: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear, size: bubbleTexture.size())
button.position = CGPoint(x: size.width/3, y: size.height/2)
button.zPosition = 1
button.name = "bubble2"
button.physicsBody = SKPhysicsBody(circleOfRadius: bubbleTexture.size().width/2)
button.physicsBody?.categoryBitMask = bubble2Category
button.physicsBody?.contactTestBitMask = bubble1Category | bubble3Category | borderCategory
button.physicsBody!.collisionBitMask = bubble1Category | bubble3Category | borderCategory
button.physicsBody?.affectedByGravity = false
button.physicsBody?.linearDamping = 0.0
button.physicsBody?.restitution = 0.0
return button
}()
lazy var bubble3: SKButton = {
var button = SKButton(texture: bubbleTexture, color: .clear , size: bubbleTexture.size())
button.position = CGPoint(x: size.width - (size.width/3), y: size.height/2)
button.zPosition = 1
button.name = "bubble3"
button.physicsBody = SKPhysicsBody(circleOfRadius: bubbleTexture.size().width/2)
button.physicsBody?.categoryBitMask = bubble3Category
button.physicsBody?.contactTestBitMask = bubble1Category | bubble2Category | borderCategory
button.physicsBody!.collisionBitMask = bubble1Category | bubble2Category | borderCategory
button.physicsBody?.affectedByGravity = false
button.physicsBody?.linearDamping = 0.0
button.physicsBody?.restitution = 0.0
return button
}()
override init(size: CGSize) {
let playableHeight = size.width/(16.0/9.0)
let playableMargin = (size.height - playableHeight)/2
playableRect = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight)
super.init(size:size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
// Set border property
setBorderBody()
addChilds()
appendBubblesToArray()
showApearBubble()
setFirstBubbleVelosity()
}
func setFirstBubbleVelosity(){
for bubble in arrayBubbles{
bubble!.physicsBody?.velocity = calculateRandomBubbleVelosity()
}
}
func setBorderBody(){
borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.categoryBitMask = borderCategory
borderBody.friction = 0.0
self.physicsBody = borderBody
}
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case bubble1Category | bubble2Category:
bubble1.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble1Category | bubble3Category:
bubble1.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble1Category | borderCategory:
bubble1.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble2Category | bubble1Category:
bubble2.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble2Category | bubble3Category:
bubble2.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble2Category | borderCategory:
bubble2.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble3Category | bubble1Category:
bubble3.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble3Category | bubble2Category:
bubble3.physicsBody?.velocity = calculateRandomBubbleVelosity()
case bubble3Category | borderCategory:
bubble3.physicsBody?.velocity = calculateRandomBubbleVelosity()
default: print("Unknown contact detected")
}
}
private func addChilds(){
addChild(bubble1)
addChild(bubble2)
addChild(bubble3)
}
private func appendBubblesToArray(){
arrayBubbles.append(bubble1)
arrayBubbles.append(bubble2)
arrayBubbles.append(bubble3)
}
private func calculateRandomBubbleVelosity() -> CGVector{
let randomVelosityByX = CGFloat.random(in: -200..<200)
let randomVelosityByY = CGFloat.random(in: -200..<200)
let randomVelosity = CGVector(dx: randomVelosityByX * cos(100.0), dy: randomVelosityByY * sin(100.0))
return randomVelosity
}
private func showApearBubble(){
bubble1.setScale(1.5)
bubble2.setScale(1.5)
bubble3.setScale(1.5)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
enumerateChildNodes(withName: "bubble1") { node, _ in
let bubble = node as! SKButton
if bubble.contains(touch.location(in: self)){
//Some your logic
}
}
}
}}

SKSpriteNode will not appear when its physics body is dynamic

The SKSprite will only appear on the scene if .isDynamic = false otherwise it will not show up. Although it does not show up, it seems to be contacting with the other nodes in the scene.
I have tried playing around with the x and y values of the "lemming" node but this still results in it not appearing.
The Question:
What do I need to add or remove in order to make the "lemming" node be affected
by gravity, appear in the scene, and contact correctly with the "cliff" node?
In the GameScene.swift file:
//MARK: - Nodes
var background = SKSpriteNode()
var cliff = SKTexture()
var lemming = SKSpriteNode()
//MARK: -Catergories
let lemmingCatergory : UInt32 = 0x1 << 1
let cliffCatergory : UInt32 = 0x1 << 2
//MARK: - Start of game functions
override func didMove(to view: SKView) {
backgroundColor = UIColor.green
scaleMode = .aspectFit
size = UIScreen.main.bounds.size
physicsWorld.gravity = CGVector(dx: 0, dy: -9.81)
physicsWorld.contactDelegate = self
addBackground()
addCliff()
addLemming()
view.showsPhysics = true
}
//MARK: -Node customization
private func addBackground() {
background = SKSpriteNode(imageNamed: "background")
background.name = "Background"
background.position = CGPoint(x: frame.midX, y: frame.midY)
background.size = UIScreen.main.bounds.size
background.physicsBody = SKPhysicsBody(rectangleOf: background.frame.size)
background.physicsBody?.isDynamic = false
background.physicsBody?.affectedByGravity = false
addChild(background)
}
private func addCliff() {
var cliffNode : SKSpriteNode!
cliff = SKTexture(imageNamed: "cliff")
cliffNode = SKSpriteNode(texture: cliff)
cliffNode.name = "Cliff"
cliffNode.position = CGPoint(x: frame.midX, y: frame.midY)
cliffNode.size = UIScreen.main.bounds.size
cliffNode.physicsBody = SKPhysicsBody(texture: cliff, size: cliff.size())
cliffNode.physicsBody!.categoryBitMask = cliffCatergory
cliffNode.physicsBody!.collisionBitMask = lemmingCatergory
cliffNode.physicsBody!.contactTestBitMask = lemmingCatergory
cliffNode.physicsBody?.isDynamic = true
cliffNode.physicsBody?.affectedByGravity = false
cliffNode.physicsBody?.pinned = true
cliffNode.physicsBody?.allowsRotation = false
addChild(cliffNode)
}
private func addLemming() {
lemming = SKSpriteNode(imageNamed: "lemming")
lemming.size = CGSize(width: lemming.size.width / 15, height: lemming.size.height / 15)
lemming.position = CGPoint(x: 0.5, y: 0)
lemming.physicsBody = SKPhysicsBody(rectangleOf: lemming.frame.size)
lemming.physicsBody!.categoryBitMask = lemmingCatergory
lemming.physicsBody!.collisionBitMask = cliffCatergory
lemming.physicsBody!.contactTestBitMask = cliffCatergory
lemming.physicsBody?.isDynamic = true
lemming.physicsBody?.affectedByGravity = true
addChild(lemming)
}
func didBegin(_ contact: SKPhysicsContact) {
print("Body A: \(String(describing: contact.bodyA.node!.name))")
print("Body B: \(String(describing: contact.bodyB.node!.name))")
}

didBeginContact not working in Swift 2 + SpriteKit

I'm working on a game, and I'm using spritekit and Swift.
This is my code:
import SpriteKit
struct collision {
static let arrow:UInt32 = 0x1 << 1
static let runner:UInt32 = 0x1 << 2
static let target:UInt32 = 0x1 << 3
static let targetCenter:UInt32 = 0x1 << 4
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var person = SKSpriteNode()
var box = SKSpriteNode()
var screenSize:CGSize!
var gameScreenSize:CGSize!
var gameStarted:Bool = false
var moveAndRemove = SKAction()
var boxVelocity:NSTimeInterval = 5.5
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, -1.0)
self.physicsWorld.contactDelegate = self
screenSize = self.frame.size
gameScreenSize = view.frame.size
createPerson()
}
func createPerson() -> Void {
person.texture = SKTexture(imageNamed:"person")
person.setScale(1.0)
person.size = CGSize(width: 80, height: 80)
person.position = CGPoint(x: screenSize.width / 2, y: 150)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.dynamic = false
self.addChild(person)
}
func createTarget() -> Void {
box = SKSpriteNode()
box.size = CGSize(width: 70, height: 100)
box.setScale(1.0)
box.position = CGPoint(x: (screenSize.width / 3) * 2, y: screenSize.height + box.size.height)
box.texture = SKTexture(imageNamed: "box")
box.physicsBody? = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
box.physicsBody?.affectedByGravity = false
box.physicsBody?.dynamic = true
box.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(box)
let distance = CGFloat(self.frame.height - box.frame.height)
let moveTargets = SKAction.moveToY(-distance, duration: boxVelocity)
let removeTargets = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([moveTargets,removeTargets])
box.runAction(moveAndRemove)
}
func createBall() ->Void {
let ball = SKSpriteNode()
ball.size = CGSize(width: 20, height: 22)
ball.zPosition = 5
let moveToXY = CGPoint(x: self.size.width, y: self.size.height)
ball.texture = SKTexture(imageNamed: "ball")
ball.position = CGPointMake(person.position.x + ball.size.width, person.position.y + ball.size.height)
ball.physicsBody? = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.dynamic = true
ball.physicsBody?.usesPreciseCollisionDetection = true
let action = SKAction.moveTo(moveToXY, duration: 1.5)
let delay = SKAction.waitForDuration(1.5)
ball.runAction(SKAction.sequence([action,delay]))
self.addChild(ball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if gameStarted == false {
gameStarted = true
let spawn = SKAction.runBlock { () in
self.createTarget()
}
let delay = SKAction.waitForDuration(1.5)
let spawnDelay = SKAction.sequence([spawn, delay])
let spanDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spanDelayForever)
} else {
createBall()
boxVelocity -= 0.1
}
}
func didBeginContact(contact: SKPhysicsContact) {
print("Detect")
}
func didEndContact(contact: SKPhysicsContact) {
print("end detect")
}
override func update(currentTime: CFTimeInterval) {
}
}
But when I run the game, the collision between objects does not. I'm trying to solve a while, but found nothing. Can someone help me?
Project files.
Try with these to modifications:
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
and
ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.contactTestBitMask = collision.target
Note the absence of "?" while you init the physicsBody and the new contactTestBitMask

spritekit collision detection inconsistency (failing to get position of both nodes)

Within didBeginContact function I'm successfully capturing the collision between a horizontal grid line and a circle.
At the collision I can successfully get the position of the circle(head).
if let headBody = contact.bodyB.node as? SnakeBodyUnit {
print("head position ", headBody.position)
}
This successfully prints out:
head position (87.536979675293, 267.116882324219)
Now if I try to examine the horizontal line:
if let tripRow = contact.bodyA.node as? HeadTripper {
print("row position ", tripRow.position)
}
I get the following error:
po tripRow
error: :1:1: error: use of unresolved identifier 'tripRow'
tripRow
Here is the condition where this detection fires:
if (contact.bodyA.categoryBitMask == ColliderType.TripRow && contact.bodyB.categoryBitMask == ColliderType.Head) {
//contact successfully fires
}
The HeadTripper class and the SnakeBodyUnit class are both SKNode classes.
Here is the SnakeBodyUnit class:
import SpriteKit
class SnakeBodyUnit : SKNode {
var bodyDir: Direction?
var id = -1
var staticIDref = -1
var partX:CGFloat = -1
var partY:CGFloat = -1
var bodyT = -1
var staticTurn: TurnCrumb?
var turnRequested = 0
var requestedTurn: Direction?
var unitHolder: SKSpriteNode?
init(size: CGSize, bodyType: Int) {
super.init()
bodyT = bodyType
let reducedSize = size.width
unitHolder = SKSpriteNode()
unitHolder!.size = CGSize(width: (size.width), height: (size.height))
if (bodyT == 0) {
//head
unitHolder!.color = UIColor.clearColor()
//unitHolder!.position = CGPoint(x:xPos, y:yPos);
let shape = SKShapeNode(circleOfRadius: reducedSize/2)
shape.fillColor = UIColor.whiteColor()
shape.strokeColor = UIColor.whiteColor()
shape.position = CGPoint(x: reducedSize/2, y: reducedSize/2)
unitHolder!.addChild(shape)
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize, reducedSize), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Head
self.physicsBody?.dynamic = false
self.physicsBody?.contactTestBitMask = ColliderType.TripRow | ColliderType.TripColumn | ColliderType.Food | ColliderType.Body | ColliderType.WallLeft | ColliderType.WallRight | ColliderType.WallBottom | ColliderType.WallTop | ColliderType.Canvas
} else if (bodyT == 1) {
//body part
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize, reducedSize), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Body
self.physicsBody!.collisionBitMask = 0
self.physicsBody?.dynamic = true
self.physicsBody?.contactTestBitMask = ColliderType.WallLeft | ColliderType.WallRight | ColliderType.WallBottom | ColliderType.WallTop | ColliderType.BodyStatic
} else if (bodyT == 2) {
//static corner
self.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(reducedSize/2, reducedSize/2), center: CGPointMake(reducedSize/2, reducedSize/2))
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.BodyStatic
self.physicsBody!.collisionBitMask = 0
self.physicsBody?.dynamic = true
}
unitHolder!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(unitHolder!)
}
func updateColor(color: UIColor) {
unitHolder!.color = color
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//fatalError("init coder not implemented")
}
}
Here is the HeadTripper class:
import SpriteKit
class HeadTripper : SKNode {
var id = -1
var rowTripLine: SKSpriteNode?
var colTripLine: SKSpriteNode?
init(size: CGSize, rowOrCol: Int) {
//init(size: CGSize, xPos: Double, yPos: Double, rowOrCol: Int) {
super.init()
//row 0
//col 1
if (rowOrCol == 0) {
//row
rowTripLine = SKSpriteNode()
rowTripLine!.color = UIColor.redColor()
//rowTripLine!.position = CGPoint(x: 0, y: yPos);
rowTripLine!.size = CGSize(width: Int(size.width), height: 1)
//borderLeft!.physicsBody = SKPhysicsBody(rectangleOfSize: size, center: CGPointMake(reducedSize/2, reducedSize/2))
rowTripLine!.physicsBody = SKPhysicsBody(rectangleOfSize: rowTripLine!.size, center: CGPointMake(rowTripLine!.size.width/2, rowTripLine!.size.height/2))
rowTripLine!.physicsBody?.affectedByGravity = false
rowTripLine!.physicsBody?.categoryBitMask = ColliderType.TripRow
rowTripLine!.physicsBody!.collisionBitMask = 0
rowTripLine!.physicsBody?.dynamic = true
rowTripLine!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(rowTripLine!)
} else {
//col
colTripLine = SKSpriteNode()
colTripLine!.color = UIColor.redColor()
//colTripLine!.position = CGPoint(x: xPos, y: 0);
colTripLine!.size = CGSize(width: 1, height: Int(size.height))
//borderLeft!.physicsBody = SKPhysicsBody(rectangleOfSize: size, center: CGPointMake(reducedSize/2, reducedSize/2))
colTripLine!.physicsBody = SKPhysicsBody(rectangleOfSize: colTripLine!.size, center: CGPointMake(colTripLine!.size.width/2, colTripLine!.size.height/2))
colTripLine!.physicsBody?.affectedByGravity = false
colTripLine!.physicsBody?.categoryBitMask = ColliderType.TripColumn
colTripLine!.physicsBody!.collisionBitMask = 0
colTripLine!.physicsBody?.dynamic = true
colTripLine!.anchorPoint = CGPoint(x: 0, y: 0)
self.addChild(colTripLine!)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init coder not implemented")
}
}
I generate the HeadTripper lines like this in the main scene:
for var i = 0; i < rowCount; i++ {
let rowTrip = HeadTripper(size: size, rowOrCol: 0)
rowTrip.id = i
rowTrip.position.x = 0
rowTrip.position.y = CGFloat(yVal)
self.addChild(rowTrip)
rowLines.append(CGFloat(rowTrip.position.y))
CGPathMoveToPoint(linePath, nil, 0, CGFloat(yVal))
CGPathAddLineToPoint(linePath, nil, CGFloat(theGrid.screenWidth), CGFloat(yVal))
yVal += (theGrid.snakeHigh + yValPad)
}
Here is how the collider types are setup:
struct ColliderType {
static let Head: UInt32 = 0
static let Food: UInt32 = 0b1
static let Body: UInt32 = 0b10
static let BodyStatic: UInt32 = 0b100
static let WallLeft: UInt32 = 0b1000
static let WallBottom: UInt32 = 0b10000
static let WallRight: UInt32 = 0b100000
static let WallTop: UInt32 = 0b1000000
static let None: UInt32 = 0b10000000
static let Canvas: UInt32 = 0b100000000
static let TripColumn: UInt32 = 0b1000000000
static let TripRow: UInt32 = 0b10000000000
}
The tripRow let setup happens on line 310. On line 309 I can see the contact.bodyA:
0x000000012758db20
{
NSObject = {
isa = 0x000000012758db20
}
_representedObject = 0x00000001275765a0
_field = 0x0000000000000000
_dynamicType = 2
_world = 0x0000000127761b90
_joints = 0x0000000127576270 "0 values"
_inUse = true
_shapeType = 2
_radius = 0
_edgeRadius = 0.0010000000474974513
_mask = 0x0000000000000000
_isPinned = false
_allowsRotation = true
_postStepBlock = 0x000000019e666c70
}
I can also see the contact.bodyA.node:
0x00000001275765a0
{
UIKit.UIResponder = {...}
}
Line 311 never gets called because its in the if condition that isn't passing.
At line 313 I attempt to look at tripRow and get this:
(lldb) po tripRow
error: <EXPR>:1:1: error: use of unresolved identifier 'tripRow'
tripRow
It sounds like issue is related to the children within HeadTripper. I have named the children "row" and "col" respectively.
print("body A name ", contact.bodyA.node?.name)
let tripRow = contact.bodyA.node as? SKSpriteNode
This gives a name of "row". Also tripRow now prints out as:
(lldb) po tripRow
0x0000000155e737c0
{
SpriteKit.SKNode = {...}
}
I think this is very close. The problem still exists that the position is not accurate. The y value should be a number like 54 and it is zero:
(lldb) po tripRow?.position
(x = 0, y = 0)
This was resolved by having just 1 SKSpriteNode referenced in the HeadTripper SKNode class instead of 2.

SKLabel won't show up

Im making a flappy bird type game in swift using sprite kit. The game works fine but I'm trying to make a way to score the game so I set up a variable and SKLabel that adds on to itself whenever a pipe is about half way across the screen. However I cannot get the label to show up any help doing this here is my code:
// flappy rainbow sheep
//
// Created by Heather Arnold on 3/3/15.
// Copyright (c) 2015 ian arnold. All rights reserved.
//
import SpriteKit
class GameScene: SKScene {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var PipeMoveAndRemove = SKAction()
let pipeGap = 225.0
var score: Int = 0
var scoreLabel: SKLabelNode = SKLabelNode(fontNamed:"System-Bold")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
score = 0
//Physics
self.physicsWorld.gravity = CGVectorMake(0.0, -10.0);
//Bird
var BirdTexture = SKTexture(imageNamed:"flappysheep")
BirdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: BirdTexture)
bird.setScale(0.5)
bird.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
scoreLabel.position.x = 50
scoreLabel.position.y = view.bounds.height - 50
scoreLabel.text = "0"
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
scoreLabel.hidden = false
self.addChild(scoreLabel)
bird.physicsBody = SKPhysicsBody(circleOfRadius:bird.size.height/4.0);
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = false
self.addChild(bird)
//off screen
//Ground
var groundTexture = SKTexture(imageNamed:"rainbowobstacle")
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(self.size.width/2.0, sprite.size.height/2.0)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody?.dynamic = false
self.addChild(ground)
//Pipes
//create the pipes
pipeUpTexture = SKTexture(imageNamed:"rainbowpipe")
pipeDownTexture = SKTexture(imageNamed:"rainbowpipe")
//move pipes
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeUpTexture.size().width)
let movePipes = SKAction.moveByX(-2500, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
PipeMoveAndRemove = SKAction.sequence([movePipes, removePipes])
//spwan pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(0.2))
let spawnThenDelay = SKAction.sequence([spawn, delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
let pipeUp = SKSpriteNode(texture:pipeUpTexture)
if (pipeUp.position.x + (pipeUp.size.width / 2) < self.view!.bounds.size.width / 2)
{
score++
}
scoreLabel.hidden = false
}
func spawnPipes(){
let pipePair = SKNode()
pipePair.position = CGPointMake(self.frame.size.width + pipeUpTexture.size().width * 2, 0)
pipePair.zPosition = -10
let height = UInt32(self.frame.size.height / 4)
let y = arc4random() % height + height
let pipeDown = SKSpriteNode(texture:pipeDownTexture)
pipeDown.setScale(2.0)
pipeDown.position = CGPointMake(0.0, CGFloat(y) + pipeDown.size.height + CGFloat(pipeGap))
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize:pipeDown.size)
pipeDown.physicsBody?.dynamic = false
pipePair.addChild(pipeDown)
let pipeUp = SKSpriteNode(texture:pipeUpTexture)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size)
pipeUp.physicsBody?.dynamic = false
pipePair.addChild(pipeUp)
pipePair.runAction(PipeMoveAndRemove)
self.addChild(pipePair)
}
class GameScene: SKScene {
let bird = SKSpriteNode()
var gameOver = false
override init(size: CGSize) {
super.init(size: size)
self.bird.position = CGPoint(x: 0, y: 0)
self.addChild(self.bird)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func endGame(){
// restart the game
let gameScene = GameScene(size: self.size)
self.view!.presentScene(gameScene)
}
override func update(currentTime: NSTimeInterval) {
// our bird doesnt intersect the frame,
// we use gameOver bool so we dont call endGame more than once
if !self.frame.intersects(self.bird.frame) && !self.gameOver{
self.gameOver = true
self.endGame()
}
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
bird.physicsBody?.velocity = CGVectorMake(0, 0)
bird.physicsBody?.applyImpulse(CGVectorMake(0, 8))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Your label might be covered over by another image. Try setting the SKLabelNode zPosition property to a higher value. Perhaps something like 900.
Try setting the zPosition of the label to one.
scoreLabel.zPosition = 1
This should make the node appear even if another node passes over it.
Here are two other functions that may assist you also:
scoreLabel.color = UIColor.blackColor()//set color to black - you can use any almost color here
scoreLabel.fontSize = 32//changes font size to make label bigger/smaller