Tagebuch: r-process

Danke das du dir die Zeit genommen hast. Ich hab mich mal darin eingelesen und das erinnert mich irgendwie an LiquidFun von Google (C++) bei dem der ganze Code gespickt ist mit solchen casts. Und jedes mal wenn man eine neue Shape-Sub-Klasse einführt, muss man überall im Code diese Stellen ergänzen (also über die Suchfunktion nach diesen Stellen im Projekt suchen) wie mit dieser Klasse umgegangen werden soll.

Naja, Kotlin steht sowieso als nächstes auf der Liste.
Scala nicht so sehr. Scala schien mir auf dem ersten Blick inkonsistent, redundant, kryptisch und vorallem nicht lösungsorientiert - erinnert sehr an C++. Aber das kommt von einem Typen der sich vielleicht eine Stunde, maximal 2, damit beschäftigt hat.

Naja ich wollte jetzt nicht dieses Thema hijacken.

War auf jedenfall mal interessant sowas zu lesen. :slight_smile:

Ich denke, dieser Eindruck war durchaus berechtigt, da ging sehr viel durcheinander, und nicht alle Konzepte waren wirklich orthogonal. Ich habe Scala jetzt nicht näher verfolgt, aber das Problem wurde durchaus gesehen, und die Idee mit ihrem neuen Dotty-Compiler war, die Sprache zu straffen und zu vereinheitlichen (und formal zu beweisen, dass das Typsystem korrekt ist). Ich werde mir das definitiv näher anschauen, wenn es soweit ist.

Siehe dazu Towards Scala 3 | The Scala Programming Language

Bin dabei, die Sternnamen durch einen Namens-Generator ein wenig aufzufüllen (und die nervigen Zahlen abzuschaffen). Probelesen ist immer eine gute Idee, ich habe gerade den Namen „Dicsux“ entfernt :rofl:

Ich bin am Überlegen, der Schiffsansicht mit der Fracht einen eigenen Screen zu spendieren, und auf der Sternsystem-Ansicht nur ein kleines Schiffs-Icon anzuzeigen.

Dafür gibt es zwei Gründe:

  • In das Sternsystem sollen noch mehr Dinge hinein: Wracks, Ruinen, selbstgebaute Strukturen, außerdem mehr Planeten *) und Monde
  • Für das Schiff wäre eine Übersicht cool, bei der z.B. auch der Research-Baum, Upgrade-Möglichkeiten, Kosten für den nächsten Sprung u.s.w. angezeigt werden

Trotzdem weiß ich, wie sehr ein zusätzlicher Klick nerven kann (auch wenn ich z.B. vom Galaxy-Map-Screen dann direkt zum Schiff springen könnte).

Was meint ihr?

(*) und auch größere, meine Gasriesen sind ja momentan eher Gaszwerge

Kanns mir gerade nicht vorstellen, was du meinst.

Was ich aber in die Entscheidung einfließen lassen würde ist: was braucht der Spieler tatsächlich. Das dann auch schnell verfügbar sein sollte.

Ich weiß nicht wie treffend das Beispiel ist, aber mal zur Verdeutlichung was ich meine:

Der Spieler möchte einen Gegenstand craften. Allerdings fehlen ihm Zwei Resourcen und diese braucht er in bestimmter Anzahl. Zudem hat der Spieler ein Gedächtnis so gut wie meines (also maximal als Kurzzeitgedächtnis vorhanden). Spieler wählt einen Planet aus und sieht die Resourcen (vllt auch mit Mengen) - vergisst dabei aber wie groß sein Vorrat ist oder entdeckt eine Resource - von der er die Anzahl einfach gerne wissen möchte. Da wäre es toll, wenn man eben die Information so schnell wie möglich bekommt. Aber schon Sinnvoll:

  1. Wenn man die Resourcen sieht, dann könnte man auch gleich sehen wie viel man hat
  2. Wenn man etwas craften möchte, dann könnte man die Rezepte schnell von der Ansicht aus aufrufen - ohne navigieren zu müssen.

Sowas kann meiner Meinung nach sehr schnell nervig werden. Vor allem, wenn ich vergesse mir den Planeten zu merken. Was dann ausartet in “Planet suchen”. Und in manchen Fällen hab ich dann zu diesem Zeitpunkt meine Anforderungen nur noch sehr schwammig im Kopf.

Ich denke, ich werde mal ein paar “User-Stories” schreiben, um ein besseres Gefühl zu bekommen, was wann benötigt wird.

Eventuell kann man die “Nervigkeit” auch als Spielelement nutzen, wenn man z.B. statt selbst zu schürfen eine Drohne losschickt mit der Order “hole Wasserstoff”.

Klingt für mich gerade allerdings als ob du tendierst Richtung Wasserfall zu gehen. Wenn das zu sehr aufeinander baut und du eine Abweichung hast - dann bringen dir die anderen Stories nix mehr.

