게임 오브젝트의 구성 요소가 손상 될 수 있는지 확인


11

Unity에서 게임을 개발하면서 [RequireComponent(typeof(%ComponentType%))]구성 요소가 모든 종속성을 충족시킬 수 있도록 최대한 활용하고 있습니다 .

이제 다양한 UI 객체를 강조하는 튜토리얼 시스템을 구현하고 있습니다. 강조 표시를 수행하려면 GameObject장면에서 참조를 가져온 다음 Instantiate ()를 사용하여 복제 한 다음 표시에 필요하지 않은 모든 구성 요소를 재귀 적으로 제거합니다. 문제는 RequireComponent이러한 구성 요소를 자유롭게 사용 하면 아직 삭제되지 않은 다른 구성 요소에 의존하기 때문에 많은 순서로 간단하게 제거 할 수 없다는 것입니다.

내 질문은 : 현재 첨부 된 Component에서 제거 할 수 있는지 여부를 결정하는 방법이 있습니까?GameObject

내가 게시하는 코드는 기술적으로 작동하지만 Unity 오류가 발생합니다 (이미지에서 볼 수 있듯이 try-catch 블록에 잡히지 않음)

여기에 이미지 설명을 입력하십시오

코드는 다음과 같습니다.

public void StripFunctionality(RectTransform rt)
{
    if (rt == null)
    {
        return;
    }

    int componentCount = rt.gameObject.GetComponents<Component>().Length;
    int safety = 10;
    int allowedComponents = 0;
    while (componentCount > allowedComponents && safety > 0)
    {
        Debug.Log("ITERATION ON "+rt.gameObject.name);
        safety--;
        allowedComponents = 0;
        foreach (Component c in rt.gameObject.GetComponents<Component>())
        {
            //Disable clicking on graphics
            if (c is Graphic)
            {
                ((Graphic)c).raycastTarget = false;
            }

            //Remove components we don't want
            if (
                !(c is Image) &&
                !(c is TextMeshProUGUI) &&
                !(c is RectTransform) &&
                !(c is CanvasRenderer)
                )
            {
                try
                {
                    DestroyImmediate(c);
                }
                catch
                {
                    //NoOp
                }
            }
            else
            {
                allowedComponents++;
            }
        }
        componentCount = rt.gameObject.GetComponents<Component>().Length;
    }

    //Recursive call to children
    foreach (RectTransform childRT in rt)
    {
        StripFunctionality(childRT);
    }
}

다음과 같다 위의 방법 그래서이 코드는 디버그 로그의 마지막 세 줄을 생성 : 그것은 입력 A로했다 GameObject두 개의 구성 요소 : ButtonPopupOpener. PopupOpener것을 requies Button성분과 같은 게임 오브젝트에 존재한다 :

[RequireComponent(typeof(Button))]
public class PopupOpener : MonoBehaviour
{
    ...
}

while 루프의 첫 번째 반복 ( "ITERATION ON Button Invite (clone)"텍스트로 표시)은 Button첫 번째 삭제를 시도했지만 PopupOpener구성 요소 에 따라 달라지지 않았습니다 . 이것은 오류를 던졌습니다. 그런 다음 PopupOpener다른 것에 의존하지 않기 때문에 구성 요소 를 삭제하려고 시도했습니다 . 다음 반복 (두 번째 텍스트 "ITERATION ON Button Invite (clone)"으로 표시) 에서 첫 번째 반복 (오류 후)에서 삭제 된 Button이후 남은 구성 요소 를 삭제하려고 시도했습니다 PopupOpener.

내 질문에 지정된 구성 요소가 현재에서 제거 할 수있는 경우는 미리 확인할 수 있습니다 여부 그래서 GameObject호출하지 않고, Destroy()또는 DestroyImmediate(). 감사합니다.


원래 문제에 대하여-GUI 요소의 비대화 형 사본 (= 시각적)을 만드는 것이 목표입니까?
wondra

예. 목표는 동일한 위치에서 동일하고 상호 작용할 수없는 동일한 사본을 동일한 방식으로 스케일링하여 실제 게임 오브젝트와 동일한 텍스트를 표시하는 것입니다. 이 방법을 사용한 이유는 나중에 UI를 편집 한 경우 (스크린 샷을 표시 한 경우 발생해야 함) 자습서를 수정할 필요가 없기 때문입니다.
Liam Lime

