I'm coding a game where I have a character following another character and I'd like to make it so that the second character jumps 1 or 2 seconds after the first character jumps. How might I accomplish this?
Here is my method that applies the impulse:
override func touchesBegan(touches: Set, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
fish.physicsBody?.velocity = (CGVectorMake(0,0))
fisherman.physicsBody?.velocity = (CGVectorMake(0,0))
fish.physicsBody?.applyImpulse(CGVectorMake(0, 1000))
fisherman.physicsBody?.applyImpulse(CGVectorMake(0, 3000))
}
}
As you already have follow action, you need just to chain it with delay action. There is a class method waitForDuration that allows you to initialize such Action fast.
Here is a code that will do delay and apply impulse afterward:
for touch in (touches as! Set<UITouch>) {
fish.physicsBody?.velocity = (CGVectorMake(0,0))
fisherman.physicsBody?.velocity = (CGVectorMake(0,0))
let delay = SKAction.waitForDuration(2.0)
let fishApplyImpulse = SKAction.applyImpulse(CGVectorMake(0, 1000),
duration sec: 0.0)
let fishActions = SKAction.sequence([delay, fishApplyImpulse])
let fishermanApplyImpulse = SKAction.applyImpulse(CGVectorMake(0, 3000),
duration sec: 0.0)
let fishermanActions = SKAction.sequence([delay, fishermanApplyImpulse])
fish.runAction(fishActions)
fisherman.runAction(fishermanActions)
}
Code uses SKAction additions for applyImpulse available starting from iOS 9.
Related
I need to keep track of animation with texture. I am animating power bar and when user clicks the screen it should stop and save the power. I can not figure out how to save power. So far I have this: on first touch power bar animates, but on the second touch it only stops but does not save power.
This is how I create animation:
textureAtlas = SKTextureAtlas(named:"images")
for i in 1...textureAtlas.textureNames.count{
let name = "\(i).png"
textureArray.append(SKTexture(imageNamed: name))
}
let animateForward = SKAction.animate(with: textureArray, timePerFrame: 0.1)
let animateBackward = SKAction.reversed(animateForward)
let sequence = SKAction.sequence([animateForward,animateBackward()])
This is how I detect touches:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouchStorage = touches.first{ // after first touch
let animateForward = SKAction.animate(with: textureArray, timePerFrame: 0.1)
let animateBackward = SKAction.reversed(animateForward)
let sequence = SKAction.sequence([animateForward,animateBackward()])
arrow.removeAllActions()
arrow.run(SKAction.repeatForever(sequence))
firstTouch = firstTouchStorage
}
for touch in touches{
if touch == firstTouch{
touchesArray.append(touch)
let angle = arrow.zRotation
if touchesArray.count == 2{
arrow.removeAllActions()
arrow.removeFromParent()
}
}
}
I am trying to solve this problem for too long, but I can not figure it out. I hope you will help me.
As #KnightOfDragon suggested : animateWithTextures has a restore: parameter, if you set that to false, when you stop animating the last texture should stay on the sprite. If you go ahead and read the textures description, then you will know what texture it stopped on, and could plan accordingly
So I am wondering about something. I have 3 functions:
func makeHeroRun(){
let heroRunAction = SKAction.animateWithTextures([ heroRun2, heroRun3, heroRun4, heroRun3], timePerFrame: 0.2)
let repeatedWalkAction = SKAction.repeatActionForever(heroRunAction)
hero.runAction(repeatedWalkAction)
}
func makeHeroJump(){
let heroJumpAction = SKAction.animateWithTextures([heroJump1, heroJump2, heroJump2], timePerFrame: 0.2)
hero.runAction(heroJumpAction)
}
func makeHeroSlide(){
let heroSlideAction = SKAction.animateWithTextures([heroSlide1, heroSlide2], timePerFrame: 0.1)
hero.runAction(heroSlideAction)
}
And also my touchesBegan:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches.first as! UITouch
var point = touch.locationInView(self.view)
if point.x < size.width / 2 // Detect Left Side Screen Press
{
makeHeroSlide()
}
else
if point.x > size.width / 2 // Detect Right Side Screen Press
{
makeHeroJump()
}
}
"Hero" is the player running in the game.
What i want is that when the "makeHeroSlide" is finish running, i want to repeat "makeHeroRun", so after the hero has been sliding, it should continue to run. And when the hero is jumping, it should stop running, and when the hero hits the ground, it should continue to run again. How can i do this? I want sort of the same functions that are in the "Line Runner" game in AppStore where the player Jumps and Roll.
You can call runAction function with completion: as second parameter which you can use to specify block to execute after it finishes executing the action.
hero.runAction(heroSlideAction, completion: {() -> Void in
// do something here
})
I am using the iOS SpriteKit Game Swift template on iOS8.3. I am trying to use the function func intersectsNode(_ node: SKNode) -> Bool to detect overlap of two circles created as SKShapeNodes. Turns out the function does not detect the intersection if its SKShapeNodes. But on further troubleshooting, it turns out the function works if I use the default Spaceship sprite of the template. Here is the code which works:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite.xScale = 0.3
sprite.yScale = 0.3
sprite.position = location
self.circles.append(sprite)
self.addChild(sprite)
if circles.count == 1 {
println("One circle")
} else {
for var index = 0; index < circles.count-1; ++index {
if sprite.intersectsNode(circles[index]) {
println(circles[index].frame)
println("Circle intersects another")
}
}
}
}
}
When two sprites overlap in the above code the function returns YES and prints the intersection string. Here is the code which does NOT work:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
let sprite = SKShapeNode(circleOfRadius: 50)
sprite.position = location
self.circles.append(sprite)
self.addChild(sprite)
if circles.count == 1 {
println("One circle")
} else {
for var index = 0; index < circles.count-1; ++index {
if sprite.intersectsNode(circles[index]) {
println(circles[index].frame)
println("Circle intersects another")
}
}
}
}
}
As you can see the code blocks are completely identical except in one case its an SKSpriteNode and the other case its SKShapeNode. I also printed the frames of the SKShapeNode Circles and I can see they have have valid frames. So I am puzzled by this as I would like to use SKShapeNodes in my code for now but I cannot use the intersectNode function as it does not work.
The documentation for intersectsNode says this:
The two nodes are considered to intersect if their frames intersect. The children of both nodes are ignored in this test.
Which means you won't get the result you want when using circles.
However, checking if two circles overlap is as easy as checking if the distance between their centers is less than the sum of their radiuses.
I want a game over screen and the game to stop when the screen is touched during a specific animation.
let lightTexture = SKTexture(imageNamed: "green light.png")
let lightTexture2 = SKTexture(imageNamed: "red light.png")
let animateGreenLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeGreenLight = SKAction.repeatActionForever(animateGreenLight)
let animateRedLight = SKAction.sequence([SKAction.waitForDuration(2.0, withRange: 0.1), SKAction.animateWithTextures([lightTexture, lightTexture2], timePerFrame: 3)])
let changeRedLight = SKAction.repeatActionForever(animateRedLight)
let greenLight = SKSpriteNode(texture: lightTexture)
greenLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
greenLight.runAction(changeGreenLight)
self.addChild(greenLight)
let redLight = SKSpriteNode(texture: lightTexture2)
redLight.position = CGPointMake(CGRectGetMidX(self.frame), 650)
redLight.runAction(changeRedLight)
self.addChild(redLight)
When the animation for the red light is on the screen, I want it to be game over. Do I have to make an if statement, and if so for what specifically?
Thank You in advance!
You can make yourself another node which has the same size and position as the red light. Additionally, that node is able to handle touch events. Then, add that node before the animation runs. This can be done with a sequence of actions, e.g.:
let addAction = SKAction.runBlock({ self.addChild(touchNode) })
let animationAction = SKAction.repeatActionForever(animateRedLight)
redLight.runAction(SKAction.sequence([ addAction, animationAction ]))
Update
Since you want the game to end when you touch anywhere, alter the code such that the block sets a variable which indicates that the animation is executed and implement touchesBegan which checks for that variable, e.g.
let addAction = SKAction.runBlock({ self.redLightAnimationRuns = true })
[...]
// In touchesBegan
if touched
{
if redLightAnimationRuns
{
endGame()
}
}
use the touchesBegan() function to call a GameOver() function when the red light is on the screen (which you can control with a variable).
So, when the red light comes on to the screen, variable redLightCurrent is set to true. in TouchesBegan(), when redLightCurrent is true, then call the gameOver() function where you can include what to do when the game is over. This will only occur when a touch has began on the screen.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
let array = Array(touches)
let touch = array[0] as UITouch
let touchLocation = touch.locationInNode(self)
if redLightCurrent {
gameOver()
}
}
This code works with the new xCode 7 and Swift 2.0
I am doing a small for fun project in Swift Xcode 6. The function thecircle() is called at a certain rate by a timer in didMoveToView(). My question is how do I detect if any one of the multiple circle nodes on the display is tapped? I currently do not see a way to access a single node in this function.
func thecircle() {
let circlenode = SKShapeNode(circleOfRadius: 25)
circlenode.strokeColor = UIColor.whiteColor()
circlenode.fillColor = UIColor.redColor()
let initialx = CGFloat(20)
let initialy = CGFloat(1015)
let initialposition = CGPoint(x: initialx, y: initialy)
circlenode.position = initialposition
self.addChild(circlenode)
let action1 = SKAction.moveTo(CGPoint(x: initialx, y: -20), duration: NSTimeInterval(5))
let action2 = SKAction.removeFromParent()
circlenode.runAction(SKAction.sequence([action1, action2]))
}
There are many problems with this.
You shouldnt be creating any looping timer in your games. A scene comes with an update method that is called at every frame of the game. Most of the time this is where you will be checking for changes in your scene.
You have no way of accessing circlenode from outside of your thecircle method. If you want to access from somewhere else you need to set up circlenode to be a property of your scene.
For example:
class GameScene: BaseScene {
let circlenode = SKShapeNode(circleOfRadius: 25)
You need to use the method touchesBegan. It should have come with your spritekit project. You can detect a touch to your node the following way:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
// detect touch in the scene
let location = touch.locationInNode(self)
// check if circlenode has been touched
if self.circlenode.containsPoint(location) {
// your code here
}
}
}