28. Juli 2025 · Phase 1

Block #01 — Phase 1: Angefangen. Einfach angefangen.

Es gibt diesen Moment wo man aufhoert abzuwaegen und einfach anfaengt. Bei mir war das ein Dienstagnachmittag im Mai 2025. Kein besonderer Tag. Kein Geistesblitz. Ich hab die IDE aufgemacht, ein neues Projekt erstellt und angefangen zu tippen.

Eigentlich hat das Projekt aber viel frueher angefangen — in dem Moment wo ich das letzte Mal einen klassischen Fussball-Manager gestartet hab und gemerkt hab dass er sich nicht mehr so anfuehlt wie frueher. Nicht weil das Spiel schlechter geworden waere. Sondern weil ich inzwischen weiss wie Software funktioniert. Veraltete Architekturen. Interfaces die in den 90ern designt wurden. Datenbankformate die niemand anfasst weil alle Angst haben was kaputt zu machen. Features die sich anhaeufen ohne dass jemand die alten rausschmeisst. Das klassische Muster von Software die zu lange lebt ohne grundlegende Ueberarbeitung.

Ich hab lange ueberlegt ob ich das wirklich machen will. Ein Fussball-Manager. Alleine. Neben dem Job. Ohne Team, ohne Budget, ohne Publisher. Ich weiss wie das klingt. Ich hab es mir selbst oft genug gesagt. Und trotzdem hat sich die Idee nicht vertreiben lassen. Irgendwann war klar: Ich mache es. Nicht weil ich weiss dass es klappt. Sondern weil ich nicht aufhoere daran zu denken.

Struktur vor Code — sechs Unterprojekte von Anfang an

Bevor ich eine einzige Zeile Spiellogik geschrieben hab, hab ich zwei Tage damit verbracht die Projektstruktur zu planen. Das klingt nach Ueberengineering. War es nicht. Ich hab mich fuer sechs separate Unterprojekte in einer Solution entschieden:

TaktikManager.Core — die reine Spiellogik. Kein Framework, keine Datenbankabhaengigkeit, kein UI-Code. Nur das was das Spiel ausmacht: Simulationsregeln, Domainobjekte, Berechnungslogik. Core darf nichts importieren das mit Datenbank oder Interface zu tun hat. TaktikManager.Data — die gesamte Datenbankschicht. Entity Framework Core, alle Migrations, alle Repository-Klassen. Core weiss nichts von Data. TaktikManager.UI — alles was der Spieler sieht. ImGui-Rendering, Screen-Klassen, Komponenten. TaktikManager.Editor — ein eigenes Werkzeug fuer Datenpflege, laeuft separat, benutzt Core und Data aber kein UI. TaktikManager.DesktopShared — geteilter Code zwischen UI und Editor: Konfigurations-Klassen, Utilities, Konstanten. TaktikManager.Tests — xUnit-Testprojekt das Core und Data referenziert aber nie UI.

Warum so viel Struktur fuer null Features? Weil ich aus Erfahrung weiss was passiert wenn man das nicht tut. Man faengt mit einer Datei an, dann zehn, dann merkt man dass die Haelfte des Codes von ueberall abhaengt und man nichts mehr anfassen kann ohne drei andere Dinge kaputt zu machen. Die Entscheidung hat sich hundertfach bewaehrt — Monate spaeter hab ich ganze Subsysteme ausgetauscht ohne dass etwas anderes kaputt ging, weil die Grenzen klar waren.

ImGui und die Pixel-fuer-Pixel Entscheidung

Eine der fruehen Entscheidungen die ich am meisten erklaert hab: Warum ImGui? ImGui ist ein Immediate Mode GUI Framework. Kein persistenter Zustand, keine XML-Definitionen, kein Binding-Framework, kein MVVM, kein State-Management-Layer. Jedes einzelne Frame wird die gesamte Oberflaeche von Null neu beschrieben. Das klingt ineffizient — in der Praxis ist es blitzschnell und extrem flexibel.

Ich hab mich dafuer entschieden weil man in einer Stunde eine neue Ansicht bauen, ausprobieren und wieder wegwerfen kann. Kein Framework das sich beschwert. Kein Projektierungs-Overhead. Code schreiben, Fenster aufmachen, fertig. Der Feedback-Loop ist minimal. Das ist fuer ein Soloprojekt extrem wertvoll weil man keine Zeit mit Boilerplate verliert.

