Assigning texture to SKSpriteNode is not working - swift

Changing the texture of a SKSpriteNode is not working
I'm trying to learn SpriteKit and am running into something that is puzzling me. I am trying to display a sprite. I try two methods that should work, but only one is actually causing the sprite to be displayed on the screen! I would really like to actually understand why. Can anybody help?
This works:
func loadLevel() {
let point: CGPoint = CGPoint(x:300,y:300)
var block = SKSpriteNode(imageNamed: "Block_blue")
block.position = point
self.addChild(block)
}
This does not:
func loadLevel() {
let point: CGPoint = CGPoint(x:300,y:300)
var block = SKSpriteNode()
block.position = point
block.texture = SKTexture(imageNamed: "Block_blue")
self.addChild(block)
}
I would expect both functions to result in displaying the sprite, but only the first works? I would like to understand what is going on here.

Related

How to change spriteKit SKSpriteNode texture

I have a Swift 4.2 SpriteKit scene and I want to programatically change the texture of an SKSpriteNode.
How do I access the SKSpriteNode contained in the SKNode to change the texture?
Here is my code:
#IBOutlet weak var spritekitsceneFG: WKInterfaceSKScene!
var SpriteTick1 = SKNode()
SpriteTick1 = (spritekitsceneFG.scene?.childNode(withName: "SKSpriteTick1"))!
Want to change texture here!
print(SpriteTick1)
Printing out the SKNode gives the following output:
<SKSpriteNode> name:'SKSpriteTick1' texture:[<SKTexture> 'tick' (64 x 64)] position:{-48.000137, -56.000198} scale:{1.00, 1.00} size:{48, 48} anchor:{0.5, 0.5} rotation:0.00
I want to be able to programatically change the texture here, the texture is called 'tick'
Ok I found A way to do it like this. Any better ideas much welcomed.
var SpriteTick1 = SKSpriteNode()
SpriteTick1 = (spritekitsceneFG.scene?.childNode(withName: "SKSpriteTick1"))! as! SKSpriteNode
let textureCross = SKTexture(imageNamed: "cross")
SpriteTick1.texture = textureTick

How to use the SpriteKit method body(at : CGPoint)?

