Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

16.02.2012, 15:57

Duality - 2D Game Engine & Visual Editor

Zitat von »Die alte Einleitung von 2012«

Hallo allerseits :)

Habe gerade eine neue Testversion meines 2D Game Frameworks "Duality" released. Die ideale Gelegenheit also, um das Teil mal ein wenig in der Welt zu verstreuen und zu sehen was passiert :D

Also, worum gehts? Angefangen hat das Ganze mit dem Ende meines letzten Projekts Nullpunkt. Dieses hatte trotz einer nur geringen Spielzeit von 30 - 45 Minuten eine Entwicklungszeit von rund zwei Jahren; Nebenprodukt im Prozess war eine komplette Engine mitsamt grafischem Leveleditor. Beides verlor mit dem Ende des Projekts jegliche Bedeutung, weil beides hochspezialisiert auf genau dieses Projekt zugeschnitten war - und zudem meinem mit der Erfahrung kritischer gewordenem Blick nicht mehr standhielt. Wäre es nicht toll, wenn ich meine Engine fürs nächste Projekt einfach wiederverwenden könnte? Und auch gleich einen Editor dafür an der Hand hätte? Sowas in der Richtung war wohl mein Gedanke, als ich mit der Entwicklung von Duality begann. Es ist der kleinste gemeinsame Nenner aller für mich persönlich wahrscheinlichen Hobbyprojekte: Eine allgemeine 2D Game Engine mit einem allgemeinen Editor.

Mag sein, aber warum uns damit belästigen? Ganz einfach: Duality ist für jeden frei verfügbar. Noch besser: Duality ist sogar Open Souce! Wer Interesse hat, kann das Ding einfach benutzen. Duality bringt vieles von dem mit, was - in meiner Erfahrung - sonst für jedes Projekt immer wieder neu geschrieben werden muss.

[...]

An dieser Stelle möchte ich mich mit einer kurzen Unterbrechung zu Wort melden, die den Hauptteil des alten Originalpostings ersetzt: Seit 2012 hat sich eine ganze Menge getan. Damit ihr nicht alle XY Seiten dieses Threads lesen müsst, gebe ich euch an dieser Stelle einfach einen Link zur offiziellen Infoseite des Projekts:


(Link)


Dort könnt ihr alle wesentlichen Punkte bequem und in Kurzfassung nachlesen. Wenn ihr euch einen ersten Eindruck verschaffen wollt, könnt ihr ganz bequem die aktuellen Binaries herunterladen, im Willkommensdialog "Browse Samples" auswählen, ein Beispielprojekt herunterladen und dort ein bisschen drin herumklicken. Voraussetzung für das Entwickeln eigener Game Plugins mit C# ist Visual Studio 2013 (Update 3) oder neuer, das als Community Edition gratis verfügbar ist.

Zitat von »Der alte Schlusssatz von 2012«

So viel von mir - jetzt belasse ichs erstmal dabei und hoffe auf Feedback :)

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Fetze« (22.10.2015, 18:20)


CodeBase

Treue Seele

Beiträge: 98

Wohnort: Österreich -> Salzburg

Beruf: Datenbank Entwickler

  • Private Nachricht senden

2

16.02.2012, 16:38

Wahnsinn bin ganz aus dem Häuschen. Sieht echt super aus. Kann man damit auch aufbauspiele machen ?
Wollte mal eines machen mit 2.5D grafik. Das wäre ja die beste Lösung für mein Problem.

CodeBase

Treue Seele

Beiträge: 98

Wohnort: Österreich -> Salzburg

Beruf: Datenbank Entwickler

  • Private Nachricht senden

3

16.02.2012, 16:44

Gerade ausprobiert und bam eine Exception bekommen:

Direkt nachdem ich den Editor gestartet habe und ohne was zu machen, Fenster ging auf und schon kam die Meldung:


4

16.02.2012, 16:56

