Issue with positioning SKSpriteNode in SKScene - swift

This is my first project with SpriteKit and I am following THIS tutorial
But when I try to give the position to the Image as he did into that tutorial at 20:10 with this code :
playScene.swift
import SpriteKit
class playScene : SKScene {
let runningBar = SKSpriteNode(imageNamed: "bar")
override func didMoveToView(view: SKView) {
println("We are at the new scene!")
self.backgroundColor = UIColor(hex: 0x80D9FF, alpha: 1)
self.runningBar.anchorPoint = CGPointMake(0.5, 0.5)
self.runningBar.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - (self.runningBar.size.height / 2))
self.addChild(self.runningBar)
}
override func update(currentTime: NSTimeInterval) {
}
}
I want to give position at bottom of the screen but I got this Output:
But the output should be:
GameScene.swift (If needed)
import SpriteKit
class GameScene: SKScene {
let playButton = SKSpriteNode(imageNamed: "play")
override func didMoveToView(view: SKView) {
self.playButton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(self.playButton)
self.backgroundColor = UIColor(hex: 0x80D9FF, alpha: 1)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches{
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.playButton{
var scene = playScene(size: size.self)
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Can anybody tell me how can I achieve this?
Thanks In advance.

You need to look at the tutorial properly. You have set the following lines wrong:
self.runningBar.anchorPoint = CGPointMake(0.5, 0.5)
self.runningBar.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - (self.runningBar.size.height / 2))
They should be:
self.runningBar.anchorPoint = CGPointMake(0, 0.5)
self.runningBar.position = CGPointMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame) + (self.runningBar.size.height / 2))
For a better understanding of the coordinate system, have a look at the documentation.

Related

SpriteKit creating a button issues

Im attempting to use some of the code from a solution found on this page for creating a button Create Button in SpriteKit: Swift
class GameScene: SKScene {
let button = SKSpriteNode(imageNamed: "yourImgName")
override func didMoveToView(view: SKView) {
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) + 50)
self.addChild(button)
//Adjust button properties (above) as needed
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
let yourNextScene = YourNextScene(fileNamed: "YourNextScene")
self.view?.presentScene(yourNextScene!)
}
}
}
}
and the current code that I have is supposed to make the player jump when the button is pressed, but nothing is currently happening when its pressed
import SwiftUI
import SpriteKit
import UIKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
let playerRadius = player.frame.width / 2.0
player.position = CGPoint(x: 200, y: 500)
player.name = "Jimmy"
addChild(player)
player.physicsBody = SKPhysicsBody(circleOfRadius: playerRadius)
player.physicsBody?.allowsRotation = false
player.physicsBody?.friction = 0
player.physicsBody?.restitution = 0
player.zPosition = 100
// Button
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPoint(x: 100, y: 100)
self.addChild(button)
// Physics
physicsBody = SKPhysicsBody(edgeLoopFrom: frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)))
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
player.physicsBody?.applyForce(CGVector(dx: 100, dy: 1000))
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
player.physicsBody?.applyForce(CGVector(dx: 0, dy: 10000))
}
}
}
}
override func update(_ currentTime: TimeInterval) { }
}
I'm thinking that maybe this is an issue with the press not being resitered at all but I'm not fully sure
Your main problem is you put all of your code inside the didMove function. You put the touchesBegan function inside the didMove function. When the didMove function finishes, touchesBegan goes away so none of your touches are going to be handled in the game.
You also declared the button and the player as local variables inside the didMove function.
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
// Rest of function omitted
}
When the didMove function finishes, the button and player go away too. You also have the same image name for the player and button.
The fix is to make the button and player variables properties of the GameScene class. Move touchesBegan out of the didMove function too.
class GameScene: SKScene {
// Declare button and player here.
var button = SKSpriteNode()
var player = SKSpriteNode()
override func didMove(to view: SKView) {
// Initialize button, player, and everything else here.
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Code in touchesBegan func goes here.
}
}

Objects Position Not Matching Up (Spritekit)

