How would I get my character image to change when there is a jump? - swift

I have this code where it animates my 2d character to make it look like its running. In my touchesBegan I have this code where when theres a tap the character jumps and I want the image to change to this jump image I have for the character. Why when I tap the screen and the character jumps the image doesn't change? Thanks! Heres the code I have?
func addHero() {
let heroTextureOne = SKTexture(imageNamed: "heroup")
let heroTextureTwo = SKTexture(imageNamed: "herodown")
let anim = SKAction.animateWithTextures([heroTextureOne, heroTextureTwo], timePerFrame: 0.2)
let run = SKAction.repeatActionForever(anim)
theHero = SKSpriteNode(texture: heroTextureTwo)
theHero.runAction(run)
theHero.physicsBody = SKPhysicsBody(rectangleOfSize: theHero.size)
theHero.physicsBody?.affectedByGravity = true
theHero.position = CGPointMake(self.size.width / 3, self.size.height / 1.0)
theHero.zPosition = 15
addChild(theHero)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch: UITouch = touches.first as! UITouch
var location = touch.locationInNode(self)
var node = self.nodeAtPoint(location)
if (theHero.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 250)) != nil) {
theHero.texture = SKTexture(imageNamed: "jumphero")
}

have you tried not using the if statement? I'm not sure that your statement is returning true, therefore not going into the block, try this, without if. Just apply impulse, then change texture:
theHero.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 250))
theHero.texture = SKTexture(imageNamed: "jumphero")

Related

How do I properly add a SKPhysicsBody to a drawn line using SKShapeNode in Spritekit?

I'm creating a game where the user must draw a line to collide with a ball to move it from one location to another. I just can't seem to find a way to put a physics body that collides with another object. The drawn line goes through other objects. I've tried several different SKPhysicBody init's on my shape, and none of them seem to work.
What's the best SKPhysicsBody initializer for a drawn line?
Are my category bit mask correct?
I understand the SkShapeNode can be converted to a SkSpriteNode, but I don't want to do this unless its the only way.
import SpriteKit
import UIKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var lines: [Line] = []
var lastTouch: CGPoint?
var touchLocation: CGPoint!
var linePhysics: CGPoint?
var ballCategory:UInt32 = 0b1
var lineCategory:UInt32 = 0b1 << 1
var goalCategory:UInt32 = 0b1 << 2
var floorCategory:UInt32 = 0b1 << 3
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
// assigns metal ball that was a color Sprite node, and is now a SKSpriteNode
let metalBall:SKSpriteNode = self.childNode(withName: "ball") as! SKSpriteNode
//assigns physical properties
metalBall.position = CGPoint(x: -150, y: 100)
metalBall.physicsBody? = SKPhysicsBody(circleOfRadius: metalBall.size.width / 2)
metalBall.physicsBody?.affectedByGravity = true
metalBall.physicsBody?.isDynamic = true
metalBall.physicsBody?.categoryBitMask = ballCategory
metalBall.physicsBody?.collisionBitMask = floorCategory | lineCategory
let floor: SKSpriteNode = self.childNode(withName: "floor") as! SKSpriteNode
floor.position = CGPoint (x: 0, y: -76)
floor.physicsBody = SKPhysicsBody(rectangleOf: floor.size)
floor.physicsBody?.affectedByGravity = false
floor.physicsBody?.isDynamic = false
floor.physicsBody?.categoryBitMask = floorCategory
floor.physicsBody?.collisionBitMask = ballCategory
}
/**
* Assigns the first touch location to a variable lastTouch
**/
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
lastTouch = firstTouch.location(in: self)
}
}
/**
* Assigns a path to where the finger touches then adds it to the line array and draws a path using shape nodes
**/
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
touchLocation = firstTouch.location(in: self)
//adds coordinates of lines to the line array
lines.append(Line(start: lastTouch!, end: touchLocation))
lastTouch = touchLocation
}
// path is a changeable variable that assigns a path depending on touch
let path = CGMutablePath()
for line in lines {
path.move(to: CGPoint(x: line.start.x, y: line.start.y))
path.addLine(to: CGPoint(x: line.end.x , y: line.end.y))
}
// colors in the path with ui color black
let shape = SKShapeNode()
shape.path = path
shape.strokeColor = UIColor.black
shape.lineWidth = 2
addChild(shape)
**shape.physicsBody = SKPhysicsBody(edgeChainFrom: shape.path!)**
shape.physicsBody?.affectedByGravity = true
shape.physicsBody?.isDynamic = false
shape.physicsBody?.friction = 1
shape.physicsBody?.restitution = 0.1
shape.physicsBody?.angularDamping = 0.0
shape.physicsBody?.linearDamping = 0
**shape.physicsBody?.categoryBitMask = lineCategory
shape.physicsBody?.collisionBitMask = ballCategory**
}
}

Rotate SkSpriteNode around internal point that is not .anchorPoint

