Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Augmented Reality
Code

Viết một Ứng dụng đo lường với ARKit: Đặt đối tượng và cảnh

by
Difficulty:IntermediateLength:LongLanguages:

Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)

Cùng với nhiều thứ khác đã nhanh chóng bị thay thế bởi những công nghệ hiện đại, có vẻ như các phương pháp đo lường bằng dây cũng sẽ ra đi. Trong loạt bài hướng dẫn hai phần này, chúng ta đang học cách sử dụng thực tế tăng cường và máy ảnh trên thiết bị iOS của bạn để tạo ra ứng dụng sẽ đo khoảng cách giữa hai điểm.

Trong bài viết đầu tiên, chúng ta đã tạo dự án cho ứng dụng và viết các phần giao diện chính của nó. Trong bài viết này, chúng ta sẽ hoàn thành nó bằng cách đo giữa hai điểm trong cảnh AR. Nếu chưa, hãy theo dõi bài viết đầu tiên để thiết lập dự án ARKit của bạn.

Xử lý chạm

Dưới đây là một trong những phần lớn nhất của hướng dẫn này: xử lý khi người dùng chạm vào thế giới của họ để có được một hình cầu xuất hiện chính xác nơi họ đã chạm. Sau đó, chúng ta sẽ tính toán khoảng cách giữa những quả cầu này để cuối cùng sẽ hiển thị cho người dùng biết khoảng cách của họ.

Tap Gesture Recognizer

Bước đầu tiên để kiểm tra các hành động chạm là tạo ra một tap gesture recognizer khi khởi chạy ứng dụng. Để làm điều này, hãy tạo một tap handler như sau:

Dòng đầu tiên tạo ra một đối tượng lớp UITapGestureRecognizer() và truyền vào hai tham số khi khởi tạo: target và action. Target là bên nhận các thông báo mà recognizer này gửi, và chúng ta muốn lớp ViewController của chúng ta là target. Action chỉ đơn giản là một phương thức sẽ được gọi mỗi khi có một hành động chạm.

Để thiết lập số lần chạm, hãy thêm:

Tiếp theo, đối tượng lớp mà chúng ta tạo ra trước đó cần biết có bao nhiêu lần chạm cần thiết để kích hoạt recognizer. Trong trường hợp của chúng ta, chỉ cần một lần chạm, nhưng trong các ứng dụng khác, bạn có thể cần phải có nhiều hơn (như nhấn đúp) trong một số trường hợp.

Thêm handler vào scene view như sau:

Cuối cùng, dòng code này thêm gesture recognizer vào sceneView, đó là nơi chúng ta sẽ làm mọi thứ. Đây là nơi xử lý preview của camera cũng như những gì mà người dùng sẽ trực tiếp nhấn để có được một hình cầu xuất hiện trên scene, vì vậy bạn nên thêm recognizer vào view với cái mà người dùng sẽ tương tác.

Phương thức xử lý hành động chạm

Khi tạo ra UITapGestureRecognizer(), có thể bạn nhớ ra chúng ta thiết lập một phương thức handleTap cho action. Bây giờ, chúng tôi đã sẵn sàng để định nghĩa phương thức đó. Để thực hiện việc này, chỉ cần thêm những thứ sau vào ứng dụng của bạn:

Mặc dù định nghĩa hàm khá rõ ràng, nhưng bạn có thể tự hỏi tại sao có một thẻ @objc ở phía trước của nó. Theo như phiên bản hiện tại của Swift, để expose các phương thức sang Objective-C, bạn cần thẻ này. Tất cả những gì bạn cần biết đó là #selector cần phương thức tham chiếu cho Objective-C. Cuối cùng, tham số của phương thức sẽ cho phép chúng ta có được vị trí chính xác đã được chạm trên màn hình.

Xác định Vị trí

Bước tiếp theo để cho các quả cầu xuất hiện ở nơi người dùng đã chạm là phát hiện vị trí chính xác mà họ đã chạm. Giờ thì, điều này không đơn giản như nhận biết vị trí và đặt một hình cầu, nhưng tôi chắc chắn rằng bạn sẽ nắm vững nó sớm thôi.

Bắt đầu bằng cách thêm ba dòng code sau vào phương thức handleTap() của bạn:

Nếu bạn còn nhớ tham số mà chúng ta đã lấy trong phương thức handleTap(), bạn có thể nhớ lại rằng nó được đặt tên là sender và nó có type là UITapGestureRecognizer. Vâng, dòng code đầu tiên đơn giản là lấy vị trí chạm trên màn hình (tương đối đối với scene view), và gán nó vào một hằng số tên là location.