Der Nachteil ist klar und ich hab ihn von Anfang an gewusst: Jeder Pixel wird von Hand gesetzt. Jeder Button, jeder Abstand, jede Hover-Animation, jede Trennlinie, jeder Tooltip — alles manuell, alles selbst gebaut. Kein Design-System das einem das abnimmt. Das kostet Zeit. Monatelang kostet es Zeit. Aber es gibt volle Kontrolle ueber jedes Detail des Spielgefuehls — und genau dieses Gefuehl ist einer der Gruende warum ich dieses Spiel ueberhaupt baue und nicht einfach eine bestehende Game-Engine nehme.

Silk.NET hat mir die nativen Bindings geliefert: OpenGL fuer die Grafik, GLFW fuer das Fenster-Management und Input, OpenAL fuer Audio. Alles in C#, ohne Umwege ueber eine Game-Engine. Keine Unity, kein Unreal, kein MonoGame. Das Spiel laeuft auf meinem Code, meiner Render-Loop, meinem Ressourcen-Management. Ich bin der einzige der entscheidet was wann wie gerendert wird. Das ist anspruchsvoll, macht aber auch keinen Aerger mit Engine-Versionen, Lizenzkosten oder Dingen die die Engine automatisch tut und die man erst wieder ausschalten muss.

Zwei Datenbanken, eine Architektur-Entscheidung die alles andere beeinflusst

Das architektonische Kernprinzip des Projekts steht in zwei Saetzen: Die MasterDB ist die unveraenderliche Wahrheit des Spiels. Die SaveDB ist der Spielstand. Das Spiel schreibt niemals in die MasterDB — egal was passiert.

Warum diese Trennung? Weil sie alle anderen Probleme loest. Updates an der MasterDB — neue Ligen, neue Spieler, Balancing-Korrekturen — beinflussen keine laufenden Spielstaende. Wenn ein Spieler eine Karriere startet wird die SaveDB als vollstaendige Kopie der relevanten MasterDB-Daten erstellt. Ab diesem Moment gehoert sie dem Spieler. Was auch immer ich am Spiel aendere bricht seine Karriere nicht. Alle Schreiboperationen in die SaveDB laufen als SQLite-Transaktionen ab — kein halbgespeicherter Zustand, kein korrupter Spielstand durch einen Crash in der falschen Millisekunde.

Entity Framework Core als ORM war die richtige Wahl. Code-First-Migrations bedeuten dass ich die Datenbankstruktur versionieren kann wie normalen Code. Wenn in drei Monaten ein neues Feld dazukommt gibt es eine Migration, keine manuelle SQL-Chirurgie, kein Datenverlust-Risiko. Die Repository-Pattern darueber gibt saubere Abstraktion: Die Spiellogik in Core weiss nicht ob dahinter SQLite, PostgreSQL oder ein In-Memory-Store fuer Tests steckt. Fuer die Test-Suite ist das Gold wert.

50 Attribute pro Spieler — warum nicht mehr, warum nicht weniger

Parallel zum technischen Aufbau hat mich eine Designfrage laenger beschaeftigt als erwartet: Wie viele Attribute braucht ein Fussball-Manager? Die grossen Vertreter des Genres haben gefuehlte zweihundert. Viele ueberlappen sich semantisch. Viele haben nachweislich kaum Einfluss auf die Simulation und existieren nur damit die Datenbank vollstaendiger wirkt. Ich hab keine Lust einen Spieler mit achtzig Prozentzahlen zu bewirtschaften der sich trotzdem nicht anders anfuehlt als der Spieler nebenan.

Ich hab mich durch verschiedene Ansaetze gearbeitet — 30 Attribute, 80 Attribute, zurueck auf 40. Am Ende: 50 Attribute pro Feldspieler.

37 sichtbare Attribute in drei Kategorien. Technik (12 Werte): Passen, Dribbling, Schuss, Erstkontakt, Flanke, Kopfball, Tackling, Zweikampf, Spieluebersicht, Positionierung, Langpass, Freistoss. Physis (7 Werte): Tempo, Ausdauer, Kraft, Sprungkraft, Agilitat, Reaktionszeit, Verletzungsanfaelligkeit. Mental (9 Werte): Konzentration, Taktische Intelligenz, Fuehrungsstaerke, Formkonsistenz, Druckresistenz, Kreativitaet, Offensivdrang, Defensivdisziplin, Lernbereitschaft. Dazu kombinierte Werte die aus mehreren Basisattributen berechnet werden: Gesamtstaerke, Potenzial, Formwert, Energielevel. Alle sichtbaren Attribute auf der Skala 1 bis 20.

