Gamescene spriteNodes in array - swift

Getting an error on this pice of code.
I Don't want to go through the spriteNode on GameScene.sks manually (in code) - the for loop must work somehow? Any ideas...
let sprites:[SKSpriteNode] = [block1, block2, block3, block4,
block5, block6, block7, block8, block9, block10, block11,
block12, block13, block14, block15, block16, block17,
block18, block19, block20, block21, block22, block23,
block24, block25, block26, block27, block28, block29,
block30, block31, block32, block33, block34, block35,
block36, block37, block38, block39, block40, block41,
block42, block43, block44, block45, block46, block47,
block48, block49, block50, block51, block52, block53,
block54, block55, block56, block57, block58, block59,
block60, block61, block62, block63, block64, block65,
block66, block67, block68, block69, block70, block71,
block72, block73, block74, block75, block76, block77,
block78, block79, block80, block81, block82, block83, block84]
for sprite in sprites {
self.sprite; in sprites = (self.childNode(withName: sprite.name) as? SKSpriteNode)!
sprite.name = "Green"
sprite.zPosition = 3
}

I'm guessing that from your not very descriptive description that you have a bunch of SKSpriteNodes in your .sks file and you want to loop through them in code to setup them up???
something like this might work better for you, if that is indeed what you are looking for.
let spriteNames: [String] = ["block1", "block2", "block3"]
let sprites = [SKSpriteNode]()
for spriteName in spriteNames {
if let sprite = self.childNode(withName: spriteName) as? SKSpriteNode {
sprite.name = "Green"
sprite.zPosition = 3
sprites.append(sprite)
}
}

Related

Adding SCNNode multiple time shows only once

I am trying to add SCNNode mutiple time in a loop at different position but I can see same type of node at once with the last position.
Below is the code
let entityArray:[entity] = [.coin, .coin, .coin, .brick, .coin, .coin, .coin, .brick]
func setupworld() {
let scene = SCNScene(named: "art.scnassets/MainScene.scn")!
var zPosition = -10
var count = 0
let delta = -4
for entity in entityArray {
var node = SCNNode()
switch entity {
case .coin:
node = scene.rootNode.childNode(withName: "coin", recursively: true) ?? node
node.position = SCNVector3(0, -5, zPosition)
case .brick:
node = scene.rootNode.childNode(withName: "brick", recursively: true) ?? node
node.position = SCNVector3(0, 0, zPosition)
}
self.sceneView.scene.rootNode.addChildNode(node)
zPosition += delta
count += 1
}
}
It shows one coin and one brick at last positions.
I am new to scenekit so would be doing something wrong, Please help me.
Building on from the other comments and as #rmaddy has said an SCNNode has a clone() function (which is the approach you should take here) and which simply:
Creates a copy of the node and its children.
One thing to be aware of when using this however, is that each cloned Node will share the same geometry and materials.
That's to say that if you wanted at any point to have some bricks with a red colour and some with a green colour you wouldn't be able to do it with this method since:
changes to the objects attached to one node will affect
other nodes that share the same attachments.
To achieve this e.g. to render two copies of a node using different materials, you must copy both the node and its geometry before assigning a new material, which you can read more about here: Apple Discussion
The reason you are only ever seeing one instance of either the coin or brick is because each time you are iterating through your loop you are saying that the newly created node is equal to either the coin or the brick, so naturally the last element in that loop will be the one that references that element from your scene.
Putting this into practice and solving your issue therefor, your setupWorld function should look like something like this:
/// Sets Up The Coins & Bricks
func setupworld(){
//1. Get Our SCNScene
guard let scene = SCNScene(named: "art.scnassets/MainScene.scn") else { return }
//2. Store The ZPosition
var zPosition = -10
//3. Store The Delta
let delta = -4
//4. Get The SCNNodes We Wish To Clone
guard let validCoin = scene.rootNode.childNode(withName: "coin", recursively: true),
let validBrick = scene.rootNode.childNode(withName: "brick", recursively: true) else { return }
//5. Loop Through The Entity Array & Create Our Nodes Dynamically
var count = 0
for entity in entityArray {
var node = SCNNode()
switch entity{
case .coin:
//Clone The Coin Node
node = validCoin.clone()
node.position = SCNVector3(0, -5, zPosition)
case .brick:
//Clone The Brick Node
node = validBrick.clone()
node.position = SCNVector3(0, 0, zPosition)
}
//6. Add It To The Scene
self.sceneView.scene.rootNode.addChildNode(node)
//7. Adjust The zPosition
zPosition += delta
count += 1
}
}

