Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Bewährte Praktiken für Engine-Entwickler

Einführung

Godot hat eine große Anzahl von Nutzern, die einen Beitrag leisten können, weil das Projekt selbst hauptsächlich auf Nutzer ausgerichtet ist, die programmieren können. Allerdings haben nicht alle von ihnen das gleiche Maß an Erfahrung in der Arbeit an großen Projekten oder in der Softwareentwicklung, was zu häufigen Missverständnissen und schlechten Praktiken beim Beitragen von Code zu dem Projekt führen kann.

Sprache

Dieses Dokument soll eine Liste bewährter Praktiken zur Verfügung stellen, an die sich die Mitwirkenden halten können, und außerdem eine Sprache schaffen, mit der sie sich auf häufige Problemsituationen beim Einreichen ihrer Beiträge beziehen können.

Obwohl eine allgemeine Liste von guten Praktiken für die Softwareentwicklung nützlich wäre, werden wir uns auf die Situationen konzentrieren, die in unserem Projekt am häufigsten vorkommen.

Beiträge werden meistens als Bugfixes, Verbesserungen oder neue Features eingestuft. Um diesen Gedanken zu abstrahieren, werden wir sie Lösungen nennen, weil man immer versucht, damit etwas zu lösen, das als Problem beschrieben werden kann.

Bewährte Praktiken

#1: Das Problem steht immer an erster Stelle

Viele Mitwirkende sind äußerst kreativ und haben einfach Spaß daran, abstrakte Datenstrukturen zu entwerfen, schöne Benutzeroberflächen zu gestalten, oder sie lieben einfach das Programmieren. Was auch immer der Fall sein mag, sie kommen mit coolen Ideen, die manchmal echte Probleme lösen, manchmal aber auch nicht.

../../_images/best_practices1.png

Letztere werden gewöhnlich bezeichnet als Lösungen, die nach einem Problem suchen. In einer idealen Welt wären sie nicht schädlich, aber in der Realität kostet es Zeit, Code zu schreiben, er nimmt Speicherplatz in Anspruch und muss gewartet werden, sobald er existiert. In der Softwareentwicklung ist es immer eine gute Praxis, alles Unnötige zu vermeiden.

#2: Um das Problem zu lösen, muss es überhaupt existieren

Dies ist eine Abwandlung der bisherigen Praxis. Es ist keine gute Idee, etwas Unnötiges hinzuzufügen, aber woran erkennt man, was notwendig ist und was nicht?

../../_images/best_practices2.png

Die Antwort auf diese Frage lautet, dass das Problem existieren muss, bevor es tatsächlich gelöst werden kann. Es darf keine Spekulation oder ein Glaube sein. Der Benutzer muss die Software wie vorgesehen verwenden, um etwas zu erstellen, das er haben möchte. Dabei kann der Benutzer auf ein Problem stoßen, das eine Lösung braucht, damit er weiterkommt oder eine bessere Produktivität erreicht. In diesem Fall wird eine Lösung benötigt.

Der Glaube, dass Probleme in der Zukunft auftreten könnten und dass die Software zum Zeitpunkt dieses Auftretens in der Lage sein muss, sie zu lösen, wird als "Future Proofing" bezeichnet und ist durch folgende Gedankengänge gekennzeichnet:

  • Ich denke, es wäre nützlich für Benutzer, ...

  • Ich denke, Benutzer bräuchten irgendwann ...

Dies wird im Allgemeinen als schlechte Angewohnheit angesehen, da der Versuch, Probleme zu lösen, die derzeit nicht wirklich existieren, sehr oft zu Code führt, der geschrieben, aber nie verwendet wird oder zu Code, dessen Verwendung und Wartung erheblich komplexer ist als notwendig.

#3: Das Problem muss komplex sein oder häufig auftreten

Software wird entwickelt, um Probleme zu lösen, aber wir können nicht erwarten, dass sie jedes Problem löst, das es auf der Welt gibt. Als Game Engine wird Godot Ihnen helfen, Spiele besser und schneller zu machen, aber es wird nicht ein gesamtes Spiel für Sie machen. Irgendwo muss eine Grenze gezogen werden.

../../_images/best_practices3.png

Ob es sich lohnt, ein Problem zu lösen, hängt von der Schwierigkeit für den Benutzer ab, es zu umgehen. Diese Schwierigkeit kann ausgedrückt werden als:

  • die Komplexität des Problems

  • die Häufigkeit des Problems

