scene in SpriteKit - ios

hello thereI'm working in my Game but I face a problem when I'm trying to change the scene. I sit up a button and it's work but when the transfer happened I can't see my Background this is my code
This is the code for my Game scene. the back ground ok
import SpriteKit
class GameScene: SKScene {
// Next scene button
var nextButton: UIButton!
let background = SKSpriteNode(imageNamed: "BG")
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
// Background Image
background.position = CGPoint(x: size.width / 2, y: size.height / 2)
background.size = CGSize(width: 2100, height: 1200)
background.zPosition = -3
addChild(background)
// Next button Action
nextButton = UIButton(frame: CGRect(x: 0, y: 0, width: frame.size.width / 2, height:300))
nextButton.center = CGPoint(x: view.frame.size.width / 7.3, y: view.frame.size.width / 3)
nextButton.setTitle("Next Button", forState: UIControlState.Normal)
nextButton.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
nextButton.addTarget(self, action: Selector("Next"), forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(nextButton)
}
func Next() {
self.view?.presentScene(Play(), transition: SKTransition.crossFadeWithDuration(1.0))
nextButton.removeFromSuperview()
background.removeFromParent()
}
}
this is the code for my Play scene. the back ground is not appearing i don't know why
import Foundation
import SpriteKit
class Play: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
// Background Image
let background = SKSpriteNode(imageNamed: "BG")
background.position = CGPoint(x: size.width / 2, y: size.height / 2)
//background.size = CGSize(width: 2100, height: 1200)
background.zPosition = -3
addChild(background)
}
}

Are you checking that the play scene is actually being transitioned too?.
You should also init the new scene with a size if you dont use .sks files.
func Next() {
nextButton.removeFromSuperview()
let scene = Play(size: self.size) // get size of current scene
self.view?.presentScene(scene, transition: SKTransition.crossFadeWithDuration(1.0))
}
If you do use .sks files (xCode visual editor) than you would need to create a new .sks file called Play and load the scene like so
func Next() {
if let scene = Play(fileNamed: "Play") {
nextButton.removeFromSuperview()
self.view?.presentScene(scene, transition: SKTransition.crossFadeWithDuration(1.0))
}
}
As a tip you dont have to remove SKSpriteNodes after changing scenes. You are already adding them to the scene (addChild) and they will get removed for you automatically.
You still have to remove UIKit stuff tho, such as your UIButton, because they get added to your view and not the SKScene.

Related

SpriteKit scrolling background image showing line between images

