TaktikManager https://taktikmanager.de Dein Fußball Manager Tue, 31 Mar 2026 20:30:14 +0000 de hourly 1 https://wordpress.org/?v=6.9.4 Block #05 — Beta gestartet: Taktik, Portraits und 16.052 Spieler https://taktikmanager.de/block-05-beta-p1-und-datenbasis/ https://taktikmanager.de/block-05-beta-p1-und-datenbasis/#respond Fri, 10 Jul 2026 10:00:00 +0000 https://taktikmanager.de/?p=164 Phase 3 ist abgeschlossen. Die Beta ist draussen. Ich hab zwei Tage lang gar nichts gemacht. Keine IDE, kein Code, kein GitHub. Counter-Strike und nichts anderes. Das war gut. Sehr gut sogar.

Dann hat es mich wieder gepackt und ich war zurueck. Wer haette das gedacht.

Die ersten Beta-Wochen waren intensiver als ich erwartet hatte — nicht wegen Bugs, sondern wegen Perspektive. Wenn man das eigene Spiel zum ersten Mal ohne Entwicklerbrille spielt aendert sich die Wahrnehmung fundamental. Dinge die man fuer gut genug gehalten hat sehen ploetzlich aus wie offene Baustellen. Das Taktikfeld zum Beispiel: Graue Kreise mit Namenskuerzeln. Funktioniert. Aber es fuehlt sich nicht nach Spielern an. Es fuehlt sich nach Schachfiguren an. Das war das erste was auf die Liste kam.

P1: Echte Portraits auf dem Taktikfeld

Das erste Beta-Feature: echte Spielerportraits. Das System ist direkt an das Datenbankfeld Player.PhotoPath in der MasterDB gebunden. Wenn ein Bild vorhanden ist wird es auf dem Taktikfeld als kreisfoermig zugeschnittenes Portrait angezeigt. Wenn kein Bild vorhanden ist gibt es den alten Token-Fallback — Namenskuerzel auf farbigem Hintergrund.

Die Bild-Verarbeitung passiert beim Import: PNG-Dateien aus dem Ordner Imports/Portraits/ werden beim MasterDB-Import eingelesen. Die technischen Anforderungen sind dokumentiert: PNG, 512×512 Pixel, quadratisches Format. Das Interface macht daraus einen 64px-Kreis auf dem Taktikfeld oder einen 128px-Kreis in der Spieler-Detailansicht. Das Portrait-Masking — das kreisfoermige Zuschneiden — laeuft zur Ladezeit einmal und wird als Textur gecacht. Kein Rechenaufwand waehrend des Renderings.

Der technische Aufwand war ueberschaubar. Die visuelle Auswirkung war unverhaeltnismaessig gross. Ploetzlich stehen auf dem Taktikfeld erkennbare Menschen und keine abstrakten Markierungen. Das ist der Unterschied zwischen einer Simulation die man bedient und einem Spiel das man wirklich spielt.

Freies Ziehen auf dem Taktikfeld

Freies Ziehen von Spieler-Icons auf dem Taktikfeld war lange auf der Feature-Liste. Das erste Design-Doc begann mit “sollte schnell gehen”. War es nicht.

Die technische Grundimplementierung war in Ordnung: GLFW-MouseDrag-Events abfangen, Position des angeklickten Spieler-Icons tracken, Render-Position anpassen. Das waren ein paar Stunden. Das eigentliche Problem war das Speichern.

Ein Spieler hat auf dem Taktikfeld eine Position die aus seiner Formations-Rolle kommt: Ein linker Mittelfeldspieler steht links im Mittelfeld. Die Formation bestimmt das. Wenn ich den Spieler jetzt frei positioniere — wohin speichere ich das? Die Formation kann ich nicht beschreiben, sie gilt fuer alle Karrieren die diese Formation benutzen. Die SaveDB muss die Override-Koordinaten speichern: “In dieser Karriere, in dieser Formation, steht Spieler X an Position Y.”

Das bedeutete eine neue EF Core Migration: AddTacticLayoutJson. Die SaveDB bekommt ein neues JSON-Feld das pro Formation und pro Spielstand die Override-Koordinaten speichert. Der FormationLayoutResolver kombiniert beim Rendern das Basis-Template der Formation mit den optionalen Override-Koordinaten aus dem Spielstand. Wenn keine Overrides vorhanden sind wird das Template verwendet. Wenn Overrides vorhanden sind gelten diese — transparent, keine sichtbare Unterschied im Handling.

Das Ergebnis: Man zieht einen Spieler an eine neue Position, speichert, laedt das Spiel neu — die Position ist noch da. Klingt selbstverstaendlich. Ohne das korrekte Datenmodell waere das Ziehen eine Geste ohne Konsequenz. Mit korrektem Speichern ist es ein echtes taktisches Werkzeug.

Formationsvarianten — Standard, Offensiv, Defensiv

Ein Team hat nicht eine Formation — es hat mehrere Varianten fuer verschiedene Situationen. Das war ein Feature das ich schon lange designt aber noch nicht implementiert hatte.

Drei Varianten pro Karriere: Standard fuer das normale Spiel. Offensiv fuer Spiele wo man ein Tor braucht — aggressivere Pressinglinie, hoehere Besetzung des letzten Drittels, anderes Tempo-Setting. Defensiv fuer Spiele wo man ein Ergebnis verteidigt — kompakteres Mittelfeld, niedrigere Pressinglinie, andere Spieleraufstellung.

