Jump to content
Unity Insider Forum

Sascha

Administrators
  • Content Count

    11,830
  • Joined

  • Last visited

  • Days Won

    597

Sascha last won the day on May 27

Sascha had the most liked content!

Community Reputation

2,409 Excellent

6 Followers

About Sascha

  • Rank
    Community Manager
  • Birthday 08/13/1990

Contact Methods

  • Website URL
    http://13pixels.de

Profile Information

  • Gender
    Male
  • Location
    Hamburg
  • Interests
    Programmierung

Recent Profile Visitors

47,039 profile views
  1. Gibt es ein Problem damit, einfach per Hand, evtl. mit Snapping, die Collider in der Szene zu platzieren?
  2. Für solche Sachen sind Events ganz gut. Du kannst z.B. in dein Achievement-SO ein Event einbauen: public event Action onChangeUnlockStatus; Und dann dieses Event im richtigen Moment auslösen: public void Unlock() { unlocked = true; onChangeUnlockStatus?.Invoke(); } Objekte, die auf das Event reagieren sollen, tragen sich dort ein, sobald sie zum reagieren bereit sind: public void Initialize(Achievement achievement) { titleText.text = achievement.title; descriptionText.text = achievement.description; achievement.onChangeUnlockStatus += OnChangeUnlockStatus; } private void OnChangeUnlockStatus() { if (achievement.unlocked) { // change image or background color } } Und sie tragen sich wieder aus, wenn sie zerstört werden: achievement.onChangeUnlockStatus -= OnChangeUnlockStatus; Der Ablauf ist also: Anzeige-GO wird initialisiert (setzt Text und Beschreibung usw.) und trägt eine Reaktion (Methode "OnChangeUnlockStatus") beim Event ein. Achievement wird freigeschaltet Alle im Event eingetragenen Reaktionen werden ausgelöst Anzeige-GO verändert sich
  3. Ah ja, genau so macht man das. Nach dem Instantiate die Instanz initialisieren. Siehe auch hier.
  4. Nö, das ist auch in Ordnung. Es bietet sich dann aber an, die Initialisierung der Prefab-Instanzen auf eine möglichst kleine Schnittstelle zu reduzieren. Anstatt also im onClick-Event des Buttons Dinge einzutragen, bei denen nur der Wert unterschiedlich ist, lieber die für die Prefabs geschriebene Komponente benutzen: private int moneyAmount; public Text label; public void Initialize(int moneyAmount) { this.moneyAmount = moneyAmount; } private void Start() { label.text = "Du hast " + moneyAmount + " gewonnen!"; } public void Collect() { Debug.Log(moneyAmount + " eingesammelt"); } und dann: var instance = Instantiate(moneyWinningPrefab); instance.Initialize(someAmountOfMoney);
  5. Musst halt eins bauen! So als Beispiel: public int moneyAmount; public void Collect() { Debug.Log(moneyAmount + " eingesammelt"); } Das packst du auf deinen Prefab, bzw irgendwo da rein (auf den Button z.B.) und dann löst du einfach Collect() mit dem Button aus. Jetzt kannst du mehrere Prefabs machen die verschiedene Dinge tun. Wenn du da mehr machen willst, oder evtl nur ein Prefab haben willst, ist es besser, dieses Script auf dem Prefab zu erweitern, als andere Scripts in den Prefabs rumpfuschen zu lassen. private int moneyAmount; public Text label; private void Start() { moneyAmount = Random.Range(10, 100); label.text = "Du hast " + moneyAmount + " gewonnen!"; } public void Collect() { Debug.Log(moneyAmount + " eingesammelt"); } Wenn du das auf dein Prefab tust, den Text reinziehst und den Button Collect() auslösen lässt, dann bist du fertig! Du kannst das Prefab sogar ohne dein Spawn-Script benutzen. Einfach mal zum Testen in die laufende Szene reinziehen.
  6. Da gibt es zwei Wege. Zum einen kannst du im Script, welches ja an dein Prefab gebunden ist, Werte angeben. Hier wäre z.B. ein Script zum Laden einer anderen Szene: public string sceneName; public void LoadScene() { SceneManager.LoadScene(sceneName); } Jetzt kann man die Komponente in die Nähe des Buttons (oder halt direkt drauf) ziehen und den Namen der zu ladenden Szene eingeben. Das funktioniert, weil du ja nicht die Methode der Klasse aufrufst, sondern der Instanz. Wenn du zwei Buttons hast, der eine zeigt auf das eine GameObject mit diesem Script drauf und der zweite zeigt auf ein anderes GO mit diesem Script, dann wird auch wirklich nur von der Komponenten-Instanz die Methode aufgerufen, auf die da gezeigt wird. Die beiden Komponenten-Instanzen können daher unterschiedliche Werte haben (z.B. für sceneName), und diese Werte werden dann bei der Ausführung berücksichtigt. Alternativ kann man der Methode einen Parameter geben: public void LoadScene(string sceneName) und dann den Wert dieses Parameters beim Button einstellen. Die erste Variante ist vielseitiger, da der Button nur einen Parameter übergeben kann, man aber beliebig viele Felder in das Script packen kann. Die erste Variante erlaubt dir, dass du eine Methode in einem Script ausführen kannst, das eben nicht schon vorher weiß, welchen Wert es haben muss. Man denke z.B. an eine Light-Komponente. Die hat ja auch kein Feld "Farbe, die ich annehme, wenn ein Button gedrückt wird". Für solche Fälle kann man dann sagen Light.color und eine Farbe direkt im Inspektor des Buttons einstellen.
  7. Du setzt halt die Variable auf 0?
  8. Du kannst entweder selber Reaktionen in UnityEvents (wie onClick) einfügen oder eine Methode auf einem Script innerhalb des Prefabs verlinken. Selber hinzufügen: var button = Instantiate(buttonPrefab); button.onClick.AddListener(() => { TuZeug(100); }); Und beim fest Verlinken, was meistens die bessere Variante ist, baust du halt ein Script mit einer Methode, das landet irgendwo auf dem Prefab, der Button löst die MEthode auf und was dann in der Methode passiert, kannst du dann da implementieren.
  9. Die sind generell überall da eine Überlegung wert, wo du Daten hast, die nicht speziell an bestimmte Objekte gebunden sind. Damit sind Items, Achievements und Charaktereigenschaften sehr gute Beispiele für Dinge, bei denen man gut ScriptableObjects benutzen kann. Naja, du ziehst das Achievement-SO aus den Assets in deine Komponente, nachdem du ein Feld dafür gemacht hast: [SerializeField] private Achievement achievement; bzw. [SerializeField] private Achievement[] achievements; für mehrere. Dann greifst du halt drauf zu: someTextComponent.text = achievement.title;
  10. Maus und Stick funktionieren unterschiedlich. Bei der Maus willst du nicht Time.deltaTime draufrechnen, beim Stick schon. Daher willst du eher nicht die "Mouse X"-Achse um einen Stick erweitern, sondern eine neue Achse erstellen, wo du den Stick des Controllers einbaust. Machst du beim alten Inputsystem im Input Manager in den Project Settings. Soll sich aber wohl auch lohnen, sich mal das neue Input-System anzuschauen.
  11. Sascha

    Managerklassen

    Jau, du kannst damit globale Events (in schick!) machen. Actions sind quasi Methoden, die können Parameter haben. Du kannst also das hier machen: public event Action<Player> onPlayerInRange; und dann muss die registrierende Klasse auch eine Methode übergeben, die einen Player-Parameter nimmt: private void DoStuff(Player player) Das Event muss dann auch mit Parameter aufgerufen werden: onPlayerInRange?.Invoke(playerInArea); Was sich super mit der taglosen Variante zur Identifikation kombinieren lässt: private Player playerInArea; private bool playerIsInside => playerInArea != null; private void OnTriggerEnter2D(Collider2D other) { var player = other.GetComponent<Player>(); if (player) { playerInArea = player; } } Du kannst natürlich auch ein Player-Pseudo-Singleton bauen und den Spieler damit global bekanntmachen. Das geht mit Soda auch, in eleganter
  12. Sascha

    Managerklassen

    Ich hab ja auch keine geäußert? Also... ich finde Interfaces auf UnityEngine.Objects ungut (Grund hier) und finde Actions (bzw. Delegate) besser für Reaktionen. Aber die grundsätzliche Idee nennt sich Observer Pattern (oder "Beobachtermuster") und ist völlig gut und üblich, und dagegen hab ich nichts gesagt. Meine ursprüngliche Aussage war "wenn deine Klasse das Wort 'Manager' im Namen hat, dann machst du etwas falsch". Und manchmal ist das Falsche einfach nur die Benennung Damit du ein Bild davon hast, wie das mit Actions aussehen kann: public class TentRangeActions : MonoBehaviour { public event Action onPlayerInRange; public event Action onPlayerOutOfRange; void Update() { if (_isPlayerInRange) { onPlayerInRange?.Invoke(); } else { onPlayerOutOfRange?.Invoke(); } } // collision events here } Und die Komponenten, die vorher das Interface hatten, machen dann so etwas: [RequireComponent(typeof(TentRangeActions))] public class Foo : MonoBehaviour { private TentRangeActions tentRangeActions; private void Awake() { tentRangeActions = GetComponent<TentRangeActions>(); } private void OnEnable() { tentRangeActions.onPlayerInRange += DoStuff; } private void OnDisable() { tentRangeActions.onPlayerInRange -= DoStuff; } private void DoStuff() { // Actual stuff as long as the player is in range } } Jetzt sucht "TendRangeActions", oder wie auch immer man das nennen will, nicht mehr einmalig nach Komponenten, sondern Komponenten melden sich bei dieser Komponente an. Dadurch kannst du zur Laufzeit Komponenten hinzufügen, wegnehmen, deaktivieren oder wieder aktivieren, und es funktioniert alles wie man es erwartet. Die reagierenden Komponenten sind mit [RequireComponent] markiert, sodass klar ist, dass sie ohne die TentRangeActions-Komponente nicht funktionieren. Foo kann sich bei onPlayerInRange anmelden, aber auf onPlayerOutOfRange verzichten. Und am wichtigsten: Kein Interface mehr Was CompareTag angeht: Strings sind doof. Wie im Link erklärt, würde ich immer Komponenten stattdessen empfehlen. Statt if (other.CompareTag("Player")) hast du dann if (other.GetComponent<Player>()) Aber das alles sind nur kleinere Änderungen, die grundlegende Idee ist dieselbe, die du schon hast. Und diese Idee ist absolut gut und richtig. Observer Pattern halt
  13. Sascha

    Managerklassen

    Ich sehe nicht so ganz, was daran eine Managerklasse sein soll. Ein ABCManager ist eine Klasse, die ABCs managed. Du hast also zum Beispiel eine Enemy-Komponente und dann hast du noch einen EnemyManager, der denen sagt, wie sie zu funktionieren haben. Und ohne diesen Manager funktioniert die Enemy-Klasse dann nicht mehr. Warum heißt deine Klasse "TentManager" und nicht einfach "Tent"? Meine Kritik ist zum Einen an der Benennung, weil das, was ein Manager tut komplett obskur ist, und zum Anderen an der Architektur dahinter (siehe EnemyManager). Wenn du aber einfach nur eine ganz normale Komponente hast und da "Manager" hinter den Namen schreibst, hat das mit meiner Kritik nichts zu tun. Wenn Unity morgen die "Light"-Komponente in "LightManager" umbenennt, dann wäre das zwar irgendwie bescheuert, aber das macht die Klasse an sich ja nicht schlechter. Nur der Suffix, der ergibt irgendwie keinen Sinn
  14. Oh, nicht unbedingt. Das war ein Beispiel für MVC, aber das muss nicht so sein. Gerade bei UI kannst du das auch in kleinerem Rahmen verbauen. Es gibt z.B. Frameworks, wo man sein UI mit XML oder HTML+CSS spezifizieren kann und dann wird das in Unity angezeigt. Damit ist dann die Spezifikation des UIs nicht mehr in Unitys eigene Semantik eingebettet, und du kannst die Elemente auch von etwas anderem anzeigen lassen (oder einfach etwas an der Anzeige ändern), ohne deine Layouts oder dein Welcher-Button-macht-was zu opfern.
  15. Joa, wenn du da Lust drauf hast, gerne. Dann aber in einem neuen Thread, hier geht's ja eigentlich um MVC.
×
×
  • Create New...