Continue in level2 - swift

I created a game where I have more scene, scene 1 = 1 level. When you get to level 2. How to make so I continued there even after the games?
I know that it is done by NSUserDefaults. But I do not know how. Thank you
here is my code:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if(touchedNode.name == "checkbutton"){
if(inputText!.text == "motorka" || inputText!.text == "Motorka" || inputText!.text == "MOTORKA"){
runAction(SKAction.playSoundFileNamed("correct.wav", waitForCompletion: false))
correct.hidden = false
inputText!.hidden = true
check.hidden = true
let nextLevel = level2(size: size)
nextLevel.scaleMode = scaleMode
let transitionType = SKTransition.crossFadeWithDuration(1.4)
let wait = SKAction.waitForDuration(0.9)
let action = SKAction.runBlock {
view?.presentScene(nextLevel, transition: transitionType)
}
self.runAction(SKAction.sequence([wait, action]))
}
else {
unCorrect.hidden = false
runAction(SKAction.playSoundFileNamed("Wrong-answer-sound-effect.mp3", waitForCompletion: false))
delay(1.0) {
self.unCorrect.hidden = true
}
}

You have to figure out what data has to be transferred into your Level 2. Once you have that, you save the data you need in Level 1 (using NSUserDefaults as you already know) and then import the data into the beginning of Level 2.
In Level 1 let's stay you have an enemy with various properties like health, strength and weapon. One option you have is to save those 3 properties as strings, store them in an array (or dictionary) and save that in NSUserDefaults.
When you get to level 2, you read the NSUserDefaults, create your enemy and set the 3 properties.
There are MANY different ways of doing the above so my example is in no way the only method or the best method.

Related

How can I play DAE animation from a specific time interval in ARKit?

My animation model has a total time interval of 45 seconds. I tap on the model and should be able to play it not right from the beginning, but from say, the 15th second.
Can anyone please help me out if you think by any means that this is possible?
EDIT:
As soon as I load my animation model, the SceneKit plays the animation. Now with the key in hand, I crop the animation with the help of a custom method I came across.
By tapping on the model, I enumerate through all the parent/child nodes to stop or remove animation from the scene. So far so good.
The problem appears when I try to add the cropped animation back on to the scene. Nothing really happens, as the scene remains idle without any action.
Am I doing something wrong here?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchLocation = touches.first!.location(in: sceneView)
// Let's test if a 3D Object was touch
var hitTestOptions = [SCNHitTestOption: Any]()
hitTestOptions[SCNHitTestOption.boundingBoxOnly] = true
let hitResults: [SCNHitTestResult] = sceneView.hitTest(touchLocation, options: hitTestOptions)
let animation = animScene?.entryWithIdentifier("myKey", withClass: CAAnimation.self)
print(" duration is...", animation!.duration)
let animationNew = subAnimation(of:(animation)!, startFrame: 10, endFrame: 360)
print("New duration is...", animationNew.duration)
sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
node.removeAllAnimations()
}
sceneView.scene.rootNode.enumerateChildNodes { (node, _) in
node.addAnimation(animationNew, forKey: "myKey")
}
}
Suppose, it's the most robust approach for playing animations containing in Collada file:
import SceneKit
func myAnimation(path: String) -> SCNAnimation? {
let scene = SCNScene(named: path)
var animation: SCNAnimationPlayer?
scene?.rootNode.enumerateChildNodes( { (child, stop) in
if let animationKey = child.animationKeys.first {
animation = child.animationPlayer(forKey: animationKey)
// variable pointee: ObjCBool { get nonmutating set }
stop.pointee = true
}
})
return animation?.animation
}
let node = SCNNode()
let animation = myAnimation(path: "animation.dae")
node.addAnimation(animation!, forKey: "FirstAnimationSet")
Seems my .dae file had a number of animations(with multiple id's) and they should be grouped as one. Once I group them together, I get control over the animation and could play them from whichever frame I want.

hitTest(_:options:) don't recognize nodes behind ARKit planes

I place an object on a wall, then try to recognize tap on it, but hit test returns 0 objects. When I change Z position of the object and place it a little bit closer to cam, it's recognized well, but this isn't a solution, because planes are always changing and it can cover the object in any moment. How can I made hitTest work correctly and recognize my nodes behind planes? Or, maybe, I use the wrong method?
fileprivate func addNode(atPoint point: CGPoint) {
let hits = sceneView.hitTest(point, types: .existingPlaneUsingExtent)
if hits.count > 0, let firstHit = hits.first, let originNode = originNode {
let node = originNode.clone()
sceneView.scene.rootNode.addChildNode(node)
node.position = SCNVector3Make(firstHit.worldTransform.columns.3.x, firstHit.worldTransform.columns.3.y, firstHit.worldTransform.columns.3.z)
let resize = simd_float4x4(SCNMatrix4MakeScale(0.2, 0.2, 0.2))
let rotation = simd_float4x4(SCNMatrix4MakeRotation(.pi / 2, -1, 0, 0))
let transform = simd_mul(firstHit.worldTransform, resize)
let finalTransform = simd_mul(transform, rotation)
node.simdTransform = finalTransform
addedNodes.insert(node)
}
}
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
print("Unable to identify touches on any plane. Ignoring interaction...")
return
}
let touchPoint = touch.location(in: sceneView)
let hits = sceneView.hitTest(touchPoint, options: [SCNHitTestOption.boundingBoxOnly: true])
let filtered = hits.filter({ addedNodes.contains($0.node) })
print("\(hits.count) vs \(filtered.count), \(hits.first?.node.name ?? "no name")")
if let node = filtered.first?.node {
node.removeFromParentNode()
addedNodes.remove(node)
return
}
addPictureToPlane(atPoint: touchPoint)
}
addedNodes - set with added objects. When I added translating transform with changing Z coordinate at least on 0.05 (close to the camera) detecting working good. At least before plane changing and moving ahead the node.
I believe what you need to do is change your SCNHitTestSearchModeparameter which allows you to set:
Possible values for the searchMode option used with hit-testing
methods.
static let searchMode: SCNHitTestOption
Whereby:
The value for this key is an NSNumber object containing the raw
integer value of an SCNHitTestSearchMode constant.
From the Apple Docs there are three possible options you can use here:
case all
The hit test should return all possible results, sorted from nearest
to farthest.
case any
The hit test should return only the first object found, regardless of
distance.
case closest
The hit test should return only the closes object found.
Based on your question therefore, you would likely need to to utilise the all case.
As such your hitTest function would probably need to look something like this (remembering that self.augmentedRealityView refers to an ARSCNView):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//1. Get The Current Touch Location
guard let currentTouchLocation = touches.first?.location(in: self.augmentedRealityView) else { return }
//2. Perform An SCNHitTest Setting The SearchMode To 1 (All) Which Returns A List Of Results Sorted From Nearest To Farthest
if #available(iOS 11.0, *) {
let hitTestResults = self.augmentedRealityView.hitTest(currentTouchLocation, options: [SCNHitTestOption.searchMode: 1])
//3. Loop Through The Results & Get The Nodes
for index in 0..<hitTestResults.count{
let node = hitTestResults[index]
print(node)
}
}
}