Jede Variante traegt vollstaendig unabhaengige Daten: eigene Formation, eigene Pressing-Einstellungen, eigenes Spieltempo, eigene Startelf, eigene Layout-Overrides. Das ist deutlich mehr Zustand als urspruenglich geplant. Dafuer eine saubere EF Core Migration: AddTacticVariantsJson fuegt einen VariantsJson-Block zur TacticState-Entitaet hinzu, zusammen mit einem ActiveVariantKey-Feld das festhalt welche Variante gerade aktiv ist. Der Wechsel zwischen Varianten passiert in der Taktik-Ansicht mit einem Tap — man schaut auf das offensive Setup, prueft den Kader, wechselt vor dem Anpfiff oder in der Halbzeit.

Die Formationsbibliothek — zwoelf Formationen speichern

Direkt daran anschliessend: die Formationsbibliothek. Bis zu zwoelf vollstaendige Formationen pro Spielstand speicherbar. Auto-Namen nach der Formations-Notation (4-3-3, 4-2-3-1, 3-5-2 usw.) oder manuell vergeben fuer eigene benannte Setups. Varianten koennen gespeicherte Bibliotheks-Formationen mit einem Klick anwenden — man baut einmal eine perfekte 4-3-3-Variante fuer gegnerische 4-4-2-Setups und kann sie in jeder Saison aus der Bibliothek laden.

Neue EF Core Migration: AddSavedTacticFormationsJson. Das ist die dritte Migration in dieser Feature-Gruppe. Ich hab sie bewusst separat gehalten: Jede Migration hat eine klare, beschreibbare Aufgabe. Keine “AddTacticStuffJson” die drei verschiedene Dinge auf einmal hinzufuegt. Migrations sollen lesbar sein wenn man in einem Jahr die Entwicklungsgeschichte nachvollzieht.

Deep-Talent-MasterDB — 36 Ligen, 16.052 Spieler

Die alte MasterDB war ausreichend fuer die Entwicklungsphase. Fuer eine Beta die andere spielen sollen war sie es nicht. Zu wenig Ligen, zu wenig geografische Breite, zu wenig Spieler fuer eine richtige Spielwelt.

Ich hab Wochen damit verbracht die Deep-Talent-MasterDB aufzubauen: 36 Ligen aus mehr als 20 Laendern, vier bis fuenf Wettbewerbsebenen pro Land, 16.052 Spieler mit vollstaendigen Attribut-Profilen. Das ist jetzt der offizielle Projektstandard gespeichert unter Data/masterdb.sqlite.

Die Datenqualitaet war genauso aufwendig wie die Datenmenge. Attribut-Verteilungen muessen realistisch sein: Ein Bundesliga-Kader soll sich statistisch anders anfuehlen als ein Drittliga-Kader — nicht nur in der Gesamtstaerke, sondern in der Tiefe des Kaders, in der Talent-Verteilung, in den Persoenlichkeitswerten. Ich hab Export-Skripte geschrieben die die statistischen Verteilungen der DB pruefen und Ausreisser markieren. Dann manuell korrigiert. Dann nochmal geprueft.

Spielwelt-Selektion — nicht alle 36 Ligen muessen aktiv sein

Mit 36 Ligen kam sofort eine neue Frage: Braucht jede Karriere wirklich alle 36 Ligen? Die Simulation ist performant, aber viele Spieler interessieren sich vielleicht nur fuer die Top-Ligen Europas. Und auf schwacherer Hardware ist weniger Last eindeutig besser.

Die Spielwelt-Selektion loest das: Beim Start einer neuen Karriere waehlt der Spieler welche Laender und Ligen aktiv sein sollen. Was nicht aktiv ist wird physisch nicht in den Spielstand uebernommen — keine versteckten Daten, keine Ligen die im Hintergrund simuliert werden ohne dass jemand davon profitiert. Drei Service-Klassen verwalten das: SimulationWorldCatalogService stellt die vollstaendige Liste aller verfuegbaren Ligen bereit. SimulationWorldSelectionService verwaltet die Auswahl des Spielers. SimulationWorldPruner entfernt alle nicht-ausgewaehlten Daten beim Erstellen der SaveDB.

Eine Weltzusammenfassung zeigt vor der Bestaetigung: Vereinsanzahl, Spieleranzahl, Ligaanzahl, geschaetzte Ladezeit fuer Spieltage — damit der Spieler weiss womit er es zu tun hat bevor er bestaetigt.

102 von 102 Tests gruen nach dem vollstaendigen System-Rollout. Editor-Smoke und UI-Smoke verifiziert. Das war der Stand nach der ersten Beta-Welle. Die naechste Phase ist die groesste visuelle Arbeit des Projekts: der Visual Track.

]]>
https://taktikmanager.de/block-05-beta-p1-und-datenbasis/feed/ 0
Block #04 — Phase 3: Der laengste Monat. Feature Complete. https://taktikmanager.de/block-04-phase-3-feature-complete/ https://taktikmanager.de/block-04-phase-3-feature-complete/#respond Tue, 31 Mar 2026 20:00:00 +0000 https://taktikmanager.de/?p=163 Maerz 2026 war der Monat wo ich das Wort “fast” endgueltig losgeworden bin. Am 24. Maerz: 68 Tests gruen, alle Phase-3-Features implementiert. Feature Complete. Ich hab kurz auf den gruenen Balken geschaut. Dann hab ich einen Kaffee gemacht. Dann hab ich weitergemacht, weil es noch mehr zu tun gab als eingeplant.

