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.
Related
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)
I'm creating a simple game with Swift and SpriteKit.
I want to add an endless background (vertically), I only found answers for background with images but I need to do it without image, only background color.
I thought about checking if the player is in the frame.maxY, if so, to move it back to the starting point, but I was wondering if there is a better idea.
//Does not matter which ring we chose as all ring's 'y' position is the same.
func moveBackgroundUp(){
if ((mPlayer.position.y >= self.frame.maxY) || (mRingOne.position.y >= self.frame.maxY)) {
mPlayer.position.y = 150 //mPlayers original starting point.
for ring in mRings {
ring.position.y = 350
}
}
}
Thanks in advance!
Don't just move a background up the screen, that' really isn't the way to go about it. What you should do is detect the position of the camera (assuming it moves with the player), and when it's position is about to occupy space outside of the occupied space of your current background sprite, add a new background sprite to the scene where the last one left off. Here is an example of how to do that with just a red sprite:
First add a property to the scene to track level position:
// To track the y-position of the level
var levelPositionY: CGFloat = 0.0
Now create a method to update your background:
func updateBackground() {
let cameraPos = camera!.position
if cameraPos.y > levelPositionY - (size.height * 0.55) {
createBackground()
}
}
func createBackground() {
// Create a new sprite the size of your scene
let backgroundSprite = SKSpriteNode(color: .red, size: size)
backgroundSprite.anchorPoint = CGPoint(x: 0.5, y: 0)
backgroundSprite.position = CGPoint(x: 0, y: levelPositionY)
// Replace backgroundNode with the name of your backgroundNode to add the sprite to
backgroundNode.addChild(backgroundSprite)
levelPositionY += backgroundSprite.size.height
}
Now you want to call updateBackground inside your overridden update(_:) method:
override func update(_ currentTime: TimeInterval) {
// All your other update code
updateBackground()
}
Also, make sure to create an initial background when you first create the scene:
override func didMove(to view: SKView) {
createBackground()
}
NOTE! - It's important to set the custom anchor point for the background sprite for this code to work properly. An anchor of (0.5, 0) allows the background sprite to be anchored in the middle of the scene on the x-axis, but at the bottom of the scene on the y-axis. This allows you to easily stack one on top of the other.
EDIT - I forgot to mention that it's also a good idea to conserve resources and remove any background nodes that are outside the viewable area and won't be coming back in (i.e. a continuous scrolling game where you can't go backwards). You could do that by updating your updateBackground method above:
func updateBackground() {
let cameraPos = camera!.position
if cameraPos.y > levelPositionY - (size.height * 0.55) {
createBackground()
}
// Make sure to change 'backgroundNode' to whatever the name of your backgroundNode is.
for bgChild in backgroundNode.children {
// This will convert the node's coordinates to scene's coordinates. See below for this function
let nodePos = fgNode.convert(fgChild.position, to: self)
if !isNodeVisible(bgChild, positionY: nodePos.y) {
// Remove from it's parent node
bgChild.removeFromParent()
}
}
}
func isNodeVisible(_ node: SKNode, positionY: CGFloat) -> Bool {
if !camera!.contains(node) {
if positionY < camera!.position.y - size.height * 2.0 {
return false
}
}
return true
}
So above you just loop through all the children inside your background node and detect if they are out of view, and if so remove them from the parent. Make sure to change my generic backgroundNode to whatever the name of your background node is.
I am making a game in which a sprite is rotating around a big circle and I want that when touches began the sprite moves to another circle that is in the center of the screen and now the sprite rotatate around it, and when the touches began occurs again the sprite return to the big circle. In my code the sprite moves to the -sprite.position.x * 2 so that makes that the sprite move to the oposite side of the big circle. How can I solve that?
One of the easiest ways to move an object in a circular path is to
Create a SKNode container
Create a sprite
Add the container to the scene
Add the sprite to the container
Set the sprite's x position to the radius of the circular path
Rotate the container
If you want to move the sprite to the another circle with a different radius,
change the sprite's x position
move the container's position (optional)
If you want to change the direction of the rotation,
reverse the container's rotation
Here's an example:
// Define circle
struct Circle {
var position:CGPoint
var radius:CGFloat
}
class GameScene: SKScene {
// 1. Create container node
let node = SKNode()
// 2. Create a sprite
let sprite = SKSpriteNode(color:SKColor.blueColor(),size:CGSizeMake(10,10))
var rotation:CGFloat = CGFloat(M_PI)
var circles:[Circle] = []
override func didMoveToView(view: SKView) {
scaleMode = .ResizeFill
circles.append(Circle(position: view.center, radius: 80))
circles.append(Circle(position: view.center, radius: 40))
// 3. Add the container to the scene
addChild(node)
// 4. Add the sprite to the container
node.addChild(sprite)
// 5. Set the sprite's x position to the radius of the circular path
if let circle = nextCircle() {
node.position = circle.position
sprite.position = CGPoint(x:circle.radius, y:0)
// 6. Rotate the container
rotate()
}
}
// Rotate the container
func rotate() {
let action = SKAction.rotateByAngle(rotation, duration: 4)
node.runAction(SKAction.repeatActionForever(action),withKey: "rotate")
}
// 9. Reverse the container's rotation
func reverse() {
rotation = -rotation
}
// Stop rotating the container
func stopRotation() {
if node.actionForKey("rotate") != nil {
node.removeActionForKey("rotate")
}
}
// Returns the next circle's parameters and rotates the array
func nextCircle() -> Circle? {
guard let circle = circles.first else {
return nil
}
// Move the current circle to the back of the queue
circles.append(circles.removeAtIndex(0))
return circle
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Change the sprite's x-position */
if sprite.actionForKey("move") == nil {
stopRotation()
// 7. change the sprite's x position
// 8. move the container's position (optional)
if let circle = nextCircle() {
let radius = CGPoint(x:circle.radius, y:0)
let moveCenter = SKAction.moveTo(circle.position, duration: 3)
let move = SKAction.moveTo(radius, duration: 3)
let rotate = SKAction.runBlock {
self.rotate()
}
sprite.runAction(SKAction.sequence([move, rotate]), withKey: "move")
node.runAction(moveCenter, withKey: "move")
}
}
}
}
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.
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).