So I'm using this code to rotate an object either clockwise or anti-clockwise.
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let touchPosition = touch.locationInNode(self)
let newRotationDirection : rotationDirection = touchPosition.x < CGRectGetMidX(self.frame) ? .clockwise : .counterClockwise
if currentRotationDirection != newRotationDirection && currentRotationDirection != .none {
reverseRotation()
currentRotationDirection = newRotationDirection
}
else if (currentRotationDirection == .none) {
setupRotationWith(direction: newRotationDirection)
currentRotationDirection = newRotationDirection
}
}
func reverseRotation(){
let oldRotateAction = ship.actionForKey("rotate")
let newRotateAction = SKAction.reversedAction(oldRotateAction!)
ship.runAction(newRotateAction(), withKey: "rotate")
}
func stopRotation(){
ship.removeActionForKey("rotate")
}
func setupRotationWith(direction direction: rotationDirection){
let angle : CGFloat = (direction == .clockwise) ? CGFloat(M_PI) : -CGFloat(M_PI)
let rotate = SKAction.rotateByAngle(angle, duration: 2)
let repeatAction = SKAction.repeatActionForever(rotate)
ship.runAction(repeatAction, withKey: "rotate")
}
My question is, this creates clockwise or anticlockwise rotation around the SKSpriteNode's anchor point.
However I would like the object to rotate about another point inside of the SKSprite (something like 4/5's of the way up from the base of the sprite image). I assume I cannot use a set point defined through CGPointMake as I would like the sprite to independently move around the screen and this would not work?
Any suggestions anyone?
Have you tried setting the anchorPoint property of your sprite to the point around which you want the sprite to rotate?
ship.anchorPoint = CGPoint(x: 0, y: 4/5)
Is this what you were looking for?

How to make a node visible before any user interaction

I took and modified a piece of code which allows me to shoot bullets in every direction. It works perfectly. But it is generating a random bullet in the moment when player touch is ended. I have 6 different bullets and for purposes of my game I need player to see them before he will tap the screen. I tried to initialize a bullet in a global function and pass it to didMoveToView. It's not working, bullet is generating once and stays at its place.
That is my code for initializing a bullet:
class GameScene: SKScene, SKPhysicsContactDelegate {
var gameOver = false
let cannon = SKSpriteNode(imageNamed: "cannon")
let bulletInCannon = SKSpriteNode()
var endOfScreenRight = CGFloat()
var endOfScreenLeft = CGFloat()
let bmrArray = ["blBl", "magBl", "rBl"]
let cgyArray = ["cBl", "gBl", "yBl"]
let yrmArray = ["yBl", "rBl", "magBl"]
let bulletArray = ["rBullet","magBullet", "blBullet", "cBullet", "gBullet", "yBullet"]
override func didMoveToView(view: SKView) {
endOfScreenLeft = (self.size.width / 2) * CGFloat(-1)
endOfScreenRight = self.size.width / 2
cannon.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(cannon)
cannon.zPosition = 0
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
bulletInCannon.position = cannon.position
addChild(bulletInCannon)
bulletInCannon.zPosition = 1000
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addCgyLine),
SKAction.waitForDuration(1.0)])))
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let bullet = SKSpriteNode(texture: bulletInCannon.texture)
bullet.position = cannon.position
addChild(bullet)
let offset = location - bullet.position
let direction = offset.normalized()
let shootAmount = direction * 1000
let realDestination = shootAmount + bullet.position
let actionMove = SKAction.moveTo(realDestination, duration: 2.0)
let actionDone = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([actionMove, actionDone]))
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
}
}
func addCgyLine () {
var cgyBlock = SKSpriteNode(imageNamed: "cyanBox")
cgyBlock.position = CGPointMake(size.width + cgyBlock.size.width/2, CGRectGetMidY(self.frame) + 60)
addChild(cgyBlock)
var randomCGY = Int(arc4random_uniform(3))
cgyBlock.texture = SKTexture(imageNamed: cgyArray[randomCGY])
let actionMove = SKAction.moveTo(CGPoint(x: -cgyBlock.size.width/2, y: CGRectGetMidY(self.frame) + 60), duration: 3)
let actionDone = SKAction.removeFromParent()
cgyBlock.runAction(SKAction.sequence([actionMove, actionDone]))
SKActionTimingMode.EaseOut
}
How can I generate a bullet and only then shoot it? I will be glad to hear any ideas, thanks!
You can generate a bullet in a cannon as a global variable, and then create a new bullet in touchesEnded which will be the bullet that actually fires from the cannon. You set that new bullet's texture to the global bullet's texture. After firing the action, give the global bullet a new texture. If you need to have a reload time, then remove the global bullet temporarily, and use a Boolean to "lock" the cannon from firing until the time has elapsed.
Here's some code that I used. If the cannon moves, then make sure to update the position of the global bullet. The subtraction of CGPoint gives me an error, but I'm using Xcode 6.4.
Edit: Quicker and easier way by giving the bulletInCannon a value
var bulletInCannon = SKSpriteNode(imageNamed: "rBullet")
override func didMoveToView(view: SKView) {
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
bulletInCannon.position = cannon.position
addChild(bulletInCannon)
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
// Create bullet that will fire from the cannon
let bullet = SKSpriteNode(texture: bulletInCannon.texture)
bullet.position = cannon.position
addChild(bullet)
let offset = location - bullet.position
let direction = offset.normalized()
let shootAmount = direction * 1000
let realDestination = shootAmount + bullet.position
let actionMove = SKAction.moveTo(realDestination, duration: 2.0)
let actionDone = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([actionMove, actionDone]))
// Generate a random texture for the global bullet
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
}
}