Der Maerz hatte drei grosse Baustellen gleichzeitig: Ein fundamentales Refactoring das laengst ueberfaellig war, die Formalisierung des Design-Systems, und die Cockpit-Architektur als neue visuelle Leitstruktur des gesamten Spiels. Keine davon ist ein Feature das ein Spieler direkt sieht. Alle drei entscheiden ob das Spiel in einem Jahr noch wartbar und erweiterbar ist.

Das grosse Refactoring — 2.749 Zeilen werden 198

Der CareerRuntimeService war ueber die Monate zu einem Monster geworden. 2.749 Zeilen Code in einer einzigen Klasse. Alles was irgendwie mit dem Spielablauf zu tun hatte landete dort. Spieltag-Simulation, Transferlogik, Gehaltsabrechnung, Saisonabschluss, Verletzungs-Updates, Moral-Updates, KI-Manager-Aktionen, Spielstand-Persistierung — alles in einer Klasse. Das ist das klassische Muster wie Software verrottet. Nicht durch schlechte Absichten. Sondern weil jedes neue Feature irgendwo angebaut werden muss, und wenn es schon eine Klasse gibt die alles kennt ist es verlockend sie nochmal ein bisschen zu erweitern.

Ich hab den Service in vier spezialisierte Services aufgeteilt:

CareerBootstrapService — alles was beim Start einer neuen Karriere passiert. MasterDB-Daten in SaveDB kopieren, Startbudget setzen, erste Saison anlegen, KI-Manager initialisieren. Laeuft genau einmal pro Karriere. CareerReadService — alle Lesezugriffe auf den Spielstand. Keine Schreiboperationen. Gibt Daten zurueck, veraendert nichts. Kann aus Tests einfach gemockt werden ohne vollstaendigen SaveDB-Zustand. CareerMutationService — alle Operationen die den Spielstand veraendern: Spieler transferieren, Gehaelter anpassen, Verletzungen registrieren, Moral-Updates einspielen. Jede Mutation laeuft als Transaktion. CareerAdvanceService — die Orchestrierung: Spieltag simulieren, Saison abschliessen, neue Saison anlegen, KI-Manager-Runde durchfuehren.

Das Ergebnis: Der urspruengliche CareerRuntimeService ist jetzt 198 Zeilen — eine Koordinationsschicht die die vier Services zusammenbringt und nichts anderes tut. Das Refactoring hat eine Woche gedauert. In dieser Woche hab ich keine neuen Features gebaut. Kein einziger Spieler haette den Unterschied bemerkt. Aber ohne dieses Refactoring waere das Projekt in einem Jahr nicht mehr wartbar gewesen. Das ist die Arbeit die niemand sieht und die trotzdem entscheidend ist.

Das Design-System — Tokens statt verstreuter Pixel-Werte

Parallel zum Refactoring hab ich das UI-Design-System endgueltig formalisiert. Das Problem: Ueber Monate hatte ich Farbwerte, Abstaende und Font-Groessen direkt in Screen-Code geschrieben. “245, 180, 42” fuer den Akzent-Ton an zehn verschiedenen Stellen. Was passiert wenn ich den Ton anpassen will? Zehn Stellen suchen, zehn Stellen aendern — und trotzdem eine vergessen. Das ist keine Wartbarkeit.

Vier Klassen die alle visuellen Entscheidungen zentralisieren:

TaktikManagerTheme — alle Farben an einem Ort. Primary, Secondary, Accent, Danger, Warning, Success — jeweils als Dark/Normal/Light Variante. Hintergrundfarben nach Zone. Textfarben nach Hierarchie-Ebene. Kein Farbwert mehr der irgendwo im Screen-Code steht. Ein Farbton aendern bedeutet eine Zeile in dieser Klasse. TaktikManagerMetrics — alle Abstaende, Typografie-Groessen und Component-Hoehen als benannte Konstanten. Referenzaufloesung 2560×1440. Spacing-Tokens XS/S/M/L/XL. Font-Groessen fuer Caption, Body, Subhead, Head, Display. TaktikManagerLayoutResolver — Breakpoint-Logik fuer die drei Aufloesung-Profile. Die Metriken werden auf die aktive Aufloesung skaliert mit Ausnahme-Regeln fuer nicht-linear-skalierbare Elemente. TaktikManagerViewportProfile — Viewport-Geometrie und Safe-Areas fuer die drei Cockpit-Zonen.

Ohne dieses System beginnt jede neue Screen-Implementierung wieder von vorne: Welche Farbe? Welcher Abstand? Welche Font-Groesse? Mit dem System zieht jeder neue Screen dieselben Tokens und sieht dadurch automatisch aus wie der Rest des Spiels. Man entscheidet einmal richtig und muss danach nie wieder entscheiden.

Die Cockpit-Shell — drei Zonen, ein Arbeitsplatz

Die Cockpit-Shell ist die visuelle Leitstruktur des Spiels. Das Grundkonzept: Der Spieler sitzt nicht an einem Desktop-Interface mit Menues. Er sitzt an einem Arbeitsplatz mit konkreten physischen Zonen.

Drei Zonen: Das Tablet als Hauptarbeitsbereich in der Mitte — 1.59:1 Aspect-Ratio, alle Haupt-Screens laufen hier. Das Handy als Kommunikations- und Benachrichtigungskanal seitlich — kurzfristige Messages, Transfer-Angebote, Vorstandsnachrichten. Die Reminder-Zone fuer laufende Aufgaben — offene Scouting-Auftraege, bevorstehende Vertragsverlaengerungen, naechste Spieltage.

Diese Aufteilung macht das Interface lesbar: Das Tablet ist der Fokus. Das Handy ist der Peripherieblick. Die Reminder-Zone ist das Gedaechtnis. Man weiss immer wohin man schaut und warum — statt dass alle Informationen gleichzeitig um Aufmerksamkeit konkurrieren.

