Swift 2 SkScene Changing - ios

I am new to both coding (swift) and making games. I am trying to get my game to switch SKScenes which I have created, for some reason when I try and switch scenes I just get a grey screen and I have no idea why. (It should be noted that the scene I am trying to transition from was already changed from the previous main scene. Below is the method I am using to change scenes. (I do not want any tranistion between the SKScenes)
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if(self.nodeAtPoint(location).name != nil && self.nodeAtPoint(location).name != "enter") {
userSequence.append(Int(self.nodeAtPoint(location).name!)!)
print("userSequence:\(userSequence)")
}
if(self.nodeAtPoint(location).name == "enter"){
//compareSequences()
if PreviewNumbersGameScene.sharedInstance.TempGeneratedSequence == userSequence.reverse(){
print(PreviewNumbersGameScene.sharedInstance.TempGeneratedSequence)
print("good")
let Winscene = WinGameScene(fileNamed: "WinGameScene")
self.scene!.view?.presentScene(Winscene)
}
else{
print("Temp:\(PreviewNumbersGameScene.sharedInstance.TempGeneratedSequence)")
loose()
print("bad")
}
}
}
}
And this is the scene I want to change to: (Name is WinGameScene)
import SpriteKit
class WinGameScene: SKScene{
//MARK: - Variables
//MARK: - View
override func didMoveToView(view: SKView) {
///* Setup your scene here */
loadView()
}
func loadView(){
backgroundColor = SKColor.greenColor()
print("winSceneLoaded")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let scene = PreviewNumbersGameScene(fileNamed: "PreviewNumbersGameScene")
self.view?.presentScene(scene)
}
}
}

In order to transition to a new scene, you should do something like this:
if let winGameScene = WinGameScene(fileNamed: "WinGameScene") {
self.view?.presentScene(winGameScene)
}else{
print("error")
}
The important thing here is that before you try to do this, you have to create a resource file called WinGameScene.sks. You can do that by going to: File->New->File...->Resource->SpriteKit Scene.

Related

Detect when rotation has completed and move to new Scene

I am trying to implement moving to another scene when a wheel stops rotating. The code I have is shown below. I cannot figure out how to detect when the velocity has reached 0.0?
import SpriteKit
import GameplayKit
class GameplayScene: SKScene {
var player: Player?;
override func didMove(to view: SKView) {
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
So from here, I would like to execute the following when the wheel has stopped spinning:
let play_scene = Questions(fileNamed: "QuestionsScene")
play_scene?.scaleMode = .aspectFill
self.view?.presentScene(play_scene!, transition: SKTransition.doorsOpenVertical(withDuration: 1))
I have now edited the class and it looks as follows:
import SpriteKit
import GameplayKit
class GameplayScene: SKScene, SKSceneDelegate {
var player: Player?
override func didMove(to view: SKView) {
self.delegate = self
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.pinned = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func didSimulatePhysics() {
if ((player?.physicsBody?.angularVelocity)! <= CGFloat(0.01))
{
print("Got it")
}
}
}
Problem is I still receive an error on the if statement within didSimulatePhysics function. The error I receive is "Thread 1: EXC_BAD_INSTRUCTION...."
Your wheel's SKPhysicsBody has a built-in property, angularVelocity, that tells you how fast it's spinning. You're already using it when you set it to r to start the wheel spinning.
To watch angularVelocity you can use didSimulatePhysics(). It gets called once every frame, right after the physics calculations are done. That will look something like this:
func didSimulatePhysics() {
if wheelIsSpinning && angularVelocity != nil && angularVelocity! <= CGFloat(0.001) {
wheelIsSpinning = false
// wheel has stopped
// add your code here
}
}
Due to the vagaries of physics modeling, the angularVelocity might never be exactly zero. So instead we watch for it to be less than some arbitrary threshold, in this case 0.001.
You don't want it to execute every frame when the wheel isn't moving, so I added a property wheelIsSpinning to keep track. You'll need to add it as an instance property to GameplayScene, and set it to true in spin().

Touch detection of SKSpriteNode with child nodes

I'm trying to trigger a touch event on a SKSpriteNode where it's child node is tapped on. When the child node it touched, the event isn't triggered. I have found a hack work around using .parent but doesn't feel like the most efficient or elegant way of doing is.
Please see code below:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first! as UITouch
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node is PlanListItem || node.parent is PlanListItem {
for plan in planListItems as [PlanListItem] {
plan.selected = false
}
// Some more code...
}
}
Help much appreciated.
You can do this inside the node subclass:
class PlanListItem:SKSpriteNode {
var isSelected: Bool = false
override init(texture size etc) {
//your init
self.userInteractionEnabled = true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("PlanListItem touched")
isSelected = !isSelected
}
}

(Swift + Spritekit) - remove node and its data entirely

I'm working a little game for iOS.
I've a SKSpriteNode in my scene - when I remove it with "removeFromParent" and touch the area it was last in, I still get the function.
My code is as following:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if tapToPlayNode.containsPoint(location){
tapToPlayNode.removeFromParent()
startNewGame()
}
}
}
func startNewGame(){
//Starts a new game with resetted values and characters in position
println("Ready.. set.. GO!")
//Shows the ui (value 1)
toggleUiWithValue(1)
}
In other words, I get "Ready.. set.. GO!" output when I touch the area even after it was deleted.
Any clues?
Bests,
Your tapToPlayNode is still retained by self and is removed from it's parent.
You should make it optional var tapToPlayNode:SKSpriteNode?and nil it after remove it from it's parent like this:
if let playNode = self.tapToPlayNode {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if playNode.containsPoint(location) {
playNode.removeFromParent()
startNewGame()
self.tapToPlayNode = nil // il it here!
break
}
}
}
You can also avoid to keep a reference of your tapToPlayNode and give it a name when initializing it like this:
node.name = #"tapToPlayNodeName"
// Add node to scene and do not keep a var to hold it!
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
// Retrieve the ode here
let tapToPlayNode = container.childNodeWithName("tapToPlayNodeName")!
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if tapToPlayNode.containsPoint(location){
tapToPlayNode.removeFromParent()
startNewGame()
}
}
}

