개발/Unity

유니티 AR - 이미지 타겟 인식 아웃라인 만들기 (Make Outline for Image Target in Vuforia)

피로물든딸기 2023. 6. 18. 23:35
반응형

Unity 전체 링크

 

참고

- 유니티 AR 뷰포리아 튜토리얼

- 코루틴으로 블럭 한 칸 이동하기

- 프로젝트에 뷰포리아 라이센스 키 적용하기

- 이미지 타겟팅으로 오브젝트 띄우기 From Image

- OnTargetStatusChanged로 타겟 상태 관리하기

- 이미지 타겟에 버추얼 버튼 추가하기

 

AR 카메라로 이미지를 인식하게 되면, 오브젝트가 등장한다.

하지만 인식이 되자마자 오브젝트가 나오기 때문에 조금 뜬금없을 수 있다.

 

해결 방법 중 하나는 Virtual 버튼(Virtual Button)을 만들어서 버튼을 눌렀을 때 오브젝트가 나오도록 할 수 있다.

 

여기서는 OnTargetStatusChanged을 이용해 마우스 버튼(모바일 터치)으로 클릭하는 경우

이미지를 인식하는 아웃라인을 만들고 해당 터치하면 오브젝트가 나오도록 수정해보자.


아웃라인 만들기

 

아웃라인이 하얀색이라서 보이지 않지만 아래의 그림은 아웃라인이다.

하얀색 angle

 

드래그해서 다운로드 받고 유니티로 옮긴 후, Sprite로 변환한다.

 

그리고 기존의 큐브를 빈 오브젝트(OutLine)의 자식으로 설정하고, Cube는 잠시 꺼둔다.

 

OutLine은 SpriteRenderer를 추가하고 TargetTracer.cs를 추가한다.

크기를 적절히 조절하여 아래와 같이 이미지를 인식하는 마커처럼 보이게 한다.

 

TargetTracer.cs는 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;

public class TargetTracker : MonoBehaviour
{
    ObserverBehaviour imageTargetBehaviour;
    
    void Start()
    {
        imageTargetBehaviour = this.GetComponentInParent<ObserverBehaviour>();
        imageTargetBehaviour.OnTargetStatusChanged += onTargetStatusChanged;
    }

    void onTargetStatusChanged(ObserverBehaviour observerbehavour, TargetStatus status)
    {
        if (status.Status == Status.TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Get Target Image!");
            this.gameObject.SetActive(true);
        }

        if (status.Status == Status.EXTENDED_TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Miss Target Image!");
            this.gameObject.SetActive(false);
        }
    }
}

 

여기서 다음의 코드를 추가한다.

먼저 처음에 설정한 Scale을 저장해둔다.

    ObserverBehaviour imageTargetBehaviour;
    Vector3 initLocalScale;

    void Start()
    {
        initLocalScale = this.transform.localScale;

        imageTargetBehaviour = this.GetComponentInParent<ObserverBehaviour>();
        imageTargetBehaviour.OnTargetStatusChanged += onTargetStatusChanged;
    }

 

그리고 코루틴을 이용해 사이즈를 크게 만든 후, 서서히 원래 크기대로 변환시킨다.

    private IEnumerator makeAROutLine()
    {
        float size = 5.0f;
        this.transform.localScale = initLocalScale * size;

        while (size >= 1.0f)
        {
            size -= 0.025f;
            this.transform.localScale = initLocalScale * size;

            yield return null;
        }
    }

 

마지막으로 onTargetStatusChanged에 코루틴 함수를 추가한다.

    void onTargetStatusChanged(ObserverBehaviour observerbehavour, TargetStatus status)
    {
        if (status.Status == Status.TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Get Target Image!");
            this.gameObject.SetActive(true);
            StartCoroutine(makeAROutLine());
        }

        if (status.Status == Status.EXTENDED_TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Miss Target Image!");
            this.gameObject.SetActive(false);
        }
    }

 

게임을 실행하면 아웃라인이 생겨서 이미지를 인식하는 효과가 나타나는 것처럼 보이게 된다.

 

전체 코드는 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;

public class TargetTracker : MonoBehaviour
{
    ObserverBehaviour imageTargetBehaviour;
    Vector3 initLocalScale;

    void Start()
    {
        initLocalScale = this.transform.localScale;

        imageTargetBehaviour = this.GetComponentInParent<ObserverBehaviour>();
        imageTargetBehaviour.OnTargetStatusChanged += onTargetStatusChanged;
    }

    private IEnumerator makeAROutLine()
    {
        float size = 5.0f;
        this.transform.localScale = initLocalScale * size;

        while (size >= 1.0f)
        {
            size -= 0.025f;
            this.transform.localScale = initLocalScale * size;

            yield return null;
        }
    }

    void onTargetStatusChanged(ObserverBehaviour observerbehavour, TargetStatus status)
    {
        if (status.Status == Status.TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Get Target Image!");
            this.gameObject.SetActive(true);
            StartCoroutine(makeAROutLine());
        }

        if (status.Status == Status.EXTENDED_TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Miss Target Image!");
            this.gameObject.SetActive(false);
        }
    }
}

클릭 이벤트 추가하기

 

콜라이더가 있으면 클릭 이벤트를 감지할 수 있으므로, 아래와 같이 Box Collider를 추가한다.

 

그리고 큐브도 다시 켜두자.

 