Wenn das Problem für die meisten Benutzer zu komplex ist, dann sollte die Software eine fertige Lösung dafür anbieten. Wenn das Problem für den Benutzer leicht zu umgehen ist, ist es nicht notwendig, eine solche Lösung anzubieten.

Die Ausnahme ist jedoch, wenn der Benutzer häufig genug auf ein Problem stößt, so dass es lästig wird, jedes Mal die einfache Lösung ausführen zu müssen. In diesem Fall sollte die Software eine Lösung anbieten, um den Anwendungsfall zu vereinfachen.

Normalerweise ist es leicht, zu erkennen, ob ein Problem komplex oder häufig ist, aber es kann sich auch als schwierig herausstellen. Deshalb ist es immer ratsam, sich mit anderen Entwicklern auszutauschen (nächster Punkt).

#4: Die Lösung muss mit anderen besprochen werden

Oft sind die Benutzer in ihre eigenen Projekte vertieft, wenn sie auf Probleme stoßen. Diese Benutzer werden natürlich versuchen, das Problem aus ihrer Perspektive zu lösen und nur an ihren eigenen Anwendungsfall denken. Infolgedessen berücksichtigen die von einem Benutzer vorgeschlagenen Lösungen nicht immer alle Anwendungsfälle und sind oft auf die Anforderungen dieses Benutzers ausgerichtet.

../../_images/best_practices4.png

Für Entwickler ist die Perspektive eine andere. Sie finden vielleicht, dass das Problem des Benutzers zu einzigartig ist, um eine Lösung (anstelle eines Workarounds) zu rechtfertigen, oder sie schlagen eine Teillösung vor (in der Regel eine einfachere oder auf niedrigerer Ebene), die für eine größere Anzahl bekannter Probleme gilt, und überlassen den Rest der Lösung dem Benutzer.

In jedem Fall ist es wichtig, vor dem Versuch, einen Beitrag zu leisten, die tatsächlichen Probleme mit den anderen Entwicklern oder Mitwirkenden zu besprechen, damit eine bessere Einigung über die Umsetzung erzielt werden kann.

Die einzige Ausnahme ist, wenn ein Bereich des Codes einen klar definierten Eigentümer hat, der direkt mit den Benutzern spricht und über das meiste Wissen verfügt, um eine Lösung direkt umzusetzen.

Godots Philosophie ist es auch, die Benutzerfreundlichkeit und Wartung der absoluten Performance vorzuziehen. Performanceoptimierungen werden berücksichtigt, sie werden jedoch möglicherweise nicht akzeptiert, wenn sie die Verwendung zu schwierig machen oder wenn die Codebasis zu komplex wird.

#5: Für jedes Problem eine eigene Lösung

Für Programmierer ist es immer eine sehr angenehme Herausforderung, optimale Lösungen für Probleme zu finden. Allerdings kann man dabei auch über das Ziel hinausschießen. Manchmal wird versucht, Lösungen zu finden, die so viele Probleme wie möglich lösen.

Die Situation verschlechtert sich oft, wenn die rein auf Spekulation basierenden Probleme (wie in Nr. 2 beschrieben) auch noch auftauchen, um diese Lösung noch fantastischer und flexibler erscheinen zu lassen.

../../_images/best_practices5.png

Das Hauptproblem ist, dass es in der Realität selten so funktioniert. Meistens führt das Schreiben einer individuellen Lösung für jedes Problem zu Code, der einfacher und besser wartbar ist.

Außerdem sind Lösungen, die auf individuelle Probleme ausgerichtet sind, besser für die Nutzer. Gezielte Lösungen ermöglichen es den Nutzern, etwas zu finden, das genau das tut, was sie brauchen, ohne ein komplexeres System erlernen zu müssen, das sie nur für einfache Aufgaben benötigen.

Große und flexible Lösungen haben auch den Nachteil, dass sie im Laufe der Zeit selten flexibel genug für alle Nutzer sind. Die Nutzer fordern immer mehr Funktionen an, was dazu führt, dass die API und die Codebasis immer komplexer werden.

#6: Gängige Anwendungsfälle bearbeiten, aber die Tür für seltene geöffnet lassen

Dies ist eine Fortsetzung des vorherigen Punktes, der weiter erklärt, warum diese Art des Denkens und Entwerfens von Software bevorzugt wird.

Wie bereits in Punkt 2 erwähnt, ist es für uns (als Menschen, die Software entwickeln) sehr schwierig, alle zukünftigen Benutzeranforderungen tatsächlich zu verstehen. Der Versuch, sehr flexible Strukturen zu schreiben, die viele Anwendungsfälle gleichzeitig abdecken, ist oft keine gute Idee.