Need to know about ENUM for contact in SpriteKit

I tried to make contact but it didn't work .
I create an enum:
enum object:UInt32{
case BB = 1
case TR = 2
case GAP = 3
}
Then I have 3 nodes:
balloon.physicsBody?.categoryBitMask = object.BB.rawValue
balloon.physicsBody?.contactTestBitMask = object.TR.rawValue
balloon.physicsBody?.collisionBitMask = object.GAP.rawValue
tree2.physicsBody?.categoryBitMask = object.TR.rawValue
tree2.physicsBody?.contactTestBitMask = object.BB.rawValue
gap.physicsBody?.categoryBitMask = object.GAP.rawValue
gap.physicsBody?.collisionBitMask = object.GAP.rawValue
gap.physicsBody?.contactTestBitMask = object.BB.rawValue
In DidBeginContact:
let result = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
switch result{
case object.BB.rawValue + object.TR.rawValue:
xoaBB(contact.bodyA.node as! SKSpriteNode, TR: contact.bodyB.node as! SKSpriteNode, toaDo: contact.contactPoint)
case object.BB.rawValue + object.GAP.rawValue:
score++
scorelabel.text = "\(score)"
default:
return
}
The problem is:
"case object.BB.rawValue + object.GAP.rawValue" didn't work
I want the ballon fly through the gap to score but it can't. Balloon just contact to gap and gets stuck there as shown here.
Can anyone help me, I really need "how to use Enum tutorial too"
As mentioned in my comment your bitmask-values doesn't quite make sense. How about something a little like this (I have not checked for typos):
enum MaskType : UInt32 {
case Balloon = 1
case Gap = 2
case Wall = 4
case None = 0
}
Then you need to decide what you are after. The following example does not register collisions, meaning you want have physical interactions happening between the objects.
balloon.physicsBody?.categoryBitMask = MaskType.Balloon.rawValue
balloon.physicsBody?.collisionBitMask = MaskType.None.rawValue
balloon.physicsBody?.contactTestBitMask = MaskType.Gap.rawValue | MaskType.Wall.rawValue
The balloon sprite will now register contacts with objects having either the .Gap or .Wall value as their categoryBitMask.
If you now setup your other spriteNode's physicsBodies appropriately you can do some nice things in the didBeginContact function. Perhaps something like this:
func didBeginContact(contact: SKPhysicsContact) {
let contactA = contact.bodyA
let contactB = contact.bodyB
func ballonHitType() -> Uint32 {
if contactA.categoryBitMask == MaskType.Baloon.rawValue {
return contactB.categoryBitMask
} else {
return contactA.categoryBitMask
}
}
// Then simply perform your wanted actions based on whatever ballonHitType you receive
}

GameplayKit Pathfinding with obstacles and agents

