Jump to content
Unity Insider Forum

Unity Einsteiger: Label Problem


risegewumps

Recommended Posts

Hallo erstmal.

 

Ich habe heute angefangen mich mit Unity auseinander zu setzen und stoße direkt auf Probleme:

Ich würde gerne ein Label durch einen ButtonClick verändern, das Problem ist nur, das ich im Internet keine Vernünftige Einweisung gefunden haben, wie ich einzelne Werte eines Labelojects ansprechen kann.

 

 

Mein Code (mit erstem Ansatz):

public class getSome : MonoBehaviour {

 

//Usethisfor initialization

void Start () {

}

public GameObject lab = (GameObject)Instantiate(Cash);

//Updateiscalledonceper frame

void Update () {

int x = lab.text;

x = x + 1;

lab.text = x;

}

}

 

Die Hirachie:

Main Camera

.Canvas

..Button

...Text

..Cash

 

 

Das Label mit main. oder MainCamera. anzusprechen war ebenfalls erfolglos.

Unter Python hätte ich das so gelöst:

 x = root.label["text"]
 x += 1
 root.label["text"] = x

Vielen Dank schonmal im Vorraus!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du hast da das Wort "Cash" in deinem Code, das nirgendwo definiert wird. Außerdem benutzt du Instantiate, was für das Erzeugen neuer Objekte während der Laufzeit ist. Ist das Absicht?

 

Dein Code zeigt in jedem Fall eine sehr große Menge Fehler auf, die von der Unkenntnis programmiertechnischer Dinge im Allgemeinen und/oder C# herrühren. Ist also kein Unity-spezifisches Problem, das du hast.

 

Zuerst einmal: C# ist typstark. Der Inhalt eines Labels (wobei du vermutlich UnityEngine.UI.Text meinst) ist ein String und mit einem String kann man nicht rechnen. Python ist sowas ziemlich egal, in C# geht so etwas nicht.

Wenn du die Zahl aus dem String des Texts auslesen willst musst du explizit parsen, und das willst du nicht, versprochen ;)

Stattdessen möchtest du eine Zahlvariable definieren, die die Punktzahl hält und bei jeder Änderung ihres Wertes diesen in den String reinschieben.

 

Als nächstes: Du willst, auch wenn das manchmal nicht vom Compiler beanstandet wird, keinen ausführbaren Code außerhalb von Methoden haben. Auch hier ist das wieder ein Unterschied zu Python, welches in vielerlei Hinsicht sehr liberal ist.

Du willst innerhalb der Klassendefinition nur Feld-Deklarationen und Methoden haben. Feld-Initialisierungen sind in der Regel nur dann okay, wenn es ein primitiver Datentyp oder eine simple Objekterzeugung mit new ist.

Sowas wie

public GameObject lab = (GameObject)Instantiate(Cash);

ist also zu vermeiden.

 

Nun aber mal zum inhaltlichen. Du möchtest per Klick auf einen Button etwas tun. Dafür ist die Update-Methode schon einmal ungeeignet, denn sie wird jeden Frame ausgeführt. Stattdessen möchtest du eine beliebig benannte, öffentliche Methode deklarieren:

public class TextUpdater : MonoBehaviour
{
 public void AddOne()
 {
   Debug.Log("Button gedrückt");
 }
}

Kopiere alleine diesen Code und ziehe die Komponente auf ein beliebiges GameObject (das wird sich später ändern).

Dann markierst du deinen Button und im "On Click()"-Feld im Inspektor des Buttons klickst du auf das + unten rechts.

Dann ziehst du das GameObject, auf dem das neue Skript sitzt, in das GameObject-Feld auf der linken Seite des neuen Eintrags.

Jetzt noch in der Auswahlliste auswählen: "TextUpdater.AddOne()".

 

Wenn du das Spiel jetzt startest und den Button drückst, schreibt er entsprechend die Nachricht aus dem Code in die Konsole.

 

Damit haben wir jetzt eine Codesektion, die durch den Button ausgeführt wird.

Jetzt brauchen wir eine Referenz auf die Textkomponente, also dein Label.

Ich bin mir ziemlich sicher, dass du das Label nicht wirklich per Code erzeugen willst, sondern es einfach im Editor hinzufügen willst (GameObject/Create/UI/Text).

 

Um jetzt an die Referenz des in der Szene bereits bestehenden UI-Texts ranzukommen, gibt es mehrere Möglichkeiten, die je nach Situation zu preferieren sind. In deinem Fall würde ich dazu raten, den TextUpdater auf dasselbe GameObject zu legen wie die Text-Komponente.

Dann erweiterst/änderst du deinen TextUpdater:

using UnityEngine.UI; // nicht vergessen

public class TextUpdater : MonoBehaviour
{
 private Text textComponent;

 void Awake()
 {
   textComponent = GetComponent<Text>();
 }

 public void AddOne()
 {
   textComponent.text = "Es wurde gedrückt!";
 }
}

GetComponent sucht auf dem GameObject, auf dem die Skriptkomponente liegt, nach einer anderen Komponente vom gegebenen Typ; also in diesem Fall der "Text"-Komponente. Die dadurch erhaltene Referenz wird einmalig in einer Variable gespeichert und in AddOne wird jedes Mal darauf zugegriffen.

 

Eingangs hatte ich schon erwähnt, dass es nicht sinnvoll ist, den Text der Text-Komponente auszulesen um +1 zu rechnen, daher muss jetzt die besagte int-Variable her:

using UnityEngine.UI; // nicht vergessen

public class TextUpdater : MonoBehaviour
{
 private Text textComponent;
 private int score = 0; // Hier ist Initialisierung okay

 void Awake()
 {
   textComponent = GetComponent<Text>();
 }

 public void AddOne()
 {
   score++;
   textComponent.text = score + "";
 }
}

und das ist schon alles an Code, was du brauchst.

 

Da C# und Python in sehr vielen Dingen grundverschieden sind, empfehle ich, nicht zu versuchen, Python-Code zu schreiben, sondern alles, was du weißt, zeitweise über Bord zu werfen und dich an C#-Anfängertutorials zu setzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Sooo... also erstmal vielen Dank für die schnelle Antwort!

Durch den Ansatz konnte ich es zum Laufen bringen, indem ich das Script auf mein Label (Cash) gezogen habe.

Schaut nun insgesamt so aus:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TextUpdater : MonoBehaviour
{
private Text textComponent;
public static int score = 0;

void Awake()
{
	textComponent = GetComponent<text>();

}

public void AddOne()
{
	score++;
	textComponent.text = score + "";
}
}

 

Allerdings war das nicht die Antwort auf meine Frage :lol:

Eigentlich wollte ich wissen, wie in Unity Objekte angesprochen werden können, ausserhalb des Objekets.

Da google nichts Einheitliches ausgeworfen hat und falls jemand mit meinem Problem hier landet, einmal meine Lösung für Buttons:

public GameObject myButton;
void Awake()
{
myButton = GameObject.Find("name des Buttons");
}

Um den Button dann zu verändern benutzt man:

myButton.GetComponent<Button>. "zu änderndes Feld vom Button" = [WERT]

 

Und vielen Dank für die Hilfe! :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die Performance ist mir bei solchen Aussagen egal.

GameObject.Find setzt voraus, dass der Name eines GameObjects nicht nur ungeändert bleibt, sondern auch noch einmalig.

Das größte Problem an letzterem ist, dass man nicht einmal weiß, welche GameObjects auf diese Weise gesucht werden. Wenn man also nicht gerade zufällig alle GameObject.Find-Aufrufe in der gesamten Codebase im Kopf hat, kann man prinzipiell kein GameObject benennen, es sei denn, man stellt sicher, dass kein anderes so heißt.

Und wenn du dann einen durch die Funktion bedingten Fehler hast, ist der auch noch nicht vernünftig zu debuggen.

GameObject-Namen sind einfach eine ganz schlechte Idee zum Identifizieren von Objekten.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Also ich bin da auch auf Hrungdaks Seite. So eine Aussage wie "sollte man nie nutzen" ist Blödsinn!

Unity hat die Möglichkeit geschaffen nach dem Namen eines Objektes zu suchen und es funktioniert.

Wie ein Objekt heißt oder heißen wird, ist bekannt und kann sogar per Script eingestellt werden, es also ist das durchaus eine praktikable Möglichkeit ein Objekt zu finden. Natürlich gibt es Konstellationen wo es es bessere Möglichkeiten gibt, das bedeutet aber noch lange nicht, dass dieser Befehl ein "Bug-Generierer" ist.

Außerdem steht in der Überschrift: "Unity Einsteiger"! Der erfahrene Programmierer darf sich also etwas zurücklehnen und den Neuling erstmal mit den "nicht bestenen" Möglichkeiten arbeiten lassen. Hauptsache er kommt dabei an sein Ziel! Auch wenn es nicht der schönste oder robustestet Code ist.

Er wird später selber merken, dass es auch andere Möglichkeiten gibt. Und da braucht man noch nicht einmal ein [serializeField] für um ne private Variable im Inspector zu bestücken, mann kann sie sogar Public machen und keiner merkt den Unterschied.

 

