Group Group Group Group Group Group Group Group Group

Custom node classes

#1

Hi,

Re: Chap 9.

I am having trouble referencing a childnode from a custom class (JoeSprite) with code. Chap 9 Shows how to refer to the sks scene. How can I do this in code? I created a protocol in the GameScene (JoeScene) and call it in the enumeration but the simulator crashes when I build it. It finds nil when force unwrapping. Xcode suggests to make it optional instead but then the sprite (“Joe”) doesn’t appear. I tried giving the sprite a name (joe.name = “Joe”) but it didn’t work. Where have I gone wrong? I can make it work when all the codes are in one class but not when I move the sprite to its own class. Here are my codes:

import SpriteKit

protocol PlayAlong {
func allSweet()
}

class JoeScene: SKScene {

var joeSprite: JoeSprite!

override func didMove(to view: SKView) {
       
    joeSprite = childNode(withName: "joe") as! JoeSprite
    
    enumerateChildNodes(withName: "//*", using: { node, _ in if let eventListenerNode = node as! PlayAlong {
        eventListenerNode.allSweet()
        }})
}  

}

import SpriteKit

class JoeSprite: JoeScene, PlayAlong {

let textureAtlas: SKTextureAtlas = SKTextureAtlas(named: "Joe")
var joe = SKSpriteNode(imageNamed: "J1.png")
var joeWalkingFrames: [SKTexture] = []
var joeBlushingAnimation = SKAction()


    override init(size: CGSize) {
        super.init(size: size)
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented") 
    }
    
   
    func allSweet()  {
        buildJoe()
        animateJoe()
    }
   
 

func buildJoe() {
    
    let joeAnimatedAtlas = SKTextureAtlas(named: "Joe")
    var walkFrames: [SKTexture] = []
    
    
    let numImages = joeAnimatedAtlas.textureNames.count
    for i in 1...numImages {
        let joeTextureName = "J\(i)"
        walkFrames.append(joeAnimatedAtlas.textureNamed(joeTextureName))
          
    }
    
    
    joeWalkingFrames = walkFrames
    
    let firstFrameTexture = joeWalkingFrames[0]
    joe = SKSpriteNode(texture: firstFrameTexture)
    joe.position = CGPoint(x: frame.midX, y: frame.midY)
    joe.size = CGSize(width: 90, height: 145)
    joe.name = "joe"
    joe.zPosition = 5
    self.addChild(joe)
    
}


func animateJoe() {
    
    let joeBlushes = SKAction.colorize(with: SKColor.red, colorBlendFactor: 0.3, duration: 1.0)
    let joeBlushes2 = SKAction.colorize(withColorBlendFactor: 0.0, duration: 1.0)
    let joeBlushing = SKAction.sequence([joeBlushes, joeBlushes2])
   
   joeBlushingAnimation = SKAction.group([SKAction.repeatForever(joeBlushing)])
    joe.run(joeBlushingAnimation)
    
    joe.run(SKAction.repeatForever(
        SKAction.animate(with: joeWalkingFrames,
                         timePerFrame: 0.1,
                         resize: false,
                         restore: true)),
                 withKey:"walkingInPlaceJoe")
}



func moveJoe(location: CGPoint) {
   
   var multiplierForDirection: CGFloat
    
   let joeSpeed = frame.size.width / 3.0
    
   let moveDifference = CGPoint(x: location.x - joe.position.x, y: location.y - joe.position.y)
    let distanceToMove = sqrt(moveDifference.x * moveDifference.x + moveDifference.y * moveDifference.y)
    
        let moveDuration = distanceToMove / joeSpeed
    
          if moveDifference.x < 0 {
        multiplierForDirection = -1.0
    } else {
        multiplierForDirection = 1.0
    }
    joe.xScale = abs(joe.xScale) * multiplierForDirection
    
    if joe.action(forKey: "walkingInPlaceJoe") == nil {
       
       animateJoe()
    }
    

    let moveAction = SKAction.move(to: location, duration:(TimeInterval(moveDuration)))
    
 
    let doneAction = SKAction.run({ [weak self] in
        self?.joeMoveEnded()
    })
    

    let moveActionWithDone = SKAction.sequence([moveAction, doneAction])
    joe.run(moveActionWithDone, withKey:"joeMoving")
}

func joeMoveEnded() {
joe.removeAllActions()

}

    func random() -> CGFloat {
    return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}

    func random(min: CGFloat, max: CGFloat) -> CGFloat {
    return random() * (max - min) + min
}


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 1 - Choose one of the touches to work with

guard let touch = touches.first else {
    return
}

let location = touch.location(in: self)

moveJoe(location: location)

}
}

Note: I did not include the math utilities here.

Thanks in advance
z

#2

@zwolf - Two possibilities immediately spring to mind.

  1. JoeSprite isn’t an SKSpriteNode, it’s an SKScene. You can still add SKScenes to the node hierarchy, but I don’t know why you’d want to.

  2. JoeScene holds a JoeSprite property (that’s the SKScene). How do you instantiate that? In didMove(), you call allSweet() on all added nodes. However, it doesn’t appear that you have added joeSprite by this time. You add it in allSweet(), don’t you?

Note: It’s difficult to follow code when it’s not all formatted the same way. Three backticks ` around the code should format it. Like this:

code block

That has three backticks on the line above code block and the line below code block. Some of your code is properly formatted because indentation (four spaces) will automatically format into code.

#3

Thank you kindly Caroline,

Especially for pointing out that I created an SKScene JoeSprite rather than an SKSpriteNode. Sometimes after hours of learning how to code pass midnight it all becomes a big muddle in my head :slight_smile: I will try to fix this. I’m not clear what you meant with ‘backticks’ and ‘code block’. I’ve come across ‘blocks’ learning to compile groups of SKActions, but this sounds like something else.

Cheers
z

#4

@zwolf sorry I wasn’t clearer - the backtick section was for formatting your code when you write a post in this forum. Not to do with your code! (It’s Markdown format)

closed #5

This topic was automatically closed after 166 days. New replies are no longer allowed.