0 1 00:00:00,760 --> 00:00:06,940 So in the last lesson, we learned about planes and anchors and implemented this delegate method to be 1 2 00:00:06,940 --> 00:00:10,380 able to detect horizontal planes in the real world. 2 3 00:00:10,390 --> 00:00:15,790 Now, in this lesson, I want to look at how we can start detecting touches on the screen and interpret it 3 4 00:00:16,000 --> 00:00:18,570 as locations in the real world. 4 5 00:00:18,640 --> 00:00:25,960 So to do that, I'm going to use a delegate method called touchesBegan and this gets called when a touch 5 6 00:00:26,050 --> 00:00:28,930 is detected in the view or in the window. 6 7 00:00:29,110 --> 00:00:35,050 So in here is where we're going to be receiving those touches that are coming from the user and use 7 8 00:00:35,080 --> 00:00:39,320 ARKit to convert it into a real world location. 8 9 00:00:39,340 --> 00:00:45,250 So the first thing we need to do is to check if there indeed were touches detected and this method wasn't 9 10 00:00:45,250 --> 00:00:46,410 just called in error. 10 11 00:00:46,870 --> 00:00:49,270 So to do that, we're going to use a "if let." 11 12 00:00:49,960 --> 00:00:56,830 And we're going to call our object touch and it's going to be set to equal touches.first. 12 13 00:00:56,830 --> 00:01:03,310 Now, if you option-click on touchesBegan, you can see that touches is actually a set of UI touch instances, 13 14 00:01:03,730 --> 00:01:09,820 but they only have more than one object when you're using multi-touch. And to do that, you need to enable 14 15 00:01:09,820 --> 00:01:15,130 multi-touch. But in our case, because that's not enabled and we're not interested in detecting multiple 15 16 00:01:15,130 --> 00:01:15,820 touches, 16 17 00:01:15,850 --> 00:01:22,150 we only want the first object in that set which is the location that the user touched on the screen. 17 18 00:01:22,750 --> 00:01:29,950 So if this touches.first does indeed contain an object, then we're going to sign it to touch and 18 19 00:01:29,950 --> 00:01:33,680 we're going to be using it in order to detect its location. 19 20 00:01:33,730 --> 00:01:44,290 So let's create another constant code touchLocation and set it to the touch that we--the UITouch that 20 21 00:01:44,290 --> 00:01:54,010 we got from earlier on, .location, and the "in" is where that location was detected. And you can see that 21 22 00:01:54,010 --> 00:02:00,230 the location is expecting a node that is a descendant of a scene present in the window that receive 22 23 00:02:00,250 --> 00:02:01,200 the touch event. 23 24 00:02:01,210 --> 00:02:08,710 So this is going to be our sceneView because that is where our touch event was initiated. 24 25 00:02:08,710 --> 00:02:08,930 All right. 25 26 00:02:08,930 --> 00:02:14,810 So once we've got the touch location remember that this is a 2D coordinate because your iPhone or your 26 27 00:02:14,810 --> 00:02:16,630 iPod screen is 2D. 27 28 00:02:16,670 --> 00:02:20,690 You can't actually push through the iPad and touch a place in 3D. 28 29 00:02:20,870 --> 00:02:29,330 So in order to convert that touch location into a 3D location inside our scene, we're going to use a 29 30 00:02:29,330 --> 00:02:33,520 method that is from ARKit called hitTest. 30 31 00:02:33,830 --> 00:02:35,230 And I'm going to show you how it works. 31 32 00:02:35,270 --> 00:02:42,920 So we're going to call the results, results, and it's going to be created by calling one of the 32 33 00:02:42,920 --> 00:02:51,860 sceneView methods called hitTest. And hitTest searches for real world objects or ARAnchors which is true 33 34 00:02:51,860 --> 00:02:58,340 in our case because we're looking for planeAnchors that is in the captured camera image corresponding 34 35 00:02:58,340 --> 00:02:59,930 to a point in the SceneKit view. 35 36 00:03:00,230 --> 00:03:07,640 So, basically, we're converting a point that we're touching in 2D space on the screen into a 3D coordinate 36 37 00:03:07,660 --> 00:03:12,640 that corresponds to one of our ARPlaneAnchors. And the hitTest method that we want 37 38 00:03:12,680 --> 00:03:18,920 is this one where it expects a point that's going to be our touchLocation and it's also expecting a 38 39 00:03:18,920 --> 00:03:22,280 type which is the type of result we're looking for. 39 40 00:03:22,280 --> 00:03:28,760 So the point that we're going to run out hitTest on is going to be touchLocation. And the type is going 40 41 00:03:28,760 --> 00:03:35,960 to be .existingPlanes using extent, and this is a planeAnchor that's already in the scene because 41 42 00:03:35,960 --> 00:03:40,940 we put it on in this delegate method and it's respecting the plane's limited size. 42 43 00:03:41,300 --> 00:03:45,160 And now, I just want to briefly explain how this hitTest works. 43 44 00:03:45,260 --> 00:03:53,210 So let's say that you've got your app running and you tap a point on your screen. Now, that tap is interpreted 44 45 00:03:53,330 --> 00:03:58,370 as a touchLocation and that will trigger the touchesBegan method, 45 46 00:03:58,430 --> 00:04:07,160 and we're going to look for the location of that touch. Now that touchLocation is a point in 2D space. 46 47 00:04:07,340 --> 00:04:12,690 It has a x and a y component on the iPhone or on the iPad screen. 47 48 00:04:12,710 --> 00:04:20,660 Now, what we want, though, is we want to convert that 2D location and we want to let it traverse through 48 49 00:04:20,660 --> 00:04:25,210 the camera image and hit a point in 3D space. 49 50 00:04:25,610 --> 00:04:29,120 So a place on our plane that we've already added to the scene. 50 51 00:04:29,720 --> 00:04:37,040 So, now that point that the hitTest has detected within the scene has a third positional component, 51 52 00:04:37,040 --> 00:04:41,570 the z component. So it is now a position in 3D space. 52 53 00:04:41,570 --> 00:04:44,690 And this is the point that we're going to be placing our dice. 53 54 00:04:44,720 --> 00:04:49,670 So depending on the z component, your dice is going to be rendered larger or smaller. 54 55 00:04:49,880 --> 00:04:54,650 So if the z was closer to you, then your dice would get rendered larger. 55 56 00:04:54,740 --> 00:04:59,110 And if the z was further away, then it would be scaled down smaller. 56 57 00:04:59,150 --> 00:05:06,560 So it's being placed in our 3D plane based on our real horizontal plane that was detected. 57 58 00:05:06,560 --> 00:05:12,080 Now, if you're interested in finding out more about how ARHitTest works, then have a look in the 58 59 00:05:12,110 --> 00:05:18,440 class documentation on Apple.com and you can read more about what are the different types of hitTests 59 60 00:05:18,440 --> 00:05:24,530 and what are the parameters for this particular method that we're using, and how it works behind 60 61 00:05:24,530 --> 00:05:25,400 the scenes. 61 62 00:05:25,400 --> 00:05:33,440 Now, once we've gotten our result back, I want to be able to print to the console and, say, if the result 62 63 00:05:33,770 --> 00:05:41,870 is empty, i.e., we didn't get one, then that means that our hit or our touch probably didn't hit one of our 63 64 00:05:41,870 --> 00:05:43,230 existing planes. 64 65 00:05:43,400 --> 00:05:46,770 So I'm going to say you touched somewhere else. 65 66 00:05:47,120 --> 00:05:53,720 But if it was not empty, i.e., we did get a result back, then I'm going to print that you touched a point 66 67 00:05:53,780 --> 00:05:54,800 on the plane. 67 68 00:05:54,950 --> 00:06:04,450 So let's say if results.isEmpty, and we're using "isEmpty" because results is an array type. 68 69 00:06:04,970 --> 00:06:08,630 So I'm going to put the exclamation mark in front of 69 70 00:06:08,630 --> 00:06:10,330 !results.isEmpty to reverse it, 70 71 00:06:10,370 --> 00:06:14,800 so we're now checking to see if results is not empty. 71 72 00:06:14,900 --> 00:06:20,870 And in that case, that means we did get a result back from our hitTest and it did find a position on 72 73 00:06:20,870 --> 00:06:22,410 an existing plane. 73 74 00:06:22,460 --> 00:06:31,310 So we're going to print "touched the plane," but otherwise, our touchesBegan was triggered. 74 75 00:06:31,340 --> 00:06:36,260 So the user did touch somewhere on the screen, just not on an existing plane, 75 76 00:06:36,410 --> 00:06:42,390 then we're going to print "touched somewhere else." 76 77 00:06:42,390 --> 00:06:43,440 All right, let's try that. 77 78 00:06:48,140 --> 00:06:55,610 So, as you can see, when I touched the plane that is level with the desk, it says, "touch the plane." 78 79 00:06:55,610 --> 00:07:01,730 And if I touch somewhere else, like, say, this slightly more elevated box that's wrapped in a pink shirt, 79 80 00:07:01,890 --> 00:07:07,070 "touched somewhere else," and it works even if I move the camera somewhere else, and the original plane 80 81 00:07:07,100 --> 00:07:08,030 is out of the view.