스크립트가 시작되면 cube 변수에 큐브를 할당하고 안 보이도록 설정한다.

    GameObject cube;

    void Start()
    {
        initLocalScale = this.transform.localScale;

        cube = this.transform.GetChild(0).gameObject;
        cube.SetActive(false);

        imageTargetBehaviour = this.GetComponentInParent<ObserverBehaviour>();
        imageTargetBehaviour.OnTargetStatusChanged += onTargetStatusChanged;
    }

 

그리고 OnMouseDown 이벤트에서 큐브가 켜지도록 설정한다.

    private void OnMouseDown()
    {
        cube.SetActive(true);
    }

 

마지막으로 이미지를 놓쳤을 때, 큐브를 다시 꺼지도록 설정한다.

    void onTargetStatusChanged(ObserverBehaviour observerbehavour, TargetStatus status)
    {
        ...
        
        if (status.Status == Status.EXTENDED_TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Miss Target Image!");
            this.gameObject.SetActive(false);
            cube.SetActive(false);
        }
    }

 

이제 아웃라인을 클릭하면 큐브가 나타나게 된다.

 

전체 코드는 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;

public class TargetTracker : MonoBehaviour
{
    ObserverBehaviour imageTargetBehaviour;
    Vector3 initLocalScale;

    GameObject cube;

    void Start()
    {
        initLocalScale = this.transform.localScale;

        cube = this.transform.GetChild(0).gameObject;
        cube.SetActive(false);

        imageTargetBehaviour = this.GetComponentInParent<ObserverBehaviour>();
        imageTargetBehaviour.OnTargetStatusChanged += onTargetStatusChanged;
    }

    private IEnumerator makeAROutLine()
    {
        float size = 5.0f;
        this.transform.localScale = initLocalScale * size;

        while (size >= 1.0f)
        {
            size -= 0.025f;
            this.transform.localScale = initLocalScale * size;

            yield return null;
        }
    }

    private void OnMouseDown()
    {
        cube.SetActive(true);
    }

    void onTargetStatusChanged(ObserverBehaviour observerbehavour, TargetStatus status)
    {
        if (status.Status == Status.TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Get Target Image!");
            this.gameObject.SetActive(true);
            StartCoroutine(makeAROutLine());
        }

        if (status.Status == Status.EXTENDED_TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Miss Target Image!");
            this.gameObject.SetActive(false);
            cube.SetActive(false);
        }
    }
}

스프라이트 색상 효과

 

마지막으로 SpriteRenderer를 추가하여 하얀색 아웃라인을 하게 만들어보자.

    SpriteRenderer spriteRenderer;
    Color32 initColor;
    byte r, g, b;

    void Start()
    {
        initLocalScale = this.transform.localScale;

        cube = this.transform.GetChild(0).gameObject;
        cube.SetActive(false);

        spriteRenderer = this.GetComponent<SpriteRenderer>();
        initColor = spriteRenderer.material.color;
        r = initColor.r;
        g = initColor.g;
        b = initColor.b;

        imageTargetBehaviour = this.GetComponentInParent<ObserverBehaviour>();
        imageTargetBehaviour.OnTargetStatusChanged += onTargetStatusChanged;
    }

 

처음에 저장한 Color에서 적당히 r, g, b 값을 바꾸면 된다.

    private void Update()
    {
        r += 4; g -= 3; b += 2;

        spriteRenderer.material.color 
            = new Color32((byte)(r % 256), (byte)(g % 256), (byte)(b % 256), 255);
    }

 

게임을 실행하면 아웃라인이 하게 깜빡이게 된다.

 

전체 코드는 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;

public class TargetTracker : MonoBehaviour
{
    ObserverBehaviour imageTargetBehaviour;
    Vector3 initLocalScale;

    GameObject cube;

    SpriteRenderer spriteRenderer;
    Color32 initColor;
    byte r, g, b;

    void Start()
    {
        initLocalScale = this.transform.localScale;

        cube = this.transform.GetChild(0).gameObject;
        cube.SetActive(false);

        spriteRenderer = this.GetComponent<SpriteRenderer>();
        initColor = spriteRenderer.material.color;
        r = initColor.r;
        g = initColor.g;
        b = initColor.b;

        imageTargetBehaviour = this.GetComponentInParent<ObserverBehaviour>();
        imageTargetBehaviour.OnTargetStatusChanged += onTargetStatusChanged;
    }

    private IEnumerator makeAROutLine()
    {
        float size = 5.0f;
        this.transform.localScale = initLocalScale * size;

        while (size >= 1.0f)
        {
            size -= 0.025f;
            this.transform.localScale = initLocalScale * size;

            yield return null;
        }
    }

    private void OnMouseDown()
    {
        cube.SetActive(true);
    }

    void onTargetStatusChanged(ObserverBehaviour observerbehavour, TargetStatus status)
    {
        if (status.Status == Status.TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Get Target Image!");
            this.gameObject.SetActive(true);
            StartCoroutine(makeAROutLine());
        }

        if (status.Status == Status.EXTENDED_TRACKED && status.StatusInfo == StatusInfo.NORMAL)
        {
            Debug.Log("Miss Target Image!");
            this.gameObject.SetActive(false);
            cube.SetActive(false);
        }
    }

    private void Update()
    {
        r += 4; g -= 3; b += 2;

        spriteRenderer.material.color 
            = new Color32((byte)(r % 256), (byte)(g % 256), (byte)(b % 256), 255);
    }
}

 

반응형