Moving a sprite with a joystick in swift Xcode - xcode

hello :D I have this code below.
import SpriteKit
class GameScene: SKScene {
let base = SKSpriteNode(imageNamed: "yellowArt/Base")
let ball = SKSpriteNode(imageNamed: "yellowArt/Ball")
let ship = SKSpriteNode(imageNamed: "yellowArt/Ship")
var stickActive:Bool = false
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blackColor()
self.anchorPoint = CGPointMake(0.5, 0.5)
self.addChild(base)
base.position = CGPointMake(0, -200)
self.addChild(ball)
ball.position = base.position
self.addChild(ship)
ship.position = CGPointMake(0, 200)
ball.alpha = 0.4
base.alpha = 0.4
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if (CGRectContainsPoint(ball.frame, location)) {
stickActive = true
} else {
stickActive = false
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if (stickActive == true) {
let v = CGVector(dx: location.x - base.position.x, dy: location.y - base.position.y)
let angle = atan2(v.dy, v.dx)
let deg = angle * CGFloat( 180 / M_PI)
print( deg + 180 )
let length: CGFloat = base.frame.size.height / 2
let xDist: CGFloat = sin(angle - 1.57079633) * length
let yDist: CGFloat = cos(angle - 1.57079633) * length
if (CGRectContainsPoint(base.frame, location)) {
ball.position = location
} else {
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
ship.zRotation = angle - 1.57079633
ship.position = CGPointMake(angle, angle)
}
} // ends stick active test
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if (stickActive == true) {
let move: SKAction = SKAction.moveTo(base.position, duration: 0.2)
move.timingMode = .EaseOut
ball.runAction(move)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
The code above creates a joystick and a ship. By moving the joystick I am able to rotate the "ship" with the joystick. However I want to move the ship in the direction that the joystick is holding. How do I tackle this problem? Thanks.

Your ship isn't going to go anywhere since you have ship.position = CGPointMake(angle, angle). As long as you're moving the joystick, the ship is going to go to that point - remove that line.
I would use the update method to move the ship around. First you need to find how much you want to move it in the x and y direction.
Create class variables for the joystick movements below your var stickActive:Bool = false statement:
var xJoystickDelta = CGFloat()
var yJoystickDelta = CGFloat()
Put the following code in your touchesMoved method:
xJoystickDelta = location.x - base.position.x
yJoystickDelta = location.y - base.position.y
Put the following code in your update method:
let xScale = 1.0 //adjust to your preference
let yScale = 1.0 //adjust to your preference
let xAdd = xScale * self.xJoytickDelta
let yAdd = yScale * self.yJoystickDelta
self.ship.position.x += xAdd
self.ship.position.y += yAdd
I hope this helps.

Related

How draw a rectangle hole on UIBlurEffect and move it on x axis (UIKit)

I'm trying to create blur effect on a view and than add a shape which will show image on this blurred layer (custom video editing functionality)
Currently I'm able to do it only dragging mask view from the right edge:
but when I try to do it from the left edge, I get such a effect:
func configureBlurView() {
let viewHeight: CGFloat = 60
let padding: CGFloat = 10
blurView = UIView()
blurView.layer.cornerRadius = 10
blurView.clipsToBounds = true
blurView.isHidden = true
blurView.translatesAutoresizingMaskIntoConstraints = false
addSubview(blurView)
addConstraints([
blurView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
blurView.bottomAnchor.constraint(equalTo: stackView.topAnchor, constant: -padding),
blurView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),
blurView.heightAnchor.constraint(equalToConstant: viewHeight)
])
addBlurEffect(for: blurView)
}
private func addBlurEffect(for view: UIView) {
let blurEffect = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
blurEffect.alpha = 0.5
blurEffect.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurEffect)
addConstraints([
blurEffect.topAnchor.constraint(equalTo: view.topAnchor),
blurEffect.leadingAnchor.constraint(equalTo: view.leadingAnchor),
blurEffect.bottomAnchor.constraint(equalTo: view.bottomAnchor),
blurEffect.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
private func makeClearHole(rect: CGRect) {
let maskLayer = CAShapeLayer()
maskLayer.fillColor = UIColor.black.cgColor
let pathToOverlay = CGMutablePath()
pathToOverlay.addRect(blurView.bounds)
pathToOverlay.addRect(rect)
maskLayer.path = pathToOverlay
maskLayer.fillRule = .evenOdd
maskLayer.cornerRadius = 10
blurView.layer.mask = maskLayer
}
I'm using touchesMoved method to change orange view dimensions:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard trimmerView.isHidden == false else { return }
if let touch = touches.first{
let currentTouchPoint = touch.location(in: self)
let previousTouchPoint = touch.previousLocation(in: self)
let deltaX = currentTouchPoint.x - previousTouchPoint.x
if trimmerView.bounds.width >= 70 {
if touchStartEdge.middle {
if trimmerViewLeadingConstraint.constant < 10 {
trimmerViewLeadingConstraint.constant = 10
} else if trimmerViewTrailingConstraint.constant > -10 {
trimmerViewTrailingConstraint.constant = -10
} else {
trimmerViewLeadingConstraint.constant += deltaX
trimmerViewTrailingConstraint.constant += deltaX
}
}
if touchStartEdge.leftEdge {
if trimmerViewLeadingConstraint.constant >= 10.0 {
trimmerViewLeadingConstraint.constant += deltaX
} else if trimmerViewLeadingConstraint.constant < 10.0 {
trimmerViewLeadingConstraint.constant = 10
}
}
if touchStartEdge.rightEdge {
if trimmerViewTrailingConstraint.constant <= -10 {
trimmerViewTrailingConstraint.constant += deltaX
} else if trimmerViewTrailingConstraint.constant > -10 {
trimmerViewTrailingConstraint.constant = -10.0
}
}
}
updateProgressBarConstraints()
makeClearHole(rect: CGRect(x: 0, y: 0, width: trimmerView.frame.width, height: trimmerView.frame.height))
UIView.animate(withDuration: 0.10, delay: 0, options: .curveEaseIn) { [weak self] in
self?.layoutIfNeeded()
}
}
}
What I'd like to achieve is to remove blur effect only in bounds of orange view.
Any ideas ?? :)
Thanks for help!!
Couple ways to do this - here's one...
Add a mask to the blur effect view. As the user drags the "trimmer" update the mask.
Here's a quick example...
We'll:
create a stack view with 10 images
overlay that with a masked blur effective view
add a "draggable trimmer view"
when we drag the trimmer, we update the mask
Example View Controller
class TrimmerVC: UIViewController {
var blurView: MaskedBlurView!
let trimmerView = DragView()
let stackView = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
// respect safe area when we setup constraints
let g = view.safeAreaLayoutGuide
stackView.distribution = .fillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
stackView.heightAnchor.constraint(equalToConstant: 80.0),
])
// let's add 10 imageviews to the stack view
for i in 1...10 {
if let img = UIImage(systemName: "\(i).circle.fill") {
let imgView = UIImageView(image: img)
imgView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
stackView.addArrangedSubview(imgView)
}
}
let blurEffect = UIBlurEffect(style: .dark)
blurView = MaskedBlurView(effect: blurEffect)
blurView.alpha = 0.5
blurView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurView)
NSLayoutConstraint.activate([
blurView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: 0.0),
blurView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 0.0),
blurView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 0.0),
blurView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 0.0),
])
trimmerView.backgroundColor = .systemOrange
view.addSubview(trimmerView)
trimmerView.didDrag = { [weak self] newX in
guard let self = self else { return }
self.blurView.clearX = newX - self.stackView.frame.origin.x
}
}
// we'll use this to update the framing when the stack view width changes
// such as on device rotation
var curStackW: CGFloat = -1
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if curStackW != stackView.frame.width {
curStackW = stackView.frame.width
var r = stackView.frame
r.origin.y += r.size.height + 20.0
r.size.width = 160
r.size.height = 40
trimmerView.frame = r
blurView.clearWidth = trimmerView.frame.width
blurView.clearX = 0
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// toggle the trimmer view between
// below the stack view and
// overlaid on the stack view
if trimmerView.frame.origin.y > stackView.frame.origin.y {
let r = stackView.frame
trimmerView.frame.origin.y = r.origin.y - 6.0
trimmerView.frame.size.height = r.height + 12.0
} else {
let r = stackView.frame
trimmerView.frame.origin.y = r.origin.y + r.height + 12.0
trimmerView.frame.size.height = 60.0
}
}
}
Example Draggable "Trimmer" View
class DragView: UIView {
var didDrag: ((CGFloat) -> ())?
let maskLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
maskLayer.fillColor = UIColor.red.cgColor
layer.mask = maskLayer
}
override func layoutSubviews() {
super.layoutSubviews()
let pathToOverlay = CGMutablePath()
pathToOverlay.addRect(bounds)
pathToOverlay.addRect(bounds.insetBy(dx: 20.0, dy: 8.0))
maskLayer.path = pathToOverlay
maskLayer.fillRule = .evenOdd
maskLayer.cornerRadius = 10
}
var touchStartX: CGFloat = 0
var frameStartX: CGFloat = 0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
touchStartX = touch.location(in: self.superview!).x
frameStartX = self.frame.origin.x
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let loc = touch.location(in: self.superview!)
self.frame.origin.x = frameStartX + (loc.x - touchStartX)
didDrag?(self.frame.origin.x)
}
}
Example Masked Blur View
class MaskedBlurView: UIVisualEffectView {
public var clearWidth: CGFloat = 100 {
didSet { updateMask() }
}
public var clearX: CGFloat = 0 {
didSet { updateMask() }
}
private let maskLayer = CAShapeLayer()
override init(effect: UIVisualEffect?) {
super.init(effect: effect)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
maskLayer.fillColor = UIColor.red.cgColor
layer.mask = maskLayer
}
func updateMask() {
let leftR = CGRect(x: 0, y: 0, width: clearX, height: bounds.height)
let rightR = CGRect(x: clearX + clearWidth, y: 0, width: bounds.width, height: bounds.height)
let bez = UIBezierPath(rect: leftR)
bez.append(UIBezierPath(rect: rightR))
maskLayer.path = bez.cgPath
}
override func layoutSubviews() {
super.layoutSubviews()
maskLayer.frame = bounds
}
}
When running (in landscape orientation) it will start like this:
I placed the "trimmer" view below the stack view to make it a little more clear what's happening.
As we drag the trimmer view, the blur view's mask will be updated:
Tapping anywhere in an empty part of the screen will toggle the trimmer view between "under the stack view" and "overlaid on the stack view":
This was just put together quickly -- you should have no trouble restructuring the code to wrap everything into a single custom view (or however it would work best for your needs).