Nanu. Das soll natürlich nicht passieren. Wenn du mir den gesamten Inhalt der Fehlermeldung und im Idealfall auch noch "logfile_editor.txt" per PN schickst, kümmer ich mich baldmöglichst drum :)

An alle anderen: Kann jemand den Fehler reproduzieren?

Wahnsinn bin ganz aus dem Häuschen. Sieht echt super aus. Kann man damit auch aufbauspiele machen ?
Wollte mal eines machen mit 2.5D grafik. Das wäre ja die beste Lösung für mein Problem.


Sehe keinen Grund, warum Aufbauspiele ausgeschlossen sein sollten. Was 2.5D angeht: Das ist ein weiter und alles andere als klar definierter Begriff. :) Duality verwendet 3D-Koordinaten, orthografische Projektion und einen entfernungsbasierten Parallax-Effekt.

5

17.02.2012, 22:15

Den Fehler kann ich nicht reproduzieren. Bei mir läuft alles ohne Probleme.

Zur Engine: Werde es mir mal heute Nacht genauer anschauen. :search:

6

26.02.2012, 17:39

Gibt mal wieder was zu testen :)


(Link)


Dynamische Beleuchtung in Duality, Techdemo gibts hier. Per-Pixel Beleuchtung mit Ambient, Directional, Point und Spot Lights. Einfach mal DualityLauncher.exe starten, ums in Bewegung zu sehen, oder eben den Editor für alle die damit lieber selbst rumspielen wollen. ;)

Interessantes Detail: Um das alles umzusetzen hab ich Duality selbst nicht angefasst - steht zu 100% in Plugin Code. Ist also keine Hexerei seitens des Entwicklers sondern etwas, das auch jeder andere mit Duality hätte basteln können!
Vermutlich werde ich Kram wie Beleuchtungstechniken und Co nicht in den Duality Core aufnehmen; ich gehe davon aus, dass nur ein winziger Bruchteil von Spielen tatsächlich darauf zurückgreifen wird. Denen kann die Techdemo hier aber natürlich als Beispiel und Vorlage dienen, für alle anderen bleibt dafür der Core etwas aufgeräumter.

Wäre cool wenn ein paar von euch mal testen könntet, ob es denn läuft. Ich bin nicht unbedingt ein Grafikprogrammierungs-Guru und mir nicht im Klaren darüber, ob mein Shader- und Grafikcode hier plattformübergreifend verlässlich arbeitet. :D

7

10.03.2012, 00:24

Eigenhändig ausprobiert habe ich Duality zwar noch nicht (fauler Freitag), aber vom Lesen und Visuellen her macht das Ganze schon einen wirklich guten Eindruck. Nicht zuletzt deshalb, weil ich gerade selbst an einem Weltraumspiel sitze, für das sich Duality ausgesprochen gut eignen dürfte. ;)

Auf meinem betagten Laptop mit Windows Vista und GeForce 8500M GT funktioniert die Techdemo für die dynamische Beleuchtung ohne Probleme. Sieht schick aus!

Gibt es eigentlich abgesehen von den Normal und Specular Maps auch noch Emissive Maps? Das wäre toll.

8

10.03.2012, 10:46

Eigenhändig ausprobiert habe ich Duality zwar noch nicht (fauler Freitag), aber vom Lesen und Visuellen her macht das Ganze schon einen wirklich guten Eindruck. Nicht zuletzt deshalb, weil ich gerade selbst an einem Weltraumspiel sitze, für das sich Duality ausgesprochen gut eignen dürfte. ;)

Auf meinem betagten Laptop mit Windows Vista und GeForce 8500M GT funktioniert die Techdemo für die dynamische Beleuchtung ohne Probleme. Sieht schick aus!

Gibt es eigentlich abgesehen von den Normal und Specular Maps auch noch Emissive Maps? Das wäre toll.

