Crazy Lazy

지형 인식을 이용한 자동차 모델 보여주기. 본문

Unity/AR 프로그래밍

지형 인식을 이용한 자동차 모델 보여주기.

Crazy Lazy 2026. 2. 9. 13:27

바닥면을 인식하여 해당 바닥을 터치하면 자동차 모델을 나타나게 해주는 AR 프로그램.

 

 

1. Create Project.

1) Type : 3D (Built-In Render Pipeline)

2) Name : MyAR_Project

3) Build Profiles > Android > Switch Platform.

4) Game > Display Size : 1920 X 1080 Portait.

5) 아래 자료 다운로드.

Low-poly Sports car #20.z01
10.00MB
Low-poly Sports car #20.z02
10.00MB
Low-poly Sports car #20.z03
10.00MB
Low-poly Sports car #20.zip
0.07MB
Indicator.png
0.02MB

 

 

2. Install Package.

1) AR Foundation. (6.0.6)

2) Google ARCore XR Plugin. (6.0.6)

3) XR Plugin Management. (4.5.1)

 

 

3. Project Settings.

1) Project Settings > Other Settings > Rendering > Auto Graphics API 체크 해제.

2) Project Settings > Other Settings > Graphics API > VulKan 삭제.

3) Project Settings > Other Settings > Package Name 입력.

4) Project Settings > Other Settings > Multithreaded Rendering 체크 해제.

5) Project Settings > Other Settings > Minimum API Level 을 API Level 24 로 변경.

6) Project Settings > Other Settings > Active Input Handling 을 Old 로 변경.

 

 

4. Materials.

1) M_Indicator : Albedo 에 Indicator.png 할당.

2) M_Plan.

 

 

5. Prefabs.

1) DetectedPlane.

2) SportCar : SportCar20_Static_USA 프리팹을 언팩 후 아래와 같이 구성하여 새로운 프리팹으로 저장.

 

 

6. Hierarchy.

1) CarScene 생성.

2) Hierarchy 를 아래와 같이 구성.

 

 

 

 

7. Scripts.

1) CarManager.cs

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class CarManager : MonoBehaviour
{
    public GameObject indicator;
    public GameObject myCar;
    public ARRaycastManager arManager;
    public float relocationDistance = 1f;
    public Button[] colorButtons;
    
    private GameObject _placeObject;
    
    void Start()
    {
        indicator.SetActive(false);

        for (int i = 0; i < colorButtons.Length; i++)
        {
            int idx = i;
            var btn = colorButtons[i];
            btn.onClick.AddListener(() =>
            {
                if (_placeObject != null)
                {
                    var carController = _placeObject.GetComponent<CarController>();
                    carController.ChangeColor(idx);
                }
            });
        }
    }

    void Update()
    {
        DetectGround();

        if (EventSystem.current.currentSelectedGameObject) return;
        if (indicator.activeInHierarchy && Input.touchCount > 0)
        {
            Touch touch = Input.GetTouch(0);
            if (touch.phase == TouchPhase.Began)
            {
                if (_placeObject == null)
                    _placeObject = Instantiate(myCar, indicator.transform.position, indicator.transform.rotation);
                else
                {
                    if (Vector3.Distance(_placeObject.transform.position, indicator.transform.position) > relocationDistance)
                        _placeObject.transform.SetPositionAndRotation(indicator.transform.position, indicator.transform.rotation);
                }
            }
        }
    }

    private void DetectGround()
    {
        Vector2 screenSize = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f);
        List<ARRaycastHit> hitInfos = new List<ARRaycastHit>();
        if (arManager.Raycast(screenSize, hitInfos, TrackableType.Planes))
        {
            indicator.SetActive(true);
            indicator.transform.position = hitInfos[0].pose.position;
            indicator.transform.rotation = hitInfos[0].pose.rotation;
            indicator.transform.position += indicator.transform.up * 0.1f;
        }
        else
        {
            indicator.SetActive(false);
        }
    }
}

 

2) CarController.cs

using UnityEngine;

public class CarController : MonoBehaviour
{
    public GameObject[] bodyObjects;
    public Color32[] colors;
    public float rotSpeed = 0.1f;

    private Material[] _carMats;
    
    void Start()
    {
        _carMats = new Material[bodyObjects.Length];
        for (int i = 0; i < _carMats.Length; i++)
            _carMats[i] = bodyObjects[i].GetComponent<Renderer>().material;

        colors[0] = _carMats[0].color;
    }

    void Update()
    {
        if (Input.touchCount > 0)
        {
            Touch touch = Input.GetTouch(0);
            if (touch.phase == TouchPhase.Moved)
            {
                Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);
                RaycastHit hitInfo;
                if (Physics.Raycast(ray, out hitInfo, Mathf.Infinity, 1 << 8))
                {
                    Vector3 deltaPos = touch.deltaPosition;
                    transform.Rotate(transform.up, deltaPos.x * -1.0f * rotSpeed);
                }
            }
        }
    }

    public void ChangeColor(int num)
    {
        for (int i = 0; i < _carMats.Length; i++)
            _carMats[i].color = colors[num];
    }
}

 

3) NoneSleepMode.cs

using UnityEngine;

public class NoneSleepMode : MonoBehaviour
{
    void Start()
    {
        Screen.sleepTimeout = SleepTimeout.NeverSleep;
    }
}

 

 

8. Result.

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

 

 

 

* References.

1) https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@6.0/manual/index.html

2) https://docs.unity3d.com/Packages/com.unity.xr.arcore@6.0/manual/index.html