Block D war die Migration aller neun Hauptscreens auf die gemeinsame Cockpit-Chrome: Finance, Scouting, Presse, Transfer, Setup, Kader, Taktik, Match, Post-Match-Analyse. Jeder Screen benutzt jetzt dieselbe Tablet-Chrome mit gemeinsamem Panel-Header, gemeinsamen Tab-Farben und gemeinsamen Button-Positionen. Vier Slices, fuenfzehn verifizierte Arbeitspakete, jeder Screen einzeln getestet. Danach sehen alle Screens aus wie Teile desselben Systems — nicht mehr wie unabhaengige Loesungen die zufaellig auf demselben Bildschirm gelandet sind.

Infrastruktur die niemand sieht, aber jeder braucht

TaktikManagerJson zentralisiert alle JSON-Serialisierungseinstellungen. Das klingt wie eine Kleinigkeit. War es bis zu dem Punkt wo ich gemerkt hab dass zehn verschiedene Stellen im Code zehn verschiedene JsonSerializerOptions-Instanzen mit teilweise inkonsistenten Einstellungen hatten — mal CamelCase, mal PascalCase, mal mit NullHandling, mal ohne. Das fuehrt zu schwer findbaren Fehlern beim Laden aelterer Spielstaende. Jetzt: Eine zentrale Instanz, konfiguriert einmal, ueberall referenziert.

AppDiagnostics ist das Produktions-Logging-System. Wenn die Beta draussen ist und jemand einen Bug erlebt brauche ich Informationen. Crash-Zeitstempel, Stack-Traces, letzter bekannter Spielzustand, OS-Version, Grafikkarten-Info — alles geht in %LOCALAPPDATA%TaktikManagerLogs. Die Log-Datei rotiert taeglich und behaelt die letzten sieben Tage. Das ist der Unterschied zwischen “das Spiel crashed bei mir” und “ich kann reproduzieren warum”.

Beta angekündigt — und die drei Planungsdokumente

Mit dem Feature-Stand Ende Maerz hab ich die Planungsdokumente endgueltig aufgeraeumt. Drei klare Dokumente mit klaren Grenzen:

Das Beta-Dokument listet was in die oeffentliche Beta kommt — der vollstaendige Stand Ende Maerz. Das Phase-4-Dokument listet was fuer v1.0 noch dazukommt — fehlende kleinere Features, Balancing, QA-Runden, erste Community-Rueckmeldungen einarbeiten. Das Post-Release-Dokument listet was bewusst aus v1.0 ausgespart wird: 3D-Modus, Vulkan-Renderer, Steam Workshop fuer Mods, vollstaendiger Kommentator, internationale Karriere-Mode.

Warum so konsequent raus aus v1.0? Weil jedes dieser Features hochriskant und zeitintensiv ist. 3D bedeutet eine komplett andere Rendering-Pipeline. Vulkan bedeutet Monate an Low-Level-Grafik. Steam Workshop bedeutet ein komplettes Infrastruktur-System. Wenn ich vor dem ersten Release anfange diese Features zu bauen gibt es keinen ersten Release. Besser ein vollstaendiges spielbares v1.0 als ein ewiges “bald fertig” mit immer neuen grossen Baustellen.

Die Beta ist angekuendigt. Via Mega Download, kostenlos, ohne Account. Das Spiel ist spielbar. Es ist nicht perfekt — aber es ist da. Jetzt koennen andere es sehen. Das war ein seltsamer Moment. Zehn Monate solo gearbeitet und dann: ein Download-Link ins Netz stellen. Ich freu mich. Ich bin nervoes. Beides gleichzeitig. Das ist wohl das richtige Gefuehl.

]]>
https://taktikmanager.de/block-04-phase-3-feature-complete/feed/ 0
Block #03 — Phase 3: Feiertage egal. Die Tiefe beginnt. https://taktikmanager.de/block-03-phase-3-tiefe-beginnt/ https://taktikmanager.de/block-03-phase-3-tiefe-beginnt/#respond Sun, 15 Feb 2026 20:00:00 +0000 https://taktikmanager.de/block-03-phase-3-tiefe-beginnt/ Drei Wochen Weihnachtsferien. Ich hab den Laptop daneben stehen gehabt. Natuerlich.

An Heiligabend hat meine Familie beim Abendessen gefragt was dieses Spiel eigentlich ist, woran ich da so lange arbeite. Ich hab es erklaert. Fussball-Manager, selbst gebaut in C#, Datenbank, Simulation, alles alleine. Mein Onkel Andi hat genickt und dann gefragt ob man da auch richtige Spieler hat. Ich hab erklaert dass es keine offiziellen Lizenzen gibt, also keine echten Transfermarkt-Namen aus rechtlichen Gruenden. Er hat dann gefragt warum man nicht einfach FIFA spielt. Gute Frage, Onkel Andi. Ehrlich gesagt eine sehr gute Frage.

Aber das ist genau der Punkt. TaktikManager will etwas anderes sein als FIFA. Ein Managerspiel das sich wie eine echte Entscheidung anfuehlt — nicht eines das 200 Attribute anzeigt von denen 160 kaum Einfluss haben. Nicht eines bei dem gekaufte Spieler wie Sammelkarten behandelt werden. Sondern eines das jeden Aspekt eines Vereins als echte Herausforderung zeigt. Darum bau ich es.

Phase 3 hat zwischen den Feiertagen begonnen. Nicht als offizielle Ankuendigung — einfach weil Phase 2 fertig war und ich weitergemacht hab.

