0 1 00:00:00,700 --> 00:00:05,940 All right, guys. I hope you're enjoying the ARKit tutorial so far because we are going to embark on creating 1 2 00:00:06,000 --> 00:00:08,690 yet another really awesome ARKit app. 2 3 00:00:08,730 --> 00:00:15,940 So the first thing that we're gonna do is create a brand-new Xcode project. And, of course, we are going 3 4 00:00:15,940 --> 00:00:19,510 to choose the augmented reality app template. Hit Next. 4 5 00:00:19,510 --> 00:00:24,040 And I'm just gonna call mine AR Ruler because that's what we're making. 5 6 00:00:24,040 --> 00:00:25,990 Feel free to call yours whatever you like. 6 7 00:00:26,080 --> 00:00:26,360 All right. 7 8 00:00:26,380 --> 00:00:29,470 So here's our brand-new ARKit project. 8 9 00:00:29,590 --> 00:00:34,710 And the first thing I'm going to do is delete the unnecessary template code that Apple gave us. 9 10 00:00:34,720 --> 00:00:39,770 So I'm gonna get rid of all three lines here and just leave sceneView.delegates and self. 10 11 00:00:39,880 --> 00:00:45,100 And then if you scroll down to the bottom, I'm going to delete all of these delegate methods and also 11 12 00:00:45,100 --> 00:00:47,470 that didReceiveMemoryWarning method. 12 13 00:00:47,500 --> 00:00:47,730 All right. 13 14 00:00:47,740 --> 00:00:51,670 So that makes our view a little bit cleaner and easier to work with. 14 15 00:00:51,670 --> 00:00:51,970 All right. 15 16 00:00:52,000 --> 00:00:57,820 So the next thing I'm gonna do is I'm going to add some debug options. So I'm gonna say 16 17 00:00:57,850 --> 00:01:07,360 sceneView.debugOtions = ARSCNDebugOptions, and I'm gonna go for the one that just showsFeaturePoints 17 18 00:01:07,360 --> 00:01:13,420 because we're going to be using the automatically detected feature points to figure out which 18 19 00:01:13,420 --> 00:01:18,800 parts of the scene are a continuous surface and be able to measure something on that surface. 19 20 00:01:18,880 --> 00:01:19,120 All right. 20 21 00:01:19,120 --> 00:01:24,600 So once we've done that, the next thing we need to do is to detect user touch on the screen. 21 22 00:01:24,730 --> 00:01:30,610 So we're going to use the method touchesBegan and we're simply going to print the console that a touch 22 23 00:01:30,670 --> 00:01:31,750 was detected. 23 24 00:01:31,750 --> 00:01:34,510 So you can pause the video now and try and give it a go. 24 25 00:01:34,600 --> 00:01:35,650 So there's nothing new here. 25 26 00:01:35,650 --> 00:01:39,900 You've done this in the last tutorial, so why don't you give it a go and see if you can do it yourself? 26 27 00:01:42,990 --> 00:01:43,220 All right. 27 28 00:01:43,230 --> 00:01:44,820 So I hope that wasn't too difficult. 28 29 00:01:44,970 --> 00:01:51,840 We're going to head below viewWillDisappear and we're simply going to start typing touchesBegan to 29 30 00:01:51,840 --> 00:01:53,550 search for the one that we want. 30 31 00:01:53,550 --> 00:01:59,070 So this is the method that tells the object and the object is, obviously, our ViewController here that 31 32 00:01:59,070 --> 00:02:02,360 one or more new touches occurred in a view or window, 32 33 00:02:02,370 --> 00:02:08,850 so exactly what we want. Now, inside here, where it says code, we're going to print to the console and say, 33 34 00:02:09,210 --> 00:02:13,970 "touch detected." All right, let's give it a spin. 34 35 00:02:14,220 --> 00:02:18,200 So the crazier thing is, actually, you can make this endless hallway illusion. 35 36 00:02:18,350 --> 00:02:19,340 All right, enough playing around. 36 37 00:02:19,340 --> 00:02:20,210 Let's have a look. 37 38 00:02:20,210 --> 00:02:22,370 So we've got feature points detected on screen. 38 39 00:02:22,370 --> 00:02:27,570 And if I tap anywhere on the screen, you'll see "touches detected" showing up. Perfect. 39 40 00:02:27,590 --> 00:02:33,620 All right. So, now that we know that our code for detecting touch is working, we're going to grab the location 40 41 00:02:33,680 --> 00:02:35,740 of the touch on the 2D screen. 41 42 00:02:35,840 --> 00:02:39,650 So instead of printing to the console, I'm just going to delete that, 42 43 00:02:39,740 --> 00:02:43,530 I'm going to use optional binding to grab hold of the location of the touch. 43 44 00:02:43,550 --> 00:02:49,120 So I'm going to say if let touchLocation = touches, 44 45 00:02:49,130 --> 00:02:54,290 so this is that set of UITouches that we get back whenever a user touches the screen. 45 46 00:02:54,740 --> 00:03:01,250 So in this set of touches, I'm going to grab the first one if it exists, and then I'm going to check for 46 47 00:03:01,250 --> 00:03:05,260 its location inside the sceneView. 47 48 00:03:05,260 --> 00:03:11,480 Now, if that succeeds, then we have this object touchLocation and I'm going to perform a hitTest using 48 49 00:03:11,480 --> 00:03:15,890 that touchLocation and see if it corresponds to a feature point. 49 50 00:03:15,890 --> 00:03:24,960 So I'm going to write let hitTestResults = sceneView.hitTest. 50 51 00:03:25,040 --> 00:03:33,020 And the point is going to be the touchLocation which is a CGPoint, as you can see. And the type is 51 52 00:03:33,020 --> 00:03:35,390 going to be a .featurePoint. 52 53 00:03:35,570 --> 00:03:42,090 So as Xcode explains, the .featurePoint ResultType is a point automatically identified by 53 54 00:03:42,090 --> 00:03:46,550 ARKitv as a part of a continuous surface, but without a corresponding anchor. 54 55 00:03:46,580 --> 00:03:53,030 So we're not interested here in detecting planes or going into the planeAnchors or 3D anchors, we're 55 56 00:03:53,030 --> 00:03:59,250 actually just interested in using the code inside ARKit to detect continuous surfaces. 56 57 00:03:59,300 --> 00:04:02,700 And this will give us the 3D location of a continuous surface 57 58 00:04:02,720 --> 00:04:05,350 I'm tapping on if it exists. 58 59 00:04:05,420 --> 00:04:06,860 So go ahead and hit enter. 59 60 00:04:07,130 --> 00:04:11,090 So, now we have this hitTestResult object that we can work with. 60 61 00:04:11,090 --> 00:04:18,350 Now, what I want to do is to use this hitTestResult which is the location in 3D space inside the real 61 62 00:04:18,350 --> 00:04:18,820 world 62 63 00:04:18,830 --> 00:04:20,260 that's on our scene. 63 64 00:04:20,540 --> 00:04:25,280 And I want to add a simple red dot to that location. 64 65 00:04:25,430 --> 00:04:31,550 So I'm going to say if let hitResult = hitTestResult. 65 66 00:04:31,670 --> 00:04:36,020 So you can see that hitTestResults are actually a array of hitTestResults, 66 67 00:04:36,020 --> 00:04:41,930 so we can actually change that to results which might make it more understandable semantically. 67 68 00:04:41,930 --> 00:04:46,090 So if let hitResult = hitTestResults.first, 68 69 00:04:46,090 --> 00:04:53,450 so grabbing that first object in that array. And if that exists, i.e., this is not nil, then we're going 69 70 00:04:53,450 --> 00:04:57,790 to call a function called addDot to add a dot to the scene 70 71 00:04:57,790 --> 00:05:05,390 basically. We're going to specify a location which is going to be the location of the hitTestResult. 71 72 00:05:05,390 --> 00:05:10,130 So, of course, Xcode is going to complain 'cause I don't have this method could addDot yet, it has no 72 73 00:05:10,130 --> 00:05:11,300 idea what I'm talking about. 73 74 00:05:11,420 --> 00:05:13,430 So I'm going to go ahead and create it down here. 74 75 00:05:13,730 --> 00:05:21,860 So I'm going to create a function called addDot and this addDot is going to have a internal and an 75 76 00:05:21,860 --> 00:05:22,880 external parameter. 76 77 00:05:23,240 --> 00:05:26,910 So remember we talked about internal/external parameters in Swift before. 77 78 00:05:26,990 --> 00:05:32,330 So the internal one is the one that you referred to inside the function, but the external one is the 78 79 00:05:32,330 --> 00:05:34,960 one that you use when you're calling the function. 79 80 00:05:35,270 --> 00:05:41,270 So the external one for addDot is "at" and the internal one is the hitResult, and then we have to specify 80 81 00:05:41,360 --> 00:05:47,270 a data type which is going to be ARHitTestResult data type. 81 82 00:05:47,270 --> 00:05:53,090 So it's a little bit mad at me because I'm missing a bracket, so I'm going to hit command A to select 82 83 00:05:53,150 --> 00:06:00,200 all, and I'm going to hit option control I to indent everything and make it look beautiful once more. 83 84 00:06:00,200 --> 00:06:08,030 Okay. So, now I'm going to open a set of curly braces and inside addDot, we are going to create a new 84 85 00:06:08,090 --> 00:06:08,810 .geometry. 85 86 00:06:08,990 --> 00:06:12,260 So this dot is going to be a SCNSphere. 86 87 00:06:12,350 --> 00:06:14,930 So just the same in the tutorial where we created the moon, 87 88 00:06:14,930 --> 00:06:17,170 we're going to create a 3D sphere. 88 89 00:06:17,480 --> 00:06:21,980 So I'm going to give this to you as a challenge and see if you remember how we created these geometries 89 90 00:06:22,070 --> 00:06:24,110 in previous tutorials. 90 91 00:06:24,110 --> 00:06:30,560 So you're going to be creating something called a dotGeometry and it is going to be a SCNSphere, 91 92 00:06:30,590 --> 00:06:38,540 so a 3D sphere. I'm not sure you can have non-3D spheres. And the material of that sphere is simply going 92 93 00:06:38,540 --> 00:06:45,740 to be a red UIColor. And then you're going to create a dotNode that has the geometry of this newly 93 94 00:06:45,740 --> 00:06:47,510 created dotGeometry. 94 95 00:06:47,510 --> 00:06:55,520 So pause the video give that a go and see if you can remember how we created 3D spheres in ARKit. 95 96 00:06:55,590 --> 00:06:55,850 All right. 96 97 00:06:55,880 --> 00:06:57,000 So let's go ahead and do that. 97 98 00:06:57,000 --> 00:07:04,440 So we said we're going to call it dotGeometry and it is going to be a SCNSphere, and we're going 98 99 00:07:04,440 --> 00:07:07,410 to create it with a radius of-- 99 100 00:07:07,440 --> 00:07:10,130 I mean, it's really up to you how big you want it, 100 101 00:07:10,350 --> 00:07:12,480 but I'm actually going to make it quite small. 101 102 00:07:12,510 --> 00:07:16,800 So half a centimeter thereabouts should do just fine. 102 103 00:07:16,800 --> 00:07:18,830 So we're creating a new SCNSphere 103 104 00:07:18,860 --> 00:07:21,930 that's about half a centimeter in radius, 104 105 00:07:21,930 --> 00:07:27,450 so about one centimeter in diameter, and then we're going to create a material for it. 105 106 00:07:27,810 --> 00:07:38,070 So let material = SCNMaterial and let's define the material's diffuse.contents that is going 106 107 00:07:38,070 --> 00:07:45,630 to consist of a UIColor that is a red color, as we said, and then I'm going to assign this material 107 108 00:07:45,660 --> 00:07:52,980 to my dotGeometry, so dotGeometry.materials = an array of materials that only contains 108 109 00:07:53,340 --> 00:07:55,480 our single red color material. 109 110 00:07:55,990 --> 00:07:56,290 Okay. 110 111 00:07:56,350 --> 00:08:04,410 So, and then we said we're going to create a node called dotNode that was going to be a SCNNode, and 111 112 00:08:04,440 --> 00:08:10,180 it was going to be created using our dotGeometry. 112 113 00:08:10,330 --> 00:08:10,680 All right. 113 114 00:08:10,680 --> 00:08:11,340 And that's it. 114 115 00:08:11,340 --> 00:08:12,480 So how is that? 115 116 00:08:12,660 --> 00:08:15,620 So if that was confusing at all, have a look back at the lesson 116 117 00:08:15,630 --> 00:08:21,590 where we created the moon and we created spheres and 3D objects in ARKit to refresh your memory. 117 118 00:08:21,990 --> 00:08:22,350 Okay. 118 119 00:08:22,380 --> 00:08:27,440 So let's go on to the next part and it's, you guessed it, another challenge. 119 120 00:08:27,510 --> 00:08:35,160 So what I want you to do is to assign the position of this dotNode using the hitResult that we passed 120 121 00:08:35,160 --> 00:08:35,560 in here. 121 122 00:08:35,940 --> 00:08:43,680 So over here, we first detected and it touches on the 2D iPad or iPhone screen, and then we converted 122 123 00:08:43,680 --> 00:08:51,510 those 2D coordinates into 3D coordinates by performing a hitTest on the scene using that location. 123 124 00:08:51,540 --> 00:08:58,320 Now, the hitTest is going to look for any featurePoints that match up with a 3D continuous surface, 124 125 00:08:58,380 --> 00:09:04,830 so, say, a table or your laptop. And then we grabbed the first object in that array of hitTest results, 125 126 00:09:05,250 --> 00:09:12,540 and then we passed that location in 3D called hitResult into this addDot method that we're creating 126 127 00:09:12,840 --> 00:09:13,410 right now. 127 128 00:09:13,410 --> 00:09:19,230 So, now that location is contained inside this internal parameter called hitResult. 128 129 00:09:19,320 --> 00:09:26,730 So can you use this hitResult to specify the position of our dotNode in 3D. 129 130 00:09:26,760 --> 00:09:29,750 So it's a little bit similar to when we created the dice. 130 131 00:09:29,850 --> 00:09:36,390 We want to create this dot where we tap on the screen. So to do this, you're going to have to specify 131 132 00:09:36,600 --> 00:09:41,890 the dotNodes position property. And it is going to be a SCNVector3, 132 133 00:09:41,910 --> 00:09:47,820 so it has an x, y, and z. And you're going to use the hitResult to specify the position. 133 134 00:09:47,820 --> 00:09:50,820 So if you're up to the challenge, pause the video and give that a go. 134 135 00:09:54,020 --> 00:09:54,420 All right. 135 136 00:09:54,450 --> 00:09:55,830 So I hope that was okay. 136 137 00:09:55,830 --> 00:09:59,940 So as we said, we're going to specify the dotNodes position property. 137 138 00:10:00,060 --> 00:10:07,110 And as you can see here, it's expecting a SCNVector3, so a vector that has three components. 138 139 00:10:07,140 --> 00:10:11,410 So I'm going to use the SCNVector3 overload to create a new one. 139 140 00:10:11,520 --> 00:10:17,340 And if you scroll down here, there's one that says x, y, and z using CGFloats, 140 141 00:10:17,340 --> 00:10:18,370 and that's what we're gonna choose. 141 142 00:10:18,870 --> 00:10:26,910 Now, inside these placeholders, I'm going to specify the x, so left and right, the y, up and down, and z 142 143 00:10:27,150 --> 00:10:28,770 towards or away from us. 143 144 00:10:29,160 --> 00:10:32,380 So I'm going to use the hitResult that we passed in here. 144 145 00:10:32,640 --> 00:10:43,410 And for the x, I'm just gonna say hitResult.worldTransform.columns.3.x. And then 145 146 00:10:43,410 --> 00:10:52,900 for the y position, I'm gonna say hitResults.worldTransform.columns.3.y. 146 147 00:10:53,130 --> 00:10:56,790 And finally, I'm going to specify the z. 147 148 00:10:56,820 --> 00:11:02,140 So hitResult.worldTransform.columns.3.z. 148 149 00:11:02,280 --> 00:11:04,920 All right. So all three dimensions are specified, 149 150 00:11:04,960 --> 00:11:09,270 so now it will know where to position our dot on the scene. 150 151 00:11:09,270 --> 00:11:15,690 And one final thing we need to do to add our dotNode onto the scene is, of course, say, 151 152 00:11:15,690 --> 00:11:23,300 sceneView.scene.rootNode.addChildNode. And the child is, of course, our brand-new dotNode. 152 153 00:11:23,940 --> 00:11:24,350 Okay. 153 154 00:11:24,380 --> 00:11:27,510 So let's go ahead and run it and see how it goes. 154 155 00:11:49,890 --> 00:11:50,160 All right. 155 156 00:11:50,190 --> 00:11:53,170 So that's the first part of creating our ARRuler. 156 157 00:11:53,170 --> 00:12:01,240 We're able to specify a location in the real world in 3D that we want to measure from and to, and we're 157 158 00:12:01,240 --> 00:12:06,720 able to put down this red dot to show on the scene where we want to start and where we want to end. 158 159 00:12:06,970 --> 00:12:12,010 So stay tuned because in the next lesson, we're going to be taking these positions and working out the 159 160 00:12:12,010 --> 00:12:18,730 distance between the start and endpoints in order to actually have the functionality of an ARRuler. 160 161 00:12:18,730 --> 00:12:19,480 So I'll see you there.