error removing child node from parent node in spriteKit Swift4

I made a parallax background, in GameScene.sks I added an empty node, added two sprites to an empty node. I have a reset button, when I click on it I need the background to be removed and added to its position (restarted), but when I add in scrollBg.removeAllChildren restart function I error occurs, how do I properly add and remove children from the scene ?
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var player: SKSpriteNode!
var scrollBg: SKNode!
var spawnTimer: CFTimeInterval = 0
let fixedDelta: CFTimeInterval = 1.0/60.0 /* 60 FPS */
let scrollSpeed: CGFloat = 700
var sinceTouch: CFTimeInterval = 0
func resetGameScene() {
scrollBG.removeAllChildren()
player.removeAllChildren()
player.position = CGPoint(x: 590 , y: 690)
pauseButton()
}
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
player = childNode(withName: "player") as? SKSpriteNode
scrollBg = childNode(withName: "scrollBG")!
resetGameScene()
}
func scrollWorld() {
scrollBg.position.y -= scrollSpeed * CGFloat(fixedDelta)
for ground in scrollBg.children as! [SKSpriteNode] {
let groundPosition = scrollBg.convert(ground.position, to:
self)
if groundPosition.y <= -ground.size.width {
let newPosition = CGPoint(x: groundPosition.x, y:
(self.size.width ) + ground.size.width * 2)
ground.position = self.convert(newPosition, to:
scrollBg)
}
}
}
override func update(_ currentTime: TimeInterval) {
sinceTouch+=fixedDelta
spawnTimer+=fixedDelta
scrollWorld()
}
}