Das Moral-System — wenn Spieler anfangen als Menschen zu funktionieren

Das erste Phase-3-System war Moral. Ich hatte lange einen Platzhalter: Moral existiert als Zahl und wird pauschal nach Siegen erhoht und nach Niederlagen gesenkt. Das ist keine Moral. Das ist eine Sieg-Zaehler-Variante.

Das echte Moral-System hat fuenf unabhaengige Einflussfaktoren die alle gleichzeitig wirken:

Spielzeit-Faktor: Ein Spieler der regelmassig eingesetzt wird hat stabile oder steigende Moral. Einer der trotz guter Form auf der Bank sitzt verliert langsam Moral — nicht sofort, sondern ueber mehrere Spieltage. Das gibt dem Manager einen Puffer bevor es zum Problem wird. Ergebnis-Faktor: Siege heben, Niederlagen senken — aber nicht gleichmaessig. Erfahrene Spieler und der Mannschaftskapitaen federn Niederlagen besser ab. Junge Talente reagieren empfindlicher auf Misserfolge. Gespraeche-Faktor: Manager-Gespraeche haben direkten Einfluss. Ein gut geplantes Gespraech nach einer schlechten Phase kann die Moral signifikant stabilisieren. Ein unueberlegtes Gespraech kann sie weiter senken. Vereinsdruck-Faktor: Oeffentliche Kritik vom Vorstand, Medienberichte, Transfergeruechte — alles hat Auswirkungen auf die Kabinenstimmung. Chemie-Faktor: Ein Spieler in einer Mannschaft mit guter kollektiver Chemie faengt Moral-Einbussen leichter ab.

Zwei Details die den Unterschied machen: Erstens hat Moral Traegheit. Eine Moral die drei Wochen gefallen ist kommt nicht sofort wieder auf Maximum wenn der Spieler einmal spielt. Sie erholt sich langsam. Das verhindert dass Moral-Management trivial wird — man muss frueh reagieren. Zweitens gibt es den MoralLog: Alle Ereignisse die die Moral eines Spielers veraendert haben werden protokolliert. Warum ist dieser Spieler unzufrieden? Man kann es nachvollziehen. Das macht den Unterschied zwischen einem Zahlensystem und einem narrativen System.

Scouting — die Kunst des Nicht-Wissens

Das Scouting-System war eine der interessantesten Design-Herausforderungen des Projekts. Das fundamentale Problem: Wenn der Spieler alle Attribute von Anfang an sieht gibt es keine Entdeckung. Man schaut auf die Zahlen, sortiert nach Gesamtstaerke und kauft den mit der hoechsten Zahl. Das ist eine Tabellenkalkulation mit Fussball-Thema.

Die Loesung: Fuenf Enthullungsstufen.

Stufe 0 (unbekannt): Name, Nationalitaet, ungefaehres Alter, grobe Positionsgruppe. Man weiss dass jemand existiert, nicht wer er ist. Stufe 1 (Basis-Scouting): Oeffentliche Attribute — Tempo, Groesse, grobe Kategorie-Einschaetzung. Stufe 2 (Erweitertes Scouting): Technische Attribute beginnen sich zu enthullen, noch mit ±2 Ungenauigkeit. Stufe 3 (Tiefes Scouting): Physis und Mental werden sichtbar, Ungenauigkeit sinkt auf ±1. Stufe 4 (Vollstaendiges Profil): Alle 37 sichtbaren Attribute exakt. Stufe 5 (Vollanalyse): Auch die 7 versteckten Attribute werden sichtbar — Talent, Verletzungsrisiko, Formvolatilitaet.

Jede Stufe kostet Zeit und Scout-Ressourcen. Wie viele Scouts hat man? Wie erfahren sind sie? Wen scoutet man zuerst? Kauft man einen Spieler der erst auf Stufe 3 analysiert ist und riskiert dass man etwas uebersieht? Oder wartet man und riskiert dass ein anderer Verein zuschlaegt? Keine richtige Antwort. Genau das macht Scouting interessant.

Spielerentwicklung — Talent ist nicht alles

Jeder Spieler hat ein Naturtalent-Attribut von 1 bis 5. Das bestimmt sein theoretisches Maximum. Ein Talent-5-Spieler kann die Hoechstwerte in allen Attributen erreichen. Einer mit Talent 1 wird immer Grenzen haben — egal wie viel er trainiert. Aber Talent allein entscheidet nichts.

Die Alterskurve: Unter 18 waechst ein Spieler am schnellsten wenn er Spielzeit bekommt. Die Peak-Jahre liegen bei 26 bis 29 — in diesem Korridor sind Spieler auf ihrem Maximum und halten es. Ab 32 beginnt langsamer Abbau, zuerst bei Physis-Attributen (Tempo, Ausdauer, Sprungkraft), spaeter bei technischen. Mental-Attribute halten sich am laengsten — ein erfahrener 35-Jaehriger kann in Taktischer Intelligenz einen 24-Jaehrigen uebertreffen.

Spielzeit ist Pflicht fuer Entwicklung. Ein Talent-5-Spieler der nie spielt entwickelt sich langsamer als ein Talent-3-Spieler der jede Woche auf dem Platz steht. Das schafft das taktische Dilemma: Spielst du den jungen Rohdiamanten der noch Fehler macht, oder den erfahrenen Spieler der nicht mehr besser wird? Kein System entscheidet das fuer dich.

Die Website geht online — und ändert die Perspektive

