How to detect which one object i touched in sceneView at ARKitExample? - ios

in Apple's ARKitExample, if i add multiple virtual objects(two chair) in one scene view. How to detect which one chair i touched in sceneView at ARKitExample?
the sceneView.hitTest() function will return the array of SCNHitTestResult, but the result.node is kind class of SCNNode, i don't know the object i touched is which one chair?
Dose anyone could help this? Thanks a lots

You are respondsible for tracking which nodes belong to which objects. I usually use a Set since SCNNode is hashable. You can then easily test if the node belongs to one of the objects you are interested in:
guard let result = sceneView.hitTest(location, options: nil).first else {
return
}
if myObjectNodes.contains(result.node) { //myObjectNodes is declared as Set<SCNNode>
//This is a match
}

To elaborate on the answer from #JoshHomann, you can do something like this
Swift 4
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first as! UITouch
if(touch.view == self.sceneView){
print("touch working")
let viewTouchLocation:CGPoint = touch.location(in: sceneView)
guard let result = sceneView.hitTest(viewTouchLocation, options: nil).first else {
return
}
if myObjectNodes.contains(result.node) { //myObjectNodes is declared as Set<SCNNode>
print("match")
}
}
}

Related

Swift: Using switch statement in touchesBegan

I want to clean up my touchesBegan(..) in SKScene. I wanted to make a case statement instead of my if .. else chain. However, I get errors when implementing, which suggests that I don't know how equality is done under the hood.
Before code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) === playLabel {
buildLevelSelect()
} else if self.nodeAtPoint(location) === aboutLabel {
createAboutView()
} else if self.nodeAtPoint(location) === storeLabel {
createStore()
}
}
}
After code: After clicking around, some label clicks work, but some others raise a Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode 0x0) error:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
switch(self.nodeAtPoint(location)){
case playLabel :
buildLevelSelect()
case aboutLabel :
createAboutView()
case storeLabel :
createStore()
default : break
}
}
You can write a sort of odd looking but functional switch if you want the === behavior as follows:
switch(true){
case self.nodeAtPoint(location) === playLabel : buildLevelSelect()
case self.nodeAtPoint(location) === aboutLabel : createAboutView()
case self.nodeAtPoint(location) === storeLabel : createStore()
default : break
}
In my opinion, this is still cleaner looking than the if-else chain.
The easiest way to accomplish this would be by populating the .name property of your nodes, so then you could switch on the name instead. So then your switch would look like this:
switch (self.nodeAtPoint(location).name ?? "") {
case "playLabel" :
buildLevelSelect()
case "aboutLabel" :
...
You can also cast the objects in the switch statement and then your code works as expected
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: self)
switch self.atPoint(touchLocation) {
case redBox as SKSpriteNode:
print("touched redBox")
case greenBox as SKSpriteNode:
print("touched greenBox")
default:
print("touched nothing")
}
}

memory grows when switching to a new scene