I've been searching for days about this new framework and trying to make usage of some of it's funcionalities,
but... there're some things that are not fitting together for me, the demobot source code isn't helping at some points,
I miss some kind of simple tutorial but here goes my main doubts:
let obstacles = scene["obstacle"]
polygonObstacles = SKNode.obstaclesFromNodePhysicsBodies(obstacles)
graph = GKObstacleGraph(obstacles: polygonObstacles, bufferRadius: 60.0)
func drawGraph() {
for node in graph.nodes as! [GKGraphNode2D] {
for destination in node.connectedNodes as! [GKGraphNode2D] {
let points = [CGPoint(node.position), CGPoint(destination.position)]
let shapeNode = SKShapeNode(points: UnsafeMutablePointer<CGPoint>(points), count: 2)
shapeNode.strokeColor = SKColor(white: 1.0, alpha: 0.5)
shapeNode.lineWidth = 5.0
shapeNode.zPosition = 3
scene.addChild(shapeNode)
}
}
}
So, when I try to draw this graph and see the connections, I get this: http://i.imgur.com/EZ3dx5v.jpg
I find it really weird, anywhere I put my obstacles, even in low numbers, the left-corner portion of the screen have always more connections(the radius don't have influence on that)
I don't use GKComponents on my game, but I tried to run some GKAgents2D to hunt the player, like this:
func calculateBehaviorForAgents(){
let mainCharacterPosition = float2(scene.mainCharacter.position)
let mainCharacterGraphNode = GKGraphNode2D(point: mainCharacterPosition)
graph.connectNodeUsingObstacles(mainCharacterGraphNode)
for i in 0...monsters.count-1{
let monster = monsters[i]
let agent = agents[i]
let behavior = GKBehavior()
let monsterPosition = float2(monster.position)
let monsterGraphNode = GKGraphNode2D(point: monsterPosition)
graph.connectNodeUsingObstacles(monsterGraphNode)
let pathNodes = graph.findPathFromNode(monsterGraphNode, toNode: mainCharacterGraphNode) as! [GKGraphNode2D]
let path = GKPath(graphNodes: pathNodes, radius: 00.0)
let followPathGoal = GKGoal(toFollowPath: path, maxPredictionTime: 1.0, forward: true)
behavior.setWeight(1.0, forGoal: followPathGoal)
let stayOnPathGoal = GKGoal(toStayOnPath: path, maxPredictionTime: 1.0)
behavior.setWeight(1.0, forGoal: stayOnPathGoal)
agent.behavior = behavior
graph.removeNodes([monsterGraphNode])
}
graph.removeNodes([mainCharacterGraphNode])
}
Now when I call the updateWithDeltaTime method, his delegate methods:
func agentWillUpdate(agent: GKAgent){}
func agentDidUpdate(agent: GKAgent){}
give me unexpected values for the agents, it's position doesn't make any sense, with giant numbers that leads to outside of the battlefield
But I saw that the his velocity vector were making sense, so I matched it to my monster and updated the agent to the monster's position
func updateWithDeltaTime(currentTime : CFTimeInterval){
for i in 0...monsters.count-1{
let agent = agents[i]
let monster = monsters[i]
monster.physicsBody?.velocity = CGVectorMake(CGFloat(agent.velocity.x), CGFloat(agent.velocity.y))
agent.updateWithDeltaTime(currentTime)
agent.position = float2(monster.position)
monster.gameSceneUpdate(currentTime)
}
Now I was getting some results, but it's far away from what I want:
The monsters are not following the character to the edges or the right-top portion of screen, I remove their points from the graph but after make a path for them to follow (the image doesn't have this points, but they exists).
Apparently because there was no path leading to there, remember the image?
The question is: how to make this agent system to work?
Maybe I'm totally wrong at the usage of agents, goals and even the graphs! I read the documentation but I still can't make it right
And more...
At first, the monster were not avoid obstacles, even with GKGoals like "avoidObstacles", passing the same PolygonObstacles,
but when I change
graph.connectNodeUsingObstacles(mainCharacterGraphNode)
graph.connectNodeUsingObstacles(monsterGraphNode)
to
graph.connectNodeUsingObstacles(mainCharacterGraphNode, ignoringObstacles: polygonObstacles)
graph.connectNodeUsingObstacles(monsterGraphNode, ignoringObstacles: polygonObstacles)
it worked! o.O
I really need some help, thank you all :D!

SpriteKit Animation isn't loading when it supposed to be. What am I doing wrong?

I'm making a simple game. Hero should jump over the enemies and if they collide - enemy should eat the hero. I have 4 types of enemies randomly spawning in front of a hero. I use the first sprite of every enemy-array as a main, the rest are for animation. But when they collide - nothing happens, it's still the first sprite.
That's how I'm doing it:
let mouseAtlas = SKTextureAtlas(named: "mouse")
mouseArray.append(mouseAtlas.textureNamed("mouse_0"));
mouseArray.append(mouseAtlas.textureNamed("mouse_1"));
mouseArray.append(mouseAtlas.textureNamed("mouse_2"));
mouseArray.append(mouseAtlas.textureNamed("mouse_3"));
Mouse is an enemy, cookie is a hero.
mouse = SKSpriteNode(texture: mouseArray[0]);
self.mouse.position = CGPointMake(CGRectGetMaxX(self.frame) + self.cookie.size.width, self.cookieSpot + self.cookie.size.height + self.cookie.size.height / 2)
self.mouse.size = CGSizeMake(self.cookie.size.width + self.cookie.size.width / 2, self.cookie.size.height + cookie.size.height / 2)
self.addChild(cookie)
self.addChild(mouse)
Then I'm applying physic bodies to them:
self.cookie.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.cookie.size.width / 2))
self.cookie.physicsBody?.affectedByGravity = false
self.cookie.physicsBody?.categoryBitMask = ColliderType.Cookie.rawValue
self.cookie.physicsBody?.collisionBitMask = ColliderType.Pet.rawValue
self.cookie.physicsBody?.contactTestBitMask = ColliderType.Pet.rawValue
self.mouse.physicsBody = SKPhysicsBody(rectangleOfSize: self.mouse.size)
self.mouse.physicsBody?.dynamic = false
self.mouse.physicsBody?.categoryBitMask = ColliderType.Pet.rawValue
self.mouse.physicsBody?.contactTestBitMask = ColliderType.Cookie.rawValue
self.mouse.physicsBody?.collisionBitMask = ColliderType.Cookie.rawValue
And then I'm trying to make an animation when contact begins:
func didBeginContact(contact: SKPhysicsContact) {
eatenByMouse(); eatenByHamster(); eatenByRabbit(); eatenByCat()
}
func eatenByMouse() {
self.groundSpeed = 0
self.cookie.hidden = true
let animateAction = SKAction.animateWithTextures(self.mouseArray, timePerFrame: 0.1)
}
Like I said, there are 4 types of enemies but they are practically the same. What am I doing wrong with that? An I also interested, could I make physics body of enemies only on one side? I mean how can one make my hero eaten only if it touched enemies mouth for example? And if it touches the tail it would proceed rolling and jumping? Many thanks!
You have to run the SKAction after creating it.
func eatenByMouse() {
self.groundSpeed = 0
self.cookie.hidden = true
let animateAction = SKAction.animateWithTextures(self.mouseArray, timePerFrame: 0.1)
self.mouse.runAction(animateAction) // added line.
}
You can use SKPhysicsContact.contactPoint to detect point of collision.

write string on sprite kit

I am trying to make a game with a goat that walks a small rode and you needs to tap it as many times as its number, but i don't know how to write the string. Does anyone know?
I need a code like this:
var baloonImage = SKSpriteNode(imageNamed: "ballong")
goatImage.position = CGPointMake(CGFloat(startLocGoatX), CGFloat(startLocGoatY))
goatImage.name = goatName
goatImage.text = "5"
self.addChild(goatImage)
Does anyone know how to make the text? And if you know; how can I change it?
you need to add SKLabelNode.
var node = SKLabelNode(fontNamed:"Futura Medium")
node.text = "Hello world!"
node.fontColor = SKColor.whiteColor()
node.fontSize = 20
node.position = CGPointMake(x, y)
self.addChild(node)