Create endless cgpath without framedrops - swift

I need to create a cgpath continuously. At the moment I do it like that:
func createLine(){
var rand = randomBetweenNumbers(1, 2)
currentY--
if rand < 1.5{
currentX--
CGPathAddLineToPoint(leftPath, nil, currentX, currentY)
}else{
currentX++
CGPathAddLineToPoint(leftPath, nil, currentX, currentY)
}
CGPathAddLineToPoint(rightPath, nil, currentX+tileSize, currentY)
lineNode.path = leftPath
rightNode.path = rightPath
}
And call it like that:
NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("startTile"), userInfo: nil, repeats: true)
But the problem is, that the frames drop lower and lower over time. Is there something I have to change so that the framerate no longer drops?
My goal is to create a random endless path.

The key to maintaining a high FPS count while drawing a progressively increasing number of lines is to quickly reach a state where adding more lines to the scene has little or no effect on frame rate. There are at least two ways to accomplish this.
The most straightforward of the two is to periodically convert the previously drawn lines to an SKTexture and display the results as texture of an SKSpriteNode. Here are the steps:
Create an SKNode to be used as a line container
Create an SKSpriteNode that will be used as a line canvas
Create an SKShapeNode to be used to draw new lines
Add the container to the scene and the canvas and shape node to the container
Draw a set of connected line segments using the path property of the shape node
When the line count reaches a predetermined value, convert the contents of the container to an 'SKTexture'
Set the texture property of the canvas to the SKTexture. Note that since the canvas is also a child of the container, its contents will also be added to the texture
Lather, rinse, repeat steps 5 - 7
Here's an example implementation in Swift that draws an endless set of lines at 60 FPS on an iPhone 6 device (you should test performance on a device not with the simulator):
class GameScene: SKScene {
// 1. Create container to hold new and old lines
var lineContainer = SKNode()
// 2. Create canvas
var lineCanvas:SKSpriteNode?
// 3. Create shape to draw new lines
var lineNode = SKShapeNode()
var lastDrawTime:Int64 = 0
var lineCount = 0
var timeScan:Int64 = 0
var path = CGPathCreateMutable()
var lastPoint = CGPointZero
override func didMoveToView(view:SKView) {
scaleMode = .ResizeFill
// 4. Add the container to the scene and the canvas to the container
addChild(lineContainer)
lineCanvas = SKSpriteNode(color:SKColor.clearColor(),size:view.frame.size)
lineCanvas!.anchorPoint = CGPointZero
lineCanvas!.position = CGPointZero
lineContainer.addChild(lineCanvas!)
lastPoint = CGPointMake(view.frame.size.width/2.0, view.frame.size.height/2.0)
}
// Returns a random value in the specified range
func randomInRange(minValue:CGFloat, maxValue:CGFloat) -> CGFloat {
let r = CGFloat(Double(arc4random_uniform(UInt32.max))/Double(UInt32.max))
return (maxValue-minValue) * r + minValue
}
func drawLine() {
if (CGPathIsEmpty(path)) {
// Create a new line that starts where the previous line ended
CGPathMoveToPoint(path, nil, lastPoint.x, lastPoint.y)
lineNode.path = nil
lineNode.lineWidth = 1.0
lineNode.strokeColor = SKColor.blueColor()
lineNode.zPosition = 100
lineContainer.addChild(lineNode)
}
// Add a random line segment
let x = randomInRange(size.width*0.1, maxValue: size.width*0.9)
let y = randomInRange(size.height*0.1, maxValue: size.height*0.9)
CGPathAddLineToPoint(path, nil, x, y)
lineNode.path = path
// Save the current point so we can connect the next line to the end of the last line
lastPoint = CGPointMake(x, y)
}
override func update(currentTime: CFTimeInterval) {
let lineDrawTime = timeScan / 10
// 5. Draw a new line every 10 updates. Increment line count
if (lineDrawTime != lastDrawTime) {
drawLine()
++lineCount
}
// 6. and 7. Add all newly and previously drawn lines to the canvas
if (lineCount == 8) {
addLinesToTexture()
lineCount = 0
}
lastDrawTime = lineDrawTime
++timeScan
}
func addLinesToTexture () {
// Convert the contents of the line container to an SKTexture
let texture = self.view!.textureFromNode(lineContainer)
// Display the texture
lineCanvas!.texture = texture
// Start a new line
lineNode.removeFromParent()
path = CGPathCreateMutable()
}
}

