SKAction with Key changes Sprite Behavior - swift

I've been learning Swift while laid up from back surgery and any help with this is greatly appreciated.
Issue Description:
I have a function that pushes a sprite backwards when hit. When I use
self.runAction(jumpBackSequence)
I get the desired behavior. (The sprite is pushed back a good distance.)
Now when I update this to use "withKey" (because I need to be able to stop the forward action again on the next contact the forward movement is greatly dimished.
self.runAction(jumpBackSequence, withKey: "moveStraightLine")
My function creating this action is below:
func pushBack(){
self.removeActionForKey("moveStraightLine")
let height:CGFloat = 5
let jumpDistanceA:CGFloat = 20
let jumpDistanceB:CGFloat = 20
let duration:NSTimeInterval = 0.15
let jumpUp = SKAction.moveByX(0, y:height, duration:duration)
let moveBack = SKAction.moveByX(jumpDistanceA, y: 0, duration: duration)
let jStep1 = SKAction.group([jumpUp, moveBack])
let jumpDown = SKAction.moveByX(0, y:(height * -1), duration:duration)
let moveBack2 = SKAction.moveByX(jumpDistanceB, y: 0, duration: duration)
let jStep2 = SKAction.group([jumpDown, moveBack2])
let moveZombie1 = SKAction.moveToX(0, duration: spawnSpeed1() )
let removeZombie1 = SKAction.removeFromParent()
let moveAndRemoveZombie1 = SKAction.sequence([moveZombie1, removeZombie1])
let jumpBackSequence = SKAction.sequence([jStep1, jStep2, moveAndRemoveZombie1])
self.runAction(jumpBackSequence)
// self.runAction(jumpBackSequence, withKey: "moveStraightLine")
}

You are using moveTo, not moveBy, so when you add the action back in to move the zombie, you are now saying starting from the closer spot, take X seconds to move to the end, not take X seconds - the time already travelled. You do not want this due to the effect you see. Use moveBy, and the zombie speed will stay consistant

I'm not sure why this behavior happened, but I realized that the runAction withKey was actually moving the code the specified distance where as the example without the key was not. When I updated the code to use a fraction of screen width it behaves the same with and without a key - which makes me think this might actually be an minor bug in Swift.
To fix the issue I updated the distances to
let jumpDistanceA:CGFloat = (self.scene?.size.width)! * 0.15
let jumpDistanceB:CGFloat = (self.scene?.size.width)! * 0.15
I hope this helps someone else.
Best regards,
mpe

Related

How do I get SK sequences to run in swift

I found this pretty useful code online but I'm having trouble getting it to run. The variable names are all correct and I've used print statements to make sure it does make it to this function. It just doesn't seem to run the sequence on the Label nodes. Thanks
func fadeOutInfoText(){
infoLabel1.removeAllActions()
infoLabel2.removeAllActions()
speechIcon.removeAllActions()
let wait:SKAction = SKAction.wait(forDuration: 0.5)
let fade:SKAction = SKAction.fadeAlpha(to: 0, duration: 0.5)
let run:SKAction = SKAction.run {
self.infoLabel1.text = ""
self.infoLabel2.text = ""
self.infoLabel1.alpha = 1
self.infoLabel2.alpha = 1
self.speechIcon.alpha = 1
self.speechIcon.isHidden = true
}
let seq:SKAction = SKAction.sequence([wait,fade,run])
let seq2:SKAction = SKAction.sequence([wait,fade])
infoLabel1.run(seq)
infoLabel2.run(seq2)
speechIcon.run(seq2)
}
NOTE: This would be a comment (not enough reputation to do so yet :)
Executing the above code line-for-line (and adding in the nodes in an empty scene) gives what appears to be the desired result. Presumably you are not calling this function from the scene's update(_:) method, for this keeps the labels and speech icon from doing anything since the actions are removed before the scene performs the actions (see here). Make sure you aren't removing all actions and changing the alpha of the labels before this set of actions can complete elsewhere.
This is a sample code for Sequence.
let sprite = SKSpriteNode(imageNamed:"Spaceship")
let scale = SKAction.scale(to: 0.1, duration: 0.5)
let fade = SKAction.fadeOut(withDuration: 0.5)
let sequence = SKAction.sequence([scale, fade])
sprite.run(sequence)
Let me know it is useful or not.

How to gradually speed up falling nodes over time?

I have two dots that fall parallel to each other from the top of my screen that are to be matched with two circles on the bottom that can be rotated with touch. I have them generated like this:
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
runAction(SKAction.repeatActionForever(
SKAction.sequence([
SKAction.runBlock(generateDots),
SKAction.waitForDuration(1.0)])))
}
func generateDots() {
let count = 2
let index=Int(arc4random_uniform(UInt32(count)))
let dots = SKSpriteNode(imageNamed: "Color\(index+1)")
dots.physicsBody = SKPhysicsBody(circleOfRadius: 10)
dots.physicsBody?.dynamic = true
dots.physicsBody?.affectedByGravity = false
for i in 0..<2 {
dots.physicsBody?.categoryBitMask = UInt32(0x1 << index)
dots.physicsBody?.contactTestBitMask = UInt32(0x1 << index)
}
addChild(dots)
dots.size = CGSizeMake(45, 45)
dots.position = CGPointMake(150, 400)
dots.runAction(
SKAction.moveByX(0, y: -900,
duration: NSTimeInterval(11.5)))
}
}
Is there any way to gradually speed up either how fast they're falling or gradually change the waitForDuration so that over time it will produce a node every 3 sec, then 2 sec, then 1 sec and so forth?
This is completely doable! You just need to add some variables.
If you want to change how fast they fall then you need to make a variable like
Var droptime:NSTimeInterval = 11.5
Then in your "dropdot()" method you need to do two things.
At the beginning subtract or devide your droptime variable like...
Droptime -= 1
Then at the end when you generate the falling action make it
Duration: droptime
Instead of what it was before.
If you want to make the generation time be shorter then you need to make a function that you can trigger each time you want to make your action that the scene runs (like you did in the viewdidload) and edit it so that it has variable wait and triggers itself. Also you will need to self trigger it once in your didMoveToView method.
func controlMethod() {
waitdur -= 1
runAction(SKAction.repeatActionForever( SKAction.sequence([
SKAction.runBlock(generateDots),
SKAction.waitForDuration(waitdur),
SKAction.runBlock(controlMethod)
])))
}
Good luck!
So sorry for the formatting! I'm on mobile... Hopefully someone can fix it.

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!