I have game that during the game your finger moves around and should avoid hitting some obstacles. I know how to use collision but I recently heard that there is a funtion called the body(at : point).
I should say that I have masked my obstacles. Because of that I can't use the contains(point) function.
I really want to use the body(at : point) function otherwise I know how to work with collisions.
Some of my code:
play_button = self.childNode(withName: "play_button") as! SKSpriteNode
for t in touches{
let fingerlocation = t.location(in: self)
if physicsworld.body(at : fingerlocation)
{
let so = level6(fileNamed:"level6")
so?.scaleMode = .aspectFill
self.view?.presentScene(so!, transition:
SKTransition.crossFade(withDuration: 1))
}
}
but it does not work.
physicsworld.body returns an SKPhysicsBody if found, so you just need to check if it exists
if let _ = physicsworld.body(at : fingerlocation)
If your goal is to make a button, then I would not do this approach.
I would recommend just subclassing SKSpriteNode and enabling the isUserInteractionEnable`
class Button : SKSpriteNode
{
override init()
{
super.init()
isUserInteractionEnabled = true
}
//then in your touchesBegan/touchesEnded code (wherever you placed it)
....
let so = level6(fileNamed:"level6")
so?.scaleMode = .aspectFill
scene.view?.presentScene(so!, transition:
SKTransition.crossFade(withDuration: 1))
....
}
I've had a similar problem, "if let body = physicsWorld.body(at: touchLocation){...}" was responding wherever I've touched the screen.
I figured out, that by default the SKScene object created its own SKPhysicsBody which was set on top of all other SKPhysicsBodies.
I solved this problem by making a new SKPhysicsBody for the scene, which didn't interfere anymore. Hope this code will help you.
inside of didMove:
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody

How to detect contact between 2 SKSpriteNodes. Swift, Sprite kit

I created this game in xcode using Sprite Kit and Swift and I am trying to figure out how I can detect when the hero makes contact with a wall. So far this is my code. However I can't seem to find collision when I do SKPhysicsBody.
class GameScene: SKScene {
var movingGround: TFMovingGround!
var hero: TFHero!
var isStarted = false
var wallGenerator: TFWallGenerator!
override func didMoveToView(view: SKView) {
backgroundColor = UIColor(red: 159.0/255.0, green:201.00/255.0, blue:244.0/255.0, alpha:1.0)
movingGround = TFMovingGround(size:CGSizeMake(view.frame.width, kMLGroundHeight))
movingGround.position = CGPointMake(0, view.frame.size.height/2)
addChild(movingGround)
hero = TFHero()
hero.position = CGPointMake(70,movingGround.position.y + movingGround.frame.size.height/2 + hero.frame.size.height/2)
addChild(hero)
hero.breath()
//Add WallGenerator
wallGenerator = TFWallGenerator(color: UIColor.clearColor(), size: view.frame.size)
wallGenerator.position = view.center
addChild(wallGenerator)
}
}
This is my code in the GameScene.
Any help would be much appreciated.
A difficult way to go about it would be to get the frame of HeroSprite and get the frame for WallsSprite and see if they overlap, using
CGRectIntersectsRect(rect1, rect2)
and doing this continuously.
A better solution would be to use collision detection. There is a pretty good tutorial here.

Trying to get Physics Shape That Matches the Graphical Representation

what I'm trying to achieve is that the Node would have the same shape as PhysicsBody/texture (fire has a complicated shape), and then I'm trying to make only fireImage touchable. So far when I'm touching outside of the fireImage on the screen and it still makes a sound. It seems that I'm touching the squared Node, but I want to touch only the sprite/texture.
Would appreciate your help.
The code is below:
import SpriteKit
import AVFoundation
private var backgroundMusicPlayer: AVAudioPlayer!
class GameScene2: SKScene {
var wait1 = SKAction.waitForDuration(1)
override func didMoveToView(view: SKView) {
setUpScenery()
}
private func setUpScenery() {
//I'm creating a Fire Object here and trying to set its Node a physicsBody/texture shape:
let fireLayerTexture = SKTexture(imageNamed: fireImage)
let fireLayer = SKSpriteNode(texture: fireLayerTexture)
fireLayer.anchorPoint = CGPointMake(1, 0)
fireLayer.position = CGPointMake(size.width, 0)
fireLayer.zPosition = Layer.Z4st
var firedown = SKAction.moveToY(-200, duration: 0)
var fireup1 = SKAction.moveToY(10, duration: 0.8)
var fireup2 = SKAction.moveToY(0, duration: 0.2)
fireLayer.physicsBody = SKPhysicsBody(texture: fireLayerTexture, size: fireLayer.texture!.size())
fireLayer.physicsBody!.affectedByGravity = false
fireLayer.name = "fireNode"
fireLayer.runAction(SKAction.sequence([firedown, wait1, fireup1, fireup2]))
addChild(fireLayer)
}
//Here, I'm calling a Node I want to touch. I assume that it has a shape of a PhysicsBody/texture:
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touch = touches.anyObject() as UITouch
let touchLocation = touch.locationInNode(self)
let node: SKNode = nodeAtPoint(touchLocation)
if node.name == "fireNode" {
var playguitar = SKAction.playSoundFileNamed("fire.wav", waitForCompletion: true)
node.runAction(playguitar)
}
}
}
}
Physics bodies and their shapes are for physics — that is, for simulating things like collisions between sprites in your scene.
Touch handling (i.e. nodeAtPoint) doesn't go through physics. That's probably the way it should be — if you had a sprite with a very small collision surface, you might not necessarily want it to be difficult to touch, and you also might want to be able to touch nodes that don't have physics. So your physics body doesn't affect the behavior of nodeAtPoint.
An API that lets you define a hit-testing area for a node — that's independent of the node's contents or physics — might not be a bad idea, but such a thing doesn't currently exist in SpriteKit. They do take feature requests, though.
In the meantime, fine-grained hit testing is something you'd have to do yourself. There are at least a couple of ways to do it:
If you can define the touch area as a path (CGPath or UIBezierPath/NSBezierPath), like you would for creating an SKShapeNode or a physics body using bodyWithPolygonFromPath:, you can hit-test against the path. See CGPathContainsPoint / containsPoint: / containsPoint: for the kind of path you're dealing with (and be sure to convert to the right local node coordinate space first).
If you want to hit-test against individual pixels... that'd be really slow, probably, but in theory the new SKMutableTexture class in iOS 8 / OX X v10.10 could help you. There's no saying you have to use the modifyPixelDataWithBlock: callback to actually change the texture data... you could use that once in setup to get your own copy of the texture data, then write your own hit testing code to decide what RGBA values count as a "hit".

How to copy SKSpriteNode with SKPhysicsBody?

I am curious about a situation that I came across today when trying to unarchive and copy a SKSpriteNode from one SKScene to another. In the output from the playground below you can see that both linearDamping and angularDamping or not being maintained after the copy (they seem to be dropping back to default values)
// PLAYGROUND_SW1.2 - SKSpriteNode Copy
import UIKit
import SpriteKit
// ORIGINAL
let spriteNode = SKSpriteNode()
spriteNode.name = "HAPPY_NODE"
let size = CGSize(width: 55.0, height: 150.0)
let physics = SKPhysicsBody(rectangleOfSize: size)
physics.linearDamping = 0.123
physics.angularDamping = 0.456
spriteNode.physicsBody = physics
// COPY
let spriteCopy = spriteNode.copy() as! SKSpriteNode
// ORIGINAL
spriteNode.name
spriteNode.physicsBody?.linearDamping
spriteNode.physicsBody?.angularDamping
spriteNode.physicsBody?.area
// COPY
spriteCopy.name
spriteCopy.physicsBody?.linearDamping
spriteCopy.physicsBody?.angularDamping
spriteCopy.physicsBody?.area
PLAYGROUND OUTPUT
I am not sure that I am copying this correctly, both SKSpriteNode and SKPhysicsBody conform to NSCopying If you look at the output above the area property is maintained after the copy and to my knowledge this is based on the size specified when the SKPhysicsBody was created.
Can anyone cast some light on this and maybe provide me with a pointer as to how I should be deep copying an SKSpriteNode?
I take one way to resolve your problem, probably is not the best way, but
//COPY
let spriteCopy = spriteNode.copy() as SKSpriteNode
let physicsCopy:SKPhysicsBody = spriteNode.physicsBody!;
...
//COPY PHYSICS BODY HARD MODE
spriteCopy.physicsBody = physicsCopy;
To fix this problem, I created one extension, and #mogelbuster suggested override default copy(), ant it sounds great.
extension SKSpriteNode
{
override open func copy() -> Any {
let node = super.copy() as! SKSpriteNode;
node.physicsBody = super.physicsBody;
return node;
}
}
With this extension you can do it, the default copy() method return Any because this you need cast to SKSpriteNode.
// COPY
let spriteCopy = spriteNode.copy() as! SKSpriteNode;
// ORIGINAL
spriteNode.name
spriteNode.physicsBody?.linearDamping
spriteNode.physicsBody?.angularDamping
spriteNode.physicsBody?.area
// COPY
spriteCopy.name
spriteCopy.physicsBody?.linearDamping
spriteCopy.physicsBody?.angularDamping
spriteCopy.physicsBody?.area