Related

Black Line in Vertical Moving Swift Background

i have a Problem and need your help.
I have modified a code from this old thread
here
That i get a vertical moving background. The code works nice. But i have between every moving background image a tiny black line.
looks like something overlaps. But why?
It would be nice if anyone can help me to fix this error.
Thanks alot
Here is my code:
var bg = SKSpriteNode()
var bg2 = SKSpriteNode()
var bg3 = SKSpriteNode()
var parallax = SKAction()
override func didMove(to view: SKView) {
bg = SKSpriteNode(imageNamed: "back1")
bg.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
bg.zPosition = 1
bg.size = self.size
bg2 = SKSpriteNode(imageNamed: "back2")
bg2.position = CGPoint(x: self.size.width/2, y: self.size.height/2+self.size.height)
bg2.zPosition = 1
bg2.size = self.size
bg3 = SKSpriteNode(imageNamed: "back3")
bg3.position = CGPoint(x: self.size.width/2, y:self.size.height/2+self.size.height+self.size.height)
bg3.zPosition = 1
bg3.size = self.size
self.addChild(bg)
self.addChild(bg2)
self.addChild(bg3)
parallax = SKAction.repeatForever(SKAction.move(by: CGVector(dx: 0, dy: -self.frame.size.height), duration: 4))
bg.run(parallax)
bg2.run(parallax)
bg3.run(parallax)}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if bg.position.y <= -self.frame.size.height {
bg.position.y = self.frame.size.height*2
//this ensures that your backgrounds line up perfectly
}
if bg2.position.y <= -self.frame.size.height {
bg2.position.y = self.frame.size.height*2
//this ensures that your backgrounds line up perfectly
}
if bg3.position.y <= -self.frame.size.height {
bg3.position.y = self.frame.size.height*2
//this ensures that your backgrounds line up perfectly
}
}
You are having floating point rounding issues. The concept of 1/2 a pixel does not exist, so when you get to a position like 12.5, the system needs to either make it 12, or 13.
Now, since decimal does not convert well to fractal binary you are going to end up with numbers like 12.499929932092434234234324 and 12.50000000342423423424, but as far as you know, it is still 12.5.
To fix this, you need to force your position to always round in the same direction, either up or down.
You are probably going to want to round down since most grids work in a 0 based indexing system.
To fix your code, we need to do:
override func update(_ currentTime: TimeInterval) {
bg.position.x.round(.down)
bg.position.y.round(.down)
bg2.position.x.round(.down)
bg2.position.y.round(.down)
bg3.position.x.round(.down)
bg3.position.y.round(.down)
// Called before each frame is rendered
if bg.position.y <= -self.frame.size.height {
bg.position.y += self.frame.size.height*2
//this ensures that your backgrounds line up perfectly
}
if bg2.position.y <= -self.frame.size.height {
bg2.position.y += self.frame.size.height*2
//this ensures that your backgrounds line up perfectly
}
if bg3.position.y <= -self.frame.size.height {
bg3.position.y += self.frame.size.height*2
//this ensures that your backgrounds line up perfectly
}
}
Now of course, as you get better with development, you are going to want to move these types of things outside of your update function. Eventually, you will want to perform these checks when position changes, this way if nothing is moving, you are not needlessly executing these lines of code to fix a position that does not need fixing.
Edit:
Noticed another problem, you need to do += the height, not = height, because if you are at -1, that will also cause a gap, since you are not accounting for the line of height - 1 (Basically, your first image ends at height - 2, and your new image starts at height, making a gap at height - 1)

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.

Why when I use SKSpriteNode with an image it doesn't bounce but when I use SKShapeNode it does work?