Really Need Help About UITouch (Sprite Kit)

i Like to create left side and right side that when i touch them , A bird will fly to the left or right !
I create a bird
var birdtexture = SKTexture(imageNamed: "flappy1.png")
bird = SKSpriteNode(texture: birdtexture)
bird.position
bird.size
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.width/2)
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = false
self.addChild(bird)
Then i create left node
var groundleft = SKSpriteNode()
groundleft.position = CGPoint(x: 0, y: 50)
groundleft.size = CGSize(width: 500, height: 100)
groundleft.color = UIColor(red: 7, green: 5, blue: 7, alpha: 20)
groundleft.physicsBody = SKPhysicsBody(rectangleOfSize:CGSizeMake(self.size.width, 100))
groundleft.physicsBody?.dynamic = true
groundleft.physicsBody?.affectedByGravity = false
groundleft.name = "toleft"
self.addChild(groundleft)
then i go to TouchBegan
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if(touchedNode.name == "toleft"){
bird.physicsBody?.velocity = CGVectorMake(0, 0)
bird.physicsBody?.applyImpulse(CGVectorMake(-10, 100))
}
why it didnt work . Is my method wrong ? Is there any another way for me to make it work ??
please help , so many thanks
In your touchesBegan method try this:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if let touch = touches.first as? UITouch {
let touchLocation = touch.locationInNode(self)
if nodeAtPoint(touchLocation) == groundleft { //your sprite node
//do something
}
}
super.touchesBegan(touches , withEvent:event)
}

sprite-kit collision not working

Hi everyone I am trying to make a basic line drawing test with xcode 6 using swift. But my collision system isn't working at all. This is the code of my collision system:
func drawLines() {
CGPathMoveToPoint(path, nil, location.x, location.y)
CGPathAddLineToPoint(path, nil, self.frame.size.width/2, self.frame.size.height / 5)
drawLine.append(SKShapeNode())
drawLine[index] = SKShapeNode()
line.append(drawLine[index])
line[index].path = path
line[index].strokeColor = UIColor.redColor()
line[index].lineWidth = 5.0
line[index].physicsBody = SKPhysicsBody(rectangleOfSize: line[index].frame.size)
line[index].physicsBody.dynamic = false
line[index].zPosition = 1
self.addChild(line[index])
index++
}
I can't figure out the problem but I think I made a mistake in that piece of code.
Here is the rest of my code:
class GameScene: SKScene {
var line: [SKShapeNode] = []
var drawLine: [SKShapeNode] = []
var path = CGPathCreateMutable()
var touch: UITouch!
var location:CGPoint!
var index = 0
let player = SKSpriteNode(imageNamed: "player")
override func didMoveToView(view: SKView) {
player.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2)
player.physicsBody = SKPhysicsBody(rectangleOfSize: player.size)
player.physicsBody.dynamic = true
self.physicsWorld.gravity = CGVectorMake(0,-1)
player.zPosition = 1
self.addChild(player)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
touch = touches.anyObject() as UITouch!
location = touch.locationInNode(self)
drawLines()
}
func drawLines() {
CGPathMoveToPoint(path, nil, location.x, location.y)
CGPathAddLineToPoint(path, nil, self.frame.size.width/2, self.frame.size.height / 5)
drawLine.append(SKShapeNode())
drawLine[index] = SKShapeNode()
line.append(drawLine[index])
line[index].path = path
line[index].strokeColor = UIColor.redColor()
line[index].lineWidth = 5.0
line[index].physicsBody = SKPhysicsBody(rectangleOfSize: line[index].frame.size)
line[index].physicsBody.dynamic = false
line[index].zPosition = 1
self.addChild(line[index])
index++
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
-------------------EDIT -------------------
I now use this line of code for the physics of my line:
line[index].physicsBody = SKPhysicsBody(rectangleOfSize:
CGSizeMake(line[index].frame.width*2 ,line[index].frame.height*2))
This problem here I think is that when I draw my line at an angle the rectangle doesn't turn with it so my rectangle ends up way to big.
As far as I know, touchesBegan returns an NSSet of touches (an array of touches roughly saying), therefore you would want to loop through them or access a specific touch by its index. I would recommend looping through touches like the following code.
override func touchesBegan(touches:NSSet) {
for touch: AnyObject in touches {
var location:CGPoint = touch.locationInNode(self.scene)
performSomethingCrazy(withLocationAt: location)
}
}
Enjoy.