Mafia 2: Definitive Edition ist eine toll spielbare Gangstergeschichte
Mafia 2: Definitive Edition (DE) ist eine Neuauflage des 2010 veröffentlichten Mafia 2. Der Spieler schlüpft in die Rolle von Vito Scaletta, der nach dem zweiten Weltkrieg in die USA zurückkehrt. Im Krieg war er nur, weil er bei einem Überfall erwischt wurde, da erschien der Militärdienst als bessere Alternative zum Gefängnis. Sein damaliger Kollege Joe steht mittlerweile in Kontakt mit der Mafia und Vito hat keinerlei Hemmungen, direkt wieder den kriminellen Weg einzuschlagen. Und wie das in Gangsterfilmen nunmal so ist: Das Spiel muss vorführen wie irregeleitet diese Entscheidung war, aber in der Zwischenzeit die Exzesse und das Drama feiern.
Shooter, Autos, die 40er und 50er
Wie schon das erste Mafia entlässt Mafia 2 den Spieler in eine ziemlich hübsch simulierte Stadt. Ähnlich wie in GTA gibt es Passanten und Autos, nur dass die wunderschönen Autos hier aus den 40ern und 50ern stammen. Mit ihnen kann man frei in der Stadt herumfahren. Wer zu schnell unterwegs ist, wird bald von der Polizei verfolgt. Führt das zu Karambolagen wird das eigene Auto beschädigt, die Reparaturen kosten, anfangs ist das Geld knapp. Neue Autos zu stehlen ist aber kein Problem und so die einfache Alternative.
Die meiste Zeit wird man aber nicht einfach in der Stadt herumfahren, denn Mafia 2 ist stark storygetrieben. Es gibt nur einen kurzen Abschnitt am Ende, in dem das freie Entdecken und Erledigen von Aktivitäten wie Autodiebstahl gefördert wird. Ansonsten gibt es immer einen klaren Missionszielpunkt. Die meisten der 15 Storykapitel beginnen so: Vito wird vom Telefon geweckt, weil Joe oder ein anderer Gangster ihn braucht. Der Spieler muss zu einem Treffpunkt fahren, dort passiert dann irgendwas. Beispielsweise soll ziemlich am Anfang ein Juwelier nachts ausgeräumt werden. Das geht nicht so unentdeckt wie geplant, daraufhin beginnt eine Flucht und Schießsequenz. Bei den Schießereien sollte man in Deckung bleiben – passend für ein Spiel aus 2010 – und die Gegner mit den vielen Waffen (wie natürlich der Tommygun) bevorzugt mit Kopfschüssen erledigen.
Was genau getan werden muss variiert ein bisschen. Doch im Grunde sind es immer Schießereien, Fahrten oder die eine von der Quote geforderte Schleichmission. Die Fahrten werden dabei manchmal etwas lang.
Getragen von Story und Inszenierung
Das alles funktioniert, weil es eine gute Geschichte ist die spannend erzählt wird. Da ist die Inszenierung: Wenn Vito vom Krieg zurückkommt ist er nicht einfach plötzlich in einer schneebedeckten Stadt. Sondern Joe holt ihn vom Bahnhof ab. An einer Straßenecke trifft ein einen Nachbar, der gerade seinen Laden mit Brettern zuhämmert und Hallo sagt, bleibt der Spieler stehen reden die beiden eine Weile miteinander. Kurz darauf rutscht ein Passant auf dem Glatteis aus. Solche Spielereien werden später seltener, aber helfen am Anfang sehr, das Spiel dichter wirken zu lassen.
Und die Mafiageschichte um Vito und Joe und ihren gewaltsamen Aufstieg in der Verbrecherwelt ist zumindest unterhaltsam. Es stört ein bisschen, dass sie im Vergleich zum ersten Teil etwas arg glatt ist. Dort war es ein zögerlicherer Einstieg, sodass das Handeln des Protagonisten realistischer wirkte. Vito ist zu psychopathisch und emotionslos, ohne dass dieser Eindruck beabsichtigt wirkt, es wird zu schnell zu brutal – dabei sei er der vernünftigere. Und doch funktionieren solche Mafiageschichten eben, mit ihren typischen Charakteren und Wendungen. Mafia 2 schafft es immerhin, eine interessante Geschichte zu erzählen und mit passendem Spielinhalt zu füllen.
Sammelitems
Der Spielinhalt besteht aus etwas mehr als den Missionen. Dazu kommen die im Spiel einfachst stehlbaren Autos, sie können aufgerüstet, umlackiert und in der eigenen Garage gelagert werden. Dort stehen dann auch ihre Kennzahlen, Höchstgeschwindigkeit zum Beispiel. Es ist nett, so seine Lieblingsautos zur Auswahl zu haben – wobei die Missionen oft andere Autos vorgeben und Verfolgungsfahrten nicht zu häufig sind. Und zumindest in der Definitiv Edition startet Vito direkt mit zwei guten Wagen. Trotzdem, das passt zum Spiel und bereichert es.
Die regulären Sammelitems dagegen sind etwas komisch. Fürs Bewältigen der Kapitel auf dem höheren Schwierigkeitsgrad werden gezeichnete Pin-Up-Bilder freigeschaltet, die aber nur lose an die Handlung angelehnt sind. An manchen Wänden kleben Steckbriefe (der Entwickler?), die gesammelt werden sollen. Vor allem aber gibt es echte Playboy-Bilder.
Doch warum? Die Handlung spielt Jahre vor dem Erscheinen der ersten Playboyausgabe und das Spiel hat auch sonst keinen besonderen Bezug zu Sex. Es gibt einzelne harmlose Szenen mit Prostituierten auf Feiern, das wars. Und was bitte hat der Playboy mit Mafia zu tun? Das ganze ist wohl eine offizielle Kooperation zwischen Studio und dem Magazin gewesen. Ich will ja nicht verneinen, dass es einen gewissen und offensichtlichen Motivationsfaktor hat die in den Level als Magazine verteilten Bilder zu sammeln, aber es ist auch komplett unpassend und störend. Ein Schmuddelfaktor, den dieses PC-Spiel nicht braucht und von dem es nichts gewinnt.
Technik unter Linux
Da Mafia 2 nur mit Proton unter Linux läuft will ich die dort auftretenden Technikprobleme nicht gegen das Spiel werten. Insgesamt war es gut spielbar, aber es gab kleinere Probleme und es muss unbedingt richtig konfiguriert werden. Laut ProtonDB hilft eine neuere Proton-Version, bei mir lief Proton-5.21-GE-1. Vor allem aber muss in den Einstellungen das FPS-Limit auf Unlimited gesetzt werden. Dann springt die FPS von 10 auf (bei mir mit V-Sync) 75. Allerdings waren die FPS und manchmal auch die Frametimes in einzelnen Szenen nicht optimal, beispielsweise wenn man mit dem Auto durch Tunnel fuhr. Und es gab vereinzelte Grafikfehler wie aus einem Hydranten schießendes Wasser, das als Vielzahl von weißen Blöcken gezeichnet wurde.
Etwas genereller gültig aber: Wie ein Spiel von 2020 sah es für mich nicht aus. Das Original ist sowieso älter, aber die Definitive Edition ist dieses Jahr und mit verbesserter Grafik herausgekommen. Vergleichsvideos zufolge ist es deutlich verbessert, aber auf mich wirkte es immer noch sehr wie ein älteres Spiel. Kein Problem, es ist ein hübscheres älteres Spiel, vor allem die Autos sehen durchaus gut aus, auch die Charaktermodelle passen und das Design der Räume und Umgebungen ist toll. Aber dann stimmt da eben doch manchmal die Sichtweite nicht, erscheinen Modelle zu spät und sind einzelne Texturen nicht realistisch.
Na gut, für mich als Besitzer des Original war es ein kostenloses Update. Doch neue Käufer sollten sich den Limitierungen bewusst sein.
Es gab auch einige kleinere Bugs. Desöfteren verschwand einer der Begleiter, anstatt Vito zu folgen. Immerhin passierte das nie in den Kampfmissionen, sondern nur auf kurzen Wegen zum Auto, wo er (es war meist Joe) dann wieder erschien. Von den im oben verlinkten Vergleichsvideo erwähnten Animationsfehlern konnte ich nichts beobachten, die wurden wohl gepatcht oder waren konsolenexklusiv.
Fazit
Mafia 2 DE ist nur auf dem Papier ein Open-World-Spiel. Ja, man kann frei in der Stadt herumfahren und es gibt sogar einige wenige Aktivitäten, doch eigentlich ist man immer in eine Mission eingebunden. Damit ähnelt es stark seinem Vorgänger und unterscheidet sich deutlich von Spielen wie Saints Row oder GTA. Doch das muss nicht schlecht sein: Denn so liegt der Fokus eben auf der Story und den Missionen. Und da liegt er gut, denn die Story trägt gut durch die etwa 14 Stunden Spielzeit, in der in den Missionen eine Gangsterfantasie nach der anderen serviert wird.
Dass der zweite Teil bei der Story nicht ganz so elegant wie der Vorgänger wirkt kann ich ihm verzeihen, da mag die Nostalgie auch eine Rolle spielen. Immerhin hat er auch weniger spielblockierende Missionen wie das berüchtigte Autorennen des ersten Teils. Spielerisch gibt es Third-Person-Schießereien mit einem epochentypischen Deckungssystem und Autofahrten in einer authentisch wirkenden US-Stadt der 40er/50er. Technisch wirkte das Spiel auf mich nicht ganz rund, was teilweise an meinem Linuxsetup liegen mag, ansonsten ist das Original auch einfach nicht mehr taufrisch. Das soll nicht stören; Insgesamt ist Mafia 2 DE sehr unterhaltsam und definitiv spielenswert.
Lara Croft and the Temple of Osiris ist verwirrend und verwirrt
Lara Croft and the Temple of Osiris ist ein kurzes Hack'n Slay mit Pistolen und Rätseln.
Da spricht erstmal nichts gegen, auch kurze Spiele im Genre können Spaß machen, wenn die Kämpfe unterhalten, es tolles Loot gibt, die Rätsel interessant sind oder die Story motiviert. The Incredible Adventures of Van Helsing beispielsweise ist so ein Hack'n Slay, das zwar kein episches Diablo 2 sein mag, aber mit eigenen Stärken trotzdem charmant und spaßig ist. Dieser Lara-Croft-Ableger fällt dagegen verblüffend stark ab.
Flotte Kämpfe, nette Rätsel
Zuerst das Gute: Die Kämpfe. Das Spiel schickt euch in mehrere kurze Dungeons, in denen einige Gegner auf euch warten. Die sind hübsch abwechslungsreich, so gibt es Skelette mit Suchprojektilen, Krokodile, die man erst umwerfen und dann mit einer Bombe erledigen muss, und übergewichtige explodierende Riesen. Die Gegner sind schnell, aber Lara Croft ist schneller, mit ihrer Ausweichrolle und den vielen Waffen sind die Kämpfe gut gelungen. Zwar sind sie meist einfach, aber ein paar der Bossgegner und die Kombinationen mit den Sprungpassagen sind dann wieder fordernder.
Auch das Leveldesign in den Dungeons ist nicht schlecht. Es gibt immer wieder kleine Schalterrätsel zu lösen, dann müssen große Bälle mit Bomben über Absperrungen geworfen werden, dabei klettert Lara von einem Vorsprung zum nächsten. Die Level sind oft erstaunlich kurz, aber dadurch sie sind selten langweilig. Und die Bossgegner kombinieren immer wieder Kämpfe mit kleinen Timing- und Rätselpassagen, sie sind allesamt gut gelungen.
Wiederspielwert?
Andererseits tut das Spiel so, als gäbe es einen Grund die Levels mehrmals zu spielen. So gibt es in ihnen Herausforderungen – ähnlich wie in Victor Vran – die dann weitere Upgrades, oder Items wie Ringe, Amulette oder sogar neue Waffen freischalten. Doch gibt das Spiel dem Spieler keinen Grund, das zu tun! Nur die Waffen wären etwas interessant, doch finden sich auch so genug und sind die Kämpfe nicht besonders schwer. Amulette und Ringe sind komplett witzlos, geben sie doch nur irrelevante Boni.
Genauso irritierend sind die vielen Schatzkisten, die überall verteilt sind. In ihnen finden sich ausschließlich (zumindest in meinem Durchlauf) Ringe und Amulette, die man ja nicht braucht. Und um die Truhen öffnen zu können, müssen gesammelte Edelsteine eingesetzt werden. Nach den meisten Dungeons kommt man in einen Schatzraum mit 20 solcher Truhen, hat aber meist nur Juwelen für eine der Truhen, und in der ist dann nichtmal etwas nützliches drin!
Lara Croft and the Temple of Osiris wirkt damit so, als hätten die Spieldesigner blind die vermeintlich motivierenden Elemente von Hack'n Slays wie eben die Truhen in ihr Spiel gepackt, dann aber nicht die Zeit gehabt, um diese Elemente drumherum ein Spiel zu basteln, das Gegenstände abwirft die in diese Truhen hereingepackt werden könnten.
Schlappe Inszenierung
Dazu kommt eine völlig hanebüchene Story. Lara und ein Konkurrent finden einen Stab, berühren ihn, daraufhin erwacht Seth und will die Menschheit versklaven. Lara, zwei ägyptische Götter und ihr Konkurrent müssen ihn aufhalten, wobei der Spieler nur einer der Figuren spielt. Okay, der Kern der Story bei Diablo mit den Seelensteinen und dem Erdenende ist auch nicht viel kreativer, aber der Unterschied ist die Inszenierung und wie die Story geschrieben ist. Was die Charaktere hier von sich geben ist einfach peinlich. Da hilft es nichts, dass einzelne der Zwischensequenzen in Spielgrafik nicht ganz schlecht aussehen.
Dass der Rest des Spiels ebenfalls nicht ganz schlecht aussieht macht es dann aber wieder ein bisschen besser. Für kurzweilige Unterhaltung kann gute Grafik nur helfen.
Spiel- oder Ignorierbar
Die ganze Sache ist dann nur etwa 5 Stunden lang. Es könnte etwas mehr sein, wenn die Zusatzlevels der DLCs enthalten wären – bei einem so kurzen Spiel nicht alles hineinzupacken ist frech. Aber das wäre kein Problem, wenn dem Spiel sein Charakter klar wäre. Es ist eine kurze und kurzweilige Unterhaltung für zwischendurch, die im Koop-Modus nochmal mehr Spaß machen soll. Hätte es sich darauf konzentriert, wäre es ein gutes Spiel geworden.
Stattdessen gibt es viel Loot, das aber komplett uninteressant ist, und nach Spielende Hinweise auf Post-Game-Inhalte, ohne dass es vorher auch nur Ansätze einer funktionieren Meta-Spielmechanik gegeben hätte. Das Komitee, das dieses Spiel zusammengestückelt hat, hat sich komplett verrannt.
Aber da die Dungeons mit ihren Kämpfen und Rätseln schon Spaß machen und das Spiel nicht zuviel Zeit kostet kann man es auch mal eben durchspielen, wenn man es umsonst oder sehr günstig bekommt. Idealerweise wohl wirklich im Koop mit mindestens einem Mitspieler, sodass das gemeinsame Bewältigen der Dungeons den Spaßfaktor erhöht. Oder man lässt es zugunsten eines besseren Spiels bleiben – viel verpassen würde man nicht.
Immutability und ein praktischer Einsatzzweck
Objekte die immutable sind können in keiner Weise modifiziert werden. Doch was bringt das?
Das Konzept
Viele Sprachen – nicht alle natürlich – unterstützen das Konzept, mal für alle Datentypen, mal nur für spezielle. Aber grundsätzlich könnte es so aussehen:
class Car immutable doors = ['door1', 'door2'] end
Wenn der Entwickler sich dann später ein entsprechendes Objekt erstellt, dann kann er das doors
-Attribut nicht ändern:
myCar = new Car(); myCar.doors.add('door3') #=> Error!
Immutability ist ein Konzept, das man besonders oft bei funktionalen Programmiersprachen finden wird. Wer sich Gedanken über Seiteneffekte macht, der wird auch Immutability bedacht haben. Es kann auch massiv dem Compiler helfen, wenn Objekte entsprechend markiert sind und er so weiß, dass diese Objekte sich nie ändern können. Programme werden so schneller oder sicherer.
Wobei, von wegen funktional, const
geht ja in eine ganz ähnliche Richtung.
Ein Anwendungsfall
Doch hilft es dem Entwickler? Das allerwichtigste ist Entwicklerzeit, -effizienz und -komfort. Immutability schadet dabei oft. Wenn ich als Entwickler ein geschütztes Attribut ändern will, habe ich wahrscheinlich einen guten Grund. Wenn ich jetzt nur des Compilers wegen ein neues angepasste Objekt erstellen und am besten noch einige Attribute kopieren muss, dann stimmen die Prioritäten nicht.
Aber jetzt lief ich in diesen Fall: Ich hatte eine Liste. Und irgendwas veränderte diese Liste. Und zwar wurde der letzte Eintrag in ihr länger. Doch nirgends in meinem Programm gab es eine solche Zuweisung, zumindest keine die eindeutig zu erkennen war.
Was wohl passiert war: Es gab ein Fold.
newList = originalList; targetMap = newList.fold({}, (prev, element) => prev..addAll(element));
Und dazu einige andere solcher Akkumulatoren, die meine Originalliste zusammenfassten (die in Wirklichkeit auch keine einfache Liste war, sondern eine Liste von Listen von Ojekten in einem Elternobjekt). Und irgendeiner davon hatte als Nebeneffekt, dass die Originalliste – denn in dieser Sprache werden meist Referenzen übergeben – ebenfalls verändert wurde.
Die Lösung: Die Liste als immutable markieren.
Die Akkumulatorfunktionen wie das fold
funktionierten immer noch. Aber anstatt die Originalliste zu verändern, erstellten sie sich automatisch ihre eigene Kopie. Die Originalliste blieb unverändert, der Bug war gefixt.
Es ist also keinesfalls so, dass Immutability nur den Code verkompliziert. Im Gegenteil, richtig eingesetzt kann es ein Programm wesentlich einfacher zu verstehen machen, dann dient das Konzept dem Entwickler. Besonders in Sprachen, die Referenzen einsetzen anstatt direkt Werte zu kopieren.
Die Radeon RX 6800 (XT) unter Linux sieht klasse aus
Ich bin gerade durch das Phoronix-Review der neuen AMD-Grafikkarten gegangen und bin beeindruckt. Sie testen die Leistung für Spiele unter Linux, mit und ohne Proton und mit AMDGPU-PRO sowie dem freien Mesa 20.3. Das Ergebnis ist einfach stark. Sie kommen alles zusammengerechnet auf dieses Benchmarkergebnis:
Die Treiber sind sehr nah beieinander, die 6800 XT is nah bei der RTX 3080 und die 6800 stärker als die 2080 Super. Zum Vergleich, so sieht das in meinem Meta-Benchmark unter Windows aus:
Das passt! Wenn auch die 3070 wahrscheinlich unter Linux anders als unter Windows etwas stärker als die kleinere 6800 ist, wird der Unterschied nicht groß sein. AMD hat also nicht nur eine Grafikkarte herausgebracht, die ordentlich unter Linux läuft. Das hatten wir schon in vorherigen Releases. Sondern AMD hat eine Karte herausgebracht, die es mit Nvidas neuen Top-Karten aufnehmen kann und deren relative Leistung unter Linux mit dem freien Treiber erhalten bleibt. Es gibt also endlich eine High-End-Grafikkarte für Linux mit einem freien Treiber.
Was großartig ist, auch wenn man selbst nicht unbedingt so viel für eine Grafikkarte ausgeben will. Die günstigeren Varianten werden ja ähnlich gut – relativ gesehen – unter Linux laufen.
Deadlight - hübsch vielleicht, doch gut?
Wenn ich Deadlight richtig einordne ist es eines dieser Indie-Spiele, für die Microsoft damals Publisher gespielt hat. So kamen die Spiele erst zur Xbox, dann auf den PC, die Entwickler wurden mehr als damals üblich unterstützt. Das war schon 2012, wobei 2016 ein Director's Cut herauskam.
Gute Sache, aber leider kein gutes Spiel. Deadlight versucht absolut mehr zu sein als ein einfacher 2D-Platformer. Da ist zum einen die Grafik, mit ihren ewigen Schatten und weitläufigen Hintergründen. Und zum anderen die Story, bei der die Zombieapokalypse mit soviel Emotionen wie möglich gefüllt wird. Die Hauptfigur Wayne hat seine Familie verloren, seine Reisegruppe wird angegriffen, die Überlebenden zu retten ist die Aufgabe. Dabei trifft er andere Überlebende, wovon einige ohne anfangs ersichtlichen Grund ihn angreifen. Soweit geht das alles in Ordnung.
Aber der Spielinhalt selbst stimmt nicht. Die Steuerung ist zu schwerfällig und fehleranfällig. Immer wieder bleibt Wayne hängen, springt nicht so schnell oder so weit wie ich will. Erklimmt er eine Kante, lässt er sich oft genug an ihr direkt wieder fallen anstatt weiterzulaufen. Und obwohl als Plattformer ein Controller die bessere Eingabemethode wäre, gibt es auch Fernkampfwaffen (eine Pistole, dann eine Schleuder – keine Waffe mehr, aber zum Betätigen entfernter Schalter) die mit der Maus tausendmal besser zu zielen sind als mit dem rechten Stick. Teilweise könnte das an Proton liegen, doch ist laut ProtonDB die Unterstützung nahezu perfekt.
Und dazu kommt (was dann garantiert nichts mehr mit Proton zu tun hat), dass der Grafikstil es an vielen Stellen nicht gerade einfach macht, schnell zu erkennen was Hintergrund und was ein erreichbarer Vorsprung ist. Die Sprungpassagen und die Level mit ihren Fallen werden durch ihr sowieso etwas abstruses Leveldesign, dem Grafikstil und dann noch den Steuerproblemen zu einem frustrierenden Ratespiel, bei dem meist nur mit viel Neuladen ein Abschnitt bewältigt wird.
Als Auflockerung sind überall in der Spielwelt kleine Sammelobjekte verteilt, zum Beispiel die Personalausweise Verstorbener. Doch warum sollte man die sammeln? Das Spiel gibt keine Belohnung. Manchmal finden sich Tagebuchauszüge von Wayne selbst, was eher aus dem Spiel rausreißt als interessant ist, denn wie sollen die dort hingelangt sein? Auch sind die Texte in dem Tagebuch erkennbar verzweifelte Versuche, dem ganzen mehr Tiefe zu geben. Sie sind zu lang und vom Spielgeschehen losgelöst, sie bringen dem Spielinhalt nichts dazu.
Bei Deadlight stimmt der Fokus einfach nicht, es hat zu wenig Substanz. Ich hatte von anderen Bewertungen beeinflusst ein nettes kleines Spiel mit guter Atmosphäre erwartet, doch so würde ich Deadlight jetzt definitiv nicht einordnen. Sondern eher als belanglos, frustrierend und unspaßig.
Age of Empires 2: Definitive Edition unter Linux
Die erneuerte Variante des Klassikers Age of Empires 2 läuft mit Proton unter Linux. Sogar der Multiplayer geht, aber nicht ohne weitere Anpassungen. Die Goldwertung auf ProtonDB ist da etwas irreführend.
Ich benutzte Proton-5.9-GE-8-ST, die modifizierte GloriousEggroll-Version. Damit lief das Offline-Match gegen die KI, ohne dass mir Probleme auffielen. Aber beim Multiplayer gab es immer fast sofort einen Disconnect.
Die Lösung fand ich auf Github:
rm ~/.steam/steam/steamapps/compatdata/813780/pfx/drive_c/windows/system32/ucrtbase.dll cd ~/.steam/steam/steamapps/compatdata/813780/pfx/drive_c/windows/system32/ wget "https://aka.ms/vs/16/release/vc_redist.x64.exe" cabextract vc_redist.x64.exe cabextract a10
Nach dem Ersetzen der Dateien lief dann auch der Multiplayer, bei mir bis jetzt stabil.
Update: Ich habe jetzt eine Weile nicht mehr gespielt, aber der Fix sollte weiterhin funktionieren. So landete erst vor drei Wochen – im Februar 2023 – in der ProtonDB ein Eintrag, laut dem Age of Empires mit einem cabextract-Workaround für den Multiplayer repariert werden konnte. Interessant ist die Abweichung, anstatt die vc_redist.x64.exe herunterzuladen kann sie dem Eintrag zufolge auch aus dem Hauptverzeichnis des Spiels kopiert werden. Und es funktionierte mit einer neueren Protonversion, Spieler sind also nicht auf die 5.9-GE8 beschränkt, sondern sollten einfach die aktuelle versuchen.
Multiplayer meinte bei mir übrigens nicht ranked, sondern ich spielte normale Multiplayermatche mit einem Kumpel. Bei der Microsoft-Accounteinbindung gab es auch Probleme, sodass ich manche dieser kleinen Aufgaben nicht erledigen konnte, die Avatar-Icons freigeschaltet hätten. Ich weiß nicht, ob diese Probleme Ranked-Spiele blockiert hätte – Erfahrungen dazu bitte gerne als Kommentar teilen.
Dafür hatte ich einen sehr positiven Eindruck von der Performance des Spiels. Auch gegen mehrere Gegner und mit vielen Einheiten auf der Karte sah ich keine FPS-Einbrüche. Gut, würde man bei einem so alten Spiel außerhalb extrem vollen Karten vielleicht auch nicht erwarten, aber Proton kann ja manchmal doch Performance kosten. Stabil war das Spiel auch, die Abstürze die ich sah hingen völlig an einer defekten Grafikkarte. Sobald die ausgewechselt war lief das Spiel bei mir komplett stabil.
7GUIs in Flutter (2/7)
Die sieben Aufgaben von 7GUIs sollen typische Problemstellungen bei der Anwendungsentwicklung widerspiegeln. Dann können die gebauten Lösungen dafür benutzt werden, verschiedene Programmiersprachen und Toolkits miteinander zu vergleichen.
Ich dachte, das ist eine gute Gelegenheit hier nochmal Flutter zu zeigen. Die Vorstellung letzten Monat zeigte relativ wenig von den damit baubaren Oberflächen.
Flutter ist ja deklarativ aufgebaut, das heißt die Oberfläche baut sich immer wieder neu und reagiert dann auf die neuen Variablenwerte. Man kann dafür StatefulWidgets
benutzen, die Variablen im Widget-Stateobjekt speichern und dann immer mit setState
anzeigen, dass die Oberfläche sich doch bitte neubauen soll. Ich werde stattdessen mit GetX der Oberfläche einen Controller zur Seite stellen, in dem die Variablen leben und der dafür sorgt, dass auch Widgets ohne reguläres Zustandsobjekt interaktiv sein können. Aber seht selbst:
1. Counter
Die erste Aufgabe ist ein Klickzähler. Doch einen Counter zu erstellen ist keine Herausforderung, das ist das Standardbeispiel auf der Flutterhomepage und bei Modulen wie GetX, die das Statemanagement übernehmen wollen. Entsprechend habe ich hier nur das GetX-Beispiel genommen und die Oberfläche angepasst.
So sieht es aus:
Die Oberfläche ist eine Row
, in der ein Textfeld und ein Button sind.
Das ist der Code:
import 'package:flutter/material.dart'; import 'package:get/get.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter', theme: ThemeData( visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { // Instantiate your class using Get.put() to make it available for all "child" routes there. final Controller c = Get.put(Controller()); @override Widget build(context) => Scaffold( appBar: AppBar(title: Text('Counter')), body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ // Use Obx(()=> to update Text() whenever count is changed. Obx(() => Text("Clicks: ${c.count}")), RaisedButton(child: Text("Count"), onPressed: () => c.increment()), ], ))); } class Controller extends GetxController { var count = 0.obs; increment() => count++; }
Das Textfeld zeigt, da mit Obx
umschlossen, immer den aktuellen Wert der Zählvariable im Controller an. Ein Druck auf den Button erhöht diesen Wert.
Außer der Zählvariable im Controller und der Funktion increment()
hat die App keine weitere Funktionalität.
So funktioniert das dann in Bewegung:
Beachte auch, dass die Elemente wie die einer typischen Android-Anwendung aussehen. Das liegt schlicht daran, dass hier eine MaterialApp
gestartet wird.
2. Temperaturconverter
Die zweite Aufgabe ist eine Oberfläche zum Unwandeln von Celsius zu Fahrenheit und umgekehrt.
So sieht meine Lösung aus:
Das ist eine einzelne Row
, in der nacheinander je ein Texteingabefeld und ein Textanzeigefeld aufgereiht sind. Und ja, 0.0 bei beiden Feldern war nicht der richtige Defaultwert, wäre aber einfach änderbar.
Der Code:
import 'package:flutter/material.dart'; import 'package:get/get.dart'; // ... main() und MyApp sind weggekürzt class MyHomePage extends StatelessWidget { final Controller c = Get.put(Controller()); @override Widget build(context) => Scaffold( appBar: AppBar(title: Text('Temperature Converter')), body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ SizedBox( width: 80, child: Obx(() => TextFormField( // to force Obx reload when value changes key: Key("C" + c.celsius.string), keyboardType: TextInputType.number, initialValue: c.celsius.value.toStringAsFixed(1), onChanged: (value) => c.fahrenheit.value = c.ctof(double.tryParse(value)), ))), Text("Celsius ="), SizedBox( width: 80, child: Obx(() => TextFormField( key: Key("F" + c.fahrenheit.string), keyboardType: TextInputType.number, initialValue: c.fahrenheit.value.toStringAsFixed(1), onChanged: (value) => c.celsius.value = c.ftoc(double.tryParse(value)), ))), Text("Fahrenheit"), ], ), )); } class Controller extends GetxController { var celsius = 0.0.obs; var fahrenheit = 0.0.obs; double ctof(double c) { return c * (9 / 5) + 32; } double ftoc(double f) { return (f - 32) * (5 / 9); } }
Hier passiert jetzt schon ein bisschen mehr. Der GetX-Controller hat zwei Variablen, celsius
und fahrenheit
, und zwei Funktionen zum Umwandeln der Werte. In der UI wird immer, wenn im Texteingabefeld etwas eingeben wird, mittels onChanged
im Controller der Wert des anderen Texteingabefeld geändert. Weil die Eingabefelder wieder mit Obx
umschlossen sind baut dann Flutter direkt die Oberfläche neu, mit dem neuen Celsius/Fahrenheit-Wert als initialValue
der Eingabefelder. Einen key
zu setzen hilft flutter bzw Obx dabei, zu erkennen wann die Oberfläche neu gebaut werden muss.
So sieht es in Bewegung aus:
Ich habe den Code auch auf Github hochgeladen, so kann jeder ihn direkt importieren und abändern. Es gibt noch fünf weitere Aufgaben, die immer komplizierter werden. Vielleicht mache eine Serie hierdraus, dann folgen sie später.
Regions of Ruin – Toller Ansatz, aber es fehlt was
Regions of Ruin ist eine Genremischung in Pixelgrafik. Die Zutaten: Ein Sidescroller mit RPG- und Aufbauspielelementen. Man spielt einen Zwerg, der die aussterbende Zwergenrasse wiedervereinen will, dabei aber eine Monsterhorde voller Orks und Goblins besiegen muss.
Vom Lagerfeuer zum Dorf
Startpunkt ist die eigene Siedlung. Wo anfangs nur zwei Zwerge sich an einem Lagerfeuer wärmen entsteht nach und nach ein richtiges Dorf, wenn mit gesammelten Rohstoffen weitere Gebäude errichtet und ausgebaut werden. So kannst du zum Beispiel eine Taverne errichten. In der Taverne warten Söldner darauf, mit in die Gruppe genommen zu werden. Wird die Taverne vergrößert gibt es mehr Söldner zur Auswahl, dann kommt auch ein Brett für Kopfgeldquests hinzu. Die Rohstoffe wie Holz dafür werden nicht nur von dir selbst gesammelt, sondern auch von Arbeitern – meist befreiten Zwergen – die zu von Gegnern gesäuberten Gebieten geschickt werden können und dann von dort in einer festen Rate Rohstoffe einbringen. Solange der Vorrat reicht und das Lager noch nicht voll ist.
Diese Gebiete sind die auf der Karte verteilten und meist sehr kurzen 2D-Level. In ihnen gibt es NPCs – die oft Quests vergeben – Rohstoffe, Schätze und Monster. Um die Quests zu erfüllen muss man fast immer Monster töten, die auf dem oder einem anderen Level verteilt sind. Die werden schnell sehr stark, wohl dem Spieler der seine Söldnergruppe voll ausgebaut hat. Schätze meint Ausrüstungsgegenstände, wobei der Zwerg neben Rüstung, Waffe, Schild und Helm noch ein Amulett und einen Ring tragen kann. Wie beim Diablosystem haben manche dieser Items magische Eigenschaften und gibt es verschiedene farbig markierte Stärkeklassen, nach einer Weile müssen sie schon mindestens blau-magisch sein um eine Option sein zu können.
Hakelige Kämpfe
Die Kämpfe gegen die Monster und das Befreien der Level sind das Hauptelement des Spiels, und hier fängt es an problematisch zu werden. Anfangs alleine konnte man die Gegner sehr überlegt ausschalten, es gibt sogar eine Schleichfunktion, samt Verstecken hinter Fässern und Bonusschaden wenn Gegner überrascht werden. Treffer von Gegnern können Verletzungen verursachen, dann sinkt der Maximalgesundheitszustand, der Heiler in der Siedlung kann die Effekte entfernen. Angriffe können geblockt werden, es gibt Standard- und stärkere langsamere Angriffe, plus Ausdauerbalken, und mit bei Levelaufstiegen aktivierten Effekten kann der Spielercharakter angepasst werden.
Später jedoch ist man in der Gruppe unterwegs, und dann werden die Kämpfe sehr chaotisch. Gewinnen wird die stärkere Gruppe, aber welche das ist ist vorher kaum zu sehen. Die Level haben keine Gefährlichkeitsanzeige. So gibt es eine Hauptstory, in der Zwergenrunen aktiviert werden sollen, was dann jeweils ein entferntes Gebiet markiert. Das vierte davon war für mich und meine Gruppe viel zu stark – aber wie soll man erkennen, wann man für die Gegnergruppe bereit ist? Ich könnte jetzt nur Zeit investieren, mehr Level bewältigen, meinen Charakter und seine Gruppe aufleveln (auch mit einem Gebäude in der Siedlung, das Söldner viel stärker macht), und es dann periodisch wieder probieren. Aber das ist doch keine Lösung.
Abnutzungserscheinungen
Das wirkt auch deswegen so unattraktiv auf mich, weil sich jetzt langsam das Prinzip der 2D-Level abgenutzt hat. Wenn die Quests praktisch immer Töte ein paar Monster sind, wird das im Laufe der Zeit weniger interessant, selbst wenn das Spiel immer mal wieder neue Ideen aufbringt, wie (nicht toll umgesetzte) Festungen samt Monsterwellen, die man überleben soll. Gleichzeitig haben die regulären Gebiete das gleiche Problem: Die Monster sind teils zu stark, ohne dass das vorher ersichtlich ist. Sowieso, Grinden zu müssen ist kein gutes Spieldesign.
Die Bedienung ist dabei auch ein Problem. Ein Spiel wie Regions of Ruin spielt sich viel besser mit dem Controller. Das wird auch unterstützt, aber das Interface ist dafür nicht ausgelegt. Das Journal zum Beispiel lässt sich mit dem Controller gar nicht aufrufen, man muss die Maus in die Hand nehmen. Muss man auch, um im Söldnermenü den ausgewählten zu wechseln. Und sollte man, um auf der Karte Gebiete auszuwählen, da die Controllersteuerung dort nur manchmal und umständlich funktioniert. Problematisch wird auch das Schleichen: Jede der Schultertasten ist belegt, aber betätigt man aus Versehen die fürs Schleichen ist das ein Umschalter, der den Zwerg massiv verlangsamt – und damit im Gefechtschaos meist direkt tötet. Es ist offensichtlich, dass der Entwickler mit Maus und Tastatur spielt. Machen manche PC-Spieler auch bei solchen Spielen, macht den meisten aber keinen Spaß.
Stilfragen sind subjektiv, Bugs sind es nicht
Wenn die Grafik wenigstens konsistent hübsch wäre! Sie ist es stellenweise. Aber der Pixelmatsch bei den Charakteren und Gegenständen hat trotz der Anleihen mit der Eleganz der Spiele meiner Kindheit nichts zu tun. Immerhin wird die Freiheit einer solchen Engine genutzt, wenn jeder Ausrüstungsgegenstand am Spielercharakter zu erkennen ist (naja, soweit die Pixelfantasie trägt), die 2D-Gebiete voller Dinge und Hintergrundelemente vollstehen und in der Siedlung die Gebäude Schritt für Schritt wachsen. Aber trotzdem: Hübsch ist anders.
Voll zu den Problemen passt dann auch, wenn die Begleiter in den späteren vertikalen Leveln hängen bleiben. Nach unten fallen geht noch, aber dem Spieler springend folgen oder nach oben klettern - keine Chance. Oder wenn sie bei den Belagerungen an den Rand der Karte rennen und sich dort ohne Gegenwehr angreifen lassen. Da setzt die KI einfach komplett aus.
Fazit: Ein gutes Konzept macht noch kein gutes Spiel
Regions of Ruin hat mit seiner Mischung ein interessantes Konzept. Es fasziniert gerade zu Beginn, wenn all die Spielelemente anfangen zusammenzugreifen: Der Siedlungsaufbau, die hektischen Kämpfe, das Aufleveln des Charakters, dann die vielen Quests die Welt füllen und durch NPCs und gesammelte Buchseiten langsam die Hintergrundgeschichte zusammenkommt. Doch schnell verpufft dieser Effekt, wenn die kurzen 2D-Level zu wenig überraschen, die Kämpfe sich als repetitiv und zu chaotisch erweisen und der stark wachsende Schwierigkeitsgrad Grinden erfordert. Dazu die Schwächen bei Bedienung, Grafik und Begleiter-KI. Das erinnert an Pixel Piracy, das ähnlich faszinierend und dann unfertig war, wobei das mit etwas mehr Spielfortschritt auch noch unweigerlich kaputt ging. So kaputt ist Regions of Ruin nun nicht, doch auch es entpuppt sich nach wenigen Stunden leider als unfertiges Konzeptspiel.
Nette kurze Ablenkung: Guacamelee! Super Turbo Championship Edition
Während zum Día de Muertos im Wohnzimmer die Kerzen auf dem Ahnenaltar brannten spielte ich passenderweise Guacamelee!. Das Metroidvania bedient sich frei aus dem mexikanischen Totenkult und anderen Kulturelementen wie den Luchadores, Wrestlern, die in alten mexikanischen Fernsehserien als Superhelden gehandelt wurden.
Als solcher hinabgestürzt ins Totenreich muss der Spieler in einer etwa siebenstündigen Geschichte die Welt retten. Denn ein Oberskelett will mit einem Ritual die Welt der Toten und der Lebenden verschmelzen und dann beide beherrschen. Das klingt Ernst, ist es aber keinen Moment, denn die Geschichte und ihre Inszenierung ist komplett überdreht. Da spricht man mit einem Hahn der eventuell Satan ist, der Lehrmeister ist gleichzeitig eine Ziege, ein Zwischenboss ist eine zu einem Großskelett verschmolzene Mariachi-Band – und das alles untermalt durch den eigentümlichen, leicht abstrakten Grafikstil.
Jetzt habe ich dieses Jahr bereits Hollow Knight durchgespielt, was meinen Blick auf Guacamelee stark beeinflusste. Zum einen sind die beiden Spiele nunmal im gleichen Genre und ähneln sich entsprechend deutlich. Sprungpassagen, Kämpfe, Bosskämpfe, Sammelquests – es ist die gleiche Art Spielinhalt. Sie haben sogar viele der gleichen Upgrades, wie eine Flugfunktion.
Andererseits sind die Spiele auch sehr anders. Hollow Knight ist hübscher, viel länger, baut mit seiner Dark-Souls-artigen Unbestimmtheit eine melancholischere und deutlich dichtere Atmosphäre auf. Länger zu sein ist wirklich schon beim Spielen ein großer Unterschied, denn man merkt es stark an der Kürze von einzelnen Abschnitten Guacamelees. So fordert das Spiel auch weniger Durchhaltevermögen, wenn Kämpfe und Sprungpassagen zwar durchaus mal schwierig werden, aber nie so lange schwierig und ohne Zwischenspeicherpunkte aufgebaut sind, dass sie die Konzentrationsspanne überschreiten. Insgesamt war Hollow Knight sehr viel schwieriger, was mir nach der Umgewöhnung auf die leicht andere Tastenbelegung Guacamelee! abgesehen von einzelnen kniffligen Passagen recht einfach machte. Das gilt auch und gerade bei den Kämpfen, bei denen Guacamelee viel fehlertoleranter ist.
Anders betrachtet ist das auch ein Vorteil. Guacamelee frustriert viel seltener, eben weil die schwierigen Passagen und Kämpfe kürzer sind. Das passt gut zu der kurzweiligen Geschichte mit all den offenen und verstecken Witzen. Wo sonst ist es ein passendes Upgrade, sich in ein Huhn zu verwandeln! Und dann taucht Manny Calaveras Totenschädel auf einem Plakat auf. Wenn dann noch Zutaten für Enchiladas gesammelt werden sollen und generell jede Ecke (ein absurdes) Mexiko atmet, dann hat das was. So wie auch die mit gesammelten Münzen kaufbaren Kostüme nett sind, die Auswirkungen aufs Spiel haben. Der Satansanzug zum Beispiel verringert die maximale Lebensenergie, füllt sie dafür aber schon bei gelandeten eigenen Nahkampfangriffen wieder auf und nicht erst, wenn ein Gegner besiegt wurde.
Guacamelee! ist schon nett. Aber es nur ein Häppchen, eine nette kleine Abwechslung, die als solche nicht die Klasse anderer Metroidvanias wie Hollow Knight erreicht. Immerhin ist es ebenfalls kein teures Spiel und seinen Preis schon wert, besonders wenn es reduziert ist.
Object-Oriented Programming is Bad
Ich mochte diesen Vortrag. Man muss den Anfang mit seinem "das wichtigste Video ever" ignorieren, aber danach hat es ein paar gute Punkte (die sich nicht alle im zugehörigen Artikel finden). Es wird ziemlich überzeugend erklärt, was die Schwachstellen der objektorierentierten Programmierung sind: Dass dessen Ideal nicht erreicht werden kann und gar kein Ideal ist, warum Objektarchitekturen gerne ausufern und wieso man oft an Fragen gelangt, die nicht entscheidbar sind. Es sind zudem ein paar gute praktische Tricks drin die man in seinen Code einbauen kann, auch wenn die meisten nicht gerade neu sind. Aber sie sind gut gebündelt und im Kontext des Hauptarguments angemessen, lange Funktionen zu rechtfertigen und gekapselte lokale Funktionen ins Spiel zu bringen ist erfrischend.
Vor allem wird betont, dass Abstraktion nicht etwas positives ist, sondern automatisch Komplexität bedeutet, was sehr schlecht ist. Und das ist mindestens so richtig und wichtig wie es oft ignoriert wird. Es kollidiert mit vielem was gelehrt wird, aber ganz besonders mit den Paradigmen aus der Enterprise-Ecke. Man kann diesen Ansatz auch mit vielen gegenteiligen Entwicklungen im Javascriptuniversum vergleichen und wird ihn bestätigt finden.
Allerdings, was es für mich verdächtig komfortabel macht: Ich sehe einige Überschneidungen mit meiner angestrebten Programmarchitektur, die ich vor ein paar Jahren hier im Blog beschrieben habe und die sich seitdem nicht groß geändert hat. Wobei ich meinen Code wesentlich objektorientierter halte als es Brian Will (von dem der Vortrag stammt) wahrscheinlich gut fände. Doch wo der Code letztens Endes lebt ist in der Argumentation nicht so wichtig, solange er nicht unnötig abstrakt und über das Programm verteilt ist, und das war so ziemlich mein Hauptziel. Dieser Aspekt und die funktionalen Anleihen, die ich mittlerweile gerne einbaue, passen gut zu den Ideen des Videos.