I have run into this strange issue. I am adding an enemy(SKSpriteNode) from inside the GameScene didMove(to view: SKView) using addChild.
The enemy has been positioned to x:100, y: 100 and it appears correctly.
I also have another animation , the completion of which I am adding another enemy at the same location . But the enemy appears at a different location.The completion block is as shown below.
holeExplosion.runHoleExplosionAction {[unowned self] in
//self.addEnemy(enemyCount: 1, hole: holeExplosion)
var modEnemy: ParentEnemy? = nil
modEnemy = Enemy1(imageNamed: "Zombie1Jump1.png", healthPower:30)
print(" \(self.scene?.position.x) \(self.scene?.parent) ")
self.addChild(modEnemy!)
modEnemy!.enemySpeed = self.enemy1Speed
modEnemy!.name = "enemy1"
modEnemy!.position = CGPoint (x: 100 , y: 100)
modEnemy!.zPosition = 2
}
Any help would be appreciated. Thanks.
Your enemy class have physicsBody delegate?? maybe you can see that first because if it has it, you have to search your isDynamic property. You can't have 2 bodies in the same space when the property is equal to true.
Related
Currently working on an application that requires a little bit of animations through an array of images. I've done tons of research online on how to resolve my issue but couldn't run into anything for Swift 4. Below is my code, it shows the first picture in the loop when I run the app but no animation at all. Everything looks fine, I don't see a problem with my code but maybe you guys can help. Appreciate it in advanced!
let atlas = SKTextureAtlas(named: “mypic”)
var TextureArray = [SKTexture]()
var person = SKSpriteNode()
override func didMove(to view: SKView) {
person = SKSpriteNode(imageNamed: "red_1.png")
person.size = CGSize(width: 150, height: 129)
person.position = CGPoint(x: 0, y: 0)
person = SKSpriteNode(imageNamed: atlas.textureNames[0])
for i in 1...atlas.textureNames.count {
let Name = "red_\(i).png"
TextureArray.append(SKTexture(imageNamed: Name))
}
self.addChild(person)
}
override func update(_ currentTime: TimeInterval) {
let myAnimation = SKAction.animate(with: TextureArray, timePerFrame: 0.1)
person.run(SKAction.repeatForever(myAnimation))
}
The animation action is placed in update, which is executed once every frame. So if the game runs at 60 FPS, update gets called 60 times in one second. This means that every second person gets 60 new myAnimation actions that it needs to run.
To fix this, consider placing the animation action somewhere else in your code, e.g. right after adding the person to the scene. Since this is a repeatForever action, the animation will run as you intended until either the action is removed from the node, or the node is removed from the scene.
Hope this helps!
I get a nil exception error during runtime and I can not explain myself why there is a problem.
First I declare a SKSpriteNode Object like this
var destinationSign: SKSpriteNode?
and later I define a function that creates this SpriteNode like this
func showDestination( x: CGFloat, y: CGFloat){
destinationSign = SKSpriteNode(fileNamed: "sign_1")
destinationSign?.position.x = x
destinationSign?.position.y = y
addChild(destinationSign!)
}
after that I have a different function and triggers this showDestination function
showDestination(x: 20 , y: 40)
Does anyone have an explanation for this behaviour?
Thanks in advance
Looks like destinationSign is nil when addChild is called. You should double check the name of the image used to init it, capital letters, underscore, etc…
Try also:
if let node = destinationSign as SKSpriteNode {
self.addChild(node)
}
to prevent the nil exception (but remember the node will not be added)
You should also consider to use a more generalized function, that could be used in other contexts, as:
func show (_ node: SKSpriteNode, at point: CGPoint) {
node = SKSpriteNode(fileNamed: "sign_1")
if let node = node as SKSpriteNode {
node.position.x = point.x
node.position.y = point.y
addChild(node)
}
}
But these solution are merely theoretical, given the fact you actually want an error if your sprite is not added to the scene.
I have less than 1 year using SpriteKit so I didn't use SKNodes as layers before until recently.
I have an SKNode layer that holds all of the fish and the user's position, for example:
var layerMainGame = SKNode()
layerMainGame.zPosition = 50
layerMainGame.addChild(userPosition)
layerMainGame.addChild(pipFish)
addChild(layerMainGame)
The interaction whether the user touched a fish or not is handled with this function, which is basically checking if their frames crossed:
if CGRectIntersectsRect(CGRectInset(node.frame, delta.dx, delta.dy), self.userPosition.frame) {
print("You got hit by \(name).")
gameOver()
}
It works. The interaction between the userPosition and pipFish works. What doesn't work is fish that are added as the game progresses. I have a function spawning different types of fish in intervals like this:
func spawnNew(fish: SKSpriteNode) {
layerMainGame.addChild(fish)
}
The interaction between the user and those fish that get added to the same layer later in the game does not work. I can pass right through them and no game over happens. When I completely remove the entire layerMainGame variable and just add them to the scene like normal, all the interactions work. Adding them all to the same SKNode layer doesn't work.
This is the function that creates a hit collision for every fish.
func createHitCollisionFor(name: String, GameOver gameOver: String!, delta: (dx: CGFloat, dy: CGFloat), index: Int = -1) {
enumerateChildNodesWithName(name) { [unowned me = self] node, _ in
if CGRectIntersectsRect(CGRectInset(node.frame, delta.dx, delta.dy), self.userPosition.frame) {
me.gameOverImage.texture = SKTexture(imageNamed: gameOver)
didGetHitActions()
me.runAction(audio.playSound(hit)!)
if index != -1 {
me.trophySet.encounterTrophy.didEncounter[index] = true
}
print("You got hit by \(name).")
}
}
}
And I call it like this:
createHitCollisionFor("GoldPiranha", GameOver: model.gameOverImage["Gold"], delta: (dx: 50, dy: 50), index: 1)
It works when the fish are not in the layer, but doesn't work when they are added to the layer.
When a node is placed in the node tree, its position property places it within a coordinate system provided by its parent.
Sprite Kit uses a coordinate orientation that starts from the bottom left corner of the screen (0, 0), and the x and y values increase as you move up and to the right.
For SKScene, the default value of the origin – anchorPoint is (0, 0), which corresponds to the lower-left corner of the view’s frame rectangle. To change it to center you can specify (0.5, 0.5)
For SKNode, the coordinate system origin is defined by its anchorPoint which by default is (0.5, 0.5) which is center of the node.
In your project you have layerMainGame added for example to the scene, his anchorPoint is by default (0.5,0.5) so the origin for the children like your fish is the center, you can see it if you change the fish positions like:
func spawnNew(fish: SKSpriteNode) {
layerMainGame.addChild(fish)
fish.position = CGPointZero // position 0,0 = parent center
}
Hope it help to understand how to solve your issue.
Update: (after your changes to the main question)
To help you better understand what happens I will give an example right away:
override func didMoveToView(view: SKView) {
var layerMainGame = SKNode()
addChild(layerMainGame)
let pipFish = SKSpriteNode(color: UIColor.yellowColor(), size: CGSizeMake(50,50))
pipFish.name = "son"
self.addChild(pipFish)
let layerPipFish = SKSpriteNode(color: UIColor.yellowColor(), size: CGSizeMake(50,50))
layerPipFish.name = "son"
layerMainGame.addChild(layerPipFish)
enumerateChildNodesWithName("son") { [unowned me = self] node, _ in
print(node)
}
}
Output:
Now I will simply change the line:
layerMainGame.addChild(layerPipFish)
with:
self.addChild(layerPipFish)
Output:
What happened?
As you can see enumerateChildNodesWithName written as your and my code print only childs directly added to self (because actually we launch enumerateChildNodesWithName which it is equal to launch self.enumerateChildNodesWithName )
How can I search in the full node tree?
If you have a node named "GoldPiranha" then you can search through all descendants by putting a // before the name. So you would search for "//GoldPiranha":
enumerateChildNodesWithName("//GoldPiranha") { [unowned me = self] ...
I am trying to add two SKSpriteNodes with their respective textures in swift, however the "Blade" does not show up on screen during the simulation. I am having no trouble getting the "Handle" to appear though. So my question is, what is the proper way to add a child of a child of self in swift so that everything works as intended?
var Handle = SKSpriteNode(imageNamed: "Handle.png")
var Blade = SKSpriteNode(imageNamed: "Blade.png")
override func didMoveToView(view: SKView) {
Handle.position = CGPointMake(self.size.width / 2, self.size.height / 14)
Blade.position = CGPointMake(Handle.position.x, Handle.position.y + 124)
self.addChild(Handle)
Handle.addChild(Blade)
}
This should get you close to where you expected it.
Blade.position = CGPointMake(0, 124)
Keep in mind when you add a sprite to another sprite the position is the position "within" that sprite. If you don't change the anchor that starts at the center of its parent. So you were starting in the center of Handle and adding half the width of the scene. Because Handle was already centered in the scene blade was off the scene.
Hopefully that makes sense and is helpful.
Also as lchamp mentioned you should lowerCamelCase
var Blade = SKSpriteNode(imageNamed: "Blade.png")
This will help you in the future identifying Classes vs Variables.
I would like to know how to remove my SKNodes when they are off screen to help my game run more smoothly.
How To Do This On Sprite Kit
Thanks So Much
Here is an easy solution in Swift 4:
class GameScene: SKScene {
let s = SKLabelNode(fontNamed: "Chalkduster")
override func didMove(to view: SKView) {
s.text = "test"
s.fontSize = 50
addChild(s)
let moveRight = SKAction.moveBy(x: 40, y: 0, duration: 0.5)
s.run(SKAction.repeatForever(moveRight))
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if ((s.parent != nil) && !intersects(s)) {
s.removeFromParent()
print("Sprite removed.")
}
}
}
You have a sprite (in this case a SKLabelNode but any sprite node will do) that is moving horizontally and you want to delete this sprite once is out of the frame bounds.
You can use the intersects function to check this and then remove that sprite from its parent. I have also checked that the sprite has a parent before removing it (by checking if s.parent is not nil) as we wish to remove the sprite only once.
https://stackoverflow.com/a/24195006/2494064
Here is a link to an answer that removes nodes that go off the top of the screen. You would just have to replicate this to cover the entire border and set all of the walls to have the same contactBitMask values.
Basically the logic is to remove the SKSpriteNodes when they contact physicsbodies that you have resting just outside the visible screen.