Zwischen den Feiertagen hab ich einen Abend damit verbracht die Website aufzusetzen. Eigentlich fuer spaeter geplant. Aber ich hatte Energie und es hat sich ergeben. WordPress, ein selbst entwickeltes Theme, Screenshot-Galerie, ein Devlog. Ein paar Stunden.

Das veraendert wie man ueber das Projekt denkt. Vorher war es mein privates Projekt das ich irgendwann fertigstellen wuerde. Mit der Website ist es oeffentlich. Jemand koennte sie finden. Jemand koennte sie lesen. Das ist gleichzeitig motivierend und unangenehm — und ich empfehle jedem der an einem langen Soloprojekt arbeitet: Mach es frueh oeffentlich. Es veraendert die eigene Haltung. Man haelt sich mehr an Qualitaetsstandards wenn man weiss dass jemand zuschauen koennte.

Februar — 2D Playback, Atmosphaere, KI-Manager und Performance

Der 2D Match-Playback war das erste grosse Februar-System. Spieler-Icons bewegen sich auf einem 2D-Pitch, der Ball hat eine sichtbare Position, Tore haben einen visuellen Flash-Effekt mit Jubel-Einblendung.

Das technische Problem: Die Match Engine simuliert ein Spiel in Millisekunden. Alle 90 Spielminuten und alle Events sind in unter einer Sekunde abgearbeitet. Der Playback soll dasselbe Spiel in echter Zeit zeigen. Das bedeutet: Waehrend die Engine laeuft muss sie alle relevanten Zustaende aufzeichnen — Minute fuer Minute ein vollstaendiger Snapshot. Spieler-Positionen entstehen dabei nicht aus echten Koordinaten, weil die Engine statistisch arbeitet. Ich musste ein Positions-Mapping entwickeln das aus Ballbesitz-Wahrscheinlichkeiten und Taktik-Formation glaubwuerdige Spieler-Positionen auf dem Pitch erzeugt. Eine Woche war reine Performance-Arbeit: 12 FPS zu Beginn, Ziel 60 FPS auf Mittelklasse-Hardware. Nach Profiling und Interpolations-Optimierung: 60 FPS stabil.

Stadion-Atmosphaere: Das .tepk-Format erstmals vollstaendig genutzt. Dynamische Crowd-Loops die auf Spielsituationen reagieren — ruhig beim Anstoss, angespannt bei Druckphasen, laut bei Torchance, Jubel bei Tor. Vereinshymnen vor Anpfiff. AudioAtmosphereService verwaltet alles mit sauberen Fade-In/Fade-Out-Uebergaengen.

Der KI-Manager bekam fuenf echte Archetypen: Defensiv-Stratege (baut auf Kompaktheit und Konter, kauft zuerst Innenverteidiger), Angriffs-Fanatiker (hohe Pressinglinie, viel Risiko, priorisiert Stuermer), Talent-Entwickler (kauft junge Spieler mit hohem Talent), Pragmatiker (analysiert Gegner, passt Taktik situativ an), Reaktiver Typ (aendert seine Strategie nach mehreren Niederlagen hintereinander). Das Gegner-Gedaechtnis ist das interessanteste technische Element: Wenn man einen Gegner regelmaessig mit derselben Taktik besiegt sollte er irgendwann reagieren. Ein Reaktiver Typ erkennt das Muster.

Spielergespraeche und Pressekonferenzen bekamen eigene Mechanik: Jede Antwort des Managers hat einen Ton-Tag (selbstsicher, defensiv, provozierend, ruhig, sachlich). Der Ton kombiniert mit dem Spielkontext ergibt Auswirkungen auf Moral ausgewaehlter Spieler, Oeffentlichkeitsbild und Vorstand-Beziehung. Kein richtiges Antwort-Muster das man “herausfinden” muss — man spielt seinen eigenen Manager-Stil.

Ende Februar: Fuenf Ligen parallel, 90 Vereine, 1.980 Spieler, ein vollstaendiger Spieltag in 1,99 Sekunden. Die letzten 0,12 Sekunden haben mich eine Woche Profiling-Arbeit gekostet. Den letzten Cache-Engpass hab ich um 2 Uhr morgens gefunden. Vier Zeilen Code. Performance halbiert. Das ist Optimierungs-Arbeit: Stundenlang nichts finden, dann vier Zeilen, und ploetzlich laeuft alles doppelt so schnell.

]]>
https://taktikmanager.de/block-03-phase-3-tiefe-beginnt/feed/ 0
Block #02 — Phase 2: Das erste spielbare Spiel https://taktikmanager.de/block-02-phase-2-kernspiel/ https://taktikmanager.de/block-02-phase-2-kernspiel/#respond Sun, 30 Nov 2025 20:00:00 +0000 https://taktikmanager.de/block-02-phase-2-kernspiel/ Phase 2 war die vier Monate wo aus einem technischen Fundament ein spielbares Spiel wurde. Das klingt nach einem geraden Weg. War es nicht. Es war ein Durchwursteln mit gelegentlichen Hochpunkten, vielen Momenten wo nichts funktionierte, und einem November der sich angefuehlt hat wie eine Gegengerade in einem langen Rennen.

August war ich nicht weg. Waehrend andere Urlaub machten hab ich zu Hause gesessen und programmiert. Das war keine Entbehrung — das war genau das was ich wollte. Zuhause, Counter-Strike abends, tagsrueber Code. Kein Reisestress, kein Termindruck. Ich bin in diesem Sommer mehr vorangekommen als in jedem anderen Monat, weil kein einziger Tag verloren gegangen ist. Die Match Engine entstand in diesen Augustwochen.

Die Match Engine — das Herzstück und zwei Wochen Kalibrierung