I am building a spritekit game and have just started the project. I have a circle on the screen which starts in the center, and when i drag my finger from the circle outward, it will show a dotted line/bezierpath connected to the ball which will help the user see where it is aiming the ball. When the user lifts their finger, the ball will shoot in the opposite direction of the aim line. (Think a game like soccer stars or pool). The issue is that the maneuver works the first time when everything starts in the middle: I drag my finger and the ball shoots in opposite direction then stops. But when I try it again, the position of the aiming line says it is the same as the ball (It should be), but then it shows up like an inch away from the ball on the screen. I feel like this may be an issue that the scene(s) behind the objects may not be the same size? But I'm confused because I think I'm only using one scene.
GameViewController viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
scene.size = view.bounds.size
//scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
GameScene Code (Doubt you need all of it but whatever):
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var startAimPoint = CGPoint()
var endAimPoint = CGPoint()
let damping:CGFloat = 0.94
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
var physicsBody = SKPhysicsBody(circleOfRadius: 35)
ball.physicsBody = physicsBody
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.friction = 10.0
ball.position = CGPoint(x: frame.midX, y: frame.midY)
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
startAimPoint = ball.position
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = location
assignAimLine(start: startAimPoint, end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
let dx2 = ball.physicsBody!.velocity.dx * damping
let dy2 = ball.physicsBody!.velocity.dy * damping
ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(start: CGPoint, end: CGPoint){
aimLine.removeFromParent()
var bezierPath = UIBezierPath()
bezierPath.move(to: start)
bezierPath.addLine(to: shortenedEnd(startPoint: start, endPoint: end))
var pattern : [CGFloat] = [10.0, 10.0]
let dashed = SKShapeNode(path: bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern))
aimLine = dashed
aimLine.position = ball.position
aimLine.zPosition = 0
self.addChild(aimLine)
}
func hypotenuse(bp: UIBezierPath) -> Double{
var a2 = bp.cgPath.boundingBox.height * bp.cgPath.boundingBox.height
var b2 = bp.cgPath.boundingBox.width * bp.cgPath.boundingBox.width
return Double(sqrt(a2 + b2))
}
func hypotenuse(startP: CGPoint, endP: CGPoint) -> Double{
var bezierPath = UIBezierPath()
bezierPath.move(to: startP)
bezierPath.addLine(to: endP)
return hypotenuse(bp: bezierPath)
}
func shortenedEnd(startPoint: CGPoint, endPoint: CGPoint) -> CGPoint{
var endTemp = endPoint
//while hypotenuse(startP: startPoint, endP: endTemp) > 150{
endTemp = CGPoint(x: endTemp.x / 1.01, y: endTemp.y / 1.01)
//}
return endTemp
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}
I tried printing the frame size for the scene and it says 400 something x 700 something (I am testing on iPhone 6 Plus), and it says the UIScreen is same size so i don't know what issue is. Overall, I just need the aiming line to be on the center of the circle more than just the first time I try the maneuver. Thanks.
Like I mentioned in the comments, your problem was how you were laying out your paths. The code below makes the path relative to the ball instead of absolute to the scene. I also fixed the issue with creating new shapes every time.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var endAimPoint = CGPoint()
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
ball.position = CGPoint(x: frame.midX, y: frame.midY)
let physicsBody = SKPhysicsBody(circleOfRadius: 35)
physicsBody.affectedByGravity = false
physicsBody.friction = 10.0
physicsBody.linearDamping = 0.94
ball.physicsBody = physicsBody
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
aimLine.path = nil
self.addChild(aimLine)
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = self.convert(location, to: ball)
assignAimLine(end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
//let dx2 = ball.physicsBody!.velocity.dx * damping
//let dy2 = ball.physicsBody!.velocity.dy * damping
//ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(end: CGPoint){
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint.zero)
bezierPath.addLine(to: end)
let pattern : [CGFloat] = [10.0, 10.0]
aimLine.position = ball.position
aimLine.path = bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern)
aimLine.zPosition = 0
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}

How to set up an SKScene with an SKNode with a texture in Swift Playgrounds?

I tried copying the template code from a SpriteKit project into a playground, but all I get is a grey screen when I present the Scene. Do I need to create an SKScene and if so how do I assign it to the scene class that I am using.
The following is my code to create and present the scene:
#objc func goToGameScene(){
print("GameView Loaded")
sceneView = SKView(frame: CGRect(x: 0, y: 0, width: 666, height: 500))
sceneView.backgroundColor = UIColor.black
PlaygroundPage.current.liveView = sceneView
if let view = self.sceneView as SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
}
And this is my SKScene class, which has a filename of GameScene.swift.
import Foundation
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var bg = SKSpriteNode()
func didBegin(_ contact: SKPhysicsContact) {
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
let bgTexture = SKTexture(image: UIImage(named: "MainScreenBackground.png")!)
let moveBGAnimation = SKAction.move(by: CGVector(dx:-bgTexture.size().width, dy:0), duration: 11)
let shiftBackground = SKAction.move(by: CGVector(dx: bgTexture.size().width, dy:0), duration: 0)
let repeatAnimationBg = SKAction.repeatForever(SKAction.sequence([moveBGAnimation, shiftBackground]))
var q = 0
while(q < 3){
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: bgTexture.size().width * CGFloat(q), y: self.frame.midY)
bg.size.height = self.frame.height
bg.run(repeatAnimationBg)
self.addChild(bg)
q+=1
bg.zPosition = -1
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
}
}
Assuming you have dragged and dropped your MainScreenBackground.png image into you Assets.xcassets folder, you can use the code below to add the image to your scene as a SKSpriteNode.
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
let bgTexture = SKSpriteNode(imageNamed: "MainScreenBackground")
self.addChild(bgTexture)
...

