Kiểm tra xem thành phần của đối tượng trò chơi có thể bị phá hủy không


11

Phát triển một trò chơi trong Unity, tôi đang sử dụng tự do [RequireComponent(typeof(%ComponentType%))]để đảm bảo các thành phần có tất cả các phụ thuộc được đáp ứng.

Tôi hiện đang thực hiện một hệ thống hướng dẫn làm nổi bật các đối tượng UI khác nhau. Để làm nổi bật, tôi lấy tham chiếu đến GameObjectcảnh trong đó, sau đó tôi sao chép nó bằng Instantiate () và sau đó loại bỏ đệ quy tất cả các thành phần không cần thiết để hiển thị. Vấn đề là với việc sử dụng tự do RequireComponenttrong các thành phần này, nhiều thành phần không thể bị xóa đơn giản theo bất kỳ thứ tự nào, vì chúng phụ thuộc vào các thành phần khác, chưa bị xóa.

Câu hỏi của tôi là: Có cách nào để xác định xem một cái Componentcó thể được gỡ bỏ khỏi GameObjectnó hiện đang được gắn vào hay không.

Mã tôi đang đăng hoạt động về mặt kỹ thuật, tuy nhiên, nó đưa ra các lỗi Unity (không bị bắt trong khối thử bắt, như đã thấy trong hình ảnh

nhập mô tả hình ảnh ở đây

Đây là mã:

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);
    }
}

Vì vậy, cách mã này tạo ra ba dòng cuối cùng của nhật ký gỡ lỗi ở trên là như sau: Nó lấy đầu vào a GameObjectvới hai thành phần: ButtonPopupOpener. PopupOpeneryêu cầu một Buttonthành phần có mặt trong cùng GameObject với:

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

Lặp lại đầu tiên của vòng lặp while (được biểu thị bằng văn bản "ITERATION ON Nút Mời (bản sao)") đã cố xóa Buttonđầu tiên, nhưng không thể vì nó phụ thuộc vào PopupOpenerthành phần. Điều này đã gây ra một lỗi. Sau đó, nó đã cố gắng để xóa PopupOpenerthành phần, như nó đã làm, vì nó không phụ thuộc vào bất cứ điều gì khác. Trong lần lặp tiếp theo (Được biểu thị bằng văn bản thứ hai "ITERATION ON Nút Mời (bản sao)"), nó đã cố xóa Buttonthành phần còn lại , điều mà bây giờ có thể làm được, vì PopupOpenerđã bị xóa trong lần lặp đầu tiên (sau lỗi).

Vì vậy, câu hỏi của tôi là liệu có thể kiểm tra trước thời hạn nếu một thành phần được chỉ định có thể được gỡ bỏ khỏi hiện tại của nó GameObject, mà không cần gọi Destroy()hay DestroyImmediate(). Cảm ơn bạn.


Về vấn đề ban đầu - là mục tiêu để tạo bản sao không tương tác (= trực quan) của (các) thành phần GUI?
wonderra

Đúng. Mục tiêu là tạo ra một bản sao giống hệt nhau, không tương tác ở cùng một vị trí, được chia tỷ lệ theo cùng một cách, hiển thị cùng một văn bản như trò chơi thực tế bên dưới. Lý do tôi thực hiện với phương pháp này là vì hướng dẫn sẽ không cần sửa đổi nếu UI được chỉnh sửa vào thời điểm sau (điều này sẽ xảy ra nếu tôi hiển thị ảnh chụp màn hình).
Liam Lime

Tôi không có kinh nghiệm với Unity nhưng tôi tự hỏi nếu thay vì nhân bản toàn bộ và xóa nội dung, bạn chỉ có thể sao chép các phần bạn cần?
Zan Lynx

Tôi đã không kiểm tra nó, nhưng Destroyloại bỏ thành phần ở cuối khung (trái ngược với Immediately). DestroyImmediateDù sao cũng bị coi là xấu, nhưng tôi nghĩ chuyển sang Destroythực sự có thể loại bỏ những lỗi này.
CAD97

