Jump to content
Unity Insider Forum

GetComponents Frage


Euphorikus

Recommended Posts

Moin, ich wollte mir eigentlich was recht simples programmieren, kam dann aber nicht weiter. Es ging darum, dass ich alle Components eines GameObjects auslesen wollte und dann den ersten Wert der Component auslesen wollte, doch dazu kam es gar nicht erst, weil ich den Array indem GetComponents speicherte gar nicht weiter ansprechen kann...

hier mal das Script:

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

public class ExampleClass : MonoBehaviour {


	public Component[] AC;
	public ExampleClass EC;
	public string wert = "inhalt";
	void Start()
	{
		EC = GetComponent<ExampleClass>();
		AC = gameObject.GetComponents(typeof(Component));

		Debug.Log ("AC[1]="+AC[1]+" ergibt das gleiche wie "+gameObject.GetComponent<ExampleClass>()+" | wert:"+EC.wert+" | AC[1].wert funktioniert nicht");
		// AC[0] ist die Transform Componente
		// AC[1] ist die ExampleClass Componente 
		// AC[1].wert funktioniert nicht


	}
}

Btw: Wie dann die erste Variable von AC[1] ausgelesen wird ohne zu wissen das die erste Variable "wert" heisst ist mir bisher auch völlig schleierhaft, ich stelle mir so etwas vor, dass ich schreibe AC[1].Variable[1], dazu müsste ich wissen, wie ich alle Variablen auslese und in ein Array speichere, dass wäre der nächste Schritt gewesen, aber bis dahin kam ich wie gesagt gar nicht erst :o

Wäre schön, wenn ihr mir da ein paar Denkanstöße geben könntet :)

 

edit: Also ich bin jetzt soweit das ich den Variablen namen der ExampleClass Komponente auslesen kann, dies geschieht mit

AC[1].GetType().GetFields().GetValue(2) //gibt "wert" zurück

nund versuche ich noch den Inhalt auszulesen, aber es klappt nicht mit AC[1].GetType().GetFields().GetValue(2).GetFields().GetValue(0)

wobei AC[1].GetType().GetFields().GetValue(2).GetFields().Length() // 0 ausgibt

Ich verstehe nicht warum mir jetzt nicht der "wert" inhalt ausgegeben wird...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Zu sagen das etwas nicht funktioniert, hilt uns nicht wirklich weiter. Ich weiß das dir zu 100% eine Fehlermeldung geworfen wird und die solltest du am besten hier posten oder versuchen vorher zu googlen was diese Fehlermeldung bedeutet.

 

ExampleClass wurde von MonoBehaviour abgeleitet. Das widerrum irgendwann von Component abgleitet wurde.

ExampleClass ec = new ExampleClass(); //Merke niemals new bei MonoBehaviours machen dient nur als Beispiel
Debug.Log(ec.wert); // Funktioniert ist ja eine Variable vom Typ ExampleClass

Component c = ec; // Funktioniert weil ExampleClass irgendwann mal von Component abgeleitet hat
Debug.Log(c.wert); // Funktioniert nicht, wir wissen zwar das in c unsere ExampleClass steckt aber der Compiler hat davon keine Ahnung
Debug.Log(((ExampleClass)c).wert); // Funktioniert weil wir dem Compiler sagen das in c eine ExampleClass steckt. Das nennt man auch casten

 

D.h du kannst zwar alle Components von einem Objekt sammeln, aber der Compiler kann nicht wissen welche Klassen das nun sind. Du kannst also nicht einfach hingehen und auf werte zugreifen die irgendwo in irgendeiner Klasse mal irgendwann definiert wurden.

Dafür musst du dem Compiler vorher sagen welche Klasse in der Variable ist. Wobei ich dir davon abraten würde das so zu machen. Sobald du die Reihenfolge deiner Components veränderst würde dein Code kaputt gehen (Weil deine ExampleClass nun nicht mehr an Index 1 sondern evtl. bei Index 10 liegt)

Musst du denn umbedingt auf den Wert zugreifen und wenn ja warum?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Schonmal danke das du dir die Zeit für mich genommen hast, hattest du meinen edit noch mitbekommen? :)

Nunja jetzt bin ich soweit, dass ich auf die Componenten zugreifen kann, die Feldervariablennamen lesen kann, aber eben noch nicht an die FieldInfo sprich den Inhalt der Feldervariablennamen herankomme, denn dieser Fehlermeldung  kommt dann:

IndexOutOfRangeException: Index has to be between upper and lower bound of the array.
System.Array.GetValue (Int32 index) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Array.cs:487)
ExampleClass.Start () (at Assets/ExampleClass.cs:17)

Liegt ziemlich genau am GetType().GetFields().GetValue(0)

also von meiner theorie her, müsste ich jetzt einfach nur den Inhalt von AC[1].GetType().GetFields().GetValue(2)  herauslesen, was aber nicht mit GetType().GetFields().GetValue(0) zu funktionieren scheint...