Tiếp theo, chúng ta đang làm vài thứ gọi là một bài kiểm tra chạm trên chính SceneView. Điều này đơn giản là kiểm tra cảnh vật thật, chẳng hạn như bảng biểu, bề mặt, tường, sàn, vv. Điều này cho phép chúng ta có được một độ sâu trường ảnh và để có được các phép đo khá chính xác giữa hai điểm. Ngoài ra, chúng ta chỉ định các kiểu đối tượng cần phát hiện, và như bạn thấy, chúng ta đang nói cho nó tìm kiếm featurePoints, về cơ bản là các bề mặt phẳng, điều này là hợp lý đối với một ứng dụng đo lường.

Cuối cùng, dòng code lấy kết quả chính xác nhất, trong trường hợp hitTest là kết quả cuối cùng và kiểm tra xem nó có nil không. Nếu có, nó bỏ qua các dòng code còn lại trong phương thức này, nhưng nếu thật sự có kết quả, nó sẽ được gán cho một hằng gọi là result.

Ma trận

Nếu bạn nhớ lại lớp học đại số, bạn có thể nhớ ra các ma trận, điều mà dường như bây giờ là rất quan trọng. Chúng thường được sử dụng trong các tác vụ đồ hoạ máy tính, và chúng ta sẽ có cái nhìn thoáng qua về chúng trong ứng dụng này.

Thêm các dòng sau vào phương thức handleTap() của bạn, và chúng ta sẽ đi sâu vào chi tiết:

Trước khi đi vào dòng code đầu tiên, điều quan trọng là phải hiểu rằng bài kiểm tra lần chạm mà chúng ta đã làm trước đó trả về một kiểu matrix_float4x4, mà về cơ bản là một ma trận bốn chiều giá trị float. Vì chúng ta đang ở trong SceneKit, chúng ta cần phải chuyển đổi nó thành một thứ mà SceneKit có thể hiểu được - trong trường hợp này, chuyển thành một SCNMatrix4.

Sau đó, chúng ta sẽ sử dụng ma trận này để tạo ra một SCNVector3, mà theo như tên của nó, là một vector với ba thành phần. Như bạn có thể đoán được, những thành phần này là x, y, và z, để cho chúng ta một vị trí trong không gian. transform.m41, transform.m42, và transform.m43 là giá trị tọa độ có liên quan cho ba thành phần vector.

Cuối cùng, hãy sử dụng phương thức newSphere() mà chúng ta đã tạo ra trước đó, cùng với thông tin vị trí mà chúng ta đã phân tích từ sự kiện chạm, để tạo ra một hình cầu và gán nó cho một hằng gọi là sphere.

Giải quyết lỗi Double-Tap

Bây giờ, bạn có thể đã nhận ra một lỗ hổng nhỏ trong code của chúng ta; nếu người dùng tiếp tục chạm, một quả cầu mới sẽ tiếp tục được tạo ra. Chúng ta không muốn điều này bởi vì nó gây khó khắn trong việc xác định quả cầu nào cần phải được đo lường. Ngoài ra, nó gây khó khăn cho người dùng để theo dõi tất cả các quả cầu!

Khắc phục bằng mảng

Bước đầu tiên để khắc phục điều này là tạo một mảng ở trên cùng của lớp.

Đây là một mảng SCNNodes bởi vì đó là kiểu mà chúng ta trả về từ phương thức newSphere() mà chúng ta đã tạo ra đối với phần đầu của hướng dẫn này. Sau đó, chúng ta sẽ đặt những quả cầu vào trong mảng này và kiểm tra có bao nhiêu quả ở đó. Dựa vào đó, chúng ta sẽ có thể thao tác trên số lượng của chúng bằng cách xoá và thêm chúng.

Ràng buộc tuỳ chọn

Tiếp theo, chúng ta sẽ sử dụng một loạt các câu lẹnh if-else và for để tìm ra có quả cầu nào ở trong mảng hay không. Đối với những người mới bắt đầu, hãy thêm những ràng buộc tuỳ chọn sau đây vào ứng dụng của bạn:

Đầu tiên, chúng ta kiểm tra có bất kỳ phần tử nào ở trong mảng spheres hay không và nếu không, chạy code ở trong mệnh đề else.

Kiểm tra các quả cầu

Sau đó, thêm code sau đây vào phần đầu (nhánh if) của câu lệnh if-else của bạn:

Vì chúng ta đã ở trong một sự kiện chạm, chúng ta biết rằng chúng ta đang tạo ra một quả cầu khác. Vì vậy nếu đã có sẵn một quả cầu, chúng ta cần lấy khoảng cách và hiển thị nó đến người dùng. Bạn có thể gọi phương thức distance() trên quả cầu, bởi vì sau đó, chúng ta sẽ tạo một phần mở rộng cảu SCNNode.

