Hero jumps over walls after colliding with them - sprite-kit

In the following code, the 'hero' starts inside of the level. As it moves around the map, the black area outside of the map becomes visible. As the hero comes up to the wall he stops. But then, if I touch the black area outside of the level, the hero jumps over the wall and into the outside-of-the-level area. Also, sometimes when the hero contacts the wall he bounces back in the opposite direction. (I'm not really sure what is causing that.) What I'm trying to do is keep the hero inside the level, and stop the bouncing back that is happening sometimes.
I'm not sure if the issue is that I'm not doing my collisions correctly or if I need to somehow stop the black area from being visible at all. I think stopping the area outside of the level from showing is what I need but playing around with let scene = GameScene(size: view.bounds.size) and changing it to let scene = GameScene(size: tileMap.frame.size) didn't work. Here is my code:
import SpriteKit
let tileMap = JSTileMap(named: "level2.tmx")
let hero = SKSpriteNode(imageNamed: "hero")
let theCamera: SKCameraNode = SKCameraNode()
class GameScene: SKScene, SKPhysicsContactDelegate {
enum ColliderType: UInt32 {
case Hero = 1
case Wall = 2
}
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
self.anchorPoint = CGPoint(x: 0, y: 0)
self.position = CGPoint(x: 0, y: 0)
hero.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
hero.xScale = 0.5
hero.yScale = 0.5
hero.zPosition = 2
hero.physicsBody = SKPhysicsBody(circleOfRadius: hero.size.height / 2)
hero.physicsBody?.affectedByGravity = false
hero.physicsBody!.dynamic = true
hero.physicsBody!.categoryBitMask = ColliderType.Hero.rawValue
hero.physicsBody!.contactTestBitMask = ColliderType.Wall.rawValue
hero.physicsBody!.collisionBitMask = ColliderType.Wall.rawValue
tileMap.zPosition = 1
tileMap.position = CGPoint(x: 0, y: 0)
self.addChild(tileMap)
self.addChild(hero)
self.addChild(theCamera)
self.camera = theCamera
camera?.position = hero.position
addWalls()
}
func didBeginContact(contact: SKPhysicsContact) {
print("Hero made contact with a wall")
}
func addWalls() {
//Go through every point up the tile map
for var a = 0; a < Int(tileMap.mapSize.width); a++ {
//Go through every point across the tile map
for var b = 0; b < Int(tileMap.mapSize.height); b++ {
//Get the first layer (you may want to pick another layer if you don't want to use the first one on the tile map)
let layerInfo:TMXLayerInfo = tileMap.layers[1] as! TMXLayerInfo
//Create a point with a and b
let point = CGPoint(x: a, y: b)
//The gID is the ID of the tile. They start at 1 up the the amount of tiles in your tile set.
let gid = layerInfo.layer.tileGidAt(layerInfo.layer.pointForCoord(point))
//My gIDs for the floor were 2, 9 and 8 so I checked for those values
if gid == 1 {
//I fetched a node at that point created by JSTileMap
let node = layerInfo.layer.tileAtCoord(point)
//I added a physics body
node.physicsBody = SKPhysicsBody(rectangleOfSize: node.frame.size)
node.physicsBody?.dynamic = false
node.physicsBody!.categoryBitMask = ColliderType.Wall.rawValue
node.physicsBody!.contactTestBitMask = ColliderType.Hero.rawValue
node.physicsBody!.collisionBitMask = ColliderType.Wall.rawValue
}
}
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
let action = SKAction.moveTo(location, duration: 1)
hero.runAction(action)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let action = SKAction.moveTo(hero.position, duration: 0.25)
theCamera.runAction(action)
}
}
My TMX file:
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" renderorder="right-down" width="24" height="42" tilewidth="32" tileheight="32" nextobjectid="13">
<tileset firstgid="1" name="grass-tiles-2-small" tilewidth="32" tileheight="32" tilecount="72">
<image source="grass-tiles-2-small.png" trans="ff00ff" width="384" height="192"/>
</tileset>
<layer name="Tile Layer 1" width="24" height="42">
<data encoding="base64" compression="zlib">
eJztzLEJAAAMw7Bs/f/jXhHIIINXJf2uNJ/P5/P5fD6fz+fz+Ut+swfI8xgR
</data>
</layer>
<layer name="Walls" width="24" height="42">
<data encoding="base64" compression="zlib">
eJztzLEJAAAMw7Dk/6d7RaCDDF7VJB2/is/n8/l8Pp/P5/P5/E/+8gMA/ACB
</data>
</layer>
</map>
Here is a video of both the bouncing when the hero touches the wall and the jumping over the wall Video of app sim

When a contact happens between a node and a physics body, SpriteKit will just detect the contact but won't take responsibility for stopping the node's action as we hitting an obstacle in the daily life. So you need to stop it manually.
Let's add a key value for SKAction of hero to distinguish the moving action from other actions which may be added in the future:
hero.runAction(action, withKey: "move")
Then, modify didBeginContact method to remove the action when contact happens, and I hope this will make what you want:
func didBeginContact(contact: SKPhysicsContact) {
print("Hero made contact with a wall")
// Stop your hero
hero.removeActionForKey("move")
}

Related

Moving Background Swift SKSpriteKit

I'm currently working on an application where I'm trying to set up a moving background. I have a transparent image full of clouds that I'm currently using. My problem is, how can I make it move more smoother? I've tried to play with the speeds but it still looks laggy. Any help would be a blessing.
Here's a video of what I got going. http://sendvid.com/78ggkzcj
And here's a picture of the cloud image. Cloud Image
Here's my code. What you think I should change or do differently?
class GameScene: SKScene {
// Background
let background = SKSpriteNode(texture:SKTexture(imageNamed: "background"))
// Clouds
var mainCloud = SKSpriteNode()
var cloud1Next = SKSpriteNode()
// Time of last frame
var lastFrameTime : TimeInterval = 0
// Time since last frame
var deltaTime : TimeInterval = 0
override func didMove(to view: SKView) {
background.position = CGPoint(x: 0, y: 0)
// Prepare the clouds sprites
mainCloud = SKSpriteNode(texture:
SKTexture(imageNamed: "cloudbg1"))
mainCloud.position = CGPoint(x: 0, y: 0)
cloud1Next = mainCloud.copy() as! SKSpriteNode
cloud1Next.position =
CGPoint(x: mainCloud.position.x + mainCloud.size.width,
y: mainCloud.position.y)
// Add the sprites to the scene
self.addChild(background)
self.addChild(mainCloud)
self.addChild(cloud1Next)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
// First, update the delta time values:
// If we don't have a last frame time value, this is the first frame,
// so delta time will be zero.
if lastFrameTime <= 0 {
lastFrameTime = currentTime
}
// Update delta time
deltaTime = currentTime - lastFrameTime
// Set last frame time to current time
lastFrameTime = currentTime
// Next, move each of the four pairs of sprites.
// Objects that should appear move slower than foreground objects.
self.moveSprite(sprite: mainCloud, nextSprite:cloud1Next, speed:100)
}
// Move a pair of sprites leftward based on a speed value;
// when either of the sprites goes off-screen, move it to the
// right so that it appears to be seamless movement
func moveSprite(sprite : SKSpriteNode,
nextSprite : SKSpriteNode, speed : Float) -> Void {
var newPosition = CGPoint.zero
// For both the sprite and its duplicate:
for spriteToMove in [sprite, nextSprite] {
// Shift the sprite leftward based on the speed
newPosition = spriteToMove.position
newPosition.x -= CGFloat(speed * Float(deltaTime))
spriteToMove.position = newPosition
// If this sprite is now offscreen (i.e., its rightmost edge is
// farther left than the scene's leftmost edge):
if spriteToMove.frame.maxX < self.frame.minX {
// Shift it over so that it's now to the immediate right
// of the other sprite.
// This means that the two sprites are effectively
// leap-frogging each other as they both move.
spriteToMove.position =
CGPoint(x: spriteToMove.position.x +
spriteToMove.size.width * 2,
y: spriteToMove.position.y)
}
}
}
}
Your code looks fine. You are getting low fps because you are running your game in the simulator. If you run on a real device, it should be smooth.

Sprite Kit - Sometimes ball disappearing from the screen?

I'm new with sprite kit. I have tried simple ball bouncing game with 2 player, another is tracking the ball slowly. But I have discovered a problem. When I move the line to ball (with edge) ball disappearing from the screen. Another times not a problem, ball bouncing. What is the problem?
I have one GameScene, sks and ViewController. My sprite nodes coming from sks. If someone explain this case. It would be better. I have attached what I did below.
My GameScene:
class GameScene: SKScene {
var ball = SKSpriteNode()
var enemy = SKSpriteNode()
var main = SKSpriteNode()
override func didMove(to view: SKView) {
ball = self.childNode(withName: "ball") as! SKSpriteNode
enemy = self.childNode(withName: "enemy") as! SKSpriteNode
main = self.childNode(withName: "main") as! SKSpriteNode
ball.physicsBody?.applyImpulse(CGVector(dx: -20, dy: -20))
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
self.physicsBody = border
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.run(SKAction.moveTo(x: location.x, duration: 0.2))
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.run(SKAction.moveTo(x: location.x, duration: 0.2))
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
enemy.run(SKAction.moveTo(x: ball.position.x, duration: 0.5))
}
View controller:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
}
}
override var prefersStatusBarHidden: Bool {
return true
}
Pad settings:
Ball settings:
Some updates
I have tried some messages in update function, then encountered with same case ball goes outside from left side of the device (using iPhone 6S)
2016-12-08 14:27:54.436485 Pong[14261:3102941] fatal error: ball out of left bounds: file
You're pinching the ball against the wall, with the enemy. This means that the force is eventually enough to create enough speed of ball movement/force to overcome the physics system, so it pops through the wall. If you make your enemy stop before it pinces the ball against the wall, you should be fine.
This 'pincing' is occurring because of this line of code:
enemy.run(SKAction.moveTo(x: ball.position.x, duration: 0.5))
This is making the enemy chase the ball, which is a good idea for a ball game, but for the way it's being moved is wrong. Using an Action means the enemy has infinite force applied to it, and is aiming for the middle of the ball.
So when the ball gets to the wall, it's stopped against a physics object with infinite static force, then this enemy comes along and applies infinite force from the other side... and the ball either pops inside the bounds of the enemy, or over the other side of the wall, because it's being crushed by infinite forces.
So you either need to take very good care of how you control the enemy with Actions, or use forces to control the enemy, as these won't be infinite, and the physics system will be able to push back on the enemy.
How easy is it to reproduce the problem? In update(), print the ball's position to see where it is when it has 'disappeared'. (this will produce a lot of output, so be warned).
From what you've posted, it doesn't look like the ball is set to collide with the border, meaning the ball will not react (i.e. bounce off) the border and the border itself is immobile (as it's an edge-based physics body). This, combined with a high ball velocity (from a hard hit) might make it possible that you have hit the ball so hard with the 'main' sprite that it's gone through the border - using preciseCollisionDetection=true might resolve this but give the border a category first and add this to the ball's collisionBitMask.
here is an example of what Steve is saying (in your .update())
if ball.position.x > frame.maxX { fatalError(" ball out of right bounds") }
if ball.position.x < frame.minX { fatalError(" ball out of left bounds") }
if ball.position.y > frame.maxY { fatalError(" ball out of top bounds") }
if ball.position.y < frame.minY { fatalError(" ball out of bottom bounds) }
you could also just spam your debug window:
print(ball.position)
This will help you to find out what is going on--if your ball is flying through the boundary, or if it's getting destroyed somewhere, or some other possible bug.
As a workaround (for now) I would just replace the above "fatalError" with "ball.position = CGPoint(x: 0, y: 0)" or some other position to "reset" the ball in case of it getting lost.
You could even store it's last position in a variable, then restore it to that should the above if-statements trigger.
var lastBallLocation = CGPoint(x: 0, y: 0) // Just to initialize
override func update( prams ) {
if ball.position.x > frame.maxX { ball.position = lastBallLocation }
// .. copy the other three cases
lastBallLocation = ball.position // update only on successful position
Or, you could try making the walls thicker (use a shape node or spritenode and lay them on the outside of the frame such as the walls of a house, and your view on screen is the "room")
each wall also has a physics body for bouncing:

How do I replace an image of an SKSpriteNode for the duration of an action then return it to it's original texture atlas loop

I am trying to make a basic run and jump game in SpriteKit.
When the view loads I wish for the sprite node to Run using images from a texture atlas. This I have managed to do.
When the screen is touched I wish this image to change to another image in the texture atlas called Jump.
When the character returns to the ground I wish it to go back to the original texture atlas loop.
So far I have coded the following:
import UIKit
import SpriteKit
class Level1: SKScene {
var Hero : SKSpriteNode!
//Creates an object for the Hero character.
let textureAtlas = SKTextureAtlas(named:"RunImages.atlas")
//Specifies the image atlas used.
var spriteArray = Array<SKTexture>();
//Creates a variable for the image atlas of him running.
var HeroBaseLine = CGFloat (0)
//This is where the Hero character sits on top of the ground.
var onGround = true
//Creates a variable to specify if Hero is on the ground.
var velocityY = CGFloat (0)
//Creates a variable to hold a three decimal point specification for velocity in the Y axis.
let gravity = CGFloat (0.6)
//Creates a non variable setting for gravity in the scene.
let movingGround = SKSpriteNode (imageNamed: "Ground")
//Creates an object for the moving ground and assigns the Ground image to it.
var originalMovingGroundPositionX = CGFloat (0)
//Sets a variable for the original ground position before it starts to move.
var MaxGroundX = CGFloat (0)
//Sets a variable for the maximum
var groundSpeed = 4
//Sets the ground speed. This number is how many pixels it will move the ground to the left every frame.
override func didMoveToView(view: SKView) {
//Misc setup tasks.
backgroundColor = (UIColor.blackColor())
//Sets the background colour when the view loads.
//Ground related tasks.
self.movingGround.anchorPoint = CGPointMake(0, 0.5)
//Positions the Ground image hard left in the X axis.
self.movingGround.position = CGPointMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame) + (self.movingGround.size.height / 2))
//Positions the Ground image at the bottom of the screen relative to half the height of the image.
self.addChild(self.movingGround)
//Creates an instance of the Ground image that follows the parameters set in the lines above when the view loads.
self.originalMovingGroundPositionX = self.movingGround.position.x
//Sets the starting position for the ground image in the x before it start to move.
self.MaxGroundX = self.movingGround.size.width - self.frame.size.width
//Sets the maximum ground size minus the width of the screen to create the loop point in the image.
self.MaxGroundX *= -1
//This multiplies the size of the ground by itself and makes the max ground size a negative number as the image is moving towards the left in x which is negative.
//Hero related tasks.
spriteArray.append(textureAtlas.textureNamed("Run1"));
spriteArray.append(textureAtlas.textureNamed("Run2"));
spriteArray.append(textureAtlas.textureNamed("Run3"));
spriteArray.append(textureAtlas.textureNamed("Run2"));
Hero = SKSpriteNode(texture:spriteArray[0]);
self.HeroBaseLine = self.movingGround.position.y + (self.movingGround.size.height / 2) + 25
//self.Hero.position = CGPointMake(CGRectGetMinX(self.frame) + 50, self.HeroBaseLine)
self.Hero.position = CGPointMake(CGRectGetMinX(self.frame) + 50, self.HeroBaseLine)
//Sets where the character will appear exactly.
self.Hero.xScale = 0.15
self.Hero.yScale = 0.15
addChild(self.Hero);
//Adds an instance of Hero to the screen.
let animateAction = SKAction.animateWithTextures(self.spriteArray, timePerFrame: 0.15);
let moveAction = SKAction.moveBy(CGVector(dx: 0,dy: 0), duration: 0.0);
//Although currently set to 0, the above line controls the displacement of the character in the x and y axis if required.
let group = SKAction.group([ animateAction,moveAction]);
let repeatAction = SKAction.repeatActionForever(group);
self.Hero.runAction(repeatAction);
//Animation action to make him run. Here we can affect the frames and x, y movement, etc.
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if self.onGround {
self.velocityY = -18
self.onGround = false
}
}
//This block specifies what happens when the screen is touched.
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if self.velocityY < -9.0 {
self.velocityY = -9.0
}
}
//This block prevents Hero from jumping whilst already jumping.
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if self.movingGround.position.x <= MaxGroundX {
self.movingGround.position.x = self.originalMovingGroundPositionX
}
//This is how the ground is positioned at the beginning of each update (each frame refresh)
movingGround.position.x -= CGFloat (self.groundSpeed)
//This is how the ground is moved relative to the ground speed variable set at the top. The number in the variable is how many pixels the frame is being moved each frame refresh.
self.velocityY += self.gravity
self.Hero.position.y -= velocityY
if self.Hero.position.y < self.HeroBaseLine {
self.Hero.position.y = self.HeroBaseLine
velocityY = 0.0
self.onGround = true
}
//This is the code for making Hero jump in accordance to the velocity and gravity specified at the top of the class in relation to the base line.
}
}
I have tried to add code in the touchesBegan section to change the image texture of the sprite node to another image in my image atlas called Jump.
I then wish for the texture atlas to return to animating once the touches ended action is called.
Any help would be greatly appreciated.
Update:
I have implemented what you have suggested but it still doesn't work quite correclty. The hero is changing to the jumping image but does not actually jump and is stuck in the jump image.
I created a JumpImages.atlas and added "Jump" image to that folder.
I have modified the code to the following:
import UIKit
import SpriteKit
class Level1: SKScene {
//Creates an object for the Hero character.
var Hero : SKSpriteNode!
//Specifies the image atlas used.
let textureAtlas = SKTextureAtlas(named:"RunImages.atlas")
//Creates a variable for the image atlas of him running.
var spriteArray = Array<SKTexture>();
var jumpArray = Array<SKTexture>();
let jumpAtlas = SKTextureAtlas(named:"JumpImages.atlas")
//jumpArray.append(jumpAtlas.textureNamed("Jump")) Didn't work in this area, moved it to the did move to view.
//This is where the Hero character sits on top of the ground.
var HeroBaseLine = CGFloat (0)
//Creates a variable to specify if Hero is on the ground.
var onGround = true
//Creates a variable to hold a three decimal point specification for velocity in the Y axis.
var velocityY = CGFloat (0)
//Creates a non variable setting for gravity in the scene.
let gravity = CGFloat (0.6)
//Creates an object for the moving ground and assigns the Ground image to it.
let movingGround = SKSpriteNode (imageNamed: "Ground")
//Sets a variable for the original ground position before it starts to move.
var originalMovingGroundPositionX = CGFloat (0)
//Sets a variable for the maximum
var MaxGroundX = CGFloat (0)
//Sets the ground speed. This number is how many pixels it will move the ground to the left every frame.
var groundSpeed = 4
override func didMoveToView(view: SKView) {
//Misc setup tasks.
//Sets the background colour when the view loads.
backgroundColor = (UIColor.blackColor())
//Ground related tasks.
//Positions the Ground image hard left in the X axis.
self.movingGround.anchorPoint = CGPointMake(0, 0.5)
//Positions the Ground image at the bottom of the screen relative to half the height of the image.
self.movingGround.position = CGPointMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame) + (self.movingGround.size.height / 2))
//Creates an instance of the Ground image that follows the parameters set in the lines above when the view loads.
self.addChild(self.movingGround)
//Sets the starting position for the ground image in the x before it start to move.
self.originalMovingGroundPositionX = self.movingGround.position.x
//Sets the maximum ground size minus the witdth of the screen to create the loop point in the image.
self.MaxGroundX = self.movingGround.size.width - self.frame.size.width
//This multiplies the size of the ground by itself and makes the max ground size a negative number as the image is moving towards the left in x which is negative.
self.MaxGroundX *= -1
//Hero related tasks.
spriteArray.append(textureAtlas.textureNamed("Run1"));
spriteArray.append(textureAtlas.textureNamed("Run2"));
spriteArray.append(textureAtlas.textureNamed("Run3"));
spriteArray.append(textureAtlas.textureNamed("Run2"));
Hero = SKSpriteNode(texture:spriteArray[0]);
self.HeroBaseLine = self.movingGround.position.y + (self.movingGround.size.height / 2) + 25
//Sets where the character will appear exactly.
self.Hero.position = CGPointMake(CGRectGetMinX(self.frame) + 50, self.HeroBaseLine)
//Scales the image to an appropriate size.
self.Hero.xScale = 0.15
self.Hero.yScale = 0.15
//Adds an instance of Hero to the screen.
addChild(self.Hero);
//Added this here as it didn't appear to work in the place recommended.
jumpArray.append(jumpAtlas.textureNamed("Jump"));
//I added this so that he runs when the view loads.
if self.onGround {
run()
}
}
//Animation function to make him run. Here we can affect the frames and x, y movement, etc.
func run() {
let animateAction = SKAction.animateWithTextures(self.spriteArray, timePerFrame: 0.15);
//Although currently set to 0, the above line controls the displacement of the character in the x and y axis if required.
let moveAction = SKAction.moveBy(CGVector(dx: 0,dy: 0), duration: 0.0);
let group = SKAction.group([animateAction,moveAction]);
let repeatAction = SKAction.repeatActionForever(group);
self.Hero.runAction(repeatAction);
}
//Animation function to make him jump.
func jump() {
self.velocityY = -18
self.onGround = false
let jumpAnimation = SKAction.animateWithTextures(jumpArray, timePerFrame: 0.15)
self.Hero.runAction(SKAction.repeatActionForever(jumpAnimation))
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//This block specifies what happens when the screen is touched.
if self.onGround {
jump()
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
//This block prevents Hero from jumping whilst already jumping.
if self.velocityY < -9.0 {
self.velocityY = -9.0
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
//This is how the ground is positioned at the beginning of each update (each frame refresh)
if self.movingGround.position.x <= MaxGroundX {
self.movingGround.position.x = self.originalMovingGroundPositionX
}
//This is how the ground is moved relative to the ground speed variable set at the top. The number in the variable is how many pixels the frame is being moved each frame refresh.
movingGround.position.x -= CGFloat (self.groundSpeed)
//This is the code for making Hero jump in accordance to the velocity and gravity specified at the top of the class in realation to the base line and run when he hits the ground.
if self.Hero.position.y < self.HeroBaseLine {
self.Hero.position.y = self.HeroBaseLine
velocityY = 0.0
if self.onGround == false {
self.onGround = true
run()
}
}
}
}
Is there anything obvious I am doing wrong? Thanks for your help.
Since you have already made your sprite run, to jump is not a hard thing. Just replace the texture of run animation with the texture of jump animation in proper place.
Firstly, I wrap the code of run animation for reuse later.
func run() {
let animateAction = SKAction.animateWithTextures(self.spriteArray, timePerFrame: 0.15);
let moveAction = SKAction.moveBy(CGVector(dx: 0,dy: 0), duration: 0.0);
let group = SKAction.group([animateAction,moveAction]);
let repeatAction = SKAction.repeatActionForever(group);
self.Hero.runAction(repeatAction);
}
Next step is for texture atlas of Jump. For demo, I just add one frame animation for jumping. Add these line after you create textureAtlas and spriteArray for Run.
var jumpArray = Array<SKTexture>()
let jumpAtlas = SKTextureAtlas(named:"JumpImages.atlas")
jumpArray.append(jumpAtlas.textureNamed("Jump"))
After you write function jump(), you can call it in touchesBegan.
func jump() {
self.velocityY = -18
self.onGround = false
println("jump over ground")
let jumpAnimation = SKAction.animateWithTextures(jumpArray, timePerFrame: 0.15)
self.Hero.runAction(SKAction.repeatActionForever(jumpAnimation))
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
if self.onGround {
jump()
}
}
Last but not least, resume running animation after back to the ground in update.
override func update(currentTime: CFTimeInterval) {
...
if self.Hero.position.y < self.HeroBaseLine {
self.Hero.position.y = self.HeroBaseLine
velocityY = 0.0
if self.onGround == false {
self.onGround = true
println("on the ground")
run()
}
}
}
Now you should get the result below. If you have any problem with the code, just let me know.

How to drag a SKSpriteNode without touching it with Swift

I'm trying to move SKSpriteNode horizontally by dragging. To get the idea of what I try to achieve you can watch this. I want player to be able to drag sprite without touching it. But I don't really know how to implement it correctly.
I tried to do something like:
override func didMoveToView(view: SKView) {
/* Setup your scene here */
capLeft.size = self.capLeft.size
self.capLeft.position = CGPointMake(CGRectGetMinX(self.frame) + self.capLeft.size.height * 2, CGRectGetMinY(self.frame) + self.capLeft.size.height * 1.5)
capLeft.zPosition = 1
self.addChild(capLeft)
let panLeftCap: UIPanGestureRecognizer = UIPanGestureRecognizer(target: capLeft, action: Selector("moveLeftCap:"))
And when I'm setting a moveLeftCap function, code that I've found for UIPanGestureRecognizer is requiring "View" and gives me an error. I also wanted to limit min and max positions of a sprite through which it shouldn't go.
Any ideas how to implement that?
You probably get that error because you can't just access the view from any node in the tree. You could to refer to it as scene!.view or you handle the gesture within you scene instead which is preferable if you want to keep things simple.
I gave it a try and came up with this basic scene:
import SpriteKit
class GameScene: SKScene {
var shape:SKNode!
override func didMoveToView(view: SKView) {
//creates the shape to be moved
shape = SKShapeNode(circleOfRadius: 30.0)
shape.position = CGPointMake(frame.midX, frame.midY)
addChild(shape)
//sets up gesture recognizer
let pan = UIPanGestureRecognizer(target: self, action: "panned:")
view.addGestureRecognizer(pan)
}
var previousTranslateX:CGFloat = 0.0
func panned (sender:UIPanGestureRecognizer) {
//retrieve pan movement along the x-axis of the view since the gesture began
let currentTranslateX = sender.translationInView(view!).x
//calculate translation since last measurement
let translateX = currentTranslateX - previousTranslateX
//move shape within frame boundaries
let newShapeX = shape.position.x + translateX
if newShapeX < frame.maxX && newShapeX > frame.minX {
shape.position = CGPointMake(shape.position.x + translateX, shape.position.y)
}
//(re-)set previous measurement
if sender.state == .Ended {
previousTranslateX = 0
} else {
previousTranslateX = currentTranslateX
}
}
}
when you move you finger across the screen, the circle gets moves along the x-axis accordingly.
if you want to move the sprite in both x and y directions, remember to invert the y-values from the view (up in view is down in scene).

How do I make an endless horizontal background in Swift?

import SpriteKit
class GameScene: SKScene {
let bgline1 = SKSpriteNode(imageNamed: "Line.png")
let bgline2 = SKSpriteNode(imageNamed: "Line.png")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
backgroundColor = SKColor.whiteColor()
bgline1.anchorPoint = CGPointZero
bgline1.position = CGPointZero
bgline1.zPosition = 1
bgline1.size = CGSizeMake(self.frame.size.width, self.frame.size.height)
self.addChild(bgline1)
bgline2.anchorPoint = CGPointZero
bgline2.position = CGPointMake(bgline1.position.x-1, 0)
bgline2.zPosition = 1
bgline2.size = CGSizeMake(self.frame.size.width, self.frame.size.height)
self.addChild(bgline2)
}
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)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered*/
bgline1.position = CGPoint(x: bgline1.position.x-4, y: bgline1.position.y)
bgline2.position = CGPoint(x: bgline2.position.x-4, y: bgline2.position.y)
if bgline1.position.x < -bgline1.size.width{
bgline1.position = CGPointMake(bgline2.position.x + bgline2.size.width, bgline1.position.y)
}
if bgline2.position.x < -bgline2.size.width{
bgline2.position = CGPointMake(bgline1.position.x + bgline1.size.width, bgline2.position.y)
}
}
}
This is my code for creating a scrolling horizontal endless background. It is supposed to make an endless background but it isn't. The code is not looping the first try but after that it does. I would like to know why it does not work and how should I fix it.
EDIT- Starts out with 2 nodes and a line and the fps is <20. Then the line ends and when it "runs out" the node count becomes 0 and fps becomes 60. Than after a few seconds the line pops back up and it runs perfectly like a endless scrolling background.
bgline2.position = CGPointMake(bgline1.size.width-1, 0)
This line of code somehow fixed the problem for me. I don't know how it fixed it but it did. If someone wants to explain this for me please do. I have not seen any tutorials out there that use this but it worked for me.
Rohit