Jep, die sind versteckt im Alphachannel der Normalmap. Je weniger Alpha, desto mehr "emissive" ist das Material an der Stelle. Bei einem Alphawert von 0 wird einfach direkt die Texturfarbe der Diffusemap verwendet ohne irgendwelche Lichtberechnungen einzubeziehen. Da der Beleuchtungs-Techdemo der gesamte Plugin-Sourcecode sowie alle Shader beiliegen ist es aber auch kein Problem, das Beleuchtungssystem komplett den eigenen Wünschen anzupassen!

Solltest du dir überlegen Duality für dein Projekt zu verwenden, geb ich dir natürlich auch gern Starthilfe was Benutzung und Technik angeht und wenns Fragen oder Anregungen gibt, einfach posten :)

9

22.03.2012, 00:06

Es gibt mal wieder ne Releaseversion mit ein paar neuen Features. Genaugenommen sogar einer ganzen Menge davon :D Zu den Highlights zählen:

:arrow: Komplett neu geschriebenes PropertyGrid zum Bearbeiten von Objekteigenschaften. Sieht besser aus, ist deutlich schneller, kann komplett mit der Tastatur gesteuert werden und hat vernünftigen Ctrl+C / Ctrl+V Support.
:arrow: Preview-Bilder für allerlei Ressourcentypen (Pixmap, AudioData, Font) für verbesserten Workflow
:arrow: Intelligentes Dragdrop, oder: "Ich zieh das hier mal nach da drüben, der Editor weiß dann schon wies gemeint ist". Einige Dinge lassen sich dadurch deutlich schneller bewerkstelligen.
:arrow: Komplettes Redesign der Editor GUI

Freue mich natürlich wie immer über jeden Tester, Feedback, Fragen und Anregungen sind immer gerngesehen :)

Downloadlinks:
--> Asteroids <--
--> Dynamic Lighting <--