Just my 2 Cents!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das ist halt genau die Art von Einstellung, wegen der es so viel schlechten Code auf der Welt gibt :)

Ich weiß ja nicht, wie oft ihr euch schon durch Code anderer Leute wühlen musstet, aber man merkt schnell, dass "ist ja nicht so schlimm" eine weit verbreitete Denkweise ist, die wesentlich öfter Frust, finanziellen Schaden und eingestampfte Projekte verursacht als es Anfängern das Leben leichter macht.

 

Es "funktioniert" auch in Unity keine Szene zu bauen sondern alles per Skript zu erzeugen - oder eine Gottklasse zu schreiben.

Und trotzdem raten wir davon ab. Mit dem Grund, dass es Nachteile hat, so zu arbeiten.

 

Leute tendieren halt auch dazu, mit der erstbesten gefundenen Möglichkeit immer weiter zu arbeiten, anstatt Optimierungen zu suchen. Ich meine, wie oft haben wir hier schon Code gesehen, wo einer 800 Zeilen Code hatte, der aus immer wieder denselben vier Zeilen bestand, weil ihm am Anfang keiner gesagt hat, dass es auch sowas wie Arrays gibt? Und das sind noch die Leute, die hier Hilfe gesucht haben. Gibt auch die Leute, die das klasse finden so. Hab dann an der Uni als Übungsbetreuer gerne Mal den Spruch gehört "wieso, klappt doch" oder "hab ich schon immer so gemacht und nie Probleme gehabt" und dann mögen die Leute immer nicht umdenken.

 

Inhaltlich kann ich nur sagen, dass es für GameObject.Find in jeder denkbaren Situation einen Ersatz gibt, der weniger nachteilhaft ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Es gibt einen Unterschied zwischen einem "Rat" etwas anders zu machen und den Statement dass dies oder jenes "nie" zu nutzen.

Das Eine ist ein Erklären warum man etwas anders machen sollte, das andere ist ein Aufzwängen seines eigenen Programmierstils.

 

Der Unterschied zwischen einer Informatik Uni und diesem Forum ist, dass hier Leute versammelt sind, die mit Unity vertraut werden wollen und ganz von vorne anfangen. Und jeder kleine Erfolg den sie verstehen hilft um das Hobby weiter zu betreiben. Es geht nicht um das totale Verständnis der Programmierung mit all seinen Facetten.

Hier geht es auch nicht um finanziellen Schaden.

Die Leute, die richtig programmieren können, fragen hier nicht! Ne, hier sind die Laien.

Dass die Leute im "allgemeinen" dazu tendieren diese vermeindlich falschen Dinge weiter zu nutzen, sehe ich nicht.

Bis jetzt haben sich bestimmt 90% der hier anwesenden verbessert. Die 10% die das nicht gemacht haben und immer wieder das Selbe fragen und machen, haben kein Verständnis für den Code. Für die ist das zu abstrakt und die werden so oder so nicht weiter kommen.

 

In diesem Forum, und ich bin ja quasi seit anbeginn dabei, merkt man ganz deutlich, dass alle alten Hasen inzwischen auf einem ganz anderen Niveau sind. Und das eigene Verständnis wird als Basis genutzt und den Neuen gerne mal vor den Latz geknallt.

Auch du, Sascha, antwortest heute ganz anders als vor 5 Jahren. Du wirfst neuerdings ständig mit SerailiseField um dich. Egal ob der Laie das versteht oder nicht. Und das machst du ohne Notwendigkeit sondern nur weil es inzwischen dein Stil ist und du das als normal ansiehst.

Das ist das Einzige worauf ich hinaus will! Ich verteidige hier nicht das GameObject.Find() , ich sehe aber auch nicht ein dass es "niemals" genutzt werden sollte. Denn das ist, wie oben schon geschrieben, "Blödsinn", weil es nicht pauschal gesagt werden kann!

Das ist genau wie mit dem Array. Bei 2 oder 3 Variablen des gleichen Typs kann man natürlich auch ein Array bilden, muss man aber nicht. Es ist immer noch lesbar und von den Anzahl der Zeilen beim Auswerten gibt es auch keine Vor- oder Nachteile.

Es ist nämlich immer auch ne Frage der Dosis!

 

Wenn ich also zu gewissen Programmier-Aussagen etwas schreibe, dann braucht ihr mir nicht erklären warum dies oder das besser ist, denn ich weiß es eh. Ihr sollt euch überlegen ob die Aussagen einen Laien gegenüber gerechtfertigt und nicht vielleicht ein bisschen überheblich sind. Empathie ist das Stichwort!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Natürlich versuche ich bei jedem User auf's Neue, einzuschätzen, wie viel ich ihm zumuten kann.