Es kann sein, dass wir etwas entwickelt haben, von dem wir glauben, dass es brillant ist, aber später herausfinden, dass die Benutzer nicht einmal die Hälfte davon nutzen oder dass sie Features benötigen, die nicht ganz in unser ursprüngliches Design passen, was uns dazu zwingt, es entweder wegzuwerfen oder noch komplexer zu gestalten.

Die Frage ist also, wie entwerfen wir eine Software, die es den Nutzern ermöglicht, sowohl das zu tun, von dem wir wissen, dass sie es jetzt brauchen, als auch das zu tun, von dem wir noch nicht wissen, ob sie es in Zukunft brauchen werden?

../../_images/best_practices6.png

Die Antwort auf diese Frage lautet: Um sicherzustellen, dass Benutzer weiterhin das tun können, was sie möchten, müssen wir ihnen Zugriff auf eine Low-Level-API gewähren, mit der sie das erreichen können, was sie möchten, auch wenn es mehr Arbeit für sie ist, weil es bedeutet eine bereits existierende Logik neu zu implementieren.

In realen Szenarien sind diese Anwendungsfälle ohnehin höchst selten und ungewöhnlich. Daher ist es sinnvoll, eine benutzerdefinierte Lösung zu schreiben. Aus diesem Grund ist es wichtig, den Benutzern weiterhin die grundlegenden Bausteine dafür bereitzustellen.

#Nr. 7: Lokale Lösungen bevorzugen

Wenn Sie nach einer Lösung für ein Problem suchen, sei es die Implementierung eines neuen Features oder die Behebung eines Bugs, ist es manchmal am einfachsten, Daten oder ein neues Feature in die Kern-Ebenen des Codes einzufügen.

Das Hauptproblem hierbei ist, dass das Hinzufügen von Elementen zu den Kern-Ebenen, die nur von einem einzigen weit entfernten Ort aus verwendet werden, nicht nur das Verständnis des Codes erschwert (da zweigeteilt), sondern auch die Kern-API größer und komplexer macht, also im Allgemeinen schwieriger zu verstehen.

Das ist schlecht, denn die Lesbarkeit und Sauberkeit der Kern-APIs ist immer von äußerster Wichtigkeit, wenn man bedenkt, wie viel Code davon abhängt, und weil es für neue Mitwirkende ein wichtiger Ausgangspunkt ist, um die Codebasis zu verstehen.

../../_images/best_practices7.png

Ein häufiger Grund dafür ist, dass es in der Regel weniger Code erfordert, einfach einen Hack in den Kern-Ebenen hinzuzufügen.

Dies ist jedoch nicht ratsam. Im Allgemeinen sollte der Code für eine Lösung näher am Ursprung des Problems liegen, auch wenn dies zusätzlichen, duplizierten, komplexeren oder weniger effizienten Code bedeutet. Es könnte mehr Kreativität erforderlich sein, aber dieser Weg ist immer der richtige.

#8: Verwenden Sie keine komplexen Komplettlösungen für einfache Probleme

Nicht jedes Problem hat eine einfache Lösung, und oft ist es die richtige Wahl, eine Drittanbieter-Bibliothek zu verwenden, um das Problem zu lösen.

Da Godot in einer großen Anzahl von Plattformen ausgeliefert werden muss, können wir die Bibliotheken nicht dynamisch linken. Stattdessen bündeln wir sie in unserem Quellcode-Baum.

../../_images/best_practices8.png

Daher sind wir sehr wählerisch bei der Auswahl und bevorzugen kleinere Bibliotheken (am liebsten solche mit nur einem Header). Wir werden nur dann etwas Größeres bündeln, wenn es keine andere Wahl gibt.

Bibliotheken müssen eine ausreichend offene Lizenz verwenden, um in Godot aufgenommen zu werden. Einige Beispiele für akzeptable Lizenzen sind Apache 2.0, BSD, MIT, ISC und MPL 2.0. Insbesondere können wir keine Bibliotheken akzeptieren, die unter der GPL oder LGPL lizenziert sind, da diese Lizenzen das statische Linken in proprietärer Software (als die Godot in den meisten exportierten Projekten verteilt wird) effektiv verbieten. Diese Anforderung gilt auch für den Editor, da wir ihn auf lange Sicht möglicherweise auf iOS einsetzen wollen. Da iOS kein dynamisches Linking unterstützt, ist statisches Linking die einzige Option auf dieser Plattform.