I have the game in which the new scene is called by pressing buttons. When you click in a new scene sent the corresponding pictures. When you return back to the game map memory always increases by 30 MB. I do not understand where the strongest link. Instruments can not detect leaks. Sorry my english. Help me please.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
for i in 0...3 {
if childNode(withName: "button\(i)")!.isHidden == false && childNode(withName: "button\(i)")!.contains(location) {
buttonOfBattlefield = childNode(withName: "button\(i)")
}
}
switch buttonOfBattlefield?.name {
case "button0"?:
battlefieldName = "A"
case "button1"?:
battlefieldName = "B"
case "button2"?:
battlefieldName = "C"
case "button3"?:
battlefieldName = "D"
default:
break
}
if battlefieldName != nil {
let myScene = GameScene(size: self.size , battlefield: battlefieldName!)
myScene.scaleMode = self.scaleMode
let reveal = SKTransition.fade(withDuration: 2.0)
self.view?.presentScene(myScene, transition: reveal)
}
}
}
Essentially there could be many factors that cause the increase of memory in your game.
I try to help you with some useful correction.
Speaking about custom protocols, you could break the strong references by adding class at the end of the line, and declare weak var to the delegate:
protocol ResumeBtnSelectorDelegate: class {
func didPressResumeBtn(resumeBtn:SKSpriteNode)
}
weak var resumeBtnDelegate:ResumeBtnSelectorDelegate?
...
Speaking about completion could be a strong reference to self so you can do like this example:
self.launchVideo(completion: {
[weak self] success in
guard let strongSelf = self else { return }
//self.showMyVideo()
strongSelf.showMyVideo()
}
same thing to run actions blocks:
let exp = SKAction.run {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.getExplosion(percentageDimension: 15,type: 0,position: enemy.position)
}
If you use third part library in objective C , you may need to remove the strong references also there:
__weak __typeof__(self) weakSelf = self;
SKAction *sequence = [SKAction sequence:#[[SKAction followPath:_ascentPath duration:1.5], [SKAction runBlock:^(void){[weakSelf explode];}]]];
[self runAction:sequence];
}
If you have some observer try to remove it, samething for NSTimers to the willMoveFromView methods.
override func willMove(from view: SKView) {
//remove UIKit objects, observers, timers..
}
The solution found. The new Game Scene need to use the method.
self.enumerateChildNodes(withName: "//*"){ (node, stop) -> Void in
node.removeAllActions()
node.removeAllChildren()
node.removeFromParent()
}
Thank you all for your help. I'm happy!

Initializer for conditional binding must have Optional type, not 'UITouch'

I am currently getting an error in this code block for a SpriteKit game. In the if let statement I am getting the following error.
Initializer for conditional binding must have Optional type, not
'UITouch'
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
StopSideMovement = false
if let touch = touches.first! as UITouch {
if touch.locationInView(self.view).x > screenWidth/2 {
MoveRight = true
} else if touch.locationInView(self.view).x < screenWidth/2 {
MoveLeft = true
}
}
}
Does anybody know how I can fix this issue? Thank you!
This line
if let touch = touches.first! as UITouch {
should be
if let touch = touches.first {
Why?
touches.first does return a UITouch?. But if you add this guy ! and write
touches.first!
then you are performing a force unwrap and you are getting (at compile time) a UITouch.
So the whole conditional unwrapping is no longer needed.
Of course you should avoid force unwrap an use safer statements (like the conditional unwrapping) unless your are absolutely sure there will be some value inside the Optional.
The type of the Set's contents are specified as being UITouch. So you don't need the downcast. you just need to unwrap it.
if let touch = touches.first {}

Set <UITouch>? does not have a member named Generator

I find it hard to fix this error please who can help me
xcode 7 beta 2
Set UITouch ? does not have a member named Generator
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
for obj in touches {
if let touch = obj as? UITouch {
let view = self.touchToView[touch]
self.handleControl(view, controlEvent: .TouchCancel)
self.touchToView[touch] = nil
}
}
}
}
enter image description here
You need to unwrap the optional - try
for obj in touches! {
// code here
}

False if Statement Still Executes

I am using the Xcode 6 Beta, and I have used this exact code in another class, setup up in the same way. For some reason it doesn't want to work here. When I touch an SKNode, I grab its name, and compare the name to two strings, if it matches either of them, I execute some code. See below.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let node: SKNode = self.nodeAtPoint(touch.locationInNode(self))
if node.name == "body" || "face" {
self.childNodeWithName("face").runAction(SKAction.rotateByAngle(6.283, duration: 0.75))
}
}
}
As I said before, this exact code work flawlessly everywhere else I have used it, but no matter which node I press, the code inside the if statement will be run. Yes, I have printed out the names of nodes I am touching, and they do not match. Any ideas?
You probably meant
if node.name == "body" || node.name == "face" {
It would be more clear with parentheses, you are doing:
if (node.name == "body") || ("face") {
And "face" is true, which probably is a bug on Apple's side as String does not conform to LogicValue so you should not be able to test it like that. Probably the literal gets interpreted as something else conforming to LogicValue e.g. a CString.
Your if is idiomatically expressed as a switch with
switch node.name {
case "body", "face":
self.childNodeWithName ...
default: break
}
I think it should
if node.name == "body" || node.name == "face" { //change this statement
replace this in your function
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let node: SKNode = self.nodeAtPoint(touch.locationInNode(self))
if node.name == "body" || node.name == "face" { //change this statement
self.childNodeWithName("face").runAction(SKAction.rotateByAngle(6.283, duration: 0.75))
}
}
}

Resources