7 versteckte Attribute die erst durch Scouting aufgedeckt werden (Skala 1 bis 5): Naturtalent, Verletzungsrisiko-Veranlagung, Formvolatilitaet, Big-Game-Performance, Pressing-Resistenz, Pressing-Intensitaet, Chemie-Faehigkeit. Diese existieren ausschliesslich als Scouting-Ziel — man kauft keinen Spieler ohne sie zu kennen, aber man sieht sie nicht einfach nach einer Suche.

6 Persoenlichkeitswerte: Professionalitaet, Ambition, Loyalitaet, Medienumgang, Geselligkeit, Fuehrungstyp. Das sind keine Spielstaerke-Attribute. Sie beeinflussen wie ein Spieler auf Gespraeche reagiert, wie schnell sich seine Moral erholt, wie er die Kabinenstimmung beeinflusst, und ob er bei einem Angebot eines anderen Vereins leicht oder schwer zu halten ist. Torwarte bekommen zusaetzlich 5 Spezialattribute: Reflexe, Stellungsspiel, Herauslaufen, Abschlag, Paraden-Konstanz.

Das Designprinzip war einfach: Jedes Attribut muss eine nachvollziehbare Funktion in einer konkreten Spielsituation haben. Wenn ein Attribut keinen Einfluss auf irgendetwas hat ist es kein Attribut — es ist Daten-Dekoration.

CSV/JSON-Import, Editor und die erste Ligastruktur

Das Import-System war der erste grosse Baustein neben der reinen Architektur: Spielerdaten, Vereinsdaten, Ligastruktur — alles importierbar ohne Eingriff in den Code. CSV fuer tabellarische Daten wie Spieler und Vereine, JSON fuer hierarchische Strukturen wie Ligabaeume und Wettbewerbsformate. Der Editor als eigenes Unterprojekt hat eine vollstaendige Import-Pipeline mit Validierung: Pflichtfelder pruefen, Wertebereiche validieren, doppelte IDs abfangen, Verweise auf nicht-existierende Vereine aufloesen.

Das ist die Grundlage fuer alles was spaeter kommt. Wenn die Community eigene Ligen einbringen will muss das tool-gestuetzt und fehlertolerant sein — kein manuelles SQL, keine undokumentierten Felder die man irgendwie herausfinden muss.

Juni war die Tiefe der Datenbankschicht. Alle Entity-Klassen entworfen, alle Migrations geschrieben, alle Repository-Implementierungen. Das ist keine glamouroese Arbeit aber sie entscheidet ob das Spiel in zwei Jahren noch wartbar ist. Parallel die ersten xUnit-Tests geschrieben: Attribut-Berechnungen, CSV-Parser-Validierung, MasterDB-Integrity-Checks. Erstmal neun Tests. Alle gruen.

Juli war die erste Liga. Der Spielplan-Generator erzeugt korrekte Doppelrunden-Hin-Rueck-Spielplaene: Heimrecht ausgeglichen, keine drei aufeinanderfolgenden Heimspiele, moeglichst gleichmaessige Abstands-Verteilung. Die erste Saison liess sich im Code durchlaufen — auch wenn noch kein einziges Spiel wirklich simuliert wurde, nur leere Ergebnis-Slots. Aber der Rahmen stand.

Ende Phase 1 — und der wichtigste Moment

Ende Juli: Phase 1 abgeschlossen. Kein einziges Feature das ein Spieler sehen wuerde — aber das Fundament auf dem alles andere aufbaut. Sechs Projekte mit klaren Grenzen, eine laufende Render-Pipeline, MasterDB und SaveDB mit EF Core Migrations, 50-Attribut-System voll dokumentiert, CSV-Import-Pipeline, Editor-Grundgeruest und eine erste xUnit-Test-Suite mit neun Tests.

Der wichtigste Moment der Phase 1 war eigentlich der kleinste: Ein Fenster. Schwarz. Mit einem ImGui-Testbutton oben links. Ich hab den Button gedrueckt. Er hat “Hallo” in die Konsole geschrieben. Das klingt laecherlich unspektakulaer. Ist es nicht. In diesem Moment haben vier Schichten zusammengearbeitet die ich alle selbst geschrieben hatte: C# redet mit Silk.NET, Silk.NET redet mit OpenGL, OpenGL malt auf den Bildschirm, ImGui zeichnet den Button, GLFW gibt den Klick weiter. Kein Fehler. Kein Absturz. Alles laeuft. Ich hab gelacht. Alleine vor dem Monitor um halb neun abends.

Phase 2 beginnt. Die erste spielbare Saison kommt.

Kommentar schreiben