Why is my node slowing down when it collects a coin?

My game basically is a jumping game when you tap the screen the heroNode jumps and collects coins coming from the right part of the screen. When it collects the coin the hero node slows down and it goes out of the view. Why does this happen? Heres the code I have.
func coins() {
let moveToLeft = SKAction.moveByX(-self.size.width, y: 0, duration: 2.0)
let repeatMoveToLeft = SKAction.repeatActionForever(moveToLeft)
let removeFromScene = SKAction.removeFromParent()
let sequenceThisMoveAndRemove = SKAction.sequence([repeatMoveToLeft, removeFromScene])
goldCoins.position = CGPointMake(self.size.width / 0.6, self.size.height / 2)
goldCoins.zPosition = 15
goldCoins.setScale(0.9)
goldCoins.runAction(sequenceThisMoveAndRemove)
addChild(goldCoins)
goldCoins.physicsBody = SKPhysicsBody(circleOfRadius: 5)
goldCoins.physicsBody?.affectedByGravity = false
goldCoins.physicsBody?.allowsRotation = false
goldCoins.physicsBody?.categoryBitMask = GoldCoinCategory
goldCoins.physicsBody?.contactTestBitMask = HeroCategory
goldCoins.physicsBody?.collisionBitMask = 0
func addHero() {
let anim = SKAction.animateWithTextures([heroTextureOne, heroTextureTwo], timePerFrame: 0.2)
let run = SKAction.repeatActionForever(anim)
theHero = SKSpriteNode(texture: heroTextureOne)
theHero.runAction(run)
theHero.physicsBody = SKPhysicsBody(circleOfRadius: 50)
theHero.physicsBody?.affectedByGravity = true
theHero.physicsBody?.allowsRotation = false
theHero.physicsBody?.categoryBitMask = HeroCategory
theHero.setScale(0.5)
theHero.position = CGPointMake(self.size.width / 4.0, self.size.height / 2.0)
theHero.zPosition = 15
addChild(theHero)
}
if firstBody.categoryBitMask == HeroCategory && sixthBody.categoryBitMask == GoldCoinCategory {
sixthBody.node!.removeFromParent()
One possibility is that you're making a lot of gold coins that never get removed from the scene graph, and that's bogging down your performance.
Look at your first four lines of coins(). You create a forever-repeating action, and then create a sequence with the forever-repeating action and then the "remove from scene" action. A sequence performs the given actions in order, but a forever-repeating action will never end, so that "remove from scene" action will never be triggered.
So when you addChild( goldCoins ), those coins are never going to go away. And the only other way they can apparently be removed is with a collision. So if you play the game, and if a lot of goldCoins get added, then you're going to have an unbounded number of coins in play. After a while, having enough of those coins, all running actions, could cause your game to slow down.
Another possibility is that all you're removing is the sprite node and not the physics body from the simulation. This is suggested by that last line you included. If you remove the node, the coin will disappear, but the physics body will still be in play, still affecting other physics bodies. If you want to fully remove the coin - and its effect on the physics simulation - you'll need to remove its physics body, too.

Running Stick Figure in Sprite Kit Animation

I am playing around a little bit in SpriteKit, and am trying to make a stick figure run. Due to my lack of experience, I believe I have started this process in an unconventional way.
Instead of drawing out each individual motion frame, I loaded the Separate images of the stick figure (ie the Head, the knee, the Thigh, the body, the arm, etc.) and told them to move accordingly. So far, everything fits together except for the motion and rotation of knee during the running sequence.
My problem is this:
When the thigh pulls back in it's stride, the knee becomes detached, and follows a straight line to the end of the stride while rotating.
My Question is this:
Is there a way to connect the thigh node to the calf node, as if it they were attached at a "knee" per say, while in motion? or is there some more efficient way?
Here is a snippet of my code, It may be a little more descriptive than I have been.
func thigh_run(){
//running config
//DONT CHANGE!
var thigh_rotation = CGFloat(M_PI)*0.8
//Can Change :)
var run_speed = NSTimeInterval(0.25)
// reset the action sequence... basically
let pos_nuetralize = SKAction.rotateByAngle(thigh_rotation/2, duration: run_speed)
let neg_nuetralize = SKAction.rotateByAngle(-thigh_rotation/2, duration: run_speed)
//strides
let thigh_pull = SKAction.rotateByAngle(-thigh_rotation, duration: run_speed*2)
let thigh_stride = SKAction.rotateByAngle(thigh_rotation/2, duration: run_speed)
// for the opposite leg
let b_thigh_pull = SKAction.rotateByAngle(-thigh_rotation/2, duration: run_speed)
let b_thigh_stride = SKAction.rotateByAngle(thigh_rotation, duration: run_speed*2)
//knee movement
let stride = SKAction.sequence([thigh_stride, thigh_pull, pos_nuetralize])
let pull = SKAction.sequence([b_thigh_pull, b_thigh_stride, neg_nuetralize])
r_arm.runAction(SKAction.repeatActionForever(stride))
f_leg_thigh.runAction(SKAction.repeatActionForever(stride))
b_leg_thigh.runAction(SKAction.repeatActionForever(pull))
//knee_run(run_speed)
}
func knee_run(speed: NSTimeInterval){
let knee_stride = SKAction.moveTo(CGPointMake(f_leg_thigh.position.x+f_leg_thigh.size.height, f_leg_thigh.position.y), duration: speed)
let knee_reset = SKAction.moveTo(CGPointMake(f_leg_thigh.position.x, f_leg_thigh.position.y-f_leg_thigh.size.height), duration: speed)
let knee_pull_rotate = SKAction.rotateToAngle(CGFloat(M_PI)/2, duration: speed, shortestUnitArc: true)
let knee_pull_rotate_half = SKAction.rotateToAngle(CGFloat(M_PI)*2, duration: speed, shortestUnitArc: true)
let knee_pull_half = SKAction.moveTo(CGPointMake(f_leg_thigh.position.x-f_leg_thigh.size.height, f_leg_thigh.position.y/2), duration: speed)
let knee_pull = SKAction.moveTo(CGPointMake(f_leg_thigh.position.x-f_leg_thigh.size.height, f_leg_thigh.position.y), duration: speed)
//Grouping of the knee moving and rotating at the same time
let knee_pull_group_half = SKAction.group([knee_pull_half, knee_pull_rotate_half])
let knee_pull_group = SKAction.group([knee_pull, knee_pull_rotate])
let f_knee_stride_seq = SKAction.sequence([knee_stride,knee_reset,knee_pull,knee_reset])
let f_knee_pull_seq = SKAction.sequence([knee_pull_group_half, knee_reset, knee_stride, knee_reset])
f_leg_calf.runAction(SKAction.repeatActionForever(f_knee_stride_seq))
b_leg_calf.runAction(SKAction.repeatActionForever(f_knee_pull_seq))
}
I understand it may be hard to read, I have Documented it for myself pretty well. the f_ prefix is for "front"leg and b is for "back"_leg. think of it as right leg and left leg however.
Anyway! Thanks in advance! I appreciate any help or tips! Comment for any further explanation! :)
The more efficient way is to use SKPhysicsJoint to connect the thigh node to the calf node.