SKSpriteNode not able to have multiple children

I'm trying to have multiple sprite nodes of the same type/parent, but when I try to spawn another node I get an error. 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent'
Here's my code:
import SpriteKit
class GameScene: SKScene {
//global declarations
let player = SKSpriteNode(imageNamed: "mage")
let fireball = SKSpriteNode(imageNamed: "fireball")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
createScene()
}
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)
spawnFireball(location)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
self.enumerateChildNodesWithName("fireball", usingBlock: ({
(node,error) in
if (self.fireball.position.x < -self.fireball.size.width/2.0 || self.fireball.position.x > self.size.width+self.fireball.size.width/2.0
|| self.fireball.position.y < -self.fireball.size.height/2.0 || self.fireball.position.y > self.size.height+self.fireball.size.height/2.0) {
self.fireball.removeFromParent()
self.fireball.removeAllChildren()
self.fireball.removeAllActions()
}
}))
}
func createScene() {
//player
player.size = CGSizeMake(100, 100)
player.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2 + 50)
player.zPosition = 2.0
self.addChild(player)
}
func spawnFireball(point: CGPoint) {
//setup
fireball.name = "fireball"
fireball.size = CGSizeMake(100, 50)
let fireballCenter = CGPointMake(fireball.size.width / 4 * 3, fireball.size.height / 2)
fireball.position = player.position
fireball.physicsBody = SKPhysicsBody(circleOfRadius: fireball.size.height/2, center: fireballCenter)
fireball.physicsBody?.affectedByGravity = false
//action
var dx = CGFloat(point.x - player.position.x)
var dy = CGFloat(point.y - player.position.y)
let magnitude = sqrt(dx * dx + dy * dy)
dx /= magnitude
dy /= magnitude
let vector = CGVector(dx: 32.0 * dx, dy: 32.0 * dy)
var rad = atan2(dy,dx)
fireball.runAction(SKAction.rotateToAngle(rad, duration: 0.0))
self.addChild(fireball)
fireball.physicsBody?.applyImpulse(vector)
}
}
You need to instantiate another SKSpriteNode. In your existing code, you create a single fireball and add it to the scene; your program crashes when you try to add the same fireball again.
First, remove your let fireball = SKSpriteNode... line. Move it to inside the spawnFireball() method, like so:
func spawnFireball(point: CGPoint) {
let fireball = SKSpriteNode(imageNamed: "fireball")
//Insert all customization here (your existing code should mostly work)
self.addChild(fireball)
}
Because the fireball variable is a local variable, you can now instantiate a new one every time you call the function. Now, just change your update() method to properly use enumerateChildrenWithName() by getting changing every self.fireball to just node.
This way, the code will loop through every existing fireball that is currently on the scene, rather than your current code, which only allows you to create one fireball.