Adding tap functionality to ColorSprites

Making a Texas Hold'Em app. This is my first time working in sprite-kit. My current problem is that I have the cards as Color Sprite objects.
This is the GameScene with the cards. Ignore the five cards on the top. I only want the bottom two to have the flip functionality.
My goal is for the user to be able to tap one of their cards and it will flip from the back of the card, to the card in their hand and vice versa. My current strategy is using this override func touchesBegan.
The array playerOne is the cards for playerOne.
The cardOne and cardTwo are the cards for the playerOne.
FlippedOne and flippedTwo are keeping track of which card is currently flipped. This way the cards would flip individually.
This same concept is repeated if there is multiple players.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if (playerCount == 0 && turn == 0) {
let cardOne = playerOne[0].getNum()
let cardTwo = playerOne[1].getNum()
var flippedOne = 0
var flippedTwo = 0
for touch in touches {
let location = touch.location(in: self)
if (playerCardOne.frame.contains(location) && flippedOne == 0) {
playerCardOne.texture = cardTextures[cardOne]
flippedOne = 1
}
if (playerCardOne.frame.contains(location) && flippedTwo == 1) {
playerCardOne.texture = back
flippedOne = 0
}
if (playerCardTwo.frame.contains(location) && flippedTwo == 0) {
playerCardTwo.texture = cardTextures[cardTwo]
flippedTwo = 1
}
if (playerCardTwo.frame.contains(location) && flippedTwo == 0) {
playerCardTwo.texture = back
flippedTwo = 0
}
}
}
}
If there is a better way of making the cards flip on tap, I am open to ideas.
My thought on why this is not working is because it's an override function.
What currently happens is that I can get one of the cards to flip from the back to the actual card face. After which I cannot get either card to flip.
the issue with why you can't repeatedly run the code is because you are resetting the flippedOne and flippedTwo variables every time touches began gets fired
Move the variables out to the scene and it will work fine.
I also recommend converting them to Bool for simplicity
Also you don't need the frame property when checking if the spriteNode contains a location
Also brackets around the if statement aren't required in Swift (you must've come from a different language to Swift)
private var flippedOne = false
private var flippedTwo = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let location = touch!.location(in: self)
if card1.contains(location) {
if flippedOne {
card1.texture = back
flippedOne = false
}
else {
card1.texture = cardTextures[cardOne]
flippedOne = true
}
}
if card2.contains(location) {
if flippedTwo {
card2.texture = back
flippedTwo = false
}
else {
card2.texture = cardTextures[cardTwo]
flippedTwo = true
}
}
}
The if statements in the touches began func can be further simplified as such...
if card1.contains(location) {
card1.texture = flippedOne ? back : cardTextures[cardOne]
flippedOne = !flippedOne
}
if card2.contains(location) {
card2.texture = flippedTwo ? back : cardTextures[cardTwo]
flippedTwo = !flippedTwo
}