Ich würde versuchen von dem Ist-Zustand auszugehen und zu überlegen - was hier Sinn macht. Und viel Wert auf eine gute Architektur legen, damit man im Worst-Case „nur“ teile der UI eventuell wegwerfen muss, wenn es doch mal falsch konzipiert war.

Nicht nur eventuell. Das würde ich auf jeden Fall - nur gibt es halt Unterschiede zwischen gewollter Nervigkeit und Probleme mit dem Design.

Weil gerade wenn es darum geht Sachen frei zu schalten, dann sollten diese auch einen Vorteil bringen. Ansonsten fehlt ja jegliches Bestreben daran zu kommen :wink:

Bei Moonlighter (war vor kurzem im Epic Store gratis) da konnte man per Tastendruck ein Craftable in seine Wunschliste einfügen. Dann wurden alle benötigten Materialien in allen Menüs entsprechend mit einem Stern markiert.

Das System fand ich cool. Auch wenn noch eine Fortschrittsanzeige wünschenswert gewesen wäre, so dass man nicht zu viel nutzlosen Kram im knappen Inventar rumgeschleppt hätte.

Man kann jetzt kaputte Cargo-Zellen reparieren:

An und für sich nicht besonders toll, aber…

  • jetzt kann ich gut “Zelle geht kaputt” als Beispiel-Event verwenden
  • ich habe dem Schiff einen Typ verpasst, von dem bestimmte Dinge abhängen, wie hier das benötigte Reparaturmaterial, oder auch die Wahrscheinlichkeit, beim Scannen Resourcen zu finden
  • das ist auch eine gute Vorlage, um kaputte Geräte zu reparieren oder kranke Crew-Mitglieder zu heilen

Ich habe mal eben so das ganze Problem mit der Forschung gelöst: Mit ein bisschen Überzeugungsarbeit habe ich dazu das schon vorhandene Crafting-System eingespannt. Dazu sind Forschungspunkte jetzt ganz normale “Resourcen”, und mit dem entsprechenden Rezept kann ich mir ein Blueprint basteln. Der Charme dabei ist, dass ich beliebige Vorbedingungen angeben kann, also andere Blueprints, Energiezellen oder anderes Verbrauchsmaterial, oder sogar Quest-Gegenstände. Außerdem kann zuviel Forschung zu Platzmangel führen - mehr Druck für den Spieler, ordentlich zu planen.

Ich hoffe mal, dass diese Lösung wirklich tragfähig ist, aber wenn ja, wäre es eine enorme Vereinfachung.

Ich hatte jetzt lange keine Lust mehr, an r-process zu arbeiten. Heute habe ich mich aufgerafft und eine Sache gefixt, die mich schon ewig gestört hat: kreuzende Routen in der Karte. Etwas frickelig, hat aber doch irgendwie Spaß gemacht. Habe sogar Tests dafür geschrieben, jawohl! Wenn ich Routen finde, die sich kreuzen, entferne ich jetzt die längere:

Seit gestern Abend hab ich darüber nachgedacht, wie man sich überschneidende Routen direkt beim Konstruieren der Sterne/Planeten vermeiden könnte, ohne alle Sterne/Planeten zu durchlaufen zu müssen - und bin zu keinem Ergebnis gelangt. :smiley:

Den Graph konstruiere ich so:

  • zufällig eine große Anzahl Knoten auswählen
  • alle Knoten, die zu nahe an einem anderen sind, entfernen
  • alle Knoten verbinden, die näher zusammenliegen als ein bestimmter Höchstabstand
  • Kreuzungen entfernen (die kürzere Verbindung bleibt bestehen)

Natürlich könnte ich schon im dritten Schritt auf Kreuzungen prüfen, aber es sind recht wenige, und es ist vom Ablauf her bequemer, das zu trennen.

Wenn jemand ganz viel Pech hat, wäre eine Knotenverteilung möglich, bei der mit dem aktuellen Algorithmus „Inseln“ entstehen, aber das ist enorm unwahrscheinlich, Um auch diese Fälle abzudecken, könnte man theoretisch nach dem zweiten Schritt ein Voronoi-Diagramm erstellen, und dann nur Knoten verbinden, die eine gemeinsame Grenze haben, aber da man eher im Lotto gewinnt als in eine solche Konstellation zu rennen, wäre das Overkill.

das Diagramm ist ja Overkill vom Feinsten, Vorschlag:

  1. markiere alle Knoten pauschal mit 0
  2. nimm einem Knoten um markiere ihn mit 1
  3. für jeden Nachbarknoten wieder zurück zu 2 (gehe nicht über Los)
  4. prüfe alle Knoten ob noch einer mit 0 markiert ist -> Insel, Graph verwerfen