Im Threadtitel steht zwar "Unity Einsteiger", aber risegewumps ist schon eine Weile mit Python unterwegs, eine der Sprachen mit den härtesten Vorgaben in Sachen Codestil. Schreib' mal in einem Pythonforum etwas, das nicht "pythonic" ist - das härtet ab :)

Und SerializeField überlege ich jedes Mal auf's neue. Wenn ich das Gefühl habe, der User ist noch nicht so weit, dann gehe ich auch immer wieder zu public zurück.

 

Ich weiß allerdings sowieso nicht, warum dich das so stört, wenn User einzelne Dinge nicht verstehen. SerializeField drüber und man kann es in Unity setzen. Was das ist, wieso das geht - ist doch egal. Denn jetzt kommt's: Ich würde wirklich nicht behaupten wollen, dass die blutigen Programmieranfänger verstehen, was public wirklich macht. Wo ist also der Unterschied zwischen SerializeField und public? Und es gibt da noch den feinen Unterschied, dass ich niemandem ein public ankreide. Habe ich nie, werde ich nie.

Wenn aber jemand serialisierte Felder neu kennenlernt, dann nehme ich auch gerne mal ein SerializeField, weil's beides erst einmal unverständlich für Anfänger ist.

 

Zu deiner Argumentation, dass man das bei GameObject.Find nicht pauschal sagen kann: Doch! Denn es ist eben nicht derselbe Fall wie beim Array. Beim Array gibt es Vor- und Nachteile, bei GameObject.Find nicht. Es sei denn, du zählst "dann muss ich weniger erklären".

Denn wie ich schon sagte, es gibt einfach keinen Fall, in dem man Find nicht mit einer vorteilhafteren Variante ersetzen kann. Und wenn du mir das nicht glaubst, darfst du mich gerne mit Fällen bombardieren, von denen du meinst, es wäre nicht so :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du hörst mir nicht zu!

Du brauchst mir nicht erklären dass man ohne Find auskommt.

Es geht nicht um mich und es geht auch nicht darum dass es ohne Find geht! Es geht darum, dass das deine Meinung ist und du sie aufzwängst.

Es geht auch nicht um das SerializeField an sich, sondern dass du deinen Stil geändert hast und dies jetzt so weiter gibst. Da ist nichts schlimmes dabei. Du sollst dich nur hinterfragen warum du das jetzt seit ein paar Monaten tust und nicht bei der einfachen (für Anfänger) Variante geblieben bist. Und du sollst das nur deswegen tun um zu erkennen das dein Horizon inzwischen viel weiter reicht als damals und du dein Wissen teilweise als Grundwissen siehst. Das ist aber kein Grundwissen!

 

Das soll's von mir zu dem Thema aber auch gewesen sein. Mir ist das Ganze nicht wichtig genug um immer wieder das gleiche zu antworten.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Bin jetzt mit Unity nicht so vertraut, aber was ist denn bitte das Problem daran eine eindeutige Namesgebung für Objekte einzuhalten?

Wär cool, wenn dus auch erklärt hättest, warum die Funktion soo unbrauchbar ist.

Hab vorher mit Kivy gearbeitet und da war die allgemeine Objekterkennung durch den Objektnamen Standart.

Ausserdem warum gibt es die Funktion, wenn sie NIE benutzt werden sollte? :`-)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die Performance ist mir bei solchen Aussagen egal.

GameObject.Find setzt voraus, dass der Name eines GameObjects nicht nur ungeändert bleibt, sondern auch noch einmalig.

Das größte Problem an letzterem ist, dass man nicht einmal weiß, welche GameObjects auf diese Weise gesucht werden. Wenn man also nicht gerade zufällig alle GameObject.Find-Aufrufe in der gesamten Codebase im Kopf hat, kann man prinzipiell kein GameObject benennen, es sei denn, man stellt sicher, dass kein anderes so heißt.

Und wenn du dann einen durch die Funktion bedingten Fehler hast, ist der auch noch nicht vernünftig zu debuggen.

GameObject-Namen sind einfach eine ganz schlechte Idee zum Identifizieren von Objekten.

 

Ausserdem warum gibt es die Funktion, wenn sie NIE benutzt werden sollte? :`-)

Weil Leute, die bei Unity Technologies arbeiten, keine Götter sind, die über jeden Zweifel erhaben sind. Wir hatten hier schon ein Thema im Forum, wo ein User hier aufgedeckt hat, dass simpelste Funktionen wie Vector3.Distance imperformanter implementiert sind als nötig. "Unity hat das" ist nun wirklich kein gutes Argument :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...