(Wen's interessiert, einige der neuen Features hab ich in meinem letzten Blogeintrag ein wenig genauer beschrieben.)

10

23.03.2012, 18:37

So, ich hab gerade ein wenig Zeit über, da kann ich ebensogut noch ein paar Worte zum intelligenten DragDrop verlieren - ich denke nach meinem letzten Post hat wohl kaum jemand wirklich eine Ahnung davon, was es eigentlich tut. :D

Also, worum gehts dabei eigentlich? Das Grundproblem ist eigentlich recht banal: Der Dualitor Workflow basierte von Anfang an zu einem gewissen Teil auf DragDrop, beispielsweise um Verknüpfungen zwischen Ressourcen (Materialien, Texturen, Sounds, etc.) und Komponenten von GameObjects (SpriteRenderer, SoundEmitter, etc.) herzustellen. Ich packe mir im Project View einfach die entsprechende Ressource mit dem Cursor und ziehe sie im Object Inspector auf den jeweiligen Slot der Komponente. Soweit kein Problem.


(Link)


Ein anderes Anwendungsgebiet von DragDrop ist das Erstellen und instanziieren von Prefabs: Wenn ich als Nutzer ein Prefab aus einem GameObject machen will, greife ich mir das GameObject im Scene View und ziehe es in den Projekt View. GameObjects haben bei den Projektressourcen nichts zu suchen, das weiß der Project View und erstellt deswegen eine Prefab-Ressource, wo das gezogene GameObject dann reingepackt wird.


(Link)


Will ich aus diesem Prefab jetzt ein GameObject instanziieren, packe ich mir das Prefab im Project View und ziehe es runter in den Scene View oder wahlweise auch den großen Hauptbereich mit der Levelansicht (Camera View). Dort wiederum haben Ressourcen nichts zu suchen, also wird das Prefab kurzerhand ausgepackt und eine Instanz des GameObjects erstellt.


(Link)


So weit so gut. Auch ohne intelligentes DragDrop war das schonmal ein relativ intuitives Verhalten. Leider funktionierten nicht alle Dinge im Editor so einfach. Nehmen wir an, man wollte (ohne intelligentes DragDrop) ein Sprite-Objekt erstellen, das eine Grafik verwendet, die irgendwo auf der Festplatte herumliegt. Folgendes müsste man dafür tun:

1. DragDrop der Bilddatei in den Project View. Die Grafik wird importiert und eine neue Pixmap-Ressource wird angelegt.
2. Jetzt brauchen wir eine Textur, welche die Grafik verwendet. Rechtsklick auf die Pixmap-Ressource und "Create Texture" im Kontextmenü wählen.
3. Objekte werden aber nicht mit Texturen, sondern mit Materialien gerendert. Also Rechtsklick auf die Texture und "Create Material" im Menü wählen.
4. Jetzt brauchen wir noch das Objekt. Rechtsklick auf eine leere Stelle im Scene view und "Create / GameObject" wählen.
5. Das Objekt benötigt nun Komponenten um ein Sprite darstellen zu können, zunächst einmal eine Position. Rechtsklick auf das neue Objekt und "Create / Components / Transform" wählen.
6. Jetzt noch die Renderer Komponente: "Create / Components / SpriteRenderer".
7. Zu guter letzt müssen wir dem Renderer noch das Material mitgeben. Also die Material-Ressource packen und in den entsprechenden Slot im PropertyGrid ziehen, wenn das Objekt ausgewählt ist.

Wenn sich an dieser Stelle jemand denkt "Wtf, so viel Aufwand für ein einfaches Sprite?!", den kann ich beruhigen: In aller Regel passierte es nicht sehr oft dass man wirklich den kompletten Strang an Aktionen ausführen musste. Oft gab es ja bereits ein passendes Material oder das Objekt existierte schon, etc.
Trotzdem gab es da natürlich Verbesserungsbedarf und hier kommt intelligentes DragDrop ins Spiel. Spulen wir mal zurück auf Anfang und schauen uns an wie die gesamte Aktion jetzt mit intelligentem DragDrop aussieht:

1. Grafik importieren. Im Prinzip noch genauso wie vorher, da hat sich nix geändert. Siehe Punkt 1 oben.
2. Jetzt noch die neue Pixmap Ressource packen und in Scene- oder Cam View absetzen.
3. Tadaa!

Diese simple Aktion veranlasst den Editor dazu, automatisch Textur und Material zu erstellen, konfigurieren und abzuspeichern, ein neues GameObject zu erstellen sowie Transform- und SpriteRenderer Komponente hinzuzufügen. Vollautomatisch und superschnell :D

Aber kommen wir langsam mal zum intelligenten Teil: Was ist da intern eigentlich gerade passiert? Diese Aktion hardcoded festzulegen wäre doch ein übler Schnitzer im flexiblen Plugin-Design von Engine und Editor. Dem Editor selbst sollte es völlig egal sein, was es für Komponenten und Ressourcen gibt und was man damit tun kann, all dieses "Wissen" kommt erst über entsprechende Plugins hinzu. Doch selbst wenn man diese Aktion hardcoded in ein Plugin packt, wirklich schön oder flexibel ist das nicht.

Also auf zur Lösung des Problems:
Eine DragDrop-Aktion enthält grundsätzlich erstmal Daten eines bestimmten Typs. Das mögliche Ziel einer DragDrop-Aktion (also beispielsweise Scene- oder Cam View) interessiert dabei gar nicht, was für Daten das genau sind, sondern nur ob man diese in eine Form bringen kann, die das jeweilige Steuerelement verwalten kann. Die Cam View arbeitet z.B. mit GameObjects, da man diese dort drinnen herum schieben kann.
Grundsätzlich könnte man der Cam View nun also beibringen wie es andere Datentypen (z.B. Pixmaps) in GameObjects konvertiert, aber was ist dann mit der Scene View? Und allen anderen Steuerelementen? Damit man dasselbe Verhalten nicht allen Steuerelementen einzeln beibringen muss, ist es ratsam, dieses zu zentralisieren. Ein erster Ansatz wäre also, dass Cam View, Scene View und Co beim Feststellen einer DragDrop-Aktion die Rohdaten extrahieren, diese an die "Zentrale" weiterleiten und darum bitten, sie in GameObjects zu konvertieren.
Dieses Konzept abstrahierend kam ich auf folgendes: In der Zentrale wird eine Liste von DataConverter-Objekte verwaltet. Jedes dieser Objekte ist in der Lage, einen bestimmten Datentyp in einen anderen Datentyp zu konvertieren. Editor-Plugins können eigene DataConverter definieren und ebenfalls in der Zentrale registrieren.


(Link)


Ich kann nun der "Zentrale" beliebige Objektdaten rüberschicken und Objekte eines bestimmten Typs zurückverlangen. Was die Zentrale nun tut ist herauszufinden auf welche Weise sich die registrierten DataConverters am effizientesten kombinieren lassen um die Anfrage zu erfüllen. Man kann sich das im Prinzip wie eine Pathfinding-Operation vorstellen.

Was also bei der oben vorgestellten DragDrop-Aktion intern passiert ist folgendes:
1. DragDrop erreicht die Cam View. Selbige verlangt GameObjects als Daten und startet "über die Zentrale" eine Konvertierung mit dem Zieltyp "GameObject"
2. Dort wird das Datenpaket geöffnet und festgestellt: Hm, Mist. Keine GameObjects drin. Fragen wir mal rum, ob sich ein Konverter mit Zieltyp "GameObject" findet
3. GameObjectFromPrefab und GameObjectFromComponents bieten sich an. Die Anfrage wird nacheinander an beide weitergeleitet.
4. GameObjectFromPrefab lässt sie nach einigem Pathfinding zurückgehen da der Konverter feststellt dass ihm die zum Arbeiten nötigen Daten fehlen. Es werden keine Prefabs gefunden und es lassen sich auch keine aus den verfügbaren Daten ableiten.
5. Bleibt noch GameObjectFromComponents. Ich überspringe jetzt aber mal die Pathfinding-Details. Die vollständige Konvertierungskette ist:


(Link)

(Nicht alle existierenden DataConverter angezeigt)

Es werden also im Rahmen der Aktion automatisch eine Textur aus der Pixmap erstellt, dann ein Material aus der Textur, dann eine SpriteRenderer-Komponente die das Material nutzt und anschließend ein GameObject welches erst alle "required Components" des SpriteRenderers hinzufügt (Transform) und dann den Renderer selbst. Und da sind wir.

Natürlich kann man jetzt sagen "Oh man, so ein Aufwand für so ne Kleinigkeit", aber dieses System hier hat tatsächlich ein paar Vorteile, die anders nur schwer erreichbar wären. Es reagiert zum Beispiel sehr flexibel wenn Nutzer eigene Komponententypen erstellen - einfach einen eigenen Konverter schreiben (Wenige Codezeilen in der Regel) und in der Zentrale registrieren. Und sofort ist der gesamte Editor in der Lage, DragDrop-, Clipboard- und Konvertierungsaktionen mit den Nutzerkomponenten durchzuführen.
Ein anderer Vorteil entsteht durch die Arbeitsaufteilung der DataConverter: Eine Konvertierung wird nicht als atomare Aktion begriffen, sondern als Teamwork. Dementsprechend können auch viele DataConverter an einem gemeinsamen Datensatz arbeiten und sich gegenseitig ergänzen. Es ist beispielsweise ohne weiteres Möglich, im Project View ein paar Sounds und eine Textur zu wählen und den ganzen Packen komplett in die Cam View zu ziehen. Das Resultat ist ein GameObject, das neben der SpriteRenderer-Komponente (aus der Textur) auch eine SoundEmitter-Komponente enthält, welche die beiden Sounds als Sound Sources hinzugefügt bekam. Das ist nur dadurch möglich, dass DataConverter "in Teamarbeit" vorgehen.

Natürlich gibt es auch DataConverter, die das explizit verhindern. Packt man beispielsweise ein Prefab gemeinsam mit einem Sound, wird das Prefab instanziiert und der Sound ignoriert. Prefabs haben vorrang, da hier angenommen wird dass diese grundsätzlich erstmal in ihrer unbearbeiteten Reinform instanziiert werden sollen. Das ist nirgendwo hardgecodet, sondern steht im DataConverter GameObjectFromPrefab - welcher in der Zentrale bei Bedarf ohne Weiteres überschrieben werden kann.

Okay, jetzt hab ich eine ganze Menge Theorie von mir gegeben. Hier zum Abschluss noch ein paar kommentierte Codefragmente:

Das hier ist mal so ein DataConverter von Innen, und zwar TextureFromPixmap.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class TextureFromPixmap : DataConverter
{
    public override bool CanConvertFrom(ConvertOperation convert)
    {
        return 
            convert.AllowedOperations.HasFlag(ConvertOperation.Operation.Convert) && 
            convert.CanPerform<Pixmap>();
    }
    public override bool Convert(ConvertOperation convert)
    {
        bool finishConvertOp = false;

        List<ContentRef<Pixmap>> dropdata = new List<ContentRef<Pixmap>>();
        var matSelectionQuery = convert.Perform<Pixmap>();
        if (matSelectionQuery != null) dropdata.AddRange(matSelectionQuery.Ref());

        // Generate objects
        foreach (ContentRef<Pixmap> pixRef in dropdata)
        {
            if (convert.IsObjectHandled(pixRef.Res)) continue;
            if (!pixRef.IsAvailable) continue;
            Pixmap pix = pixRef.Res;

            // Find Material matching Texture
            ContentRef<Texture> texRef = ContentRef<Texture>.Null;
            if (pixRef.IsDefaultContent)
            {
                var defaultContent = ContentProvider.GetAllDefaultContent();
                texRef = defaultContent.Where(r => r.Is<Texture>() && (r.Res as Texture).BasePixmap == pix).FirstOrDefault().As<Texture>();
            }
            else
            {
                string texPath = pix.FullName + Texture.FileExt;
                texRef = ContentProvider.RequestContent<Texture>(texPath);
                if (!texRef.IsAvailable && convert.AllowedOperations.HasFlag(ConvertOperation.Operation.CreateRes))
                {
                    // Auto-Generate Texture
                    texRef = Texture.CreateFromPixmap(pix);
                }
            }

            if (!texRef.IsAvailable) continue;
            convert.AddResult(texRef.Res);
            finishConvertOp = true;
            convert.MarkObjectHandled(pixRef.Res);
        }

        return finishConvertOp;
    }
}


Und so wird er "in der Zentrale" registriert:

C#-Quelltext

1
CorePluginHelper.RegisterDataConverter<Texture>(new DataConverters.TextureFromPixmap());

(Die Zentrale hat noch einige andere Aufgaben - man kann dort z.B. auch Editor-Metadaten für Core-Klassen hinterlegen, z.B. zu nutzende Icons, etc.)

Hier eine zum Verständnis gekürzte und editierte Version dessen was die Cam View tut, um an ihre Daten zu kommen:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
private void LocalGLControl_DragDrop(object sender, DragEventArgs e)
{
    DataObject data = e.Data as DataObject;
    ConvertOperation convert = new ConvertOperation(data, ConvertOperation.Operation.All);
    if (convert.CanPerform<GameObject>())
    {
        var dragObjQuery = convert.Perform<GameObject>();
        List<GameObject> dragObj = dragObjQuery.ToList();

        // .. snip ..
    }
}

Mithilfe der ConvertOperation-Klasse sind es im Prinzip nur ein paar Zeilen, um jeden beliebigen Objekttyp anzufordern. Sie kapselt auch die ganzen Zugriffe auf die "Zentrale".

So. Hoffe, das war so halbwegs informativ :)

Werbeanzeige