본문 바로가기
개발/Unity

유니티 - 오브젝트를 선택된 상태로 만들기 : (4) 오브젝트 하나만 선택하기

by 피로물든딸기 2022. 5. 17.
반응형

Unity 전체 링크

 

오브젝트를 선택된 상태로 만들기 : (1) bool
오브젝트를 선택된 상태로 만들기 : (2) 이미지로 만들기
오브젝트를 선택된 상태로 만들기 : (3) Shader Outline
오브젝트를 선택된 상태로 만들기 : (4) 오브젝트를 하나만 선택하기


여러 오브젝트(블럭, Block) 중 하나만 선택하도록 해보자.

선택된 오브젝트는 outline이 나타나도록 한다.

 

먼저 카메라에 SelectBlock.cs를 추가한다.

카메라를 기준으로 Raycast를 이용해 블럭을 선택한다.

SelectBlock은 카메라에만 존재하므로 싱글턴으로 만들면 좋다.

    static SelectBlock instance = null;
    public static SelectBlock Instance
    {
        get
        {
            if (null == instance) instance = FindObjectOfType<SelectBlock>();
            return instance;
        }
    }

    void Awake()
    {
        if (null == instance) instance = this;
    }

 

오브젝트가 선택된 경우, Outline이 추가되는 것은 아래의 코드로 분리한다. (링크의 OnMouseDown/Up) 

object를 넘겨주면 outline을 추가하고 remove에서 renderer를 넘겨주면 원래의 material로 변경한다.

    Material outline;
    Renderer renderers;
    List<Material> materialList = new List<Material>();

    void addOutline(Transform obj)
    {
        if (obj == null) return;

        renderers = obj.GetComponent<Renderer>();

        materialList.Clear();
        materialList.AddRange(renderers.sharedMaterials);
        materialList.Add(outline);

        renderers.materials = materialList.ToArray();
    }

    void removeOutline(Renderer renderer)
    {
        if (renderer != null)
        {
            materialList.Clear();
            materialList.AddRange(renderer.sharedMaterials);
            materialList.Remove(outline);

            renderer.materials = materialList.ToArray();
        }
    }

 

오브젝트의 해제와 선택은 아래와 같다.

해제 - 현재 선택된 오브젝트를 null로 변경하고 outline 제거

선택 - selectedTarget을 선택한 오브젝트로 변경하고 outline을 추가

    Transform selectedTarget;
	
    void clearTarget()
    {
        if (selectedTarget == null) return;

        selectedTarget = null;
        removeOutline(renderers);
    }

    void selectTarget(Transform obj)
    {
        if (obj == null) return;
       
        selectedTarget = obj;
        addOutline(obj);
    }

 

이제 Update에서 Raycast로 오브젝트를 선택하면 된다.

특정 오브젝트만 선택되도록 하기 위해 LayerMask = "Block"을 이용하였다.

    RaycastHit hit;
    
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.red);

            int layer = 1 << LayerMask.NameToLayer("Block");
            if (Physics.Raycast(ray, out hit, Mathf.Infinity, layer))
            {
                Transform obj = hit.transform;
                selectTarget(obj);
            }
        }
    }

 

선택하고 싶은 오브젝트에 Layer = "Block"으로 변경해두고 테스트해보자.

 

오브젝트를 선택해보자. outline이 추가된다.

그러나 다른 오브젝트를 눌러도 이전에 선택된 오브젝트가 해제되지 않는다.

 

여기까지의 코드는 아래와 같다.

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

public class SelectBlock : MonoBehaviour
{
    RaycastHit hit;
    Transform selectedTarget;

    Material outline;
    Renderer renderers;
    List<Material> materialList = new List<Material>();

    static SelectBlock instance = null;
    public static SelectBlock Instance
    {
        get
        {
            if (null == instance) instance = FindObjectOfType<SelectBlock>();
            return instance;
        }
    }

    void Awake()
    {
        if (null == instance) instance = this;
    }

    void Start()
    {
        outline = new Material(Shader.Find("Draw/OutlineShader"));
    }

    void addOutline(Transform obj)
    {
        if (obj == null) return;

        renderers = obj.GetComponent<Renderer>();

        materialList.Clear();
        materialList.AddRange(renderers.sharedMaterials);
        materialList.Add(outline);

        renderers.materials = materialList.ToArray();
    }