나는 Unity에 대한 경험이 없지만 전체를 복제하고 물건을 제거하는 대신 필요한 부분 만 복사 할 수 있는지 궁금합니다.
Zan Lynx

나는 그것을 테스트하지는 않았지만 Destroy프레임의 끝에서 구성 요소를 제거합니다 ( Immediately 와 반대 ). DestroyImmediate어쨌든 나쁜 스타일로 간주되지만 Destroy실제로 전환 하면 이러한 오류가 제거 될 수 있다고 생각 합니다.
CAD97

Destroy (올바른 방법이므로)로 시작한 다음 DestroyImmediate로 전환하여 작동하는지 확인했습니다. 파괴는 여전히 지정된 순서대로 진행되는 것으로 보이며, 이는 프레임의 끝에서 바로 동일한 예외를 발생시킵니다.
Liam Lime

답변:


9

확실히 가능하지만 상당히 많은 레거시가 필요합니다 . 제거하려는 구성 요소 유형 에 해당 필드 중 하나를 지정할 수있는 유형 이있는 유형 의 Component첨부 파일 이 있는지 확인할 수 있습니다 . 모두 확장 방법으로 싸여 있습니다.GameObjectAttributeRequireComponentm_Type#

static class GameObjectExtensions
{
    private static bool Requires(Type obj, Type requirement)
    {
        //also check for m_Type1 and m_Type2 if required
        return Attribute.IsDefined(obj, typeof(RequireComponent)) &&
               Attribute.GetCustomAttributes(obj, typeof(RequireComponent)).OfType<RequireComponent>()
               .Any(rc => rc.m_Type0.IsAssignableFrom(requirement));
    }

    internal static bool CanDestroy(this GameObject go, Type t)
    {
        return !go.GetComponents<Component>().Any(c => Requires(c.GetType(), t));
    }
}

GameObjectExtensions프로젝트의 아무 곳에 나 놓은 후 (또는 스크립트에서 일반적인 방법으로) 구성 요소를 제거 할 수 있는지 확인할 수 있습니다. 예 :

rt.gameObject.CanDestroy(c.getType());

예를 들어, 클릭을 차단되는 거의 투명 패널을 오버레이 또는 질감으로 렌더링 - 그러나이되지 않은 대화 형 GUI 개체를 만들기 위해 다른 수단이 될 수 비활성화 모든 (대신 파괴의) Component에서 파생 s의 MonoBehaviourIPointerClickHandler.


감사! 기성품 솔루션이 Unity와 함께 제공되는지 궁금하지만, 나는 그렇지 않습니다. :) 코드 감사합니다!
Liam Lime

@LiamLime 글쎄, 나는 내장 된 것이 없다는 것을 실제로 확인할 수는 없지만 일반적인 Unity 패러다임은 결함을 catch방지하기보다는 코드를 내결함성 (즉 , 로그, 계속)하기 때문에 가능하지 않습니다 (즉, if가능하면). 그러나 C # 스타일은 많지 않습니다 (이 답변의 스타일). 결국 원래 코드는 일이 일반적으로 수행되는 방식에 더 가까웠을 수 있습니다 ... 그리고 모범 사례는 개인 취향의 문제입니다.
wondra

7

클론에서 구성 요소를 제거하는 대신 구성 요소를 설정 .enabled = false하여 비활성화 할 수 있습니다. 종속성 검사를 수행하기 위해 구성 요소를 활성화 할 필요가 없기 때문에 종속성 검사가 트리거되지 않습니다.

  • 비헤이비어가 비활성화 되어도 여전히 객체에 있으며 속성을 변경하고 메서드를 호출 할 수도 있지만 엔진은 더 이상 Update메서드를 호출하지 않습니다 .
  • 렌더러가 비활성화되면 객체가 보이지 않게됩니다.
  • Collider가 비활성화되면 충돌 이벤트가 발생하지 않습니다.
  • UI 구성 요소가 비활성화되면 플레이어는 더 이상 해당 구성 요소와 상호 작용할 수 없지만 렌더러도 비활성화하지 않는 한 여전히 렌더링됩니다.

구성 요소에는 사용 또는 사용 안함 개념이 없습니다. 행동, 충돌체 및 기타 유형이 있습니다. 따라서 프로그래머는 다양한 하위 클래스에 대해 GetComponent를 여러 번 호출해야합니다.
DubiousPusher
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.