So in my game i want to really make the life index more dynamic and not just an label that counts down. I've add three sprites on top of the screen like this:
They are inserted by this code in a for loop:
// Add Life to player
var lifeIndexCount = 0
var positionAdd:CGFloat = 10.0
for lifeIndexCount in 0..<3 {
let lifeIndex = SKSpriteNode(imageNamed: "bullet.png")
lifeIndex.position = CGPoint(x: self.frame.size.width * -1.5, y: self.frame.size.height * 0.93)
let lifeIndexMove = SKAction.moveTo(CGPoint(x: (size.width * 0.05) + positionAdd, y: size.height * 0.93), duration: NSTimeInterval(0.7))
let lifeIndexRotation = SKAction.rotateByAngle(CGFloat(-2 * M_PI), duration: 0.3)
lifeIndex.runAction(SKAction.sequence([lifeIndexMove, lifeIndexRotation]))
addChild(lifeIndex)
positionAdd = positionAdd + 25.0
I want to remove each one as soon as the player ship misses a shot. Like a little animation of the life sprite rotating and get of the screen. I've set my contact delegate but i don't know how to grab them and animate so they can go off when a miss shot occurred.
func didBeginContact(contact: SKPhysicsContact) {
var contactBody1: SKPhysicsBody
var contactBody2: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
contactBody1 = contact.bodyA
contactBody2 = contact.bodyB
} else {
contactBody1 = contact.bodyB
contactBody2 = contact.bodyA
}
if ((contactBody1.categoryBitMask == 1) && (contactBody2.categoryBitMask == 2)) {
//Run when the bullet contacts the line
contactBody2.node!.removeFromParent()
// I WANT TO INSERT CODE HERE TO REMOVE A LIFE AT THE TOP
} else if ((contactBody1.categoryBitMask == 2) && (contactBody2.categoryBitMask == 4)) {
//Run when the bullet contacts the limbo
}
}
Thanks!
There are several ways to do it, but they all pretty much boil down to giving the node you want animated and removed a name, and matching that name in didBeginContact:. I'm fond of enumerateChildNodesWithName for readability and ease of use. The baked in pattern matching is pretty handy if your use case has something like arrowSprite1, arrowSprite2, etc...
It's also a good idea to clean up anything else that node may be doing, like cancelling SKActions or requests sent out to selectors.
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKNode_Ref/index.html#//apple_ref/occ/instm/SKNode/enumerateChildNodesWithName:usingBlock:
Related
I've added several sprite (SKSpriteNode) in my Scene like this:
let NPuzzle_Texture = SKTexture(imageNamed: "SKTexture.png")
NPuzzle0 = SKSpriteNode(texture: NPuzzle_Texture)
NPuzzle1 = SKSpriteNode(texture: NPuzzle_Texture)
NPuzzle2 = SKSpriteNode(texture: NPuzzle_Texture)
NPuzzle0.position = CGPoint (x: 100, y:125)
NPuzzle0.position = CGPoint (x: 300, y:125)
NPuzzle0.position = CGPoint (x: 500, y:125)
background.addChild(NPuzzle0)
background.addChild(NPuzzle1)
background.addChild(NPuzzle2)
I've also added several sprites in a different texture:
let Grey_Back = SKTexture(imageNamed: "Grey_Back.png")
grey_back = SKSpriteNode(texture: Grey_back)
grey_back.position = CGPoint (x: 1024, y:125)
grey_back.alpha = 0.5
background.addChild(grey_back)
Now I add new sprites (SKSpriteNode) to precedent grey_back like this:
grey_back.addChild(new_sprite1)
grey_back.addChild(new_sprite2)
grey_back.addChild(new_sprite3)
When I try to see if positions of new_sprite intersects with NPuzzle sprites, nothing is happening. But if the new_sprite are added to the scene with:
background.addChild(new_sprite0)
background.addChild(new_sprite1)
background.addChild(new_sprite2)
it works. In fact it doesn't work if the sprites have been added with differents parents (background and grey_back). What I don't understand is grey_back is the child of background, so, it should work. Why it doesn't ?
Here is an example of the code to check if sprites intersects eachothers:
switch selectedNode.name {
case "new_sprite0":
if selectedNode.frame.intersects(NPuzzle0.frame) && (selectedNode.angle == 0) {
Thanks !
switch selectedNode.name {
let framePoint = CGPointMake(selectedNode.frame.origin.x, selectedNode.frame.origin.y)
let translatedPoint = selectedNode.parent!.convertPoint(framePoint, toNode:NPuzzle0.parent!)
let translatedFrame = CGRectMake(translatedPoint.x, translatedPoint.y, selectedNode.frame.size.width, selectedNode.frame.size.height)
if translatedFrame.intersects(NPuzzle0.frame) && (selectedNode.zRotation == 0)
{
print("ok")
}
Obviously, because frames are relative to parents, the node MUST be added to the scene. You can see I also used a forced unwrap for parent...
I have created some particles animations with specific sprites which works fine if I use them in the function:
override init(size: CGSize)
I use the following lines:
let sheet_particles = Particles()
let particles_node = SKSpriteNode(texture: sheet_particles.particle000())
particles_node.name = kparticles
particles_node.position = CGPoint(x: 500, y: 500)
particles_node.zPosition = 5
background.addChild(particles_node)
particles_node.runAction(particlesAction)
To make them appear in my scene.
The problem I have is if I try to use them in other functions in my scene, I can not see them.
func panForTranslation(translation : CGPoint) {
let position = selectedNode.position
if selectedNode.name! == kpuzzleNodeName {
selectedNode.position = CGPoint(x: position.x + translation.x * 2, y: position.y + translation.y * 2)
switch selectedNode.name2 {
case "0":
if selectedNode.frame.intersects(NPuzzle13.frame) {
particles_node.position = selectedNode.position
particles_node.runAction(particlesAction)
NPuzzle13.hidden = false
selectedNode.removeFromParent()
}
I see no particles sprite when the condition "0" happens but I see correctly the NPuzzle13. When I check the position of the particles_node node, its position is equal with the node selectedNode. All that is OK, except for the visibility of the particles... What am I missing? Thanks.
About zPosition seems all correct. I dont see any anchorPoint in your code.
I think your switch-case is jumped (not fired, not executed) because you check switch selectedNode.name2 instead of switch selectedNode.name
sorry for the wall of text, I just wanted to give a good explanation of how my game is set up so you can understand my problem better.
I have a few questions about the game that I am currently making using swift.
I am making a game where a player has to dodge enemies. The player is an SKSpriteNode image which gets it’s physics body and is added to the scene here:
override func didMoveToView(view: SKView) {
player.position = CGPoint(x: size.width/2, y: size.height/2)
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width/2)
player.physicsBody?.dynamic = true
player.physicsBody?.categoryBitMask = PhysicsCategory.Player
player.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
player.physicsBody?.collisionBitMask = PhysicsCategory.None
player.physicsBody?.usesPreciseCollisionDetection = true
addChild(player)
}
This implementation seems to work great, I have collision working between the Player and Enemy.
The enemies are initialised as SkSpriteNodes too:
func addEnemy() {
let enemy = SKSpriteNode(imageNamed: “enemy”)
enemy.name = “enemy”
enemy.position = CGPoint(x: size.height/2, y: size.width/2)
self.addChild(enemy)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.size.width/2.75)
enemy.physicsBody?.dynamic = true
enemy.physicsBody?.categoryBitMask = PhysicsCategory.Enemy
enemy.physicsBody?.contactTestBitMask = PhysicsCategory.Player
enemy.physicsBody?.collisionBitMask = PhysicsCategory.None
// code for making the enemies move here (including speed)
}
enemy.runAction(SKAction.sequence([moveAction,removeAction]))
}
and are constantly spawned every 0.7 seconds in didMoveToView(view: SKView) like this:
runAction(SKAction.repeatActionForever(
SKAction.sequence([
SKAction.runBlock(addEnemy),
SKAction.waitForDuration(0.7)
])
))
So my question is as follows. Is this the most appropriate way to do what I am trying to achieve? That is; spawning multiple different types of enemies constantly at different intervals?
I am struggling to figure out how I could (for example), make the enemy start spawning only after 10 seconds has passed, or how I could change the direction of the enemies with the press of a UIButton on screen? It seems since they are all called and created when the view loads, I can’t be making constant changes to things like direction, speed etc.
What is a better way of doing this, or how can I do what I need?
Thanks :)
It sounds like what you want to do is use the update method instead of using a repeating SKAction.
In update you calculate how much time has passed using currentTime and then decided on if you should spawn an enemy or not. This also gives you a chance to spawn an enemy any way you want based on the current state of your game.
As far as the button. It really depends on your game, but most games have some sort of count down or requires some sort of user interaction before game play begins.
EDIT
My swift is a bit rusty but it would be something like this...
var lastUpdateTime = 0.0;
var spawnTimer = 0.0;
var nextSpawnTime = 10.0;
override func update(currentTime: CFTimeInterval) {
if (lastUpdateTime == 0.0)
{
lastUpdateTime = currentTime
return
}
spawnTimer += currentTime-lastUpdateTime
//Do any other update logic here
if (spawnTimer >= nextSpawnTime)
{
spawnTimer = 0.0
spawn()
}
lastUpdateTime = currentTime
}
func spawn ()
{
//spawn and change nextSpawnTime if you wish
println("spawn")
}
Hopefully that helps and makes sense.
I'm using Sprite Kit's built-in physics engine in a game I'm writing. In order to check whether or not a character is on the ground, I'm using the bodyAlongRayStart() function of the SKScene's physicsWorld to check for bodies underneath the bottom-left and bottom-right corners of the character (this function belong's to the character's class):
func isInAir(sceneRef:GameScene) -> Bool {
var isInAir:Bool = false
let distance:CGFloat = 32 // how far from the origin we should check
// define where the rays start and end
let bottomLeftRayStart:CGPoint = CGPoint(x: self.position.x - (self.hitbox.size.width/2), y: self.position.y - (self.hitbox.size.height/2))
let bottomLeftRayEnd:CGPoint = CGPoint(x: self.position.x - (self.hitbox.size.width/2), y: (self.position.y - (self.hitbox.size.height/2) - distance))
let bottomRightRayStart:CGPoint = CGPoint(x: self.position.x + (self.hitbox.size.width/2), y: self.position.y - (self.hitbox.size.height/2))
let bottomRightRayEnd:CGPoint = CGPoint(x: self.position.x + (self.hitbox.size.width/2), y: (self.position.y - (self.hitbox.size.height/2) - distance))
// Are there any bodies along the lines?
var bottomLeftBody:SKPhysicsBody! = sceneRef.physicsWorld.bodyAlongRayStart(bottomLeftRayStart, end:bottomLeftRayEnd)
var bottomRightBody:SKPhysicsBody! = sceneRef.physicsWorld.bodyAlongRayStart(bottomRightRayStart, end:bottomRightRayEnd)
// no bodies found
if bottomLeftBody == nil && bottomRightBody == nil {
isInAir = true
} else {
// if one of the rays finds a wall (floor) or slope, we're not in the air
if (bottomLeftBody != nil && (bottomLeftBody!.categoryBitMask == _entityType.wall.rawValue || bottomLeftBody!.categoryBitMask == _entityType.slope.rawValue)) || (bottomRightBody != nil && (bottomRightBody.categoryBitMask == _entityType.wall.rawValue || bottomRightBody!.categoryBitMask == _entityType.slope.rawValue)) {
isInAir = false
}
}
return isInAir
}
This code is called in the scene's update() function (though I should probably move it to didSimulatePhysics()...) and it works fine! What's annoying, however, is that Swift in its infinite wisdom decides to print Chance to the console every time it detects a body along the ray.
With only one character in the scene it's not too much of a bother, but I'll be adding enemies soon (who work in much the same way, but will have different functions for handling their movement) and all those println() calls will slow the simulator right down. I can't see a way to suppress this; the call is coming from the class library which I can't alter.
Is there a reliable way to override the standard println() function, gloabally? Something like this:
func println(object: Any) {
#if DEBUG
Swift.println(object)
#endif
}
And if so, where in my project should I put it? It's currently in AppDelegate.swift but other libraries (and the bodyAlongRayStart() function) are still printing to the console even when I change the flag...
I submitted this bug to Apple. They told me it's fixed in the next version of Xcode. If it's really bugging you, you can download Xcode 6.3 beta.
So I'm playing around and slowly making my first iOS game. I'm trying to get my sprite object to set out a straight path angled towards the location of the player (which is where the user last touched).
The code I have makes the bee move towards the player and stop when it gets to it, instead of going from one side of the screen, through the player and off the other side of the screen.
var beeSpeed = 2.0
var moveAccross = SKAction.moveTo(CGPointMake(player.position.x,player.position.y), duration:beeSpeed)
badBee.runAction(moveAccross)
I will be having many objects constantly spawning that should all set their path once to the player location at the time they spawn.
Any help would be great,
I think I figured it out, if anyone could check my code and make sure it's doing the right thing that would be great :)
let actionMove = SKAction.moveTo(CGPoint(x: player.position.x-player.position.x-bee.size.width, y: player.position.y), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
bee.runAction(SKAction.sequence([actionMove, actionMoveDone]))
You have to calculate the slope from the spawning point to the player and then interpolate to somewhere outside the screen.
func add(location:CGPoint)
{
let bee = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(10, 10))
bee.name = "Bee"
bee.position = location
self.addChild(bee)
let slopeToPlayer = (bee.position.y - player.position.y) / (bee.position.x - player.position.x)
let constant = bee.position.y - slopeToPlayer * bee.position.x
let finalX : CGFloat = bee.position.x < player.position.x ? 500.0 : -500.0 // Set it to somewhere outside screen size
let finalY = constant + slopeToPlayer * finalX
let distance = (bee.position.y - finalY) * (bee.position.y - finalY) + (bee.position.x - finalX) * (bee.position.x - finalX)
let beeSpeed : CGFloat = 100.0
let timeToCoverDistance = sqrt(distance) / beeSpeed
let moveAction = SKAction.moveTo(CGPointMake(finalX, finalY), duration: NSTimeInterval(timeToCoverDistance))
let removeAction = SKAction.runBlock { () -> Void in
bee.removeFromParent()
println("removed")
}
bee.runAction(SKAction.sequence([moveAction,removeAction]))
}