[AR, 유니티] 3강. AR 3D-Obj Control (feat. Screen touch)

2022. 11. 16. 00:28코딩 3막 <AR>

728x90

지난 시간, AR Foundation의 기본 컴포넌트인 Session을 통해 AR카메라로 plane과 face를 인식하는 작업을 했습니다

오늘은 이어서 카메라가 인식한 plane 위에 3D 오브젝트를 배치하고 앱 터치를 통해 그 오브젝트를 이동시켜보겠습니다

 


먼저 해야할 것은 유니티 import가 지원되는 obj나 fbx 파일 같은 3D 파일이 필요하다

필요한 오브젝트를 구하기 위해 가장 쉬운 방법은 구글 서칭이 있지만 오늘 추천할 꿀팁을 소개하겠음!

바로바로 3D 오브젝트를 쉽게 다운받을 수 있는 사이트 <sketchfab> 입니다

 

Sketchfab - The best 3D viewer on the web

With a community of over one million creators, we are the world’s largest platform to publish, share, and discover 3D content on web, mobile, AR, and VR.

sketchfab.com

먼저 위 사이트에 들어갑니다

 

그럼 상위 메뉴에서 EXPLORE - Downloadable을 선택하시면 돈을 주고 구매가 가능한 오브젝트들과

무료로 다운로드가 가능한 오브젝트들이 많이 업로드되어 있습니다

간단히 저는 penguin을 입력하고 귀여운 펭귄 오브젝트를 다운받아보겠습니다

어떤 것을 고를까요?

그럼 오브젝트가 준비되었다는 가정하에 다음으로 넘어가겠습니다


AR Raycast Manager

사용자가 AR공간에 3D 오브젝트를 배치하려면 사용자가 가리키는 부분이 오브젝트를 배치할수 있는 공간인지 확인하는 컴포넌트

raycast

public bool Raycast()는 screenPoint, hitResults, trackableTypes를 이용해서 공간상에 있는 어떤 오브젝트를 

ray를 쏴서 확인을 하고 그 오브젝트를 반환받는 일을 하고 있는 함수


 

그럼 이제 공간에 배치할 3D오브젝트가 스폰하는 방식으로 새로운 스크립트를 만들어보겠습니다

게임오브젝트를 새로 하나 추가한 후 ARPlaceOnPlane이라는 새로운 스크립트를 추가해줍니다

1. AR Place On Plane.cs

using UnityEngine.XR.ARFoundation; 추가
ARRaycastManager를 public으로 선언  public으로 선언하면 유니티 에디터상에서 객체들을 instance
 placeObject를 public으로 선언 어떤 오브젝트를 공간에 배치할건지 선택
screen의 center point를 반환 화면의 센터부분에 매 프레임마다 오브젝트를 위치시키는 함수
hits라는 반환받을 객체를 선언  AR raycast에서 쏜 ray와 충돌하는 모든 오브젝트를 반환하는데 쓰이는 인자
planes라는 반환받을 객체를 선언 객체에 Ray가 닿게되면 그 결과값을 반환하도록 인자

UpdateCenterObject() 함수를 통해 사용자가 바라보는 방향(AR카메라의 방향)에 3D 오브젝트를 자동 생성

 

[스크립트]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
 
public class ARPlaceOnPlane : MonoBehaviour
{
    public ARRaycastManager arRaycaster;
    public GameObject placeObject;
 
    private void Update()
    {
        UpdateCenterObject();
    }
    private void UpdateCenterObject()
    {
        Vector3 screenCenter = Camera.current.ViewportToScreenPoint(new Vector3(0.5f, 0.5f));
 
        List<ARRaycastHit> hits = new List<ARRaycastHit>();
        arRaycaster.Raycast(screenCenter, hits, TrackableType.Planes);
 
        if (hits.Count > 0)
        {
            Pose placementPose = hits[0].pose;
            placeObject.SetActive(true);
            placeObject.transform.SetLocalPositionAndRotation(placementPose.position, placementPose.rotation);
        }
        else
        {
            // 부딪히는 평면이 없을 경우 오브젝트 비활성화
            placeObject.SetActive(false);
        }
    }
}
cs

PlaceObjectByTouch() 함수를 통해 평면을 인식하여 Touch를 통해 3D 오브젝트들이 스폰되는 기능 

평면 뿐만 아니라 이미지, 얼굴 등 다양한 type으로 ray 가능

if (Input.touchCount > 0) 만약 스크린 위에 터치가 일어난다면
Touch touch = Input.GetTouch(0); touch에 대한 객체정보를 받아와서 어느 위치에서 touch가 발생했는지 확인
if (arRaycaster.Raycast(touch.position, hits, TrackableType.Planes))
            {
                Pose hitPose = hits[0].pose;
            }
touch가 일어난 방향으로 ray를 쏘고 ray를 쏴서 얻은 첫번째 객체의 위치를 hitPose에 저장
if (!spawnObject)
spawnObject = Instantiate(placeObject, hitPose.position, hitPose.rotation);
만약 공간 상에 위치된 오브젝트가 없을 경우 터치가 일어날 때 마다 게임오브젝트가 인스턴스화하여 spawnObject에 저장
spawnObject.transform.position = hitPose.position;
spawnObject.transform.rotation = hitPose.rotation;
두번째 프레임부터는 이미 화면상에 object가 spawn되어 있기 때문에 position과 rotation을 업데이트

[스크립트]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void PlaceObjectByTouch()
    {
        if (Input.touchCount > 0)   // 터치가 하나라도 일어났을 때
        {
            Touch touch = Input.GetTouch(0); // 터치가 일어난 것에 대한 객체 정보 가운데 가장 처음(index : 0) 터치한 객체의 정보
            
            List<ARRaycastHit> hits = new List<ARRaycastHit>();
            if (arRaycaster.Raycast(touch.position, hits, TrackableType.Planes))
            {
                Pose hitPose = hits[0].pose;
 
                // 터치가 일어날 때 마다 게임오브젝트가 인스턴스화 될 수 있게 함. 그러나 동일한 오브젝트가 여러개 생성되지 않도록 spawnObject라는 곳에 인스턴스화 된 객체를 미리 저장
                if (!spawnObject)
                    spawnObject = Instantiate(placeObject, hitPose.position, hitPose.rotation);
                else
                {
                    spawnObject.transform.position = hitPose.position;
                    spawnObject.transform.rotation = hitPose.rotation;
                }
            }
        }
    }
cs

공감해주셔서 감사합니다

728x90