Ich verwende auch bei meinem Sonnensystem Generator Zufallszahlen. Dabei kommen durchaus Sonnensystem raus, die ich so nicht haben will (mind. 3 erdähnliche Planeten, etc.). Diese Checks mache ich und generiere dann ggf. einmal neu.

Hmmm, dann lieber so:

  • minimalen Spannbaum bestimmen
  • zusätzlich alle Knoten verbinden, die näher zusammenliegen als ein bestimmter Höchstabstand
  • Kreuzungen entfernen

Der minimale Spannbaum vermeidet Inselbildung von vornherein.

Edit: Ein einsamer Stern in einer der Ecken wäre vielleicht doch nicht so unwahrscheinlich. Hab’s so implementiert wie beschrieben, mit Kruskal als MST-Algorithmus.

Ich würde das ganz anders machen. Ich glaub ich würde erstmal ein „glattes“ Netz im Reihe erstellen wo alles den selben Abstand hat. Danach würde ich die Punkte zufällig etwas verschieben um Unordnung rein zu bekommen. In der ersten Variante gäbe es dann zwar nicht mehr als 4 Verbindungen zwischen den Planeten - aber man kann ja die Netzgenerierung anpassen. Also so die Richtung:

Vorher:
image

Nachher:
image

Ich denke, dass das eine ganz andere Verteilung gäbe.

Für so einen kleinen Graph ist es völlig in Ordnung, auch etwas ineffizientere Verfahren (sprich O(n^2)) einzusetzen, wenn es am Ende auch natürlich aussieht.

Ich würde auch behaupten, dass die Ausführungszeit von "Einmahl"verfahren praktisch komplett vernachlässigbar ist. Die Performance in der Schleife ist kritischer.

Aber ich mag Tomate_Salats meine Lösung, die ich für meine noch ausstehende Universumsgenerierung verwenden werde. Diese Gittermethode würde wahrscheinlich kaum anders aussehen, und man könnte auch explizit bestimmte Verteilungen generieren.

1 „Gefällt mir“

So genau wollte ichs gar nicht wissen… Ich meinte doch nur, dass man sich kreuzende Routen nur dann finden kann, wenn diese sich schon kreuzen.

Obwohl meine Dialoge ziemlich einfach und statisch sind, ist die Berechnung der Positionen ein ziemlicher Graus. Mal ein Beispiel:

private val bounds: Rectangle = Rectangle(0F, 0F, 600F, 620F).center()
...
init {
    val titleMeasures = Assets.mainFont.size(title, 0.5F)
    val titleWidth = titleMeasures.first
    val titleHeight = titleMeasures.second

    val w2 = bounds.width / 2F
    val lineHeight = bounds.y + bounds.height - titleHeight - 40F
    fixedPrimitives = arrayOf(
            //background
            Picture(Assets.dialog, bounds),
            Rect(bounds, Color.GOLD),
            //title
            Line(at(bounds.x, lineHeight), at(bounds.x + bounds.width, lineHeight), Color.GOLD),
            Text(title, at(bounds.x + (bounds.width - titleWidth) / 2F, bounds.y + bounds.height - titleHeight), 0.5F, Color.GOLD))

    val leftBounds = Rectangle(bounds.x, bounds.y + 20F, w2, titleHeight + 20F).pad(-20F, 0F)
    val closeActor = TextButton("dialog.body.close".i18n(), leftBounds) { it.parent.remove() }
    addActor(closeActor)

    val rightBounds = Rectangle(bounds.x + w2, bounds.y + 20F, w2, titleHeight + 20F).pad(-20F, 0F)
    scanActor = TextButton("dialog.body.scan".i18n(), rightBounds) { scan() }
    addActor(scanActor)

    miningActors = Array(6) { y ->
        val bounds = Rectangle(bounds.x + w2, bounds.y + 95F + cellWidth * (5 - y), w2, titleHeight + 20F).pad(-20F, 0F)
        TextButton("dialog.body.extract".i18n(), bounds) { extract(y) }
                .also {
                    addActor(it)
                    touchable = toTouchable(game.ship.canExtract(body, y))
                }
    }
    ...
}

Ich habe also beschlossen, so etwas ähnliches wie einen LayoutManager in Swing zu schreiben, nur sehr viel einfacher und statischer. Im Prinzip eine Klasse, in die ich ein Rechteck reinstecke, und aus der dann eine Map von Namen auf kleinere Rechtecke wieder rauskommen. Diese benutze ich dann „manuell“, um alles auszurichten.

Meine ersten Versuche sahen recht vielversprechend aus. Vielleicht schaffe ich es auch, ein kleines DSL dafür zu schreiben.

Außerdem habe ich beschlossen, den Code erst einmal öffentlich zugänglich zu machen. Das ist noch keine Entscheidung, ob das Projekt Open-Source wird. Die URL ist https://gitlab.com/Landei/r-process