Node not moving after changing scenes

I made a (basic) game, works perfect, goes to GameOverScene, and when it comes back from GameOverScene to GameScene, the player(spritenode) is not moving anymore..
I commented in the GameScene code which functions its about
I get no error from Xcode!
the bug is in the gamescene.swift file(functions: swipedRight + swipedLeft + swipedUp + swipedDown):
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var kikker:SKSpriteNode = SKSpriteNode()
var auto1:SKSpriteNode = SKSpriteNode()
var lastYieldTimeInterval:NSTimeInterval = NSTimeInterval()
var lastUpdateTimerInterval:NSTimeInterval = NSTimeInterval()
let playerCategory:UInt32 = 0x1 << 1
let auto1Category:UInt32 = 0x1 << 0
required init(coder aDecoder:NSCoder) {
fatalError("NSCoder not supported")
}
override init(size:CGSize) {
super.init(size:size)
anchorPoint = CGPoint(x:0, y:1.0)
let background = SKSpriteNode(imageNamed: "bg5")
var auto1:SKSpriteNode = SKSpriteNode(imageNamed: "auto1")
background.position = CGPoint(x:0, y:0)
background.anchorPoint=CGPoint(x:0,y:1.0)
addChild(background)
kikker = SKSpriteNode(imageNamed:"kikker5")
kikker.anchorPoint = CGPoint(x:0.5, y:0.5)
kikker.xScale = 0.22
kikker.yScale = 0.22
self.physicsWorld.gravity = CGVectorMake(0,0)
self.physicsWorld.contactDelegate = self
kikker.physicsBody?.categoryBitMask = playerCategory
kikker.physicsBody?.contactTestBitMask = auto1Category
kikker.physicsBody?.collisionBitMask = 0
kikker.physicsBody?.usesPreciseCollisionDetection = true
kikker.physicsBody = SKPhysicsBody(circleOfRadius: kikker.size.width/2)
kikker.physicsBody?.dynamic = false
kikker.position = CGPointMake(self.frame.size.width/2, -610)
addChild(kikker)
println(kikker.position)
}
func didBeginContact(contact: SKPhysicsContact!) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if(contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask){
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if((firstBody.categoryBitMask & auto1Category) != 0 && (secondBody.categoryBitMask & playerCategory) != 0)
{
println("aasda")
aangereden(contact.bodyB.node as SKSpriteNode, player: contact.bodyA.node as SKSpriteNode)
}
}
func addCar(){
var auto1:SKSpriteNode = SKSpriteNode(imageNamed: "auto2")
auto1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(auto1.size.width/2, auto1.size.height/2) )
auto1.physicsBody?.dynamic = true
auto1.physicsBody?.categoryBitMask = auto1Category
auto1.physicsBody?.contactTestBitMask = playerCategory
auto1.physicsBody?.collisionBitMask = 0
auto1.physicsBody?.usesPreciseCollisionDetection = true
let position : CGFloat = 51 + (CGFloat(arc4random_uniform(4)+2)*(-111))
auto1.anchorPoint = CGPoint(x:0.5, y:0.5)
auto1.position = CGPointMake(-auto1.size.width/2, position)
self.addChild(auto1)
let minDuration = 2.5
let maxDuration = 4.0
let rangeDuration = maxDuration - minDuration
let duration = Int(arc4random()) % Int(rangeDuration) + Int(minDuration)
var actionArray:NSMutableArray = NSMutableArray()
actionArray.addObject(SKAction.moveTo(CGPointMake(375 + auto1.size.width/2, position), duration: NSTimeInterval(duration)))
actionArray.addObject(SKAction.removeFromParent())
auto1.runAction(SKAction.sequence(actionArray))
}
println("DOOD")
player.removeFromParent()
}
func updateWithTimeSinceLastUpdate(timeSinceLastUpdate:CFTimeInterval){
lastYieldTimeInterval += timeSinceLastUpdate
if (lastYieldTimeInterval > 2.5){
lastYieldTimeInterval = 0
addCar()
}
}
//#1 function not working after changing scenes: the function is executing, the println works
but for some reason the runAction doesnt do its job, the node(kikker) is not moving as it should
func swipedRight1(sender:UISwipeGestureRecognizer){
var naarRechts = SKAction()
var positionX = kikker.position.x
println("right2")
if(kikker.position.x<200){
println(positionX)
positionX = kikker.position.x + 125
println(positionX)
naarRechts = SKAction.moveToX(positionX , duration: 0.25)
kikker.zRotation=(-1.570)
kikker.runAction(naarRechts)
}
}
//#2 function not working, same story
func swipedLeft1(sender:UISwipeGestureRecognizer){
var naarLinks = SKAction()
var positionX = kikker.position.x
if(kikker.position.x>150){
positionX = kikker.position.x - 125
naarLinks = SKAction.moveToX(positionX , duration: 0.25)
kikker.zRotation=(1.570)
kikker.runAction(naarLinks)
}
}
//#3 function not working, same story
func swipedDown1(sender:UISwipeGestureRecognizer){
var naarBeneden = SKAction()
var positionY = kikker.position.y
if(kikker.position.y>(-600)){
positionY = kikker.position.y - 111
naarBeneden = SKAction.moveToY(positionY , duration: 0.25)
kikker.zRotation=3.141
kikker.runAction(naarBeneden)
}
}
//#4 function not working, same story
func swipedUp1(sender:UISwipeGestureRecognizer){
var naarBoven = SKAction()
var positionY = kikker.position.y
if(kikker.position.y < (-60)){
positionY = kikker.position.y + 111
naarBoven = SKAction.moveToY(positionY, duration: 0.25)
kikker.zRotation=0
kikker.runAction(naarBoven)
}
if(positionY > (-60)){
var gameOverScene:SKScene = GameOverScene(size: self.size)
self.view?.presentScene(gameOverScene)
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
var timeSinceLastUpdate = currentTime - lastUpdateTimerInterval
lastUpdateTimerInterval = currentTime
if (timeSinceLastUpdate > 1){
timeSinceLastUpdate = 1/60
lastUpdateTimerInterval = currentTime
}
updateWithTimeSinceLastUpdate(timeSinceLastUpdate)
}
}
here the GameOverScene file:
import UIKit
import SpriteKit
class GameOverScene: SKScene {
override init(size:CGSize){
super.init(size:size)
self.backgroundColor = SKColor.whiteColor()
var message:NSString = NSString()
message = "Game Over"
var label:SKLabelNode = SKLabelNode(fontNamed:"DamascusBold")
label.text = message
label.fontColor = SKColor.blackColor()
label.position = CGPointMake(self.size.width/2, self.size.height/2)
self.addChild(label)
var scene:GameScene!
self.runAction(SKAction.sequence([SKAction.waitForDuration(3.0),
SKAction.runBlock({
// var transition:SKTransition = SKTransition.flipHorizontalWithDuration(0.5)
var scene1:SKScene = GameScene(size: self.size)
self.view?.presentScene(scene1)
})
] ))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
gameviewcontroller file:
import UIKit
import SpriteKit
import AVFoundation
class GameViewController: UIViewController, UITextFieldDelegate{
var scene:GameScene!
func swipedRight(sender: UISwipeGestureRecognizer){
scene.swipedRight1(sender)
}
func swipedLeft(sender: UISwipeGestureRecognizer){
scene.swipedLeft1(sender)
}
func swipedDown(sender: UISwipeGestureRecognizer){
scene.swipedDown1(sender)
}
func swipedUp(sender: UISwipeGestureRecognizer){
scene.swipedUp1(sender)
}
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as SKView
skView.multipleTouchEnabled = false
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = SKSceneScaleMode.AspectFill
skView.presentScene(scene)
let swipeRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedRight:"))
swipeRight.direction = .Right
view.addGestureRecognizer(swipeRight)
let swipeLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedLeft:"))
swipeLeft.direction = .Left
view.addGestureRecognizer(swipeLeft)
let swipeUp:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedUp:"))
swipeUp.direction = .Up
view.addGestureRecognizer(swipeUp)
let swipeDown:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("swipedDown:"))
swipeDown.direction = .Down
view.addGestureRecognizer(swipeDown)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
You're calling your swipedUp1 (etc) methods on the wrong scene. Here's what's happening:
Your view controller has a reference to the GameScene instance that you start with. Its gesture action methods call to that, and all is well.
When you start a new game from GameOverScene, that creates a new instance of GameScene and presents it in the view. Now you have two instances of GameScene: the one your view controller is still pointing to, and the one the view is now rendering.
When you're gesture actions fire, they're still talking to the first GameScene. So your log lines get printed, but you don't see anything happen because the second GameScene is the one being displayed.
You probably don't want two scenes sticking around, anyway.
You can fix both problems by eliminatimg the scene property in your view controller and having your gesture actions call through to view.scene instead (after appropriate casting):
func swipedUp(sender: UISwipeGestureRecognizer) {
let skView = view as SKView
let gameScene = skView.scene as GameScene
gameScene.swipedUp1(sender)
}
This way, the swipe goes to whichever scene the view controller's view is currently presenting.
Alternatively, you could keep your original swipedUp (etc) code, and change scene from a stored property to a read-only computed one that always gets you the currently presented scene:
var scene: GameScene {
let skView = view as SKView
return skView.scene as GameScene
}

Do I have to delete a sprite node once I end up using it?

I'm creating a series of moving pipes in my scene. But it always crashes after ~30 pipes are generated. Is it because of too many nodes in the scene and no memories for new ones? The code is like this:
import SpriteKit
class GameScene: SKScene {
var mainPipe: SKSpriteNode = SKSpriteNode()
var space:Float = 1000
var pipeCount:Int = 0
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blackColor()
self.size.width = 640
self.size.height = 1136
}
func randomOffset() -> Float{
var rNum:Float = Float(arc4random()%181) // 0-180
return rNum
}
var durations: CFloat = 5.0
var colorPipes:UIColor = UIColor.grayColor()
func spawnPipeRow(offs:Float){
self.pipeCount = self.pipeCount + 1
println("\(self.pipeCount)")
//offs is the random number
//let offset = offs + (space/2) - 105
let offset = offs + Float(self.size.height/100) - 180
// mainPipe = SKSpriteNode(color:colorPipes, size:CGSize(width: view.bounds.size.width/3, height:700))
mainPipe = SKSpriteNode(color:colorPipes, size:CGSize(width: self.size.width/5, height:self.size.height/1.5))
let pipeBottom = (mainPipe as SKSpriteNode).copy() as SKSpriteNode
let pipeTop = (mainPipe as SKSpriteNode).copy() as SKSpriteNode
let xx = self.size.width * 2.0
self.setPositionRelativeBot(pipeBottom, x:Float(xx), y: offset )
self.setPositionRelativeTop(pipeTop, x:Float(xx), y: offset + space)
pipeBottom.physicsBody = SKPhysicsBody(rectangleOfSize: pipeBottom.size)
pipeTop.physicsBody = SKPhysicsBody(rectangleOfSize: pipeTop.size)
pipeBottom.physicsBody?.dynamic = false
pipeTop.physicsBody?.dynamic = false
//pipeTop.physicsBody?.contactTestBitMask = birdCategory
//pipeBottom.physicsBody?.contactTestBitMask = birdCategory
self.addChild(pipeBottom)
self.addChild(pipeTop)
var actionArray1:NSMutableArray = NSMutableArray()
actionArray1.addObject(SKAction.moveTo(CGPointMake(-1000, pipeBottom.size.height - 200), duration: NSTimeInterval(durations)))
var actionArray2:NSMutableArray = NSMutableArray()
actionArray2.addObject(SKAction.moveTo(CGPointMake(-1000, pipeTop.size.height - 200), duration: NSTimeInterval(durations)))
actionArray1.addObject(SKAction.removeFromParent())
actionArray2.addObject(SKAction.removeFromParent())
pipeBottom.runAction(SKAction.sequence(actionArray1))
pipeTop.runAction(SKAction.sequence(actionArray2))
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
}
}
override func update(currentTime: CFTimeInterval) {
var timeSinceLastUpdate = currentTime - lastUpdateTimerInterval
lastUpdateTimerInterval = currentTime
if(timeSinceLastUpdate > 1){
timeSinceLastUpdate = 1/60
lastUpdateTimerInterval=currentTime
}
updateWithTimeSinceLastUpdate(timeSinceLastUpdate)
/* Called before each frame is rendered */
}
func setPositionRelativeBot(node:SKSpriteNode, x: Float, y: Float){
let xx = (Float(node.size.width)/2) + x
let yy = (Float(self.size.height)/2) - (Float(node.size.height)/2) + y
node.position.x = CGFloat(xx)
node.position.y = CGFloat(yy)
}
func setPositionRelativeTop(node:SKSpriteNode, x:Float, y:Float){
let xx = (Float(node.size.width)/2) + x
let yy = (Float(self.size.height)/2) + (Float(node.size.height)/2) + y
node.position.x = CGFloat(xx)
node.position.y = CGFloat(yy)
}
var lastUpdateTimerInterval:NSTimeInterval = NSTimeInterval()
var lastYieldTimeInterval:NSTimeInterval = NSTimeInterval()
var speedOfBird: CDouble = 1.8
func updateWithTimeSinceLastUpdate(timeSinceLastUpdate:CFTimeInterval){
lastYieldTimeInterval += timeSinceLastUpdate
if(lastYieldTimeInterval > speedOfBird ){
lastYieldTimeInterval=0
self.spawnPipeRow(self.randomOffset())
if speedOfBird > 0.8{
speedOfBird -= 0.1}
}
}
}
You should remove your sprites from the scene when you no longer need them. However your problem is probably not related to the memory occupied by your textures:
SpriteKit Programming Guide
An SKTexture object is created and attached to the sprite. This
texture object automatically loads the texture data whenever the
sprite node is in the scene, is visible, and is necessary for
rendering the scene. Later, if the sprite is removed from the scene or
is no longer visible, Sprite Kit can delete the texture data if it
needs that memory for other purposes. This automatic memory management
simplifies but does not eliminate the work you need to do to manage
art assets in your game.
The texture object itself is just a placeholder for the actual texture
data. The texture data is more resource intensive, so Sprite Kit loads
it into memory only when needed.
If you already have an SKTexture object, you can create new textures
that reference a portion of it. This approach is efficient because the
new texture objects reference the same texture data in memory.
Try to delete them with this code :
override func update(currentTime: CFTimeInterval) {
self.enumerateChildNodesWithName("nodeName") {
node, stop in
if (node is SKSpriteNode) {
let sprite = node as SKSpriteNode
// Check if the node is not in the scene
if (sprite.position.x < -sprite.size.width/2.0 || sprite.position.x > self.size.width+sprite.size.width/2.0
|| sprite.position.y < -sprite.size.height/2.0 || sprite.position.y > self.size.height+sprite.size.height/2.0) {
sprite.removeFromParent()
println("outside")
}
}
}
}
Don't forget to named your node :
node.name = "nodeName"
Hope your crash will stop

Resources