Swift SpriteKit: Use of unresolved identifier "player"?

When I try to run an action on a sprite I've set a constant for in didMoveToView, I use the same name for it in the touchesBegan function and get a "use of unresolved identifier: "player" error. I have another game where I so the exact same thing and it runs perfectly. Need help! Here is my code:
import SpriteKit
class GameScene: SKScene {
var movingGround : MCTGround!
var fruitGenerator : MCTFruitGen!
var cloudGenerator: MCTCloudGen!
var isStarted = false
override func didMoveToView(view: SKView) {
let player = SKSpriteNode(imageNamed: "koala_idle")
player.position = CGPointMake(95, 150)
addChild(player)
backgroundColor = UIColor(red: 159.0/255.0, green: 201.0/255.0, blue: 244.0/255.0, alpha: 1.0)
movingGround = MCTGround(size: CGSizeMake(view.frame.width, 20))
movingGround.position = CGPointMake(0, view.frame.size.height / 4)
addChild(movingGround)
fruitGenerator = MCTFruitGen(color: UIColor.clearColor(), size: view.frame.size)
fruitGenerator.position = view.center
addChild(fruitGenerator)
let frames = [
SKTexture(imageNamed: "koala_idle"),
SKTexture(imageNamed: "koala_walk01"),
SKTexture(imageNamed: "koala_walk02"),
]
let duration = 1.5 + drand48() * 1.0
let move = SKAction.animateWithTextures(frames, timePerFrame:0.10)
let wait = SKAction.waitForDuration(duration)
let rest = SKAction.setTexture(frames[0])
let sequence = SKAction.sequence([move, rest])
player.runAction(SKAction.repeatActionForever(sequence))
cloudGenerator = MCTCloudGen(color: UIColor.clearColor(), size: view.frame.size)
cloudGenerator.position = view.center
addChild(cloudGenerator)
cloudGenerator.populate(7)
cloudGenerator.startGeneratingWithSpawnTime(1)
}
func start() {
isStarted = true
cloudGenerator.startGeneratingWithSpawnTime(1)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
jumpPlayer()
movingGround.start()
fruitGenerator.startGeneratingFruitEvery(1)
}
override func update(currentTime: CFTimeInterval) {
}
func jumpPlayer() {
let jumpUpAction = SKAction.moveByX(0, y: 40, duration: 0.5)
let jumpDownAction = SKAction.moveByX(0, y: -40, duration: 0.5)
let jumpSequence = SKAction.sequence([jumpUpAction, jumpDownAction])
player.runAction(jumpSequence) // This is where my error is
}
}
Make player a class scope variable (property). Declare it not in a function, but a level above.
var player: SKSpriteNode?
override func didMoveToView(view: SKView) {
player = SKSpriteNode ...
then access it via player?. The safest way.
Your problem is that you are declaring player inside didMoveToView. This makes it private to the didMoveToView method. All you need to do is move let player = SKSpriteNode(imageNamed: "koala_idle") to where you declare movingGround and the other variables with it.
The beginning your code should look like this:
import SpriteKit
class GameScene: SKScene {
var movingGround : MCTGround!
var fruitGenerator : MCTFruitGen!
var cloudGenerator: MCTCloudGen!
let player = SKSpriteNode(imageNamed: "koala_idle")
var isStarted = false
override func didMoveToView(view: SKView) {
player.position = CGPointMake(95, 150)
addChild(player)
Go to File Inspector (right click on the file referenced in the error and select Show File Inspector). In Target Membership make sure your actual app is selected.
In my case only the Test modules were selected.
So as far as I can tell (I'm new to all this), the referenced file was hidden/unregistered with the ViewController.

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.