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.