Detect touch on child node of object in SpriteKit

I have a custom class that is an SKNode, which in turn has several SKSpriteNodes in it. Is there a way I can detect touches on these child SKSpriteNodes from my game scene?
I'm working in swift
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch
let touchLocation = touch.locationInNode(self)
if([yourSprite containsPoint: touchLocation])
{
//sprite contains touch
}
}
Source: http://www.raywenderlich.com/84434/sprite-kit-swift-tutorial-beginners
Examine Apple's SceneKitVehicle demo. Someone kindly ported it to Swift.
The code you want is in the GameView.swift file. In the GameView you'll see the touchesBegan override. Here's my version of it for Swift 2.1:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let scene = self.overlaySKScene else {
return
}
let touch = touches.first!
let viewTouchLocation = touch.locationInView(self)
let sceneTouchPoint = scene .convertPointFromView(viewTouchLocation)
let touchedNode = scene.nodeAtPoint(sceneTouchPoint)
if (touchedNode.name == "Play") {
print("play")
}
}
If it's not clear; the GameView is set as the app's view class by way of the Storyboard.
You'll need to compare the location of your Touch to the location of your SKNode
You can get the location of your touch at one of the following methods, using locationInNode():
touchesBegan()
touchesMoved()
touchesEnded()
Swift 5+
If you wish to encapsulate touch logic in a node and deal with it locally, you can simply set interaction to true in the corresponding node.
isUserInteractionEnabled = true
And then, of course, override touchesBegan to your desire.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {}
If you still wish to receive touches in the scene when touches occur inside a child node, you can, for instance, define a protocol and property for the child node's delegate and set the scene to be it.
e.g:
final class GameScene: SKScene {
private let childNode = ChildNode()
override func didMove(to view: SKView) {
addChild(childNode)
childNode.delegate = self
}
}
extension GameScene: TouchDelegate {}
protocol TouchDelegate {
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
}
final class ChildNode: SKSpriteNode {
var delegate: TouchDelegate?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
delegate?.touchesBegan(touches, with: event)
}
}

Swift scene kit: move SCNNode to tap position?

Ok, working with ARKit in Swift here and trying to get a grip on this -
for my game controls I want to be able to control the point (position in 3d space) that the SCNNode is moving towards when the users finger is down on the screen, meaning started by the touchesBegan func.
I want it to be like Apple's fox game with the joystick her but in AR: https://developer.apple.com/library/content/samplecode/Fox/Introduction/Intro.html
My main issue is it doesn't seem that my SCNAction's position to move is being updated correctly on touchesMoved, and moreover I need the SCNNode to move at same speed to the position regardless of how far it is away.
Here's what I have, it is working but not correct:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let results = sceneView.hitTest(touch.location(in: sceneView), types: [ARHitTestResult.ResultType.featurePoint])
guard let hitFeature = results.last else { return }
checkIfNodeTapped(touches: touches, node: theDude.node)
theDude.moveToPos(pos: getARPos(hitFeature: hitFeature))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let results = sceneView.hitTest(touch.location(in: sceneView), types: [ARHitTestResult.ResultType.featurePoint])
guard let hitFeature = results.last else { return }
theDude.updateWalkTo(pos: getARPos(hitFeature: hitFeature))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// if virtualObjectManager.virtualObjects.isEmpty {
//
// return
// }
// virtualObjectManager.reactToTouchesEnded(touches, with: event)
//Remove move actions
theDude.stopMoving()
}
func updateWalkTo(pos: SCNVector3)
{
walkAction = SCNAction.move(to: pos, duration: 1)
}
func moveToPos(pos: SCNVector3)
{
walkAction = SCNAction.move(to: pos, duration: 1)
self.node.runAction(walkAction, forKey: "walk")
}
func stopMoving()
{
self.node.removeAction(forKey: "walk")
}
where the walkAction is just a defined SCNAction. How can I fix this so that the node runs towards wherever the user's finger is on the screen (converted to AR points)?

Resources