[OpenSource] GameUtils

    • Beta
    • Open Source

    Es gibt 152 Antworten in diesem Thema. Der letzte Beitrag () ist von Artentus.

      Nur so zur Info: der lock-Block funktioniert auch in etwa so, wie Monitor.Enter.

      Edit: es macht auch keinen Unterschied, ob ich den GDI-Renderer oder den Direc2D-Renderer verwende.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Artentus“ ()

      Mir gefällt das Design für den SpieleEntwickler nicht sonderlich.
      Dieses PhysicTester-Monstrum - da ist mir viel zuviel drin.

      Ich würde was bevorzugen mit ZeichenObjekten, die selbst wissen, wie sie sich zeichnen sollen. - guck: ich hab ein Polygon gemacht, was sich zeichnen kann, und auch seinen Namen schreiben:

      C#-Quellcode

      1. public class PhysicPolygon : PhysicsObject, IDisposable {
      2. IBrush Brush;
      3. public string Name;
      4. public IFont Font;
      5. public PhysicPolygon(IBrush brush, float mass, IEnumerable<Vector2> polygon)
      6. : base(mass, new Polygon(polygon)) {
      7. Brush = brush;
      8. }
      9. public void Render(IRenderer renderer) {
      10. using(IGeometry geometry = renderer.Factory.CreateGeometry()) {
      11. geometry.BeginFigure(base.Polygon[0]);
      12. geometry.AddLines(base.Polygon.Points.Skip(1).ToArray());
      13. geometry.EndFigure(FigureEnd.Closed);
      14. geometry.Close();
      15. renderer.DrawGeometry(geometry, Brush, 2);
      16. if(Name != null) renderer.DrawText(Name, Font, Brush, base.Center);
      17. }
      18. }
      19. public void Dispose() {
      20. if(Brush == null) return;
      21. foreach(var d in new IDisposable[] { Brush, Font })if(d!=null) d.Dispose();
      22. Brush = null;
      23. }
      24. }
      Die Texte wabern übrigens ziemlich eigentümlich herum.


      Auch Verschachtelung habich reduziert - Verschachtelung ab einem bestimmten Grad erschwert ziemlich die Lesbarkeit - also sowas findich nicht sehr fein:

      C#-Quellcode

      1. PhysicsObject leftWall = new PhysicsObject(0, new Polygon(new[] { new Vector2(-10, -10), new Vector2(0, -10), new Vector2(0, 370), new Vector2(-10, 370) }));
      Da sind Vectoren in einem Poligon in einem PhysicsObject.
      Insbesondere, dass man für jeden Vector neu new Vector2() hinschreiben muß, käst mich an, ich habe da eine Lösung gefunden, aber unkonventionell:

      C#-Quellcode

      1. var leftWall = new PhysicsObject(0, Vector2.Init[-10, -10][0, -10][0, 370][-10, 370]);
      ein annerer Konstruktor, und eine listige statische Vector2.Init-Property, die eine List<Vector2> initialisiert.

      Also initialisiere ich den PhysicsTester jetzt so:

      C#-Quellcode

      1. public class PhysicsTester : IUpdateable, IRenderable {
      2. List<PhysicPolygon> polygons = new List<PhysicPolygon>();
      3. int counter;
      4. public PhysicsTester() {
      5. var leftWall = new PhysicsObject(0, Vector2.Init[-10, -10][0, -10][0, 370][-10, 370]);
      6. var rightWall = new PhysicsObject(0, Vector2.Init[480, -10][490, -10][490, 370][480, 370]);
      7. var topWall = new PhysicsObject(0, Vector2.Init[0, 0][480, 0][480, -10][0, -10]);
      8. var bottomWall = new PhysicsObject(0, Vector2.Init[0, 360][480, 360][480, 370][0, 370]);
      9. Program.PhysicsManager.Objects.Add(leftWall);
      10. Program.PhysicsManager.Objects.Add(rightWall);
      11. Program.PhysicsManager.Objects.Add(topWall);
      12. Program.PhysicsManager.Objects.Add(bottomWall);
      13. var brush = Program.Renderer.Factory.CreateSolidColorBrush(new Color4(1, 1, 1, 1));
      14. var font = Program.Renderer.Factory.CreateFont("Consolas", 13);
      15. polygons.Add(new PhysicPolygon(brush, 1, Vector2.Init[120, 140][150, 200][90, 200]) { Name = "Hans", Font = font });
      16. polygons.Add(new PhysicPolygon(brush, 1, Vector2.Init[360, 110][390, 170][330, 170]));
      17. brush = Program.Renderer.Factory.CreateSolidColorBrush(new Color4(1, 1, 1, 0));
      18. polygons.Add(new PhysicPolygon(brush, 1, Vector2.Init[220, 260][250, 320][190, 320]));
      19. polygons.Add(new PhysicPolygon(brush, 1, GetEqualPolygon(new Vector2(360, 260), 50, 6)) { Name = "SechsEck", Font = font });
      20. foreach(var obj in polygons) Program.PhysicsManager.Objects.Add(obj);
      21. }
      22. public void Update() {
      23. if(counter < 10) {
      24. var vects = Vector2.Init[900, -800][-800, -200][50, -1000][-350, -500];
      25. //im Zip werden aus den Vectoren Forces erstellt und anonym mittm Polygon zusammengefasst, und dann im ForEach auf die Polygone angewendet.
      26. foreach(var item in polygons.Zip(vects, (pol, vect) => new { pol, force = new Force(vect, pol.Center) })) item.pol.ApplyForce(item.force);
      27. counter++;
      28. }
      29. }
      30. public void Render(IRenderer renderer) {
      31. foreach(var obj in polygons) obj.Render(renderer);
      32. }
      Ich fands auch nixgutt, dass der Brush erst beim Rendern erzeugt wird - das gehört inne Initialisierung.

      Achso, und mein Iterator für gleichseitige Polygone ist auch wieder da:

      C#-Quellcode

      1. private IEnumerable<Vector2> GetEqualPolygon(Vector2 center, int radius, int edgeCount) {
      2. var step = 2 * Math.PI / edgeCount;
      3. var angle = 0.0;
      4. for(var i = edgeCount; i-- > 0; angle += step) { //alle edgeCount Ecken (um den Nullpunkt)
      5. var x = (float)(radius * Math.Sin(angle));
      6. var y = (float)(radius * Math.Cos(angle));
      7. yield return new Vector2(x, y) + center; // yielden inklusive der Verschiebung
      8. }
      9. }


      Die Polygon-Klasse ist mir eh schleierhaft - was kann die eigentlich, was ein Vector2-Array nicht gekonnt hätte?

      Ah - und überladene Konstruktoren sollte einander aufrufen, statt redundanten Code zu enthalten:

      C#-Quellcode

      1. //lieber so:
      2. public PhysicsObject(float mass, Graphics.Polygon polygon, Graphics.Vector2 linearVelocity, float angularVelocity) {
      3. if(mass < 0)
      4. throw new ArgumentException("mass", "Die Masse muss positiv oder 0 sein.");
      5. if(!polygon.IsValid)
      6. throw new ArgumentException("polygon", "Das übergebene Polygon ist nicht gültig.");
      7. Initialize(mass, GraphicsPolygonToIPolygon(polygon), new Vector2(linearVelocity.X, linearVelocity.Y), angularVelocity);
      8. }
      9. public PhysicsObject(float mass, Graphics.Polygon polygon)
      10. : this(mass, polygon, new Graphics.Vector2(0, 0), 0) {
      11. }
      12. public PhysicsObject(float mass, IEnumerable< Graphics.Vector2> polygonPoints)
      13. : this(mass, new Graphics.Polygon(polygonPoints)) { //neuer Konstruktor, der die Polygon-Schachtelei umschifft
      14. }
      15. // als so:
      16. /// <summary>
      17. /// Erstellt ein neues Physikobjekt.
      18. /// </summary>
      19. /// <param name="mass">Die Masse des Objekts.</param>
      20. /// <param name="polygon">Ein konvexes Polygon, das Form und Größe des Objekts beschreibt.</param>
      21. /// <param name="linearVelocity">Die Geschwindigkeit, die das Objekt zu Beginn haben soll.</param>
      22. /// <param name="angularVelocity">Die Winkelgeschwindigkeit, die das Objekt zu Beginn haben soll.</param>
      23. /// <remarks>
      24. /// Die Masse muss positiv oder 0 sein. Wird 0 übergeben, so gilt das Objekt als statisch und wird nicht durch WEchselwirkungen mit anderen Objekten beeinflusst.
      25. /// Das übergebene Polygon muss konvex sein, konkave Polygone verursachen zwar keinen Fehler, werden vom Algorithmus jedoch nicht korrekt berechnet.
      26. /// </remarks>
      27. /// <exception cref="System.ArgumentException">Wird ausgelöst, wenn <paramref name="mass"/> kleiner als 0 ist, oder wenn das übergebene Polygon ungültig ist.</exception>
      28. public PhysicsObject(float mass, Graphics.Polygon polygon, Graphics.Vector2 linearVelocity, float angularVelocity)
      29. {
      30. if (mass < 0)
      31. throw new ArgumentException("mass", "Die Masse muss positiv oder 0 sein.");
      32. if (!polygon.IsValid)
      33. throw new ArgumentException("polygon", "Das übergebene Polygon ist nicht gültig.");
      34. Initialize(mass, GraphicsPolygonToIPolygon(polygon), new Vector2(linearVelocity.X, linearVelocity.Y), angularVelocity);
      35. }
      36. /// <summary>
      37. /// Erstellt ein neues Physikobjekt.
      38. /// </summary>
      39. /// <param name="mass">Die Masse des Objekts.</param>
      40. /// <param name="polygon">Ein konvexes Polygon, das Form und Größe des Objekts beschreibt.</param>
      41. /// <remarks>
      42. /// Die Masse muss positiv oder 0 sein. Wird 0 übergeben, so gilt das Objekt als statisch und wird nicht durch WEchselwirkungen mit anderen Objekten beeinflusst.
      43. /// Das übergebene Polygon muss konvex sein, konkave Polygone verursachen zwar keinen Fehler, werden vom Algorithmus jedoch nicht korrekt berechnet.
      44. /// </remarks>
      45. /// <exception cref="System.ArgumentException">Wird ausgelöst, wenn <paramref name="mass"/> kleiner als 0 ist, oder wenn das übergebene Polygon ungültig ist.</exception>
      46. public PhysicsObject(float mass, Graphics.Polygon polygon)
      47. {
      48. if (mass < 0)
      49. throw new ArgumentException("mass", "Die Masse muss positiv oder 0 sein.");
      50. if (!polygon.IsValid)
      51. throw new ArgumentException("polygon", "Das übergebene Polygon ist nicht gültig.");
      52. Initialize(mass, GraphicsPolygonToIPolygon(polygon), new Vector2(0, 0), 0);
      53. }
      Dass der PhysicsTester absolut schlecht programmiert ist, weiß ich. Ich hab den nur schnell zum Testen zusammengeschustert, es war aber nicht wirklich geplant, das so einzusetzen, keine Sorge. ;)
      Den Konstruktor hab ich mal eben angepasst.

      Was die Polygon-Struktur und auch alle anderen Strukturen in GameUtils.Graphics angeht: die sind noch nicht alle fertig.

      ErfinderDesRades schrieb:

      Ich fands auch nixgutt, dass der Brush erst beim Rendern erzeugt wird - das gehört inne Initialisierung.
      Das fand ich auch nicht gut, deswegen hab ich mich gestern nochmal mit @~blaze~: beraten und da ein bisschen was angepasst.
      Wir haben mal versucht, zusammen ein Netzwerkprotokoll zu erstellen, hat nicht so gut funktioniert. :P
      Unsere Engines funktionieren auch, wie ich das gesehen habe, gänzlich unterschiedlich, es ist also denke ich besser, wenn jeder bei seinem Kram bleibt. So erhält man auch eine gewisse Vielfalt uns sieht, wie Verschieden man Probleme lösen kann.
      @ErfinderDesRades:
      Wenn ich mal fragen darf: wie hast die Vector2.Init-Funktion so hinbekommen, dass man sie mit diesen eckigen Klammern aufrufen kann?

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Artentus“ ()

      na, dassis schon ziemlich missbräuchlich - aber wenns um praktischen Code geht, kennich keine Moral :):

      C#-Quellcode

      1. using System;
      2. using System.Collections.Generic;
      3. using System.Linq;
      4. using System.Text;
      5. namespace Artentus.GameUtils.Graphics {
      6. public struct Vector2 {
      7. //...
      8. public static Vector2Initer Init { get { return new Vector2Initer(); } }
      9. //...
      10. }
      11. public class Vector2Initer:List< Vector2> {
      12. public Vector2Initer this[float x, float y] { get {
      13. base.Add(new Vector2(x, y));
      14. return this;
      15. } }
      16. }
      17. }
      Also ein überladener List<Vector2> - Indexer, was ja eiglich ein Getter ist, wird gewissermaßen als Setter missbraucht, nämlich, um aus den Indizees Vectoren zu bilden und zu adden, und Rückgabewert ist immer der Initer selbst, sodass man weitere indizierte Zugriffe treiben kann.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Wie kommt man nur auf solche Sache? :D
      Naja, so ganz konform ist das nun wirklich nicht, da kann man leicht verwirrt werden. Ich form das mal ein wenig um.

      Edit. man kanns leider nicht so umbauen, dass es irgendwie konform wird. :(

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Artentus“ ()

      Tja, Microsoft hats leider noch nicht geschafft, genügend brauchbare Auflistungs-Initialisierer verfügbar zu machen.
      Also siehs als besonderes Feature von Indexern an ;)

      Übrigens in vb ist eine Variante des From-Schlüsselwort so gemeint, aber an die Indexer-Technik kommt das immer noch nicht ganz ran.
      Ihr könnt ab sofort den SourceCode auch auf GitHub betrachten:
      github.com/Artentus/GameUtils/
      Ich werde dort ab und an auch Updates online stellen, die ich hier nicht veröffentliche, weil sie nicht vollständig sind o.ä., ihr könnt euch dort also immer auf dem neusten Stand halten.
      Es wieder einige Zeit vergangen und ich hab eifrig an der Engine weitergearbeitet.
      Dieses mal sind die Subsystem Input und UI dazugekommen, am Netzwerk hab ich mich auch versucht, der Code dort ist aber nicht lauffähig.
      Auch habe ich das komplette Rendering-System generalüberholt. Die meisten Interfaces wurden rausgeschmissen und durch abstrakte Klassen ersetzt und vieles wurde um neue Funktionen erweitert.
      Der Input wird in meiner Lib unabhängig vom Update-Loop geregelt (ist so halb Eventbasiert), damit ich maximale Reaktionsgeschwindigkeit gewährleisten kann. Ich nehm normalerweise 60, damit das mit der Bildaktualisierungsrate übereinstimmt.
      Aber du kannst dir demnächst sowieso mal genau ansehen, wie ich geplant hatte, die Engine zu benutzen, ich sitz gerade an nem mehr oder weniger großen Beispielprojekt dran.
      Es hat sich mal wieder einiges getan.
      Da ich momentan an einer größeren Beispielanwendung arbeite, bin ich auf viele Unzulänglichkeiten in der Engine gestoßen. Deswegen habe ich den kompletten Engine-Core neu geschrieben und dies ausgemerzt, die Engine sollte nun wesentlich einfacher zu benutzen sein. Am wichtigsten war hier wieder die Synchronität, die ich nun hoffe endlich erreicht zu haben, da bis vor kurzen trotz meiner vorherigen Bemühungen massive Probleme dadurch entstanden.
      Auch andere Kleinigkeiten haben sich getan, so wurde z.B. das UI-Sybsystem noch einmal etwas überarbeitet und das Rendering-Sybsystem hat mal wieder ein paar Zusätze spendiert bekommen.
      Update 8 ist draußen.
      Es gibt jetzt ein Audio-Subsystem, für das eine Standardimplementierung auf Basis von @thefiloe: 's CSCore vorhanden ist.
      Ich hab auch noch mal etwas am Enginecore geschraubt und alles enger miteinander verknüpft.
      Der Bug, dass mehrere Tastatureingaben schnell hintereinander zum Absturz führten, ist jetzt wie versprochen auch weg.

      Fill the Row werde ich auch gleich noch updaten, dauert nur immer etwas mit dem Hochladen. ;)

      Erster offizieller Release - GameUtils Alpha 1.0

      Die Alpha 1.0 ist endlich fertig, die Engine ist nun an einem Punkt, an dem ich sie als "benutzbar" einstufen würde. Bitte beachtet aber, dass trotzdem noch große Änderungen kommen können, und ihr vielleicht lieber noch bis zur Beta warten wollt (wenn ihr die Engine denn überhaupt benutzen wollt :P).
      Von nun an könnt ihr alle Komponenten auch einzeln von NuGet runterladen, Links dafür sind im Startpost. Auch Tutorials werden vermutlich demnächst kommen.

      Fröhliche Weihnachten noch euch allen. :)