Das Grundprinzip der Match Engine war frueh klar: 90 Minuten werden Minute fuer Minute simuliert. Jede Minute berechnet das System Ballbesitz, Druckphasen, Chancenkreation und Expected Goals (xG). Tore entstehen wenn ein xG-Wert einen Zufallsschwellenwert ueberschreitet. Der Zufallswert ist seeded und damit deterministisch — dasselbe Spiel mit denselben Inputs ergibt immer dasselbe Ergebnis. Das ist technisch wichtig: Es bedeutet dass man ein Spiel jederzeit reproduzieren kann, ohne jeden Zwischenzustand speichern zu muessen.

Die Kalibrierung hat zwei Wochen gedauert. Nicht weil das Grundprinzip schwierig gewesen waere, sondern weil ich echte statistische Zielwerte hatte: Durchschnittlich 2,7 Tore pro Spiel (Premier League Langzeitschnitt), 55% Heimsiegquote, das staerkere Team gewinnt in circa 68% der Begegnungen. Diese Werte kommen nicht von selbst. Jeder Tuning-Parameter hat Auswirkungen auf alle anderen. Ich hab Tausende von Testsaisons laufen lassen, Statistiken automatisch ausgewertet und Parameter angepasst.

Die finalen Werte: HomeAdvantageStrength 32.0 als Basiswert fuer den Heimvorteil, ShotBaseProbability 0.092 als Wahrscheinlichkeit dass aus einer Druckphase ein Schussversuch entsteht, FormSigma zwischen 0.04 und 0.14 je nach Spieler-Formkonstanz-Attribut fuer die Gauss-Verteilung der Tagesform. Dazu ein Energie-Decay der ab Minute 60 greift: Spieler werden in der zweiten Haelfte schwaecher, aber nicht linear — die Kurve haengt vom Ausdauer-Attribut ab. Ein Big-Game-Faktor fuer Pokalfinale und Entscheidungsspiele. Zusammen ergibt das ein System das sich nach echtem Fussball anfuehlt.

Spielplan-Generator und das Doppelrunden-Problem

Einen korrekten Doppelrunden-Spielplan zu generieren ist mathematisch nicht schwer — aber es gibt Randbedingungen die alle gleichzeitig erfuellt sein muessen: Jede Mannschaft spielt gegen jede andere genau zweimal, einmal heim und einmal auswaerts. Das Heimrecht muss ueber die Saison ausgeglichen sein. Keine Mannschaft darf mehr als zwei Heimspiele hintereinander haben. Die Abstands-Verteilung soll moeglichst gleichmaessig sein — kein Verein hat drei Wochen Pause und dann vier Spiele in zehn Tagen.

Ich hab den Generator zweimal umgebaut bis er in allen Faellen korrekt war. Die erste Version hatte einen subtilen Fehler bei ungeraden Vereinszahlen. Die zweite war korrekt aber zu langsam. Die dritte Version ist korrekt und erzeugt einen 34-Spieltage-Plan fuer 18 Mannschaften in unter einer Millisekunde. Der Pokal benutzt eine separate Auslosung die Ligazugehoerigkeit beruecksichtigt — kleinere Vereine treffen in fruehen Runden auf andere kleine Vereine, die grossen kommen spaeter ins Turnier.

September — der Sprint

September war der intensivste Einzelmonat des ganzen Projekts bis dahin. Vier Wochen, vier grosse Systeme.

Transfer-Screen mit Watchlist und Kompatibilitaets-Scoring. Das System berechnet nicht nur ob ein Spieler gut ist, sondern ob er in die eigene Taktik, auf die Altersstruktur des Kaders und ins Budget passt. Nicht “dieser Spieler hat 17 in Technik” sondern “dieser Spieler loest das konkrete Problem deiner Mannschaft”. Das erfordert dass das System weiss was das Problem ist — fehlende Taktische Intelligenz im Mittelfeld, zu geringe Ausdauer in der Abwehr, kein Freistoss-Spezialist. Das Scoring macht das transparent ohne den Spieler zu bevormunden.

Finanzsystem mit getrenntem Transfer- und Gehaltsbudget. Dynamische Stadioneinnahmen die von Vereinsreputation und Ergebnistrend abhaengen — ein Verein der drei Spiele in Folge verliert kriegt weniger Zuschauer, weniger Einnahmen, mehr Vorstandsdruck. Gehaltsstruktur mit Bonusklauseln (Meisterschaft, Pokalfinale, Torgrenzen). Eine Saisonprognose die zeigt wohin man finanziell steuert wenn man weiter so wirtschaftet.

Verletzungssystem mit drei Schweregraden: Leicht (1–3 Spieltage), Mittel (4–12 Spieltage), Schwer (13–30 Spieltage). Das Risiko haengt vom Verletzungsanfaelligkeit-Attribut, dem aktuellen Energielevel und der Spielintensitaet ab. Spieler mit 90 Minuten auf dem Ruecken und niedrigem Energielevel sind signifikant gefaehrdeter als frische Einwechselspieler — das schafft echte Rotations-Anreize die sich im Spielplan niederschlagen muessen.

Speichersystem mit mehreren Slots und automatischer Sicherung nach jedem Spieltag. Die Implementierung muss atomisch sein: Der Spielstand wird zuerst in eine temporaere Datei geschrieben und erst dann zur echten Slot-Datei umbenannt. Niemals ein halbgeschriebener Spielstand der beim naechsten Laden nicht gelesen werden kann.

Oktober — die kleinen Schlachten

Oktober war kein Monat mit einem grossen Feature. Es war ein Monat mit zwanzig kleinen Problemen die alle geloest werden mussten bevor das Spiel sich nach einem echten Spiel anfuehlte.