    void removeOutline(Renderer renderer)
    {
        if (renderer != null)
        {
            materialList.Clear();
            materialList.AddRange(renderer.sharedMaterials);
            materialList.Remove(outline);

            renderer.materials = materialList.ToArray();
        }
    }

    void clearTarget()
    {
        if (selectedTarget == null) return;

        selectedTarget = null;
        removeOutline(renderers);
    }

    void selectTarget(Transform obj)
    {
        if (obj == null) return;

        selectedTarget = obj;
        addOutline(obj);
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.red);

            int layer = 1 << LayerMask.NameToLayer("Block");
            if (Physics.Raycast(ray, out hit, Mathf.Infinity, layer))
            {
                Transform obj = hit.transform;
                selectTarget(obj);
            }
        }
    }
}

간단한 해결방법은 오브젝트를 선택하기 전 해제를 먼저하면 된다. 

selectedTarget 코드 전에 clearTarget을 호출하자.

    void selectTarget(Transform obj)
    {
        if (obj == null) return;

        clearTarget();
        selectedTarget = obj;
        addOutline(obj);
    }

 

그리고 Raycast에서 Block을 선택하지 않은 경우 = LayerMask가 Block인 오브젝트를 못 찾은 경우에

오브젝트 선택을 해제하자.

    if (Input.GetMouseButtonDown(0))
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.red);

        int layer = 1 << LayerMask.NameToLayer("Block");
        if (Physics.Raycast(ray, out hit, Mathf.Infinity, layer))
        {
            Transform obj = hit.transform;
            selectTarget(obj);
        }
        else /* Block을 선택하지 않은 경우 */
        {
            clearTarget();
        }
    }

 

이제 오브젝트가 하나만 선택된다. 

그리고 빈 공간(Block을 얻지 못한 경우)을 클릭하면 해제되는 것을 알 수 있다.

 

그러나 현재 같은 오브젝트를 누를 경우 선택이 해제가 되지 않는다.

 

selectTarget에 obj가 현재 선택된 오브젝트(selectedTarget)과 같은 경우에는 선택을 해제하는 코드를 추가한다.

    void selectTarget(Transform obj)
    {
        if (obj == null) return;
       
        if (obj == selectedTarget)
        {
            clearTarget();
            return;
        }

        clearTarget();
        selectedTarget = obj;
        addOutline(obj);
    }

 

이제 아래의 기능이 모두 작동한다.

 

- 오브젝트 선택 

- 다른 오브젝트를 선택하면 기존 오브젝트 해제

- 빈 공간 선택 시 해제

- 같은 오브젝트를 선택하는 경우 해제 

 

전체 코드는 다음과 같다.

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

public class SelectBlock : MonoBehaviour
{
    RaycastHit hit;
    Transform selectedTarget;

    Material outline;
    Renderer renderers;
    List<Material> materialList = new List<Material>();

    static SelectBlock instance = null;
    public static SelectBlock Instance
    {
        get
        {
            if (null == instance) instance = FindObjectOfType<SelectBlock>();
            return instance;
        }
    }

    void Awake()
    {
        if (null == instance) instance = this;
    }

    void Start()
    {
        outline = new Material(Shader.Find("Draw/OutlineShader"));
    }

    void addOutline(Transform obj)
    {
        if (obj == null) return;

        renderers = obj.GetComponent<Renderer>();

        materialList.Clear();
        materialList.AddRange(renderers.sharedMaterials);
        materialList.Add(outline);

        renderers.materials = materialList.ToArray();
    }

    void removeOutline(Renderer renderer)
    {
        if (renderer != null)
        {
            materialList.Clear();
            materialList.AddRange(renderer.sharedMaterials);
            materialList.Remove(outline);

            renderer.materials = materialList.ToArray();
        }
    }

    void clearTarget()
    {
        if (selectedTarget == null) return;

        selectedTarget = null;
        removeOutline(renderers);
    }

    void selectTarget(Transform obj)
    {
        if (obj == null) return;

        if (obj == selectedTarget)
        {
            clearTarget();
            return;
        }

        clearTarget();
        selectedTarget = obj;
        addOutline(obj);
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.red);

            int layer = 1 << LayerMask.NameToLayer("Block");
            if (Physics.Raycast(ray, out hit, Mathf.Infinity, layer))
            {
                Transform obj = hit.transform;
                selectTarget(obj);
            }
            else /* Block을 선택하지 않은 경우 */
            {
                clearTarget();
            }
        }
    }
}

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글