TouchesMoved Lag with Pathfinding

I'm making a game where the player follows a touch. The game has obstacles and pathfinding, and I'm experiencing extreme lag with touches moved. Upon loading the game, everything works, but after a few seconds of dragging my finger around (especially around obstacles) the game freezes to the point where moving a touch will freeze all physics (0 fps) until the touch is ended.
I'm assuming the culprit is my function makeGraph(), which finds a path for the player from player.position:CGPoint() to player.destination:CGPoint(), storing a path in player.goto:[CGPoint()], the update function then takes care of moving to the next goto point.
This function is called every cycle of touchesmoved, so the player can switch directions if the finger moves over an obstacle
My question is: 1) what in this code is causing the unbearable lag, 2) how can I make this code more efficient?
My code:
initializing vars:
var obstacles = [GKPolygonObstacle(points: [float2()])]
var navgraph = GKObstacleGraph()
setting up a graph (called at start of level):
func setupGraph(){
var wallnodes :[SKSpriteNode] = [SKSpriteNode]()
self.enumerateChildNodes(withName: "wall") {
node, stop in
wallnodes.append(node as! SKSpriteNode)
}
self.enumerateChildNodes(withName: "crate") {
node, stop in
wallnodes.append(node as! SKSpriteNode)
}
obstacles = SKNode.obstacles(fromNodeBounds: wallnodes)
navgraph = GKObstacleGraph(obstacles: obstacles, bufferRadius: Float(gridSize/2))
}
touchesmoved: I keep track of multiple touches using strings. only one finger may act as the "MoveFinger." this string is created at touchesbegan and emptied at touchesended
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
touchloop: for touch in touches {
if MoveFinger == String(format: "%p", touch) {
let location = touch.location(in: self)
player.destination = location
makeGraph()
player.moving = true
player.UpdateMoveMode()
continue touchloop
}
// .... more if statements ....
}
}
and graph function (called every touchesmoved cycle):
func makeGraph(){
let startNode = GKGraphNode2D(point: float2(Float(player.position.x), Float(player.position.y)))
let endNode = GKGraphNode2D(point: float2(Float(player.destination.x), Float(player.destination.y)))
let graphcopy = navgraph
graphcopy.connectUsingObstacles(node: startNode)
graphcopy.connectUsingObstacles(node: endNode)
let path = graphcopy.findPath(from: startNode, to: endNode)
player.goto = []
for node:GKGraphNode in path {
if let point2d = node as? GKGraphNode2D {
let point = CGPoint(x: CGFloat(point2d.position.x), y: CGFloat(point2d.position.y))
player.goto.append(point)
}
}
if player.goto.count == 0 {
//if path is empty, go straight to destination
player.goto = [player.destination]
} else {
//if path is not empty, remove first point (start point)
player.goto.remove(at: 0)
}
}
any ideas?