I am very new to Swift and I am trying to create a ball that bounces up and down. When I use:
import Foundation
import SpriteKit
class Ball {
let movingObject: SKShapeNode
init() {
movingObject = SKShapeNode(circleOfRadius: 25)
movingObject.physicsBody = SKPhysicsBody(circleOfRadius: 25)
movingObject.physicsBody?.affectedByGravity = true
movingObject.physicsBody?.restitution = 1
movingObject.physicsBody?.linearDamping = 0
}
}
it works fine. However, when I try to use an image, it doesn't bounce.
class Ball {
let movingObject: SKSpriteNode
let picture: String
init(picture: String) {
self.picture = picture
movingObject = SKSpriteNode(imageNamed: picture)
movingObject.physicsBody = SKPhysicsBody(circleOfRadius: movingObject.size.width * 0.5)
movingObject.physicsBody?.affectedByGravity = true
movingObject.physicsBody?.restitution = 1
movingObject.physicsBody?.linearDamping = 0
}
}
The only difference between your two physics bodies is the radius. Therefore, this has to be the problem.
Try setting the radius to 25 like you did with the shape node to confirm, then try to reason about why movingObject.size.width * 0.5 isn't coming out to a reasonable value. You can set a breakpoint and use the debugger to print movingObject.size to help.
About the SKSpriteNode sources:
/**
Initialize a sprite with an image from your app bundle (An SKTexture is created for the image and set on the sprite. Its size is set to the SKTexture's pixel width/height)
The position of the sprite is (0, 0) and the texture anchored at (0.5, 0.5), so that it is offset by half the width and half the height.
Thus the sprite has the texture centered about the position. If you wish to have the texture anchored at a different offset set the anchorPoint to another pair of values in the interval from 0.0 up to and including 1.0.
#param name the name or path of the image to load.
*/
public convenience init(imageNamed name: String)
In the first case you use 25 as radius, in the next you must check if movingObject.size.width` * 0.5 is a valid measure.
When you are in debug phases try to help yourself by turning on the showsPhysics property:
Code of example:
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsPhysics = true
...
}
}
You can easily view the physicBody boundaries of your objects and you can notice it immediately if something is wrong.

How to generate a new node when another one hits a certain y value

I have a circle moving up a line, and when that circle reaches a certain y point, how can I make it so that another node would generate from below?
Here is the code I currently have for populating the circles, but I am not able to use it with a physics body, as it generates too many nodes and slows down my app:
func createCirclesOnLine(line: CGFloat) {
var currentY : CGFloat = -110
let maxY = self.size.width * 15
let spacing : CGFloat = 120
while currentY < maxY {
let circle = SKSpriteNode(imageNamed: "first#2x")
circle.physicsBody?.dynamic = false
circle.position = CGPointMake(line, currentY)
//circle.physicsBody?.restitution = -900
circle.size = CGSizeMake(75, 75)
// circle.physicsBody = SKPhysicsBody(rectangleOfSize: circle.size)
let up = SKAction.moveByX(0, y: 9000, duration: 90)
circle.runAction(up)
foregroundNode.addChild(circle)
currentY += CGFloat((random() % 400) + 70)
}
Will post more code if necessary.
There are two ways you can go about this. One is to simply check every circle's y position to see if it's above the screen. You'll need a reference to the circles so...
class GameScene: SKScene {
var circles = Array<SKSpriteNode>()
...
In your createCirlcesOnLine function, add each circle to the array as you create it.
...
self.addChild(circle)
circles.append(circle)
Then, in your update method, enumerate through the circles to see if any of them are above the top of the screen.
override func update(currentTime: NSTimeInterval) {
for circle in circles {
if circle.position.y > self.size.height + circle.size.height/2 {
//Send circle back to the bottom using the circle's position property
}
}
}
This solution will work but causes a lot of unnecessary checks on every update cycle.
A second more efficient (and slightly more complicated) recommendation is to add an invisible node above the top of the screen that stretches the screen width. When the circle collides with it, just move it to the bottom of the screen. Look into implementing the SKPhysicsContactDelegate protocol and what needs to happen for that to work. If you run into problems with this solution, post a separate question with those issues.

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.