Der Post-Match-Analyse-Screen: xG-Verlauf ueber die 90 Minuten als Kurve. Tagesform beider Mannschaften. Energie-Kurven im Spielverlauf. Spieler-Einzelwertungen mit kurzem erklaerenden Text. Der Screen soll sich anfuehlen wie der Blick eines Trainers auf die Statistiken nach dem Abpfiff — nicht wie eine Datenbankabfrage.

Aufloesung-Profile fuer HD (1920×1080), WQHD (2560×1440) und 4K (3840×2160). ImGui mit absoluten Pixel-Koordinaten bedeutet dass jeder Abstand, jede Font-Groesse, jeder Component-Offset von der Aufloesung abhaengt. Ich hab ein LayoutResolver-System entwickelt das alle Masse von der Referenzaufloesung 2560×1440 auf die aktive skaliert — mit Ausnahme-Regeln fuer Dinge die nicht linear skalierbar sind. Spaeter wurde das zum vollstaendigen Design-System ausgebaut.

Das Chemie-System: Spieler die regelmaessig zusammen spielen entwickeln Verbindungen die ihre gemeinsame Leistung leicht verbessern. Das Designproblem: Wie verhindert man dass es zu einem simplen “kaufe moeglichst viele Spieler aus demselben Land”-Mechanismus wird? Loesung: Gemeinsame Spielzeit wird fuenfmal staerker gewichtet als Nationalitaet. Zwei Spieler die seit drei Saisons zusammen spielen haben mehr Chemie als zwei frische Neuzugaenge derselben Nation. Das macht Chemie zu einer Langzeitinvestition und belohnt Kaderkontinuitaet statt Shopping-Tourismus.

November — Audio, zwei Wochen Hoelle, und das ImGui-Styling

Audio. Ich hatte geschaetzt dass die Integration drei Tage dauert. Es wurden zwei Wochen.

Das Problem war nicht Audio an sich — das Problem war die Architektur. ImGui mit OpenGL in einem eigenen Render-Loop hat keinen natuerlichen Platz fuer Audio. In einer Game-Engine gibt es einen Game-Loop der jedes Frame Audio-Events verarbeitet. Ich habe keinen Game-Loop. Ich habe einen Render-Loop der Frames zeichnet. Ich musste NAudio einbinden, einen eigenen Audio-Thread aufbauen der unabhaengig vom Render-Loop laeuft, eine Thread-sichere Ereignis-Queue implementieren ueber die Spielereignisse (Tor, Abpfiff, Karte) an den Audio-Thread kommuniziert werden, und dabei sicherstellen dass Audio-Events nicht mit dem Rendering interferieren.

Der erste Test nach einer Woche: Crowd-Loop beim Spielstart. Er spielte — aber nicht synchron. Manchmal dreifach gleichzeitig weil der Loop-Trigger mehrfach ausgeloest wurde. Manchmal gar nicht weil der Thread noch nicht bereit war. Drei Tage nur fuer den Crowd-Loop. Dann Tor-Jubel: Korrekt, aber mit variablem Delay je nach CPU-Last. Nochmal drei Tage. Am Ende: Audio-Events werden mit Timestamp in die Queue geschrieben, der Audio-Thread verarbeitet sie in Order und puffert Loops mit Fade-In und Fade-Out. Kein Doppel-Trigger, kein unvorhersehbares Delay.

Das .tepk-Format (TaktikManager Extension Pack) kam gleichzeitig: Ein ZIP-Archiv mit manifest.json und Audio-Dateien. Vereinshymnen, eigene Crowd-Sounds, Kommentatoren-Clips — alles modbar von Anfang an und nicht als Nachgedanke spaeter.

Im November hab ich auch das ImGui-Styling systematisch angegangen. Ich hatte monatelang mit “funktioniert aber sieht nicht gut aus” gelebt. Jetzt: Farbpalette definiert mit sechs Hauptfarben in je drei Helligkeitsstufen, einem Akzent-Ton und zwei Warn-Toenen. Abstaende vereinheitlicht als vier Spacing-Tokens (XS/S/M/L). Hover-Zustaende fuer alle interaktiven Elemente. Typografie-Hierarchie mit drei Font-Groessen und zwei Weights. Jeder Pixel von Hand. Ich bin kein Designer. Aber es ist das Beste das ich alleine hinbekomme, und es ist sichtbar besser als vorher.

Ende November: Phase 2 abgeschlossen. Match Engine mit xG und deterministischem Seed, Kalibrierung auf reale Fussball-Statistiken, Spielplan-Generator fuer Liga und Pokal, Verletzungssystem, Speichersystem, Chemie-System, Finanzsystem, Transfer-Screen mit Kompatibilitaets-Scoring, Audio mit eigenem Thread und .tepk-Support, Post-Match-Analyse, drei Manager-Modi, HD/WQHD/4K-Profile. In Mai hatte ich ein schwarzes Fenster. In November hatte ich ein spielbares Spiel. Phase 3 beginnt. Die Tiefe kommt.

]]>
https://taktikmanager.de/block-02-phase-2-kernspiel/feed/ 0
Block #01 — Phase 1: Angefangen. Einfach angefangen. https://taktikmanager.de/block-01-phase-1-fundament/ https://taktikmanager.de/block-01-phase-1-fundament/#respond Mon, 28 Jul 2025 20:00:00 +0000 https://taktikmanager.de/block-01-phase-1-fundament/ 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.

]]>
https://taktikmanager.de/block-01-phase-1-fundament/feed/ 0