How to make SKSpriteNode with fixed position (after adding physics)?

I have several programmatically made SKSpriteNode's. Some of them I want to move around, some I want to be static (have a fixed position). When adding physics to the nodes (need that to be able to do collision detection, right?) and set physicsBodyXXXX.dynamic = false they stay in the same position when moving other object over them. That's fine!
But, I'm still able to grab the node I want to be statically positioned, and move them around. How can I mask out the node I don't want to move in touches function? Or is there another solution?
Tried to find a property like static which made the node's position fixed, but can't find it...
Here's my code for auto generating nodes (in override func didMoveToView(view: SKView):
for Character in englishWord{
// Make letters:
let letterToMove = SKSpriteNode(imageNamed: "\(Character)")
//then setting size and position
var physicsBodyLetterToMove = SKPhysicsBody(rectangleOfSize: letterToMove.size)
physicsBodyLetterToMove.affectedByGravity = false
physicsBodyLetterToMove.allowsRotation = false
physicsBodyLetterToMove.dynamic = false
letterToMove.physicsBody = physicsBodyLetterToMove
self.addChild(letterToMove)
// Make empty boxes for the letters:
let letterRecBox = SKSpriteNode(imageNamed: "EmptyBox")
//then setting size and position
var physicsBodyLetterRecBox = SKPhysicsBody(rectangleOfSize: letterRecBox.size)
physicsBodyLetterToMove.affectedByGravity = false
physicsBodyLetterRecBox.dynamic = false
letterRecBox.physicsBody = physicsBodyLetterRecBox
self.addChild(letterRecBox)
}
So the touches func's:
var selected: [UITouch: SKNode] = [:]
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
selected = [:]
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
selected[touch as UITouch] = nodeAtPoint(location)
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
for (touch, node) in selected{
if !contains([self], node){
let action = SKAction.moveTo(location, duration: 0.1)
node.runAction(SKAction.repeatAction(action, count: 1))
}
}
}
}
Any idea?
Setting dynamic to false will make the node unaffected by physics. SKActions and touch events are not considered physics so they will still affect your nodes that are not dynamic.
You could do something like:
YourSpriteNode.name = #"staticNode"; //Right after you create it
Then alter your touch method:
for (touch, node) in selected{
if !contains([self], node){
if(![node.name isEqualToString:#"staticNode"])
{
let action = SKAction.moveTo(location, duration: 0.1)
node.runAction(SKAction.repeatAction(action, count: 1))
}
}
}
The newly introduced if statement will prevent any node named "staticNode" from getting moved due to your SKActions. Other nodes will move as expected.
Thanx, that worked.
Only, I had to write it like this:
letterRecBox.name = "staticNode"
...
if !(node.name == "staticNode"){
...then do something...
}