I'm learning how to make games in iOS, so I decided to replicate the first level of Mario Bros using my own assets, just to learn how to make them as well.
The issue I'm having right now is that, when creating the scrolling background, using this formula I found on Hacking with Swift, I keep getting a line in between my images.
I've checked that my images have no border in the AI file. (The images are the ones above)
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var player = SKSpriteNode()
private var bg = SKSpriteNode()
override func didMove(to view: SKView) {
let playerTexture = SKTexture(imageNamed: "player")
player = SKSpriteNode(texture: playerTexture)
player.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
addBackground()
self.addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
// MARK: UTILITY FUNCTIONS
func addBackground() {
let bgTexture = SKTexture(imageNamed: "bg")
for i in 0 ... 1 {
bg = SKSpriteNode(texture: bgTexture)
//bg.position = CGPoint(x: (i == 0 ? bgTexture.size().width : bgTexture.size().width - 1) * CGFloat(i), y: self.frame.midY)
bg.position = CGPoint(x: (bgTexture.size().width * CGFloat(i)) - CGFloat(1 * i), y: 0)
bg.size.height = self.frame.height
//bg.anchorPoint = CGPoint.zero
bg.zPosition = -10
self.addChild(bg)
let moveLeft = SKAction.moveBy(x: -bgTexture.size().width, y: 0, duration: 5)
let moveReset = SKAction.moveBy(x: bgTexture.size().width, y: 0, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
bg.run(moveForever)
}
}
}
Just create a new project, copy-paste it and you should see it running
I also changed my GameScene.sks size to: 2688 x 1242
And one last change I made to make the background appear in full screen was to set the LaunchScreen as stated in this answer which seems to be an issue in Xcode 12
I understand that the formula from Hacking with Swift post is making the images overlap by 1px, I've tried removing the 1 * i part from it, yet results are not different.
Other thing I did was to verify the images were "joining" perfectly together, and they do, the lines you can see in the image below are from the AI canvas, both images join in between "cactuses".
I also tried running it into a device in case it might be a bug in the simulator:
Your image has a thin black border on the left handside and along the top, 1 pixel wide. It's black. Remove that and try again.

How to move a character back and forth across horizontally in swift

Currently trying to get a monster character to move across the screen horizontal back and forth. I'm extremely new to Swift as I just started learning last week and also haven't exactly master OOP. So how do I properly call the enemyStaysWithinPlayableArea func while it calls the addEnemy func. Heres what I have so far:
import SpriteKit
class GameScene: SKScene {
var monster = SKSpriteNode()
var monsterMove = SKAction()
var background = SKSpriteNode()
var gameArea: CGRect
// MARK: - Set area where user can play
override init(size: CGSize) {
// iphones screens have an aspect ratio of 16:9
let maxAspectRatio: CGFloat = 16.0/9.0
let playableWidth = size.height/maxAspectRatio
let margin = (size.width - playableWidth)/2
gameArea = CGRect(x: margin, y: 0, width: playableWidth, height: size.height)
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(to view: SKView) {
// I want to run addEnemy() while it checks for enemyStayWithinPlayableArea()
// run(SKAction.repeatForever(...)) ???
enemyStayWithinPlayableArea()
// MARK: - Setting the background
background = SKSpriteNode(imageNamed: "background")
background.size = self.size
// Set background at center of screen
background.position = CGPoint(x:self.size.width/2 ,y:self.size.height/2)
// Layering so everything in the game will be in front of the background
background.zPosition = 0
self.addChild(background)
}
func addEnemy() {
// MARK: - Set up enemy and its movements
monster = SKSpriteNode(imageNamed: "monster")
monster.zPosition = 5
// this is where the enemy starts
monster.position = CGPoint(x: 0, y: 300)
self.addChild(monster)
// I want it to move to the end of the screen within 1 sec
monsterMove = SKAction.moveTo(x: gameArea.maxX, duration: 1.0)
monster.run(monsterMove)
}
func enemyStayWithinPlayableArea(){
addEnemy()
// when the monster reaches the right most corner of the screen
// turn it back around and make it go the other way
if monster.position.x == gameArea.maxX{
monsterMove = SKAction.moveTo(x: gameArea.minX, duration: 1.0)
monster.run(monsterMove)
}
// when the monster reaches the left most corner of the screen
// same idea as above
if monster.position.x == gameArea.minX{
monsterMove = SKAction.moveTo(x: gameArea.maxX, duration: 1.0)
monster.run(monsterMove)
}
}
}
So far the enemy moves across the screen to the right edge of the screen, so that part works perfectly. I just need help making sure it continues in a loop (perhaps using the SKAction.repeatForever method but not sure how).
To understand movement of image in spritekit check this example.
let moveLeft = SKAction.moveTo(CGPoint(x: xpos, y: ypos), duration: duration)
let moveRight = SKAction.moveTo(CGPoint(x: xpos, y: ypos), duration: duration)
sprite.runAction(SKAction.repeatActionForever(SKAction.sequence([moveLeft, moveRight])))
for horizontal movement y position never been changed it should be same as in move left and move right.to stop repeatforever action use this.
sprite.removeAllActions()
Make two functions moveToMax and moveToMin with respective SKActions.And run action with completion handler.
func moveToMin(){
monsterMove = SKAction.moveTo(x: gameArea.minX, duration: 1.0)
monster.run(monsterMove, completion: {
moveToMax()
})
}

Background blocks particle effects

I added a background and a explosion particle effect to my app. the background and the sprites work fine together but the explosion effect appears behind the background. The zposition for the background is set to zero, some assistance would be legit.
background = SKSpriteNode(imageNamed: "Starfield")
background.size = CGSize(width: 430, height: 700)
background.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 )
self.addChild(background)
background.zPosition = 0
func explosion(pos: CGPoint) {
var emitterNode = SKEmitterNode(fileNamed: "Explosion.sks")
emitterNode!.particlePosition = pos
self.addChild(emitterNode!)
// Don't forget to remove the emitter node after the explosion
self.runAction(SKAction.waitForDuration(2), completion: { emitterNode!.removeFromParent() })
}
Here's the solution for anyone who needs it.
func explosion(pos: CGPoint) {
var emitterNode = SKEmitterNode(fileNamed: "Explosion.sks")
emitterNode!.particlePosition = pos
self.addChild(emitterNode!)
// Don't forget to remove the emitter node after the explosion
self.runAction(SKAction.waitForDuration(2), completion: { emitterNode!.removeFromParent() })
emitterNode?.zPosition = 2
}

Swift: How to fill view with a SK Sprite Node?

I am trying to learn Swift by creating my own game (app), but I am having a problem.
I have a SKSpriteNode which I use as the background for the game. It's an image I've made. My problem is that my background won't fill the view so I get these grey bars outside of the Node.
What is wrong in my code?
override func didMoveToView(view: SKView) {
/* Setup your scene here */
Hintergrund = SKSpriteNode(imageNamed: "PenaltyLocker_BG1")
Hintergrund.size = CGSize(width: view.frame.width, height: view.frame.height)
Hintergrund.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
Hintergrund.zPosition = 1
Hintergrund.name = "Hintergrund"
addChild(Hintergrund)
Now when I start the simulator for iPhone it looks like this: http://imgur.com/a/mBOWn
Change to use the size of the SKScene.
self.scene.size.width / self.scene.size.height
Hintergrund.size = CGSize(width: self.scene.size.width, height: self.scene.size.height)
Hintergrund.position = CGPoint(x: self.scene.size.width/2, y: self.scene.size.height/2)
You can also check this tutorial for more info.

How to interact with a SKSpriteNode after another one of the same var name has been created?

I appologise for the confusing question title, but I wasn't quite sure how to phrase my question. My ultimate goal is to remove an SKSpriteNode a certain amount of time after it's creation. However, there is a small complication. Using the following code
func remove() {
laser.removeFromParent()
}
runAction(
SKAction.sequence([
SKAction.waitForDuration(5.0),
SKAction.runBlock(remove)
])
)
I'm able to remove the SKSpriteNode I named 'laser'. When I call my function once, fireLasers(), everything runs smoothly and the laser disappears after 5 seconds. The problem is when I call it twice within a period of 5 seconds. When this happens, the first laser will stick around indefinitely and the second disappears earlier than intended. I understand why this happens, but would like to know if there's a way around it. Here's the code for the GameScene which creates the world, background image, player Sprite, and defines the fireLasers function. There's a lot of stuff that goes on in the ViewController that works with the velocities and directions of the sprites, but I hope this is sufficient to find a solution.
import SpriteKit
class GameScene: SKScene {
var player = SKSpriteNode()
var world = SKShapeNode()
var worldTexture = SKSpriteNode()
var laser = SKShapeNode()
override func didMoveToView(view: SKView) {
self.anchorPoint = CGPointMake(0.5, 0.5)
self.size = CGSizeMake(view.bounds.size.width, view.bounds.size.height)
// Add world
world = SKShapeNode(rectOfSize: CGSizeMake(7000, 7000))
world.physicsBody = SKPhysicsBody(edgeLoopFromPath: world.path!)
world.fillColor = SKColor.blackColor()
self.addChild(world)
// Add Background Image
worldTexture = SKSpriteNode(imageNamed: "grid")
worldTexture.size = CGSize(width: 7000, height: 7000)
world.addChild(worldTexture)
// Add player
player = SKSpriteNode(imageNamed: "Spaceship")
player.size = CGSize(width: 50, height: 50)
player.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 50, height: 50))
player.physicsBody?.affectedByGravity = false
player.physicsBody?.dynamic = true
world.addChild(player)
}
override func update(currentTime: CFTimeInterval) {
world.position.x = -player.position.x
world.position.y = -player.position.y
}
func fireLasers() {
laser = SKShapeNode(rectOfSize: CGSizeMake(10, 50))
laser.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 10, height: 50))
laser.position.x = player.position.x
laser.position.y = player.position.y + CGFloat(50)
laser.fillColor = SKColor.redColor()
laser.physicsBody?.dynamic = true
laser.physicsBody?.affectedByGravity = false
world.addChild(laser)
func remove() {
laser.removeFromParent()
}
runAction(
SKAction.sequence([
SKAction.waitForDuration(5.0),
SKAction.runBlock(remove)
])
)
}
}
Instead of keeping a reference to the laser (which will, as you noticed, force you to only have one around at a time), why not run an action on the created laser nodes themselves? This will allow you to use the handy SKAction class method removeFromParent():
let newLaser = makeNewLaser()
let laserAction = SKAction.sequence([SKAction.waitForDuration(5), SKAction.removeFromParent()])
newLaser.runAction(laserAction)

Resources