Tiếp theo, chúng ta cần biết đã có nhiều hơn tối đa hai quả cầu hay chưa. Để làm điều này, chúng ta chỉ cần sử dụng thuộc tính count trong mảng spheres và một câu lệnh if. Chúng ta lặp qua tất cả các quả cầu trong mảng và xoá chúng khỏi cảnh. (Đừng lo, chúng ta sẽ thêm chúng sau).

Sau cùng, vì chúng ta đã ở trong câu lệnh if nói cho chúng ta biết có nhiều hơn hai quả cầu, chúng ta có thể xoá cái thứ ba trong mảng để đảm bảo rằng chỉ còn lại duy nhất hai cái trong mảng.

Thêm các quả cầu

Cuối cùng, trong mệnh đề else, chúng ta biết rằng mảng spheres là rỗng, do đó những gì chúng ta cần làm là thêm quả cầu mà chúng ta đã tạo lúc bắt đâu gọi phương thức. Bên trong mệnh đề else, thêm code sau đây:

Tuyệt! Chúng ta vừa mới thêm quả cầu vào mảng spheres, và mảng của chúng ta đã sẵn sàng cho lần chạm kế tiếp. Bây giờ chúng ta đã chuẩn bị xong mảng của chúng ta cới các quả cầu sẽ xuất hiện trên màn hình, vậy bây giờ, hãy thêm những cái sau đây vào mảng.

Để lặp qua và thêm các quả cầu, thêm code sau:

Đây chỉ là một vòng lặp for đơn giản, và chúng ta đang thêm các quả cầu (SCNNode) như là một con của node gốc của scene. Trong SceneKit, đây là cách được khuyến khích dùng để thêm thứ gì đó.

Phương thức đầy đủ

Phương thức handleTap() sẽ trông giống như sau:

Tính toán khoảng cách

Bây giờ, nếu bạn nhớ, chúng ta đã gọi một phương thức distance(to:) trên SCNNode của chúng ta, quả cầu, và tôi đảm bảo rằng Xcode đang cảnh báo bạn vì sử dụng một phương thức chưa được khai báo. Hãy khắc phục điều đó nào, bằng cách tạo một extension của lớp SCNNode.

Để tạo một extension, chỉ cần thêm code sau đây bên ngoài lớp ViewController của bạn:

Điều này đơn giản là cho phép bạn thay đổi lớp (Như bạn đang chỉnh sửa lớp thật). Sau đó, chúng ta sẽ thêm một phương thức sẽ tính toán khoảng cách giữa hai node.

Dưới đây là khai báo hàm để làm điều đó:

Bạn có thấy, có một tham số chính là một SCNNode, và nó trả về kết quả là một CGFloat. Để tính toán thật sự, thêm code này vào hàm distance() của bạn: 

Ba dòng code đầu tiên trừ các vị trí x, y và z của SCNNode hiện tại khỏi toạ độ node được trền như là một tham số. Chúng ta sau đó sẽ gắn các giá trị này vào trong công thức tính khoảng cách để lấy về khoảng cách. Ngoài ra, bởi vì tôi muốn kết quả xuất ra đơn vị inche, tôi đã tạo một hằng cho việc chuyển đổi giữa mét và inche để dễ dàng sau này.

Bây giờ, để lấy khoảng cách giữa hai node, hãy hình dung về lớp toán thời phổ thông: bạn có thể nhớ ra công thức tính khoảng cách trong mệnh đề Đề cát. Ở đây, chúng ta đang áp dụng nó vào các điểm trong không gian ba chiều.

Sau cùng, chúng ta trả về giá trị được nhân với tỷ lệ chuyển đổi sang inch để có được đơn vị phù hợp. Nếu bạn sống bên ngoài nước Mỹ, bạn có thể để nguyên là mét hoặc chuyển đổi sang một đơn vị khác mà bạn muốn.

Tóm tắt

Vâng, cuối cùng thì mọi thứ đã được hoàn tất! Dự án sau cùng sẽ giống như sau:

Final result showing the measuring app in action

Như bạn có thể thấy, việc đo đạc là không hoàn hảo, nhưng nó nghĩ một máy 15-inch là khoảng 14.998 inche là không tệ chút nào.

Bây giờ bạn đã biết cách đo khoảng cách bằng thư viện mới của Apple, ARKit. Ứng dụng này có thể được sử dụng cho nhiều thứ, và tôi thách bạn nghĩ ra những cách khác để sử dụng nó trong thế giới thật, và hãy để lại những suy nghĩ của bạn trong phần bình luận bên dưới nhé.

Ngoài ra, nhớ kiểm tra repo GitHub của ứng dụng này. Và trong khi bạn vẫn ở đây, hãy tìm hiểu thêm những hướng dẫn phát triển cho iOS của chúng tôi ở trên Envato Tuts+!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.