Ao desenvolver um jogo no Unity, estou fazendo uso liberal [RequireComponent(typeof(%ComponentType%))]
para garantir que os componentes tenham todas as dependências atendidas.
Agora estou implementando um sistema de tutorial que destaca vários objetos da interface do usuário. Para fazer o realce, estou fazendo uma referência à GameObject
cena, clono-a com Instantiate () e retiro recursivamente todos os componentes que não são necessários para exibição. O problema é que, com o uso liberal RequireComponent
desses componentes, muitos não podem ser simplesmente removidos em nenhuma ordem, pois dependem de outros componentes, que ainda não foram excluídos.
Minha pergunta é: Existe uma maneira de determinar se um Component
pode ser removido do GameObject
que está atualmente anexado.
O código que estou postando funciona tecnicamente, no entanto, gera erros do Unity (não capturados no bloco try-catch, como visto na imagem
Aqui está o código:
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);
}
}
Portanto, a maneira como esse código gerou as últimas três linhas do log de depuração acima é o seguinte: Ele tomou como entrada a GameObject
com dois componentes: Button
e PopupOpener
. PopupOpener
requer que um Button
componente esteja presente no mesmo GameObject com:
[RequireComponent(typeof(Button))]
public class PopupOpener : MonoBehaviour
{
...
}
A primeira iteração do loop while (denotada pelo texto "ITERATION ON Button Invite (clone)") tentou excluir a Button
primeira, mas não pôde, pois depende do PopupOpener
componente. isso gerou um erro. Em seguida, tentou excluir o PopupOpener
componente, o que foi feito, pois não é dependente de mais nada. Na próxima iteração (indicada pelo segundo texto "ITERATION ON Button Invite (clone)"), ele tentou excluir o Button
componente restante , o que agora poderia fazer, pois PopupOpener
foi excluído na primeira iteração (após o erro).
Portanto, minha pergunta é se é possível verificar antecipadamente se um componente especificado pode ser removido de sua corrente GameObject
, sem chamar Destroy()
ou DestroyImmediate()
. Obrigado.
Destroy
remove o componente no final do quadro (ao contrário deImmediate
ly).DestroyImmediate
é considerado um estilo ruim, mas acho que mudar paraDestroy
pode realmente eliminar esses erros.Respostas:
Definitivamente, é possível, mas exige bastante trabalho: você pode verificar se há um
Component
anexo aoGameObject
qual possui umAttribute
tipoRequireComponent
que possui um de seusm_Type#
campos atribuíveis ao tipo de componente que você está prestes a remover. Tudo envolvido em um método de extensão:depois de soltar
GameObjectExtensions
em qualquer lugar do projeto (ou alternativamente torná-lo um método regular no script), você pode verificar se um componente pode ser removido, por exemplo:No entanto, pode haver outros meios para criar um objeto GUI não interativo - por exemplo, renderizando-o em textura, cobrindo-o com um painel quase transparente que interceptará cliques ou desativará (em vez de destruir) todos os
Component
derivados deMonoBehaviour
ouIPointerClickHandler
.fonte
catch
, log, continue) ao invés de evitar falhas (ou sejaif
, possível). No entanto, isso não é muito estilo C # (como um nesta resposta). No final, seu código original pode ter sido mais próximo de como as coisas geralmente são feitas ... e as melhores práticas são uma questão de preferência pessoal.Em vez de remover os componentes no clone, você pode apenas desativá-los, definindo
.enabled = false
-os. Isso não acionará as verificações de dependência, porque um componente não precisa ser ativado para atender a dependência.Update
método.fonte