Ich brauche sowas wie AC[1].GetType().GetFields().GetValue(2).GIBMIRDENINHALT

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke der Nachfrage :) na ich wollte einfach nur den Inhalt von Variablen einer unbekannten Componente auslesen können...

Geht mir darum, dass es doch viel schicker ist, wenn man das Auslesen der Inhalte von Componenten automatisiert hat, das erspart doch total viel Arbeit, wenn ich mir vorstelle ich habe bald theoretisch > 1000 Componenten ja dann gute Nacht *grins*, dann kommt man ja mit der manuellen Arbeit gar nicht mehr hinterher..

In AC[] werden ja schon die Componten gespeichert, nur verstehe ich nicht, wie ich jetzt im Script auf die Componten weiter zugreifen kann... da komme ich einfach nicht weiter..

AC[1].GetType().GetFields().GetValue(2)  liest ja schonmal den Variablennamen aus der in der Componte gespeichert ist, aber wie komme jetzt an den Inhalt der Variable, ich hab da jetzt echt ziemlich viel durchprobiert, aber klappt einfach nichts .__. kann doch nicht so schwer sein +.+

AC[1].GetType().GetFields().GetValue(2) //ergibt "System.String wert"

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du scheinst etwas ganz Grundlegend falsch zu machen.

Was genau versuchst du zu erreichen? Wie ich bereits erklärt habe kannst du nicht einfach Variablen von unbekannten Componenten auslesen.

Das lässt der Compiler schon garnicht zu und selbst wenn würde es zur Laufzeit evtl. zu haufenweise Fehlern führen. Denn du kannst schlecht zur Laufzeit wissen was in der Component nun genau drin steht und genau das versucht die Fehlermeldung zu verhindern.

 

Was versuchst du zu erreichen? Ich meine was willst du beim "auslesen" automatisieren? Wenn du es nicht erklären kannst gib uns ein Beispiel was du machen willst und was genau dich dann daran stört.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Na ich habe Werte in Variablen die sollen im Game angezeigt werden :)

Ich hab ne "Formschön " Class geschrieben die mir die Werte formschön anzeigt. Aktuell ist es so, dass ich nur Zugriff auf die Werte bekomme, wenn ich in der "Formschön" Class angebe aus welchen Komponenten ich Werte auslesen möchte, da alle Variablen je nachdem welche Komponente ich habe verschiedene Variablennamen haben, muss ich vorher prüfen welche Komponentenwerte ich anzeigen will und dementsprechend muss ich für jede Komponente die einzelnen Variablennamen aufschreiben, das sieht dann so aus. GetComponent<Bildung>().IQ; GetComponent<Bildung>().Job; GetComponent<Bildung>().Ausbildung; etc. als Beispiel...

Ich hab mehrere Componenten wie Bildung und jede Componente hat unzählige Variablen ...

... ich kann wohl kaum alle Componenten + Variablen in die Formschön Class schreiben, kann ich schon, aber da wollte ich halt gerne die Zeit sparen :o, denn es werden wirklich sehr viele Componenten...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich verstehe noch nicht zu 100% was du möchtest. Klar irgendwelche Werte schön anzeigen. Aber warum genau? Willst du dem Spieler die Werte anzeigen oder einfach nur für dich?

Weil wenn du dem Spieler die Werte anzeigen willst, dann wirst du sowieso niemals alle Variablen benötigen die eine Component hat.

Wenn du die Werte sehen möchtest hast du bereits den Unity Inspector.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die Frage ist wirklich, was du da machst:
- bei einer Klassen-Componente kann man viel einfacher über die Klasse zugreifen
- bei einer Editorklasse kann man einen Inspektor schreiben, um die Werte im Editor visuell ansprechend darzustellen
- Reflection macht eigentlich nur Sinn, wenn man keinen Zugriff auf die Basisklassen hat (private oder protected fields), weil man z.b. eine Thirdpartykomponente mit einer DLL verwendet oder wenn man die Unity-eigenen Components allgemein auslesen möchte, aber selbst hier ist es besser den konkreten Typ zu verwenden und lieber alle Unity-Componenten abzuprüfen.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 6 Minuten schrieb Sascha:

Ja ne, da denk mal lieber nochmal drüber nach.

Sehe ich genauso, damit machst du jedes Frame folgendes:

// Für jedes Frame:
foreach (Objekt in alleObjektederSzene)
{
  foreach (Komponente in Objekt.alleKomponentenObjekt)
  {
  }
}

und damit kannst du gleich, warte jedes Frame 0.1 Sekunde in deinen Quellcode einbauen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 1 Minute schrieb Zer0Cool:

Sehe ich genauso, damit machst du jedes Frame folgendes:


// Für jedes Frame:
foreach (Objekt in alleObjektederSzene)
{
  foreach (Komponente in Objekt.alleKomponentenObjekt)
  {
  }
}

und damit kannst du gleich, warte jedes Frame 0.1 Sekunde in deinen Quellcode einbauen.

Ne, das passiert nur wenn ein Panel aufgerufen wird und die Werte aktualisiert werden müssen..

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 4 Minuten schrieb Euphorikus:

Ne, das passiert nur wenn ein Panel aufgerufen wird und die Werte aktualisiert werden müssen..

Unity hat interne Methoden und Events die du gar nicht mitbekommst. Wie gesagt, performancetechnisch "kritisch" (hängt davon ab was sonst noch in der Szene ist). Je weniger Skripte (bzw. Componenten) an den Objekten in der Szene hängen umso besser.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gerade eben schrieb Zer0Cool:

Unity hat interne Methoden und Events die du gar nicht mitbekommst.

Aktuell werden die Werte nur beim Start() abgerufen, später dann wenn ich es will, wüsste nicht warum Unity jetzt alle paar Sekunden etwas refreshen sollte, wenn ich es nicht explizit kommandiert habe. Wenn GameObjects deaktiviert sind macht sich Unity doch keine Mühe da irgendwas zu berechnen, dass wäre ja Schwachsinn, oder liege ich falsch..

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das was du machen willst ist weitaus komplizierter als man anfangs vermuten mag. Es ist ziemlich einfach Variablen aus Klassen auszulesen und diese Auszugeben.

Jetzt will man diese Werte ja nicht einfach nur augeben sondern "hübsch" anzeigen. Das bedeutet nicht einfach nur die Werte anzeigen sondern z.B soll eine Zahl die Lebenspunkte darstellen, also erstellt man einen Balken dessen Länge abhängig von den Lebenspunkten ist. Sowas kann man aber nicht einfach so generalisieren, dafür nutzt man schon recht komplexe Systeme. Und meistens können diese Systeme auch noch viele Tücken haben. Vorallem wenn es daran geht das die GUI immer aktuell gehalten werden soll. Wenn du unzählige Variablen hast ist es extremst unperformant jeden Frame die Grafiken neu zu generieren weil evtl. sich ein Wert geändert hat. Viel sauberer wäre es wenn sich die GUI nur ändert wenn sich ein Wert ändert. Etc etc etc.

Dafür gibt es dann in Unity schon solche Tools: https://www.assetstore.unity3d.com/en/#!/content/49004

Davon rate ich dir aber ab. Du bist noch ganz am Anfang und ich bezweifle das dir sowas wirklich weiterhelfen wird. Klar ist es toll Sachen dynamisch zu machen, aber das was du willst ist viel zu viel. Du wirst niemals soviele Variablen haben das du nicht in der Lage sein wirst die von Hand anzuzeigen.

Dazu kommt wenn du deine GUI gut designst dann wird das auch immer weniger zum Problem. Einfaches Beispiel:

Anstatt eine Riesenklasse zu schreiben die alles darstellt was du irgendwo in deiner GUI rumfliegen hast, schreib kleine Klassen. Diese kleinen Klassen bekommt dann eben die Variable/Variablen die du anzeigen möchtest und die GUI Elemente bei denne es angezeigt werden soll.

public class CustomerDisplay
{
	public Label NameLabel;
	public Label IncomeLabel;
	public Customer Data;

	public void OnUpdate()
	{
		NameLabel.text = Data.name;
		IncomeLabel.text = Data.income;
	}
}

Sowas kann man nun immer wiederverwenden, wenn du irgendwann mal einen Customer anzeigen willst kannst du diese Klasse wieder nutzen. Und du kannst mir nicht erzählen das du jemals soviele Variablen haben wirst das es zuviel Aufwand ist mal schnell eine Klasse zu schreiben die das ganze 1 zu 1 mappt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn du das ganze GO deaktivierst, dann denke ich nicht.

Falls du immer noch an der Reflektion bastelst, hier ist eine Methode die ich mal ausgegraben hatte. Sie kopiert eine Komponente, da sollte eigentlich alles drin sein, was du brauchst:

    // Thanks to:
    // http://answers.unity3d.com/questions/530178/how-to-get-a-component-from-an-object-and-add-it-t.html
    // Usage: var copy = GetCopyOf(someOtherComponent);
    public Component GetCopyOf(Component other)
    {
        Component comp = new Component();
        Type type = comp.GetType();
        if (type != other.GetType()) return null; // type mis-match
        BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;
        PropertyInfo[] pinfos = type.GetProperties(flags);
        foreach (var pinfo in pinfos)
        {
            if (pinfo.CanWrite)
            {
                try
                {
                    pinfo.SetValue(comp, pinfo.GetValue(other, null), null);
                }
                catch { } // In case of NotImplementedException being thrown. For some reason specifying that exception didn't seem to catch it, so I didn't catch anything specific.
            }
        }
        FieldInfo[] finfos = type.GetFields(flags);
        foreach (var finfo in finfos)
        {
            finfo.SetValue(comp, finfo.GetValue(other));
        }
        return comp as Component;
    }

Ansonsten bekommst du so ALLE Componenten eines GO:

public GameObject toInspect;
private Component[] allComponents;

// Alle Komponenten des GameObjekts toInspect auslesen
allComponents = toInspect.GetComponents<Component>();


 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...