Tôi đã thử cả hai, bắt đầu với Hủy diệt (vì đó là cách thích hợp) và sau đó chuyển sang Hủy diệt để xem nó có hoạt động không. Phá hủy dường như vẫn đi theo thứ tự được chỉ định, gây ra các ngoại lệ tương tự, chỉ ở phần cuối của khung.
Liam Lime

Câu trả lời:


9

Nó chắc chắn là có thể, nhưng nó đòi hỏi khá nhiều legwork: bạn có thể kiểm tra nếu có một Componentgắn vào GameObjecttrong đó có một Attributeloại RequireComponenttrong đó có một trong những của m_Type#lĩnh vực chuyển nhượng để loại thành phần mà bạn muốn loại bỏ. Tất cả được gói gọn trong một phương thức mở rộng:

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));
    }
}

sau khi bỏ GameObjectExtensionsbất kỳ vị trí nào trong dự án (hoặc thay vào đó biến nó thành một phương thức thông thường trên tập lệnh), bạn có thể kiểm tra xem một thành phần có thể được gỡ bỏ hay không, ví dụ:

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

Tuy nhiên, có thể có các cách khác để tạo đối tượng GUI không tương tác - ví dụ: hiển thị nó thành kết cấu, phủ nó bằng bảng điều khiển gần như trong suốt sẽ chặn các nhấp chuột hoặc vô hiệu hóa (thay vì phá hủy) tất cả Componentxuất phát từ MonoBehaviourhoặc IPointerClickHandler.


Cảm ơn! Tôi đã tự hỏi nếu một giải pháp làm sẵn có xuất xưởng với Unity, nhưng tôi đoán là không :) Cảm ơn vì mã của bạn!
Liam Lime

@LiamLime Vâng, tôi thực sự không thể xác nhận rằng không có nội dung nào nhưng không chắc vì mô hình Unity điển hình đang tạo mã chịu lỗi (nghĩa là catchđăng nhập, tiếp tục) thay vì ngăn chặn lỗi (nghĩa là ifcó thể, sau đó). Đó là, tuy nhiên, không có nhiều kiểu C # (chẳng hạn như một trong câu trả lời này). Cuối cùng, mã ban đầu của bạn có thể đã gần hơn với cách mọi thứ thường được thực hiện ... và thực tiễn tốt nhất là vấn đề sở thích cá nhân.
wonderra

7

Thay vì loại bỏ các thành phần trên bản sao, bạn chỉ có thể vô hiệu hóa chúng bằng cách cài đặt .enabled = falsechúng. Điều này sẽ không kích hoạt kiểm tra phụ thuộc, bởi vì một thành phần không cần phải được kích hoạt để đáp ứng sự phụ thuộc.

  • Khi một Hành vi bị vô hiệu hóa, nó vẫn sẽ ở trên đối tượng và bạn thậm chí có thể thay đổi các thuộc tính của nó và gọi các phương thức của nó, nhưng động cơ sẽ không còn gọi Updatephương thức của nó nữa.
  • Khi Trình kết xuất bị vô hiệu hóa, đối tượng sẽ biến thành vô hình.
  • Khi Collider bị vô hiệu hóa, nó sẽ không gây ra bất kỳ sự kiện va chạm nào xảy ra.
  • Khi một thành phần UI bị vô hiệu hóa, người chơi không thể tương tác với nó nữa, nhưng nó vẫn sẽ được kết xuất, trừ khi bạn cũng hủy kích hoạt trình kết xuất của nó.

Các thành phần không có khái niệm kích hoạt hoặc vô hiệu hóa. Hành vi, Colliders và một số loại khác làm. Vì vậy, điều này sẽ yêu cầu lập trình viên thực hiện nhiều cuộc gọi đến GetComponent cho các lớp con khác nhau.
DubiousPizer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.